@omnitend/dashboard-for-laravel 0.40.0 → 0.41.1

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 (112) hide show
  1. package/dist/charts.d.ts +19 -0
  2. package/dist/components/base/DAccordion.vue.d.ts +12 -0
  3. package/dist/components/base/DAccordionItem.vue.d.ts +12 -0
  4. package/dist/components/base/DAlert.vue.d.ts +17 -0
  5. package/dist/components/base/DAspect.vue.d.ts +12 -0
  6. package/dist/components/base/DAutocomplete.vue.d.ts +32 -0
  7. package/dist/components/base/DAvatar.vue.d.ts +12 -0
  8. package/dist/components/base/DBadge.vue.d.ts +12 -0
  9. package/dist/components/base/DBreadcrumb.vue.d.ts +12 -0
  10. package/dist/components/base/DButton.vue.d.ts +97 -0
  11. package/dist/components/base/DButtonGroup.vue.d.ts +12 -0
  12. package/dist/components/base/DButtonToolbar.vue.d.ts +12 -0
  13. package/dist/components/base/DCard.vue.d.ts +12 -0
  14. package/dist/components/base/DCol.vue.d.ts +12 -0
  15. package/dist/components/base/DCollapse.vue.d.ts +12 -0
  16. package/dist/components/base/DContainer.vue.d.ts +12 -0
  17. package/dist/components/base/DDropdown.vue.d.ts +12 -0
  18. package/dist/components/base/DDropdownDivider.vue.d.ts +2 -0
  19. package/dist/components/base/DDropdownItem.vue.d.ts +12 -0
  20. package/dist/components/base/DForm.vue.d.ts +12 -0
  21. package/dist/components/base/DFormCheckbox.vue.d.ts +12 -0
  22. package/dist/components/base/DFormCheckboxGroup.vue.d.ts +12 -0
  23. package/dist/components/base/DFormGroup.vue.d.ts +12 -0
  24. package/dist/components/base/DFormInput.vue.d.ts +2 -0
  25. package/dist/components/base/DFormInvalidFeedback.vue.d.ts +12 -0
  26. package/dist/components/base/DFormOtp.vue.d.ts +12 -0
  27. package/dist/components/base/DFormRadio.vue.d.ts +12 -0
  28. package/dist/components/base/DFormRadioGroup.vue.d.ts +12 -0
  29. package/dist/components/base/DFormSelect.vue.d.ts +27 -0
  30. package/dist/components/base/DFormSpinbutton.vue.d.ts +12 -0
  31. package/dist/components/base/DFormTags.vue.d.ts +12 -0
  32. package/dist/components/base/DFormText.vue.d.ts +12 -0
  33. package/dist/components/base/DFormTextarea.vue.d.ts +2 -0
  34. package/dist/components/base/DImage.vue.d.ts +12 -0
  35. package/dist/components/base/DInputGroup.vue.d.ts +12 -0
  36. package/dist/components/base/DLink.vue.d.ts +12 -0
  37. package/dist/components/base/DListGroup.vue.d.ts +12 -0
  38. package/dist/components/base/DListGroupItem.vue.d.ts +12 -0
  39. package/dist/components/base/DModal.vue.d.ts +12 -0
  40. package/dist/components/base/DNav.vue.d.ts +12 -0
  41. package/dist/components/base/DNavItem.vue.d.ts +12 -0
  42. package/dist/components/base/DNavbar.vue.d.ts +12 -0
  43. package/dist/components/base/DNavbarBrand.vue.d.ts +12 -0
  44. package/dist/components/base/DNavbarNav.vue.d.ts +12 -0
  45. package/dist/components/base/DNavbarToggle.vue.d.ts +12 -0
  46. package/dist/components/base/DOffcanvas.vue.d.ts +12 -0
  47. package/dist/components/base/DOverlay.vue.d.ts +12 -0
  48. package/dist/components/base/DPagination.vue.d.ts +2 -0
  49. package/dist/components/base/DPlaceholder.vue.d.ts +12 -0
  50. package/dist/components/base/DPopover.vue.d.ts +12 -0
  51. package/dist/components/base/DProgress.vue.d.ts +12 -0
  52. package/dist/components/base/DRow.vue.d.ts +12 -0
  53. package/dist/components/base/DSpinner.vue.d.ts +2 -0
  54. package/dist/components/base/DTable.vue.d.ts +34 -0
  55. package/dist/components/base/DTabs.vue.d.ts +12 -0
  56. package/dist/components/base/DToast.vue.d.ts +12 -0
  57. package/dist/components/base/DToaster.vue.d.ts +12 -0
  58. package/dist/components/base/DTooltip.vue.d.ts +12 -0
  59. package/dist/components/charts/DXBarChart.vue.d.ts +7 -0
  60. package/dist/components/charts/DXDoughnutChart.vue.d.ts +24 -0
  61. package/dist/components/charts/DXLineChart.vue.d.ts +7 -0
  62. package/dist/components/charts/chartTheme.d.ts +59 -0
  63. package/dist/components/charts/useThemedChart.d.ts +40 -0
  64. package/dist/components/extended/DXBasicForm.vue.d.ts +10 -0
  65. package/dist/components/extended/DXChoiceField.vue.d.ts +26 -0
  66. package/dist/components/extended/DXCurrencyInput.vue.d.ts +42 -0
  67. package/dist/components/extended/DXDashboard.vue.d.ts +105 -0
  68. package/dist/components/extended/DXDashboardNavbar.vue.d.ts +86 -0
  69. package/dist/components/extended/DXDashboardSidebar.vue.d.ts +61 -0
  70. package/dist/components/extended/DXField.vue.d.ts +86 -0
  71. package/dist/components/extended/DXFieldLabel.vue.d.ts +18 -0
  72. package/dist/components/extended/DXFieldShell.vue.d.ts +73 -0
  73. package/dist/components/extended/DXForm.vue.d.ts +112 -0
  74. package/dist/components/extended/DXFormField.vue.d.ts +41 -0
  75. package/dist/components/extended/DXNumericField.vue.d.ts +23 -0
  76. package/dist/components/extended/DXRepeater.vue.d.ts +46 -0
  77. package/dist/components/extended/DXRepeaterCards.vue.d.ts +45 -0
  78. package/dist/components/extended/DXStatCard.vue.d.ts +56 -0
  79. package/dist/components/extended/DXSwitch.vue.d.ts +47 -0
  80. package/dist/components/extended/DXTable.vue.d.ts +495 -0
  81. package/dist/components/extended/DXTableEditorModal.vue.d.ts +100 -0
  82. package/dist/components/extended/DXTablePagination.vue.d.ts +50 -0
  83. package/dist/components/extended/DXTableShell.vue.d.ts +24 -0
  84. package/dist/components/extended/DXUserAvatar.vue.d.ts +40 -0
  85. package/dist/components/extended/dxFieldContext.d.ts +11 -0
  86. package/dist/composables/defineForm.d.ts +41 -0
  87. package/dist/composables/useAsyncOptions.d.ts +20 -0
  88. package/dist/composables/useContainerWidth.d.ts +96 -0
  89. package/dist/composables/useForm.d.ts +46 -0
  90. package/dist/composables/useResourceEditor.d.ts +77 -0
  91. package/dist/composables/useToast.d.ts +2 -0
  92. package/dist/dashboard-for-laravel.js +4166 -4156
  93. package/dist/dashboard-for-laravel.js.map +1 -1
  94. package/dist/dashboard-for-laravel.umd.cjs +5 -5
  95. package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
  96. package/dist/index.d.ts +93 -0
  97. package/dist/style.css +2 -2
  98. package/dist/types/index.d.ts +477 -0
  99. package/dist/types/navigation.d.ts +43 -0
  100. package/dist/utils/api.d.ts +30 -0
  101. package/dist/utils/formSchema.d.ts +59 -0
  102. package/dist/utils/objectPath.d.ts +18 -0
  103. package/docs/public/api-reference.json +26 -5
  104. package/docs/public/docs-map.md +1 -1
  105. package/package.json +1 -1
  106. package/resources/css/theme.scss +33 -26
  107. package/resources/js/components/base/DAutocomplete.vue +3 -3
  108. package/resources/js/components/base/DButton.vue +39 -5
  109. package/resources/js/components/extended/DXDashboardSidebar.vue +9 -5
  110. package/resources/js/components/extended/DXSwitch.vue +10 -7
  111. package/resources/js/components/extended/DXTable.vue +86 -88
  112. package/resources/js/composables/useResourceEditor.ts +41 -1
@@ -0,0 +1,47 @@
1
+ interface Props {
2
+ /** Whether the switch is on. */
3
+ modelValue?: boolean;
4
+ /** Static text beside the toggle; the fallback when no contextual text applies. */
5
+ label?: string;
6
+ /** Text shown when on — overrides `label` in the on state (e.g. "Product is current"). */
7
+ textWhenTrue?: string;
8
+ /** Text shown when off — overrides `label` in the off state (e.g. "Product is not current"). */
9
+ textWhenFalse?: string;
10
+ /**
11
+ * On-state colour. `"success"` (default) reads the house green-on / red-off
12
+ * style — an active/enabled/good switch ("Product is current", "Stock is
13
+ * tracked", "Visible"). `"neutral"` is for semantically-mixed switches
14
+ * ("contains alcohol", an allergen toggle, "hidden on web shop") that
15
+ * shouldn't imply good/bad — it uses the brand primary on / grey off (#158).
16
+ */
17
+ onVariant?: "success" | "neutral";
18
+ /**
19
+ * `"sm"` renders a compact box (a few px shorter than the default, which
20
+ * matches the input height) for dense rows that repeat the switch per item (a
21
+ * per-line visibility toggle in a modal). Omit for the standard size, which
22
+ * lines the switch up with the `.form-control`
23
+ * height so a switch lines up with inputs beside it.
24
+ */
25
+ size?: "sm";
26
+ }
27
+ declare var __VLS_4: {
28
+ on: boolean;
29
+ };
30
+ type __VLS_Slots = {} & {
31
+ default?: (props: typeof __VLS_4) => any;
32
+ };
33
+ declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
34
+ "update:modelValue": (value: boolean) => any;
35
+ }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
36
+ "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
37
+ }>, {
38
+ modelValue: boolean;
39
+ onVariant: "success" | "neutral";
40
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
41
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
42
+ export default _default;
43
+ type __VLS_WithSlots<T, S> = T & {
44
+ new (): {
45
+ $slots: S;
46
+ };
47
+ };
@@ -0,0 +1,495 @@
1
+ import type { FieldDefinition, LabelCols } from "../../types";
2
+ export type FilterType = 'text' | 'select' | 'select-native' | 'number' | 'date' | false;
3
+ export interface FilterOption {
4
+ value: string;
5
+ text: string;
6
+ }
7
+ /**
8
+ * Adapts the built-in `apiUrl` provider's request/response to a backend whose
9
+ * convention differs from dfl's (spatie query-builder params, an envelope
10
+ * without the `{data, pagination}` shape). The sanctioned replacement for the
11
+ * axios-interceptor bridges #132 removed the route for: `request` maps the
12
+ * outgoing params (its return is what goes on the wire); `response` maps the
13
+ * body back, receiving the ORIGINAL dfl-shape params (`page`, `perPage`, …)
14
+ * so it can synthesize paginator metadata a foreign envelope lacks.
15
+ */
16
+ export interface DXTableApiAdapter {
17
+ request?: (params: Record<string, any>) => Record<string, any>;
18
+ response?: (body: any, context: {
19
+ params: Record<string, any>;
20
+ }) => {
21
+ data: any[];
22
+ pagination?: PaginationData;
23
+ filterValues?: Record<string, string[]>;
24
+ };
25
+ }
26
+ export interface TableField {
27
+ key: string;
28
+ label?: string;
29
+ /**
30
+ * The key this column's filter is sent under, when it differs from the
31
+ * column's own key (default: `key`). Lets a *display* column filter on a
32
+ * different server param — e.g. a "Customer" column rendering a name but
33
+ * filtering on `customer_id` — instead of forcing the column to be named
34
+ * after the server's filter param and the human-facing value into a
35
+ * `#cell` slot.
36
+ */
37
+ filterKey?: string;
38
+ /**
39
+ * For a `select` filter: allow SEVERAL values at once (#51). The filter
40
+ * renders as a multi-select typeahead, the filters map holds an ARRAY of
41
+ * the chosen option values, and the query string carries Laravel bracket
42
+ * notation (`filters[status][]=active&filters[status][]=pending`).
43
+ * Client-side, a row matches when its value equals ANY chosen value. An
44
+ * empty selection means "no filter". The "All …" reset row is omitted in
45
+ * this mode (removing the chips/✕ is the way back); `filterNullText`
46
+ * stays available as a choosable value.
47
+ */
48
+ filterMultiple?: boolean;
49
+ /**
50
+ * For a `select` filter: adds an option meaning "has no value", e.g.
51
+ * `filterNullText: "Unassigned"` on an assignee column. Selecting it sends
52
+ * `filterNullValue` (default `"null"`) as the filter value; client-side, it
53
+ * matches rows whose value is null, undefined or empty.
54
+ */
55
+ filterNullText?: string;
56
+ /** The value sent when the `filterNullText` option is chosen. Default `"null"`. */
57
+ filterNullValue?: string;
58
+ /**
59
+ * Text of the "clear this filter" option at the top of a `select` filter's
60
+ * list (default: the filter's placeholder, e.g. "All statuses"). The list
61
+ * otherwise offers no way back to *unfiltered* once a value is picked —
62
+ * only the ✕, which isn't discoverable from inside the open dropdown.
63
+ */
64
+ filterAllText?: string;
65
+ sortable?: boolean;
66
+ hint?: string;
67
+ /**
68
+ * The inline filter control for this column, or `false`/omit for none. See
69
+ * `FilterType`. `'select'` (typeahead) and `'select-native'` (plain
70
+ * `<select>`) share the same options and value semantics — pick the native
71
+ * one when OS-native menu behaviour matters more than type-to-narrow.
72
+ */
73
+ filter?: FilterType;
74
+ filterOptions?: FilterOption[];
75
+ filterPlaceholder?: string;
76
+ /**
77
+ * For a `select` filter on a **client-side** table: derive the dropdown's
78
+ * options from the distinct values present in the loaded rows. Defaults to
79
+ * `true` — with no `filterOptions` and no server `filterValues` the
80
+ * alternative is an empty dropdown, which can only ever be a bug.
81
+ *
82
+ * Set `false` to opt out (e.g. a column whose raw values are opaque ids you
83
+ * would rather label yourself via `filterOptions`).
84
+ *
85
+ * Explicit `filterOptions` and server-supplied `filterValues` still WIN, in
86
+ * that order. Provider/API and Inertia modes ignore this flag: they only
87
+ * ever hold one page of rows, so there is no complete value set to derive
88
+ * from (see `getFieldFilterOptions`).
89
+ */
90
+ deriveFilterOptions?: boolean;
91
+ /**
92
+ * Fixed width for this column, applied via a `<colgroup>` `<col>`. A number
93
+ * is treated as pixels (`200` → `200px`); a string is used verbatim
94
+ * (`"20%"`, `"12rem"`). Pair with the table-level `fixedLayout` prop so the
95
+ * width is authoritative and the column stops resizing to fit its content —
96
+ * without `fixedLayout` a wider cell can still stretch the column (#156).
97
+ */
98
+ width?: string | number;
99
+ /**
100
+ * Minimum width for this column, applied via the same `<col>` as `width`.
101
+ * A number is pixels; a string is used verbatim.
102
+ */
103
+ minWidth?: string | number;
104
+ [key: string]: any;
105
+ }
106
+ export interface PaginationData {
107
+ current_page: number;
108
+ per_page: number;
109
+ total: number;
110
+ from: number;
111
+ to: number;
112
+ last_page?: number;
113
+ total_unfiltered?: number;
114
+ }
115
+ export interface BTableSortBy {
116
+ key: string;
117
+ order?: 'asc' | 'desc';
118
+ }
119
+ export interface BTableProviderContext {
120
+ sortBy?: BTableSortBy[];
121
+ filter?: string;
122
+ currentPage: number;
123
+ perPage: number;
124
+ }
125
+ export type BTableProvider<T = any> = (context: Readonly<BTableProviderContext>) => Promise<T[] | undefined> | T[] | undefined;
126
+ export interface EditTab {
127
+ /** Unique key for this tab */
128
+ key: string;
129
+ /**
130
+ * Display label (optional, auto-derived from key if omitted). May be a
131
+ * function of the model (the edited row merged with the live form data),
132
+ * e.g. `label: (item) => \`Products (${item.products_count ?? 0})\``.
133
+ */
134
+ label?: string | ((item: any) => string);
135
+ /** Field keys to display in this tab (from editFields) */
136
+ fieldKeys: string[];
137
+ /** Conditional display (optional) */
138
+ when?: boolean | ((item: any) => boolean);
139
+ /** Lazy load tab content (optional, default false) */
140
+ lazy?: boolean;
141
+ }
142
+ /**
143
+ * Discriminated data-source descriptor (#130). The additive, type-safe
144
+ * replacement for the legacy `items` / `provider` / `apiUrl` / `inertiaUrl` /
145
+ * `clientSide` prop matrix, in which every prop was independently optional and
146
+ * the mode was inferred by precedence — so invalid combinations compiled and
147
+ * some were silently ignored.
148
+ *
149
+ * Pass ONE `source`: the mode is explicit and its required companion prop is
150
+ * enforced by the compiler (`{ mode: 'provider' }` without a `provider` won't
151
+ * type-check). The legacy props still work and take over when `source` is
152
+ * omitted; a future major removes them with a codemod (#130 Phase 3).
153
+ *
154
+ * `apiAdapter` (transport translation) and `pagination` (server-supplied page
155
+ * metadata) stay sibling props — they're orthogonal to which mode is active.
156
+ */
157
+ export type DXTableSource<TItem = any> = {
158
+ mode: 'client';
159
+ items: TItem[];
160
+ } | {
161
+ mode: 'provider';
162
+ provider: BTableProvider<TItem>;
163
+ } | {
164
+ mode: 'api';
165
+ url: string;
166
+ } | {
167
+ mode: 'inertia';
168
+ items: TItem[];
169
+ url?: string;
170
+ };
171
+ export interface Props<TItem = any> {
172
+ /** Table title */
173
+ title?: string;
174
+ /** Name for item (singular) - automatically pluralized (e.g., "product" → "products") */
175
+ itemName?: string;
176
+ /** Table data items (Inertia mode) */
177
+ items?: TItem[];
178
+ /** Provider function for API mode (alternative to items/apiUrl) */
179
+ provider?: BTableProvider<TItem>;
180
+ /** API endpoint URL for auto-provider mode (alternative to provider function) */
181
+ apiUrl?: string;
182
+ /** Adapts the built-in provider's request params / response body to a
183
+ * backend convention that differs from dfl's (see DXTableApiAdapter). */
184
+ apiAdapter?: DXTableApiAdapter;
185
+ /**
186
+ * Discriminated data-source (#130) — the type-safe alternative to the
187
+ * legacy `items` / `provider` / `apiUrl` / `inertiaUrl` / `clientSide`
188
+ * props. When set it WINS over those props; when omitted, the legacy props
189
+ * apply unchanged. See `DXTableSource`.
190
+ */
191
+ source?: DXTableSource<TItem>;
192
+ /** Table field definitions */
193
+ fields: TableField[];
194
+ /** Sort configuration (v-model support) */
195
+ sortBy?: BTableSortBy[];
196
+ /** Filter values (v-model support) - key is field key, value is the filter
197
+ * string, or an ARRAY of values for a `filterMultiple` column (#51). */
198
+ filters?: Record<string, string | string[]>;
199
+ /** Dynamic filter options from server - key is field key, value is array of values */
200
+ filterValues?: Record<string, string[]>;
201
+ /** Inertia route URL (if provided, handles navigation automatically) */
202
+ inertiaUrl?: string;
203
+ /** Loading/busy state (v-model support) */
204
+ busy?: boolean;
205
+ /** Loading state (deprecated, use busy instead) */
206
+ loading?: boolean;
207
+ /** Loading text */
208
+ loadingText?: string;
209
+ /** Error message */
210
+ error?: string | null;
211
+ /** Pagination data (Inertia mode) */
212
+ pagination?: PaginationData;
213
+ /** Show pagination controls */
214
+ showPagination?: boolean;
215
+ /** Show per-page selector */
216
+ showPerPageSelector?: boolean;
217
+ /**
218
+ * Show the footer's item-count caption ("N items." / "X to Y out of Z
219
+ * items."). Defaults to `true`. Set `false` to suppress just the caption,
220
+ * independent of the pager — a page ported from a plain `<b-table>` that
221
+ * hides the pager with `:show-pagination="false"` would otherwise gain a
222
+ * caption it never had (#127).
223
+ */
224
+ showCount?: boolean;
225
+ /** Per-page options for selector */
226
+ perPageOptions?: number[];
227
+ /** Current page (for provider mode) */
228
+ currentPage?: number;
229
+ /**
230
+ * Items per page. With a `v-model:per-page` it is controlled state; a bare
231
+ * `:per-page="20"` is the INITIAL page size (see the initial-value vs
232
+ * controlled note above). When passed explicitly it takes PRECEDENCE over any
233
+ * per-page preference persisted in localStorage — `:per-page="20"` always
234
+ * starts at 20, never a size the user once picked on another table (#124).
235
+ * Omit it to let a stored preference (URL-keyed tables only) or the default
236
+ * apply.
237
+ */
238
+ perPage?: number;
239
+ /** Striped (banded) rows. Off by default — banding adds little on short
240
+ * tables and can read as noise; opt in with `:striped="true"`. */
241
+ striped?: boolean;
242
+ /** Hover effect on rows */
243
+ hover?: boolean;
244
+ /** Responsive table */
245
+ responsive?: boolean;
246
+ /** Fluid container */
247
+ fluid?: boolean;
248
+ /** Container CSS class */
249
+ containerClass?: string;
250
+ /** Column size (Bootstrap grid) */
251
+ columnSize?: string | number;
252
+ /**
253
+ * Wrap the table in a `DCard`. Defaults to `true`. Set `false` for a plain,
254
+ * borderless variant — the same header, filter row, table and pagination
255
+ * rendered directly on the page background (no card border/shadow), for
256
+ * data-heavy admin index pages that read as busy inside a card.
257
+ *
258
+ * The table renders FLUSH to the card border (rows and stripes reach the
259
+ * edge) while the header and pagination stay padded. Only the table region
260
+ * is rounded to follow the card's corners — the card itself does not clip
261
+ * (#166), so a `position: absolute` popover you render into `#header` or
262
+ * any other slot is free to overhang the card.
263
+ *
264
+ * The one place content is still clipped is INSIDE the table: Bootstrap's
265
+ * `.table-responsive` is a horizontal scroll container, so an overlay in a
266
+ * `cell(<key>)` slot is bounded by it. Teleport it (bvn's own menus already
267
+ * do — `DDropdown` teleports to `<body>`, the column-filter menus are
268
+ * `fixed`), or pass `:responsive="false"`.
269
+ */
270
+ card?: boolean;
271
+ /**
272
+ * Form field definitions for edit modal (if provided, enables edit on row
273
+ * click). `import type` is erased at compile time, so this creates no
274
+ * runtime import cycle (#131).
275
+ */
276
+ editFields?: FieldDefinition[];
277
+ /** Tab definitions for organizing edit modal content */
278
+ editTabs?: EditTab[];
279
+ /** Edit modal title (can be function for dynamic titles) */
280
+ editModalTitle?: string | ((item: any) => string);
281
+ /** Edit modal size */
282
+ editModalSize?: 'sm' | 'md' | 'lg' | 'xl';
283
+ /**
284
+ * Field layout for the edit/create modal's form, forwarded to its `DXForm`
285
+ * ("vertical" = label above input, "horizontal" = label left). Defaults to
286
+ * **"horizontal"** to match the Omni Tend form convention (a raw modal form
287
+ * previously fell back to DXForm's vertical default, which looked out of
288
+ * place next to horizontal page forms). Pass `"vertical"` to opt out.
289
+ *
290
+ * `"auto"` hands the decision to `DXForm`'s container-width observer, which
291
+ * measures the FORM rather than the window — so a modal narrowed by its own
292
+ * breakpoint stacks to vertical even on a wide viewport, where a media query
293
+ * would still report "plenty of room". See `useContainerWidth`.
294
+ */
295
+ editLayout?: 'vertical' | 'horizontal' | 'auto';
296
+ /**
297
+ * Label column width for the edit modal's horizontal layout, forwarded to
298
+ * `DXForm`'s `labelCols` (a single width or a per-breakpoint object). Only
299
+ * meaningful when `editLayout` is "horizontal".
300
+ */
301
+ editLabelCols?: LabelCols;
302
+ /**
303
+ * Wrap the edit/create modal's form in a bordered card. Off by default (the
304
+ * modal body already provides a boundary); opt in for a more contained,
305
+ * panelled look around a tabbed edit form.
306
+ */
307
+ editCard?: boolean;
308
+ /** Override the modal's Save button label (default: "Save {item}"). */
309
+ saveText?: string;
310
+ /** Override the modal's Create button label (default: "Create {item}"). */
311
+ createText?: string;
312
+ /** Override the modal's Delete button label (default: "Delete {item}"). */
313
+ deleteText?: string;
314
+ /** API endpoint pattern for updates (e.g., "/api/products/:id") */
315
+ editUrl?: string;
316
+ /**
317
+ * API endpoint pattern to fetch the FULL record for the edit modal
318
+ * (e.g., "/api/products/:id"). When set, opening a row seeds the form from
319
+ * this fetch (shown with a loading state) instead of the thin list row —
320
+ * use when the list payload omits fields the edit form needs (notes, nested
321
+ * relations, repeater rows, …). The response is unwrapped from `data.data`
322
+ * (Laravel API Resource) or used as-is.
323
+ */
324
+ showUrl?: string;
325
+ /** API endpoint pattern for deletions (e.g., "/api/products/:id") */
326
+ deleteUrl?: string;
327
+ /**
328
+ * Guard run when Delete is clicked, before the confirm dialog and request.
329
+ * Return a message for a non-deletable item to show it immediately (as a
330
+ * toast) and skip both the confirm and the delete request; return
331
+ * `null`/`undefined` to proceed with the normal confirm + delete. Lets you
332
+ * short-circuit a doomed delete (e.g. a record with dependents that the
333
+ * server would reject) with an immediate, specific reason.
334
+ *
335
+ * AWAITED, so it may return a promise — the same shape as `saveGuard`, so
336
+ * an `async` guard behaves the way its author expects. A guard that throws
337
+ * also aborts, surfacing its message.
338
+ */
339
+ deleteGuard?: (item: TItem) => string | null | undefined | Promise<string | null | undefined>;
340
+ /**
341
+ * Guard run when the modal's Save/Create is clicked, before the request.
342
+ * Return a message to abort the save — it is shown immediately (as a toast)
343
+ * and no request is made; return `null`/`undefined` to proceed. Covers both
344
+ * the `editUrl` and `createUrl` paths.
345
+ *
346
+ * AWAITED (as `deleteGuard` is), so it may return a promise: the case it
347
+ * exists for is an async control in an `edit-value` slot — an image
348
+ * upload — that has not finished when Save is clicked. Without the guard the
349
+ * form submits the previous media map, the save SUCCEEDS, the toast says
350
+ * saved and the modal closes: the image is lost with no error anywhere,
351
+ * because every individual step worked. Await the upload here and the save
352
+ * simply waits for it.
353
+ *
354
+ * `item` is the row being edited, `null` in create mode (as in the
355
+ * `edit-value` slot's binding); `data` is the live form data. Aborting
356
+ * leaves the modal open with the user's edits intact. A guard that throws
357
+ * also aborts, surfacing its message.
358
+ *
359
+ * Dismissing the modal while the guard is still pending — by any route —
360
+ * abandons the save; nothing is written for a record the user has closed.
361
+ */
362
+ saveGuard?: (item: TItem | null, data: Record<string, any>) => string | null | undefined | Promise<string | null | undefined>;
363
+ /** API endpoint for creating new items (e.g., "/api/products") — enables "New" button */
364
+ createUrl?: string;
365
+ /**
366
+ * Render the built-in "New {item}" button in the card header (default
367
+ * `true` whenever `createUrl` is set). Set `false` to drive the create
368
+ * modal from your own trigger elsewhere — e.g. a button in the dashboard
369
+ * navbar's actions slot calling the exposed `openCreate()`. With no
370
+ * `title` and no `header` slot, the header is then dropped entirely
371
+ * rather than left empty.
372
+ */
373
+ showCreateButton?: boolean;
374
+ /**
375
+ * Render a `<tfoot>` mirroring the header row, so `#foot(<key>)` slots can
376
+ * hold per-column totals that line up under their data (#99). Without it
377
+ * there is no footer to put them in. For a fully custom footer, use the
378
+ * `custom-foot` slot instead.
379
+ */
380
+ footClone?: boolean;
381
+ /**
382
+ * Show a message row when there are no rows (default `true`). An empty
383
+ * table body is indistinguishable from a broken one, so the message is on
384
+ * by default. Override the wording with `emptyText`, or the whole row with
385
+ * the `empty` slot.
386
+ */
387
+ showEmpty?: boolean;
388
+ /**
389
+ * Message shown when there are no rows. Defaults to "No {items} found", or
390
+ * "No {items} match your filters" when a column filter is active.
391
+ */
392
+ emptyText?: string;
393
+ /**
394
+ * Rows currently expanded, as a `v-model`. Pair with the `row-expansion`
395
+ * slot to render detail content under a row (#112) instead of forcing every
396
+ * per-row detail into a modal.
397
+ */
398
+ expandedItems?: TItem[];
399
+ /**
400
+ * Class(es) applied to each row's `<tr>` — a string, or a function of the
401
+ * row. Use for conditional row styling (a variant, a muted/disabled look)
402
+ * without reaching into the table's internal DOM from global CSS.
403
+ */
404
+ rowClass?: string | ((item: TItem, index: number) => string | string[] | Record<string, boolean>);
405
+ /**
406
+ * Whether a given row is actionable. Only relevant when rows are clickable
407
+ * (an `editFields` modal or a `row-clicked` listener). A row this returns
408
+ * `false` for gets no pointer cursor, no hover highlight, and does not fire
409
+ * `row-clicked` or open the edit modal — so a row that isn't clickable
410
+ * doesn't *look* clickable, and a click on it can't quietly do something.
411
+ */
412
+ rowClickable?: (item: TItem, index: number) => boolean;
413
+ /** Enable client-side filtering, sorting, and pagination on items array */
414
+ clientSide?: boolean;
415
+ /**
416
+ * Apply `table-layout: fixed` to the underlying table. Off by default, so
417
+ * existing tables keep their content-sized columns. When on, column widths
418
+ * stop depending on cell content, so they no longer reshuffle when a filter
419
+ * narrows the rows — the table stays put as you filter (#156). Combine with
420
+ * per-field `width`/`minWidth` to control the proportions; columns with no
421
+ * declared width share the remaining space equally.
422
+ */
423
+ fixedLayout?: boolean;
424
+ /**
425
+ * Field on each row whose value uniquely identifies it, forwarded to the
426
+ * inner bvn table's `primaryKey` (all three data modes). Without it bvn keys
427
+ * client-side rows by their INDEX, which mis-associates per-row STATEFUL cell
428
+ * components when a row is inserted/removed mid-interaction — a debounced
429
+ * edit in one row can end up saved against the wrong record after a
430
+ * concurrent delete of a row above. Set it (e.g. `primary-key="id"`) so bvn
431
+ * keys rows by that field's value and each row keeps its identity across
432
+ * data mutations. Non-breaking: omit it and rows key by index exactly as
433
+ * before.
434
+ */
435
+ primaryKey?: string;
436
+ }
437
+ declare const _default: <T = any>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
438
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
439
+ readonly onRowCreated?: ((item: any, response: any) => any) | undefined;
440
+ readonly onCreateError?: ((error: any) => any) | undefined;
441
+ readonly onRowUpdated?: ((item: T, response: any) => any) | undefined;
442
+ readonly onEditError?: ((item: T, error: any) => any) | undefined;
443
+ readonly onRowDeleted?: ((item: T, response: any) => any) | undefined;
444
+ readonly onDeleteError?: ((item: T, error: any) => any) | undefined;
445
+ readonly "onUpdate:sortBy"?: ((sortBy: BTableSortBy[]) => any) | undefined;
446
+ readonly "onUpdate:busy"?: ((busy: boolean) => any) | undefined;
447
+ readonly onRowClicked?: ((item: T, index: number, event: MouseEvent) => any) | undefined;
448
+ readonly "onUpdate:expandedItems"?: ((items: T[]) => any) | undefined;
449
+ readonly onPageChange?: ((page: number) => any) | undefined;
450
+ readonly onSortChange?: ((sort: {
451
+ key: string;
452
+ order: "asc" | "desc";
453
+ }) => any) | undefined;
454
+ readonly onFilterChange?: ((filters: Record<string, string | string[]>) => any) | undefined;
455
+ readonly onPerPageChange?: ((perPage: number) => any) | undefined;
456
+ readonly "onUpdate:filters"?: ((filters: Record<string, string | string[]>) => any) | undefined;
457
+ readonly "onUpdate:perPage"?: ((perPage: number) => any) | undefined;
458
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onUpdate:sortBy" | "onUpdate:busy" | "onRowClicked" | "onUpdate:expandedItems" | "onRowCreated" | "onCreateError" | "onRowUpdated" | "onEditError" | "onRowDeleted" | "onDeleteError" | "onPageChange" | "onSortChange" | "onFilterChange" | "onPerPageChange" | "onUpdate:filters" | "onUpdate:perPage"> & Props<T> & Partial<{}>> & import("vue").PublicProps;
459
+ expose(exposed: import("vue").ShallowUnwrapRef<{
460
+ refresh: () => void;
461
+ /**
462
+ * Open the built-in create modal (same as clicking the default "New {item}"
463
+ * button). Lets the create action live outside the table card — e.g. in a
464
+ * page header or the dashboard navbar. No-op unless `editFields` are set.
465
+ */
466
+ openCreate: () => void;
467
+ }>): void;
468
+ attrs: any;
469
+ slots: {
470
+ [x: `head-end(${string})`]: ((props: {
471
+ field: TableField;
472
+ label: string;
473
+ }) => any) | undefined;
474
+ } & {
475
+ [x: string]: ((props: any) => any) | undefined;
476
+ } & {
477
+ [x: string]: ((props: any) => any) | undefined;
478
+ } & {
479
+ header?: (props: {}) => any;
480
+ } & {
481
+ 'thead-top'?: (props: any) => any;
482
+ } & {
483
+ 'thead-sub'?: (props: any) => any;
484
+ };
485
+ emit: ((evt: "rowCreated", item: any, response: any) => void) & ((evt: "createError", error: any) => void) & ((evt: "rowUpdated", item: T, response: any) => void) & ((evt: "editError", item: T, error: any) => void) & ((evt: "rowDeleted", item: T, response: any) => void) & ((evt: "deleteError", item: T, error: any) => void) & ((evt: "update:sortBy", sortBy: BTableSortBy[]) => void) & ((evt: "update:busy", busy: boolean) => void) & ((evt: "rowClicked", item: T, index: number, event: MouseEvent) => void) & ((evt: "update:expandedItems", items: T[]) => void) & ((evt: "pageChange", page: number) => void) & ((evt: "sortChange", sort: {
486
+ key: string;
487
+ order: "asc" | "desc";
488
+ }) => void) & ((evt: "filterChange", filters: Record<string, string | string[]>) => void) & ((evt: "perPageChange", perPage: number) => void) & ((evt: "update:filters", filters: Record<string, string | string[]>) => void) & ((evt: "update:perPage", perPage: number) => void);
489
+ }>) => import("vue").VNode & {
490
+ __ctx?: Awaited<typeof __VLS_setup>;
491
+ };
492
+ export default _default;
493
+ type __VLS_PrettifyLocal<T> = {
494
+ [K in keyof T]: T[K];
495
+ } & {};
@@ -0,0 +1,100 @@
1
+ import type { EditTab } from "./DXTable.vue";
2
+ import type { LabelCols } from "../../types";
3
+ interface Props {
4
+ /** Whether the modal is open (v-model:show). */
5
+ show: boolean;
6
+ /** Modal title. */
7
+ title: string;
8
+ /** Modal size. */
9
+ size?: 'sm' | 'md' | 'lg' | 'xl';
10
+ /** True while the full record is loading via showUrl. */
11
+ loading: boolean;
12
+ /** The edit/create form (a useForm instance), or null before first open. */
13
+ form: any;
14
+ /** Bumped per modal open to remount the DXForm subtree (fresh DXField state). */
15
+ formInstanceKey: number;
16
+ /** Active tab index (v-model:active-tab). */
17
+ activeTab: number;
18
+ /** Field definitions passed to DXForm. */
19
+ fields: any[];
20
+ /** Tab definitions passed to DXForm. */
21
+ tabs?: EditTab[];
22
+ /** The row being edited (null in create mode) — the slot/context row. */
23
+ item: any;
24
+ /** Whether the modal is in create mode (vs edit). */
25
+ isCreateMode: boolean;
26
+ /** Which action is in flight, so Save/Delete show their own loading label. */
27
+ pendingAction: 'save' | 'delete' | null;
28
+ /** Delete endpoint — gates the Delete button. */
29
+ deleteUrl?: string;
30
+ /**
31
+ * Field layout forwarded to the modal's DXForm (defaults handled upstream).
32
+ * Includes `"auto"`, which lets the form stack to vertical when the MODAL is
33
+ * narrow even though the viewport is wide — the container-query case a
34
+ * viewport media query cannot see.
35
+ */
36
+ layout?: 'vertical' | 'horizontal' | 'auto';
37
+ /** Label column width forwarded to DXForm (horizontal layout). */
38
+ labelCols?: LabelCols;
39
+ /** Wrap the modal form in a card (opt-in — off by default). */
40
+ card?: boolean;
41
+ /** Singular item noun for the default button labels (e.g. "customer"). */
42
+ itemName?: string;
43
+ /** Override the Save button label (edit mode). */
44
+ saveText?: string;
45
+ /** Override the Create button label (create mode). */
46
+ createText?: string;
47
+ /** Override the Delete button label. */
48
+ deleteText?: string;
49
+ }
50
+ declare var __VLS_21: `edit-value(${string})`, __VLS_22: {
51
+ item: any;
52
+ value: any;
53
+ update: any;
54
+ field: any;
55
+ }, __VLS_25: `edit-span(${string})`, __VLS_26: {
56
+ item: any;
57
+ value: any;
58
+ update: any;
59
+ close: () => void;
60
+ }, __VLS_29: `tab-content(${string})`, __VLS_30: {
61
+ item: any;
62
+ tab: any;
63
+ }, __VLS_33: `tab-before(${string})`, __VLS_34: {
64
+ item: any;
65
+ tab: any;
66
+ }, __VLS_37: `tab-after(${string})`, __VLS_38: {
67
+ item: any;
68
+ tab: any;
69
+ };
70
+ type __VLS_Slots = {} & {
71
+ [K in NonNullable<typeof __VLS_21>]?: (props: typeof __VLS_22) => any;
72
+ } & {
73
+ [K in NonNullable<typeof __VLS_25>]?: (props: typeof __VLS_26) => any;
74
+ } & {
75
+ [K in NonNullable<typeof __VLS_29>]?: (props: typeof __VLS_30) => any;
76
+ } & {
77
+ [K in NonNullable<typeof __VLS_33>]?: (props: typeof __VLS_34) => any;
78
+ } & {
79
+ [K in NonNullable<typeof __VLS_37>]?: (props: typeof __VLS_38) => any;
80
+ };
81
+ declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
82
+ cancel: () => any;
83
+ delete: () => any;
84
+ "update:activeTab": (value: number) => any;
85
+ save: () => any;
86
+ "update:show": (value: boolean) => any;
87
+ }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
88
+ onCancel?: (() => any) | undefined;
89
+ onDelete?: (() => any) | undefined;
90
+ "onUpdate:activeTab"?: ((value: number) => any) | undefined;
91
+ onSave?: (() => any) | undefined;
92
+ "onUpdate:show"?: ((value: boolean) => any) | undefined;
93
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
94
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
95
+ export default _default;
96
+ type __VLS_WithSlots<T, S> = T & {
97
+ new (): {
98
+ $slots: S;
99
+ };
100
+ };