@omnitend/dashboard-for-laravel 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/extended/DXForm.vue.d.ts +28 -6
- package/dist/components/extended/DXTable.vue.d.ts +39 -1
- package/dist/components/extended/DXTableEditorModal.vue.d.ts +7 -2
- package/dist/composables/useContainerWidth.d.ts +96 -0
- package/dist/dashboard-for-laravel.js +8138 -7998
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +7 -7
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/style.css +1 -1
- package/docs/public/api-reference.json +40 -7
- package/docs/public/docs-map.md +1 -1
- package/package.json +1 -1
- package/resources/css/theme.scss +80 -2
- package/resources/js/components/extended/DXDashboardSidebar.vue +1 -1
- package/resources/js/components/extended/DXForm.vue +83 -9
- package/resources/js/components/extended/DXTable.vue +217 -12
- package/resources/js/components/extended/DXTableEditorModal.vue +7 -2
- package/resources/js/components/extended/DXTablePagination.vue +19 -3
- package/resources/js/components/extended/DXTableShell.vue +70 -1
- package/resources/js/composables/useContainerWidth.ts +303 -0
- package/resources/js/index.ts +7 -0
|
@@ -44,12 +44,33 @@ interface Props {
|
|
|
44
44
|
*/
|
|
45
45
|
cardTabs?: boolean;
|
|
46
46
|
/**
|
|
47
|
-
* Form-wide field layout:
|
|
48
|
-
*
|
|
49
|
-
* `
|
|
50
|
-
*
|
|
47
|
+
* Form-wide field layout:
|
|
48
|
+
*
|
|
49
|
+
* - `"vertical"` (default) — label above input, always.
|
|
50
|
+
* - `"horizontal"` — label left, input right, always.
|
|
51
|
+
* - `"auto"` — horizontal when the form's **own container** is at least
|
|
52
|
+
* `layoutThreshold` px wide, vertical below that. Container-driven, not
|
|
53
|
+
* viewport-driven: a page narrowed by the dashboard sidebar, or a form
|
|
54
|
+
* inside a modal, stacks even though the window is wide (which no
|
|
55
|
+
* Bootstrap media query can see).
|
|
56
|
+
*
|
|
57
|
+
* Overridable per-field via `field.layout`. A field with `span: true`
|
|
58
|
+
* always renders full-width, regardless of layout.
|
|
51
59
|
*/
|
|
52
|
-
layout?: "vertical" | "horizontal";
|
|
60
|
+
layout?: "vertical" | "horizontal" | "auto";
|
|
61
|
+
/**
|
|
62
|
+
* Container width (px) at or above which `layout: "auto"` goes horizontal.
|
|
63
|
+
* Ignored for the explicit `"vertical"`/`"horizontal"` layouts.
|
|
64
|
+
*
|
|
65
|
+
* Default 640, measured rather than guessed: with the default 3-column
|
|
66
|
+
* label the label's text area is `containerWidth / 4 - 18` px, so 640 gives
|
|
67
|
+
* it 142px — enough for a ~20-character label ("Unit price (ex VAT)"
|
|
68
|
+
* measures 128px at the theme's label font) to stay on one line, with the
|
|
69
|
+
* control column still 474px. Below ~584px that label starts wrapping,
|
|
70
|
+
* which is the cramped-label symptom this exists to avoid. Raise it if your
|
|
71
|
+
* labels run longer, or lower `labelCols` instead.
|
|
72
|
+
*/
|
|
73
|
+
layoutThreshold?: number;
|
|
53
74
|
/**
|
|
54
75
|
* Label column width for horizontal layout (mirrors BFormGroup's
|
|
55
76
|
* `labelCols`/`labelCols*` props). Overridable per-field via
|
|
@@ -73,13 +94,14 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps,
|
|
|
73
94
|
onSubmit?: (() => any) | undefined;
|
|
74
95
|
"onUpdate:activeTab"?: ((value: number) => any) | undefined;
|
|
75
96
|
}>, {
|
|
76
|
-
layout: "vertical" | "horizontal";
|
|
97
|
+
layout: "vertical" | "horizontal" | "auto";
|
|
77
98
|
card: boolean;
|
|
78
99
|
submitText: string;
|
|
79
100
|
submitLoadingText: string;
|
|
80
101
|
showSubmit: boolean;
|
|
81
102
|
autoErrorTab: boolean;
|
|
82
103
|
cardTabs: boolean;
|
|
104
|
+
layoutThreshold: number;
|
|
83
105
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
84
106
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
85
107
|
export default _default;
|
|
@@ -67,6 +67,21 @@ export interface TableField {
|
|
|
67
67
|
filter?: FilterType;
|
|
68
68
|
filterOptions?: FilterOption[];
|
|
69
69
|
filterPlaceholder?: string;
|
|
70
|
+
/**
|
|
71
|
+
* For a `select` filter on a **client-side** table: derive the dropdown's
|
|
72
|
+
* options from the distinct values present in the loaded rows. Defaults to
|
|
73
|
+
* `true` — with no `filterOptions` and no server `filterValues` the
|
|
74
|
+
* alternative is an empty dropdown, which can only ever be a bug.
|
|
75
|
+
*
|
|
76
|
+
* Set `false` to opt out (e.g. a column whose raw values are opaque ids you
|
|
77
|
+
* would rather label yourself via `filterOptions`).
|
|
78
|
+
*
|
|
79
|
+
* Explicit `filterOptions` and server-supplied `filterValues` still WIN, in
|
|
80
|
+
* that order. Provider/API and Inertia modes ignore this flag: they only
|
|
81
|
+
* ever hold one page of rows, so there is no complete value set to derive
|
|
82
|
+
* from (see `getFieldFilterOptions`).
|
|
83
|
+
*/
|
|
84
|
+
deriveFilterOptions?: boolean;
|
|
70
85
|
/**
|
|
71
86
|
* Fixed width for this column, applied via a `<colgroup>` `<col>`. A number
|
|
72
87
|
* is treated as pixels (`200` → `200px`); a string is used verbatim
|
|
@@ -233,6 +248,19 @@ export interface Props<TItem = any> {
|
|
|
233
248
|
* borderless variant — the same header, filter row, table and pagination
|
|
234
249
|
* rendered directly on the page background (no card border/shadow), for
|
|
235
250
|
* data-heavy admin index pages that read as busy inside a card.
|
|
251
|
+
*
|
|
252
|
+
* The table renders FLUSH to the card border (rows and stripes reach the
|
|
253
|
+
* edge) while the header and pagination stay padded, which requires the card
|
|
254
|
+
* to clip to its radius (`overflow: hidden`).
|
|
255
|
+
*
|
|
256
|
+
* **Consequence for slot content:** anything you render into a slot that
|
|
257
|
+
* positions itself OUTSIDE the card and does not teleport is clipped at the
|
|
258
|
+
* card edge. Library components are unaffected — `DDropdown` teleports to
|
|
259
|
+
* `<body>` by default, and the column-filter menus position themselves
|
|
260
|
+
* `fixed` — so this only bites a hand-rolled `position: absolute` overlay
|
|
261
|
+
* (a custom popover in a `cell(<key>)` or `#header` slot). Teleport it, or
|
|
262
|
+
* use `:card="false"`. Narrowing the clip to the table region is tracked
|
|
263
|
+
* as #166.
|
|
236
264
|
*/
|
|
237
265
|
card?: boolean;
|
|
238
266
|
/**
|
|
@@ -253,8 +281,13 @@ export interface Props<TItem = any> {
|
|
|
253
281
|
* **"horizontal"** to match the Omni Tend form convention (a raw modal form
|
|
254
282
|
* previously fell back to DXForm's vertical default, which looked out of
|
|
255
283
|
* place next to horizontal page forms). Pass `"vertical"` to opt out.
|
|
284
|
+
*
|
|
285
|
+
* `"auto"` hands the decision to `DXForm`'s container-width observer, which
|
|
286
|
+
* measures the FORM rather than the window — so a modal narrowed by its own
|
|
287
|
+
* breakpoint stacks to vertical even on a wide viewport, where a media query
|
|
288
|
+
* would still report "plenty of room". See `useContainerWidth`.
|
|
256
289
|
*/
|
|
257
|
-
editLayout?: 'vertical' | 'horizontal';
|
|
290
|
+
editLayout?: 'vertical' | 'horizontal' | 'auto';
|
|
258
291
|
/**
|
|
259
292
|
* Label column width for the edit modal's horizontal layout, forwarded to
|
|
260
293
|
* `DXForm`'s `labelCols` (a single width or a per-breakpoint object). Only
|
|
@@ -390,6 +423,11 @@ declare const _default: <T = any>(__VLS_props: NonNullable<Awaited<typeof __VLS_
|
|
|
390
423
|
}>): void;
|
|
391
424
|
attrs: any;
|
|
392
425
|
slots: {
|
|
426
|
+
[x: `head-end(${string})`]: ((props: {
|
|
427
|
+
field: TableField;
|
|
428
|
+
label: string;
|
|
429
|
+
}) => any) | undefined;
|
|
430
|
+
} & {
|
|
393
431
|
[x: string]: ((props: any) => any) | undefined;
|
|
394
432
|
} & {
|
|
395
433
|
[x: string]: ((props: any) => any) | undefined;
|
|
@@ -27,8 +27,13 @@ interface Props {
|
|
|
27
27
|
pendingAction: 'save' | 'delete' | null;
|
|
28
28
|
/** Delete endpoint — gates the Delete button. */
|
|
29
29
|
deleteUrl?: string;
|
|
30
|
-
/**
|
|
31
|
-
|
|
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';
|
|
32
37
|
/** Label column width forwarded to DXForm (horizontal layout). */
|
|
33
38
|
labelCols?: LabelCols;
|
|
34
39
|
/** Wrap the modal form in a card (opt-in — off by default). */
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useContainerWidth — container-query-style responsiveness for Vue components.
|
|
3
|
+
*
|
|
4
|
+
* Bootstrap's breakpoints are **media** queries: they only ever see the WINDOW.
|
|
5
|
+
* A dashboard page is narrowed by the persistent sidebar (and a form inside a
|
|
6
|
+
* modal is narrower still), so a component can have very little room while the
|
|
7
|
+
* viewport is wide — and no media query ever fires. This composable closes that
|
|
8
|
+
* gap by observing an element's **own rendered width** with a `ResizeObserver`
|
|
9
|
+
* and exposing it reactively, so the template can pick a layout from the space
|
|
10
|
+
* it actually has.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const { containerRef, width, isBelow, isNarrowerThan } = useContainerWidth({
|
|
14
|
+
* threshold: 640,
|
|
15
|
+
* });
|
|
16
|
+
* // <div ref="containerRef"> … </div>
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* One observer can drive several independent decisions — `isBelow` uses the
|
|
20
|
+
* configured `threshold`, and `isNarrowerThan(n)` answers for any other width:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const stacked = computed(() => isNarrowerThan(576));
|
|
24
|
+
* const hideDetailColumn = computed(() => isNarrowerThan(900));
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import { type Ref } from "vue";
|
|
28
|
+
/**
|
|
29
|
+
* What to observe: a template ref, or a getter (so the element can be resolved
|
|
30
|
+
* lazily / conditionally — e.g. `() => enabled ? el.value : null` to observe
|
|
31
|
+
* nothing at all).
|
|
32
|
+
*/
|
|
33
|
+
export type ContainerWidthTarget = Ref<HTMLElement | null | undefined> | (() => HTMLElement | null | undefined);
|
|
34
|
+
export interface UseContainerWidthOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Width (px) that `isBelow` compares against. Omit if you only need
|
|
37
|
+
* `width` / `isNarrowerThan`. Pass a getter (`() => props.threshold`) when
|
|
38
|
+
* it can change at runtime — `isBelow` re-evaluates when it does.
|
|
39
|
+
*/
|
|
40
|
+
threshold?: number | (() => number | undefined);
|
|
41
|
+
/**
|
|
42
|
+
* Hysteresis band (px) applied to `isBelow` only, to stop a layout flipping
|
|
43
|
+
* back and forth at the boundary. Once `isBelow` is true it stays true
|
|
44
|
+
* until the width reaches `threshold + hysteresis`.
|
|
45
|
+
*
|
|
46
|
+
* Defaults to `0` so `isBelow` means exactly `width < threshold` unless you
|
|
47
|
+
* ask for otherwise. Opt in whenever the layout you drive from it can
|
|
48
|
+
* change the observed width — the classic loop is: narrow layout is taller
|
|
49
|
+
* → an ancestor with `overflow:auto` gains a vertical scrollbar → the
|
|
50
|
+
* container loses ~15-17px → it crosses back over the threshold → repeat.
|
|
51
|
+
* A band wider than a scrollbar (24px is comfortable) breaks that cycle.
|
|
52
|
+
* (`isNarrowerThan` is deliberately raw — it holds no per-width state.)
|
|
53
|
+
*/
|
|
54
|
+
hysteresis?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Width assumed before the first measurement, and permanently where there
|
|
57
|
+
* is no `ResizeObserver` (SSR, or a very old browser).
|
|
58
|
+
*
|
|
59
|
+
* Defaults to `0` — i.e. "assume the narrowest until proven otherwise", so
|
|
60
|
+
* an unmeasured render picks the STACKED layout. That is the mobile-first
|
|
61
|
+
* choice and the safe one: a stacked layout is legible at every width,
|
|
62
|
+
* whereas a wide layout guessed wrongly truncates content. It also means
|
|
63
|
+
* the server-rendered markup matches the first client render.
|
|
64
|
+
*/
|
|
65
|
+
initialWidth?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Which box to report. `content-box` (default) is the space actually
|
|
68
|
+
* available to children — what a layout decision usually wants.
|
|
69
|
+
* `border-box` matches `getBoundingClientRect().width`.
|
|
70
|
+
*/
|
|
71
|
+
box?: "content-box" | "border-box";
|
|
72
|
+
/**
|
|
73
|
+
* Extra debounce (ms) before publishing a new width. Defaults to `0`:
|
|
74
|
+
* `ResizeObserver` already batches to one delivery per frame, and the
|
|
75
|
+
* callback reads the size the observer *hands* it (no forced layout), so
|
|
76
|
+
* there is nothing to thrash. Set it only for genuinely expensive
|
|
77
|
+
* downstream work — a timer adds a frame of visible lag.
|
|
78
|
+
*/
|
|
79
|
+
debounce?: number;
|
|
80
|
+
}
|
|
81
|
+
export interface UseContainerWidthReturn {
|
|
82
|
+
/** Bind to the element you want measured (`<div ref="containerRef">`). */
|
|
83
|
+
containerRef: Ref<HTMLElement | null>;
|
|
84
|
+
/** The observed width in px. `initialWidth` until first measured. */
|
|
85
|
+
width: Readonly<Ref<number>>;
|
|
86
|
+
/** False until a real measurement has landed (always false without a `ResizeObserver`). */
|
|
87
|
+
hasMeasured: Readonly<Ref<boolean>>;
|
|
88
|
+
/** `width < threshold`, with `hysteresis` applied. False when no `threshold` was given. */
|
|
89
|
+
isBelow: Readonly<Ref<boolean>>;
|
|
90
|
+
/** Raw `width < px`, for driving other decisions off the same observer. */
|
|
91
|
+
isNarrowerThan: (px: number) => boolean;
|
|
92
|
+
/** Disconnect early. Called automatically when the owning scope is disposed. */
|
|
93
|
+
stop: () => void;
|
|
94
|
+
}
|
|
95
|
+
export declare function useContainerWidth(options?: UseContainerWidthOptions): UseContainerWidthReturn;
|
|
96
|
+
export declare function useContainerWidth(target: ContainerWidthTarget, options?: UseContainerWidthOptions): UseContainerWidthReturn;
|