@omnitend/dashboard-for-laravel 0.40.0 → 0.41.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/charts.d.ts +19 -0
- package/dist/components/base/DAccordion.vue.d.ts +12 -0
- package/dist/components/base/DAccordionItem.vue.d.ts +12 -0
- package/dist/components/base/DAlert.vue.d.ts +17 -0
- package/dist/components/base/DAspect.vue.d.ts +12 -0
- package/dist/components/base/DAutocomplete.vue.d.ts +32 -0
- package/dist/components/base/DAvatar.vue.d.ts +12 -0
- package/dist/components/base/DBadge.vue.d.ts +12 -0
- package/dist/components/base/DBreadcrumb.vue.d.ts +12 -0
- package/dist/components/base/DButton.vue.d.ts +88 -0
- package/dist/components/base/DButtonGroup.vue.d.ts +12 -0
- package/dist/components/base/DButtonToolbar.vue.d.ts +12 -0
- package/dist/components/base/DCard.vue.d.ts +12 -0
- package/dist/components/base/DCol.vue.d.ts +12 -0
- package/dist/components/base/DCollapse.vue.d.ts +12 -0
- package/dist/components/base/DContainer.vue.d.ts +12 -0
- package/dist/components/base/DDropdown.vue.d.ts +12 -0
- package/dist/components/base/DDropdownDivider.vue.d.ts +2 -0
- package/dist/components/base/DDropdownItem.vue.d.ts +12 -0
- package/dist/components/base/DForm.vue.d.ts +12 -0
- package/dist/components/base/DFormCheckbox.vue.d.ts +12 -0
- package/dist/components/base/DFormCheckboxGroup.vue.d.ts +12 -0
- package/dist/components/base/DFormGroup.vue.d.ts +12 -0
- package/dist/components/base/DFormInput.vue.d.ts +2 -0
- package/dist/components/base/DFormInvalidFeedback.vue.d.ts +12 -0
- package/dist/components/base/DFormOtp.vue.d.ts +12 -0
- package/dist/components/base/DFormRadio.vue.d.ts +12 -0
- package/dist/components/base/DFormRadioGroup.vue.d.ts +12 -0
- package/dist/components/base/DFormSelect.vue.d.ts +27 -0
- package/dist/components/base/DFormSpinbutton.vue.d.ts +12 -0
- package/dist/components/base/DFormTags.vue.d.ts +12 -0
- package/dist/components/base/DFormText.vue.d.ts +12 -0
- package/dist/components/base/DFormTextarea.vue.d.ts +2 -0
- package/dist/components/base/DImage.vue.d.ts +12 -0
- package/dist/components/base/DInputGroup.vue.d.ts +12 -0
- package/dist/components/base/DLink.vue.d.ts +12 -0
- package/dist/components/base/DListGroup.vue.d.ts +12 -0
- package/dist/components/base/DListGroupItem.vue.d.ts +12 -0
- package/dist/components/base/DModal.vue.d.ts +12 -0
- package/dist/components/base/DNav.vue.d.ts +12 -0
- package/dist/components/base/DNavItem.vue.d.ts +12 -0
- package/dist/components/base/DNavbar.vue.d.ts +12 -0
- package/dist/components/base/DNavbarBrand.vue.d.ts +12 -0
- package/dist/components/base/DNavbarNav.vue.d.ts +12 -0
- package/dist/components/base/DNavbarToggle.vue.d.ts +12 -0
- package/dist/components/base/DOffcanvas.vue.d.ts +12 -0
- package/dist/components/base/DOverlay.vue.d.ts +12 -0
- package/dist/components/base/DPagination.vue.d.ts +2 -0
- package/dist/components/base/DPlaceholder.vue.d.ts +12 -0
- package/dist/components/base/DPopover.vue.d.ts +12 -0
- package/dist/components/base/DProgress.vue.d.ts +12 -0
- package/dist/components/base/DRow.vue.d.ts +12 -0
- package/dist/components/base/DSpinner.vue.d.ts +2 -0
- package/dist/components/base/DTable.vue.d.ts +34 -0
- package/dist/components/base/DTabs.vue.d.ts +12 -0
- package/dist/components/base/DToast.vue.d.ts +12 -0
- package/dist/components/base/DToaster.vue.d.ts +12 -0
- package/dist/components/base/DTooltip.vue.d.ts +12 -0
- package/dist/components/charts/DXBarChart.vue.d.ts +7 -0
- package/dist/components/charts/DXDoughnutChart.vue.d.ts +24 -0
- package/dist/components/charts/DXLineChart.vue.d.ts +7 -0
- package/dist/components/charts/chartTheme.d.ts +59 -0
- package/dist/components/charts/useThemedChart.d.ts +40 -0
- package/dist/components/extended/DXBasicForm.vue.d.ts +10 -0
- package/dist/components/extended/DXChoiceField.vue.d.ts +26 -0
- package/dist/components/extended/DXCurrencyInput.vue.d.ts +42 -0
- package/dist/components/extended/DXDashboard.vue.d.ts +105 -0
- package/dist/components/extended/DXDashboardNavbar.vue.d.ts +86 -0
- package/dist/components/extended/DXDashboardSidebar.vue.d.ts +61 -0
- package/dist/components/extended/DXField.vue.d.ts +86 -0
- package/dist/components/extended/DXFieldLabel.vue.d.ts +18 -0
- package/dist/components/extended/DXFieldShell.vue.d.ts +73 -0
- package/dist/components/extended/DXForm.vue.d.ts +112 -0
- package/dist/components/extended/DXFormField.vue.d.ts +41 -0
- package/dist/components/extended/DXNumericField.vue.d.ts +23 -0
- package/dist/components/extended/DXRepeater.vue.d.ts +46 -0
- package/dist/components/extended/DXRepeaterCards.vue.d.ts +45 -0
- package/dist/components/extended/DXStatCard.vue.d.ts +56 -0
- package/dist/components/extended/DXSwitch.vue.d.ts +47 -0
- package/dist/components/extended/DXTable.vue.d.ts +493 -0
- package/dist/components/extended/DXTableEditorModal.vue.d.ts +100 -0
- package/dist/components/extended/DXTablePagination.vue.d.ts +50 -0
- package/dist/components/extended/DXTableShell.vue.d.ts +24 -0
- package/dist/components/extended/DXUserAvatar.vue.d.ts +40 -0
- package/dist/components/extended/dxFieldContext.d.ts +11 -0
- package/dist/composables/defineForm.d.ts +41 -0
- package/dist/composables/useAsyncOptions.d.ts +20 -0
- package/dist/composables/useContainerWidth.d.ts +96 -0
- package/dist/composables/useForm.d.ts +46 -0
- package/dist/composables/useResourceEditor.d.ts +77 -0
- package/dist/composables/useToast.d.ts +2 -0
- package/dist/dashboard-for-laravel.js +2855 -2848
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +5 -5
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/index.d.ts +93 -0
- package/dist/style.css +1 -1
- package/dist/types/index.d.ts +477 -0
- package/dist/types/navigation.d.ts +43 -0
- package/dist/utils/api.d.ts +30 -0
- package/dist/utils/formSchema.d.ts +59 -0
- package/dist/utils/objectPath.d.ts +18 -0
- package/docs/public/api-reference.json +3 -3
- package/docs/public/docs-map.md +1 -1
- package/package.json +1 -1
- package/resources/js/components/extended/DXDashboardSidebar.vue +9 -5
- package/resources/js/components/extended/DXTable.vue +3 -0
- package/resources/js/composables/useResourceEditor.ts +41 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { PaginationData } from "./DXTable.vue";
|
|
2
|
+
interface Props {
|
|
3
|
+
/** The active pagination metadata (client-side / inertia / provider). */
|
|
4
|
+
pagination: PaginationData;
|
|
5
|
+
/** The effective page size, shown in the selector. */
|
|
6
|
+
perPage: number;
|
|
7
|
+
/** Page sizes offered by the selector. */
|
|
8
|
+
perPageOptions: number[];
|
|
9
|
+
/** Whether to render the pager (still hidden when there is only one page). */
|
|
10
|
+
showPagination: boolean;
|
|
11
|
+
/** Whether to render the per-page selector. */
|
|
12
|
+
showPerPageSelector: boolean;
|
|
13
|
+
/** Whether to render the item-count caption (the "X out of Z items" line). */
|
|
14
|
+
showCount: boolean;
|
|
15
|
+
/** Singular item noun for the info line (e.g. "product"). */
|
|
16
|
+
singularItemName: string;
|
|
17
|
+
/** Plural item noun for the info line (e.g. "products"). */
|
|
18
|
+
pluralItemName: string;
|
|
19
|
+
/** Whether a column filter is active — gates the "Filtered from N" note. */
|
|
20
|
+
hasActiveFilters: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Upper bound (px) on the pager ROW width at which the number window may
|
|
23
|
+
* collapse to the compact "current / last" status (#162). At or above it
|
|
24
|
+
* the full windowed pager always renders, whatever the page count.
|
|
25
|
+
*
|
|
26
|
+
* Default **576** — Bootstrap's `sm` breakpoint, i.e. the width the rest of
|
|
27
|
+
* the design system already treats as phone-sized. Its job is to GUARANTEE
|
|
28
|
+
* that a desktop-width table is never quietly downgraded: a 45-page window
|
|
29
|
+
* that doesn't quite fit at 800px should wrap (today's behaviour), not lose
|
|
30
|
+
* its numbers.
|
|
31
|
+
*
|
|
32
|
+
* Below it, compact engages only when the full pager genuinely would not
|
|
33
|
+
* fit on one line (see `fullPagerWidth`) — so a three-page pager still
|
|
34
|
+
* shows `1 2 3` on a phone, where "2 / 3" would be strictly worse.
|
|
35
|
+
*
|
|
36
|
+
* Measured on the ROW, not the viewport, so a table squeezed by a sidebar
|
|
37
|
+
* or inside a modal collapses even though the window is wide.
|
|
38
|
+
*/
|
|
39
|
+
compactThreshold?: number;
|
|
40
|
+
}
|
|
41
|
+
declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
42
|
+
"page-change": (page: number) => any;
|
|
43
|
+
"per-page-change": (value: string | number | (string | number)[] | null) => any;
|
|
44
|
+
}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
|
|
45
|
+
"onPage-change"?: ((page: number) => any) | undefined;
|
|
46
|
+
"onPer-page-change"?: ((value: string | number | (string | number)[] | null) => any) | undefined;
|
|
47
|
+
}>, {
|
|
48
|
+
compactThreshold: number;
|
|
49
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
50
|
+
export default _default;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Wrap the content in a `DCard`. When false, render plain/borderless. */
|
|
3
|
+
card?: boolean;
|
|
4
|
+
}
|
|
5
|
+
declare var __VLS_5: {}, __VLS_7: {}, __VLS_9: {}, __VLS_11: {};
|
|
6
|
+
type __VLS_Slots = {} & {
|
|
7
|
+
header?: (props: typeof __VLS_5) => any;
|
|
8
|
+
} & {
|
|
9
|
+
default?: (props: typeof __VLS_7) => any;
|
|
10
|
+
} & {
|
|
11
|
+
header?: (props: typeof __VLS_9) => any;
|
|
12
|
+
} & {
|
|
13
|
+
default?: (props: typeof __VLS_11) => any;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
16
|
+
card: boolean;
|
|
17
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
19
|
+
export default _default;
|
|
20
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
21
|
+
new (): {
|
|
22
|
+
$slots: S;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ColorVariant } from "bootstrap-vue-next";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** The user whose initial is shown. The first letter of `name` is used. */
|
|
4
|
+
user?: {
|
|
5
|
+
name: string;
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
} | null;
|
|
8
|
+
/** Overrides the initial derived from `user.name` (e.g. two-letter initials). */
|
|
9
|
+
initial?: string;
|
|
10
|
+
/** Show a notification dot on the avatar (e.g. unread updates). */
|
|
11
|
+
badge?: boolean;
|
|
12
|
+
/** Colour of the notification dot. */
|
|
13
|
+
badgeVariant?: ColorVariant;
|
|
14
|
+
/**
|
|
15
|
+
* Accessible text for the notification dot, announced to screen readers
|
|
16
|
+
* (the dot itself is a colour-only signal). Ignored unless `badge` is set.
|
|
17
|
+
*/
|
|
18
|
+
badgeLabel?: string;
|
|
19
|
+
};
|
|
20
|
+
declare var __VLS_1: {};
|
|
21
|
+
type __VLS_Slots = {} & {
|
|
22
|
+
default?: (props: typeof __VLS_1) => any;
|
|
23
|
+
};
|
|
24
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
25
|
+
initial: string;
|
|
26
|
+
user: {
|
|
27
|
+
name: string;
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
} | null;
|
|
30
|
+
badge: boolean;
|
|
31
|
+
badgeVariant: ColorVariant;
|
|
32
|
+
badgeLabel: string;
|
|
33
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
34
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
35
|
+
export default _default;
|
|
36
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
37
|
+
new (): {
|
|
38
|
+
$slots: S;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComputedRef, InjectionKey } from "vue";
|
|
2
|
+
/**
|
|
3
|
+
* Lets `DXField` forward its `info-popover` slot to the `DXFieldLabel` it
|
|
4
|
+
* renders — at any of its several label sites — without repeating the forward
|
|
5
|
+
* at each one. The provided value is a render function for the popover body, or
|
|
6
|
+
* `null` when the field has no `info-popover` slot.
|
|
7
|
+
*
|
|
8
|
+
* Optional: a `DXFieldLabel` used standalone (no `DXField` ancestor) injects the
|
|
9
|
+
* default `null` and falls back to its own `popover` slot / `info` text.
|
|
10
|
+
*/
|
|
11
|
+
export declare const dxFieldInfoPopoverKey: InjectionKey<ComputedRef<(() => unknown) | null>>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type UseFormReturn } from "./useForm";
|
|
2
|
+
import type { FieldDefinition } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Extended field definition that includes default value
|
|
5
|
+
*/
|
|
6
|
+
export interface FormFieldDefinition<TValue = any> extends FieldDefinition {
|
|
7
|
+
/**
|
|
8
|
+
* Default/initial value for the field. Optional: when omitted, the field
|
|
9
|
+
* seeds from its type (`false` for checkbox/switch, `[]` for
|
|
10
|
+
* checkbox-group/switch-list/repeater, `0` for numerics, `""` otherwise) —
|
|
11
|
+
* previously the type required this even though the runtime had a
|
|
12
|
+
* fallback, so typed consumers could never use it.
|
|
13
|
+
*/
|
|
14
|
+
default?: TValue;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Infer data type from field definitions
|
|
18
|
+
*/
|
|
19
|
+
type InferFormData<TFields extends readonly FormFieldDefinition[]> = {
|
|
20
|
+
[K in TFields[number] as K["key"]]: K["default"];
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Return type for defineForm
|
|
24
|
+
*/
|
|
25
|
+
export interface DefineFormReturn<TData extends Record<string, any>> {
|
|
26
|
+
form: UseFormReturn<TData>;
|
|
27
|
+
fields: FieldDefinition[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Define a form with type-safe field definitions.
|
|
31
|
+
* Reduces duplication by inferring initial values from field definitions.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const { form, fields } = defineForm([
|
|
35
|
+
* { key: "email", type: "email", label: "Email", default: "", required: true },
|
|
36
|
+
* { key: "password", type: "password", label: "Password", default: "", required: true },
|
|
37
|
+
* { key: "remember", type: "checkbox", label: "Remember me", default: false },
|
|
38
|
+
* ] as const);
|
|
39
|
+
*/
|
|
40
|
+
export declare function defineForm<const TFields extends readonly FormFieldDefinition[]>(fieldDefinitions: TFields): DefineFormReturn<InferFormData<TFields>>;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ComputedRef, type Ref } from "vue";
|
|
2
|
+
import type { FieldDefinition, FieldOption } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Async option loading for a `DXField`: runs `field.optionsLoader` on mount and
|
|
5
|
+
* (when `field.reloadOptionsOnChange` is set) whenever the model changes,
|
|
6
|
+
* exposing the loaded options. A monotonic request token guards against
|
|
7
|
+
* out-of-order responses clobbering newer ones; loader failures are swallowed
|
|
8
|
+
* so the last good options (or the static `field.options` fallback) stay put.
|
|
9
|
+
*
|
|
10
|
+
* Extracted verbatim from DXField's inline logic (#135) — behaviour unchanged.
|
|
11
|
+
* `getField` is an accessor (props aren't destructurable without losing
|
|
12
|
+
* reactivity); `effectiveModel` is the model passed to the loader.
|
|
13
|
+
*/
|
|
14
|
+
export interface UseAsyncOptionsReturn {
|
|
15
|
+
/** Options returned by the most recent successful load, or null if none yet. */
|
|
16
|
+
loadedOptions: Ref<FieldOption[] | null>;
|
|
17
|
+
/** Trigger a load (no-op when the field has no `optionsLoader`). */
|
|
18
|
+
loadOptions: () => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export declare function useAsyncOptions(getField: () => FieldDefinition, effectiveModel: ComputedRef<any> | Ref<any>): UseAsyncOptionsReturn;
|
|
@@ -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;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type ComputedRef, type WritableComputedRef } from "vue";
|
|
2
|
+
import { type ApiError } from "../utils/api";
|
|
3
|
+
export interface ValidationErrors {
|
|
4
|
+
[key: string]: string[];
|
|
5
|
+
}
|
|
6
|
+
export type FormError = ApiError;
|
|
7
|
+
export interface FormSubmitOptions<TPayload = unknown, TResponse = unknown> {
|
|
8
|
+
onSuccess?: (data: TResponse) => void;
|
|
9
|
+
onError?: (error: FormError) => void;
|
|
10
|
+
onBefore?: (payload: TPayload) => void;
|
|
11
|
+
onFinish?: () => void;
|
|
12
|
+
transform?: (payload: TPayload) => unknown;
|
|
13
|
+
preserveErrors?: boolean;
|
|
14
|
+
resetOnSuccess?: boolean;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
}
|
|
17
|
+
export interface FormState<TData extends Record<string, any>> {
|
|
18
|
+
data: TData;
|
|
19
|
+
errors: ValidationErrors;
|
|
20
|
+
processing: boolean;
|
|
21
|
+
message: string;
|
|
22
|
+
touched: Record<string, boolean>;
|
|
23
|
+
recentlySuccessful: boolean;
|
|
24
|
+
shouldShowMessage: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface UseFormReturn<TData extends Record<string, any>> extends FormState<TData> {
|
|
27
|
+
hasErrors: ComputedRef<boolean>;
|
|
28
|
+
hasError: (field: keyof TData | string) => boolean;
|
|
29
|
+
getError: (field: keyof TData | string) => string;
|
|
30
|
+
getState: (field: keyof TData | string) => true | false | null;
|
|
31
|
+
setErrors: (errors?: ValidationErrors) => void;
|
|
32
|
+
setMessage: (message: string) => void;
|
|
33
|
+
clearErrors: () => void;
|
|
34
|
+
clearError: (field: keyof TData | string) => void;
|
|
35
|
+
reset: (only?: Array<keyof TData>) => void;
|
|
36
|
+
field: <K extends keyof TData>(key: K) => WritableComputedRef<TData[K]>;
|
|
37
|
+
fields: {
|
|
38
|
+
[K in keyof TData]: WritableComputedRef<TData[K]>;
|
|
39
|
+
};
|
|
40
|
+
submit: <TResponse = unknown>(method: "get" | "post" | "put" | "patch" | "delete", url: string, options?: FormSubmitOptions<TData, TResponse>) => Promise<TResponse>;
|
|
41
|
+
post: <TResponse = unknown>(url: string, options?: FormSubmitOptions<TData, TResponse>) => Promise<TResponse>;
|
|
42
|
+
put: <TResponse = unknown>(url: string, options?: FormSubmitOptions<TData, TResponse>) => Promise<TResponse>;
|
|
43
|
+
patch: <TResponse = unknown>(url: string, options?: FormSubmitOptions<TData, TResponse>) => Promise<TResponse>;
|
|
44
|
+
delete: <TResponse = unknown>(url: string, options?: FormSubmitOptions<TData, TResponse>) => Promise<TResponse>;
|
|
45
|
+
}
|
|
46
|
+
export declare function useForm<TData extends Record<string, any>>(initialData?: TData): UseFormReturn<TData>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import type { FieldDefinition } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* The subset of `DXTable`'s props the resource editor reads. Declared
|
|
5
|
+
* structurally (not imported from DXTable.vue) so the composable stays a
|
|
6
|
+
* lower-level dependency — DXTable imports the composable, not the reverse.
|
|
7
|
+
* DXTable's full `Props` is structurally assignable to this.
|
|
8
|
+
*/
|
|
9
|
+
export interface ResourceEditorProps<T = any> {
|
|
10
|
+
/** Field definitions for the edit/create modal (enables edit-on-row-click). */
|
|
11
|
+
editFields?: FieldDefinition[];
|
|
12
|
+
/** Tab definitions — only their `key` is read here (DXForm consumes the rest). */
|
|
13
|
+
editTabs?: Array<{
|
|
14
|
+
key: string;
|
|
15
|
+
}>;
|
|
16
|
+
/** Modal title, or a function of the edited row. */
|
|
17
|
+
editModalTitle?: string | ((item: any) => string);
|
|
18
|
+
/** `PUT` endpoint pattern for updates, e.g. `/api/products/:id`. */
|
|
19
|
+
editUrl?: string;
|
|
20
|
+
/** `GET` endpoint pattern to fetch the full record on open, e.g. `/api/products/:id`. */
|
|
21
|
+
showUrl?: string;
|
|
22
|
+
/** `DELETE` endpoint pattern, e.g. `/api/products/:id`. */
|
|
23
|
+
deleteUrl?: string;
|
|
24
|
+
/** `POST` endpoint for creating new items, e.g. `/api/products`. */
|
|
25
|
+
createUrl?: string;
|
|
26
|
+
/** Guard run before delete — a message short-circuits with a toast. Awaited. */
|
|
27
|
+
deleteGuard?: (item: T) => string | null | undefined | Promise<string | null | undefined>;
|
|
28
|
+
/** Guard run before save — a message aborts with a toast. Awaited. */
|
|
29
|
+
saveGuard?: (item: T | null, data: Record<string, any>) => string | null | undefined | Promise<string | null | undefined>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The events the editor emits back through DXTable's `emit`. Declared as the
|
|
33
|
+
* exact call signatures it uses (a subset of DXTable's emits) so DXTable's typed
|
|
34
|
+
* `emit` is structurally assignable here.
|
|
35
|
+
*/
|
|
36
|
+
export interface ResourceEditorEmit<T = any> {
|
|
37
|
+
(event: 'rowCreated', item: any, response: any): void;
|
|
38
|
+
(event: 'createError', error: any): void;
|
|
39
|
+
(event: 'rowUpdated', item: T, response: any): void;
|
|
40
|
+
(event: 'editError', item: T, error: any): void;
|
|
41
|
+
(event: 'rowDeleted', item: T, response: any): void;
|
|
42
|
+
(event: 'deleteError', item: T, error: any): void;
|
|
43
|
+
}
|
|
44
|
+
export interface UseResourceEditorOptions {
|
|
45
|
+
/** Invalidate whichever data source is active, after a successful CRUD op. */
|
|
46
|
+
refresh: () => void;
|
|
47
|
+
/** Singular item noun (e.g. "product") — for toasts and the modal title. */
|
|
48
|
+
singularItemName: Ref<string>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The resource editor for `DXTable` — the create/edit/delete concern lifted out
|
|
52
|
+
* of the god-component (#129). Owns the edit-modal state, form seeding and
|
|
53
|
+
* visibility rules, the `showUrl` full-record fetch, submission (create `POST` /
|
|
54
|
+
* edit `PUT` / delete), the success/error toasts, and the edit-modal slot-key
|
|
55
|
+
* computeds. It never touches the table's data source, sort, filter, per-page
|
|
56
|
+
* or pagination state — the only coupling back to the table is the `refresh`
|
|
57
|
+
* callback it calls after a successful op.
|
|
58
|
+
*
|
|
59
|
+
* DXTable keeps `handleRowClick` (which emits `rowClicked` regardless) and calls
|
|
60
|
+
* `openEdit` from it; `openEdit` is a no-op unless `editFields` are set.
|
|
61
|
+
*/
|
|
62
|
+
export declare function useResourceEditor<T = any>(props: ResourceEditorProps<T>, emit: ResourceEditorEmit<T>, options: UseResourceEditorOptions): {
|
|
63
|
+
showEditModal: Ref<boolean, boolean>;
|
|
64
|
+
editForm: Ref<any, any>;
|
|
65
|
+
editFormInstanceKey: Ref<number, number>;
|
|
66
|
+
activeTabIndex: Ref<number, number>;
|
|
67
|
+
selectedItem: [T | null] extends [Ref<any, any>] ? import("@vue/shared").IfAny<Ref<any, any> & T, Ref<Ref<any, any> & T, Ref<any, any> & T>, Ref<any, any> & T> : Ref<import("vue").UnwrapRef<T> | null, T | import("vue").UnwrapRef<T> | null>;
|
|
68
|
+
isCreateMode: Ref<boolean, boolean>;
|
|
69
|
+
pendingAction: Ref<"delete" | "save" | null, "delete" | "save" | null>;
|
|
70
|
+
editLoading: Ref<boolean, boolean>;
|
|
71
|
+
computedModalTitle: import("vue").ComputedRef<string>;
|
|
72
|
+
openEdit: (item: T) => void;
|
|
73
|
+
openCreate: () => void;
|
|
74
|
+
save: () => Promise<void>;
|
|
75
|
+
cancel: () => void;
|
|
76
|
+
remove: () => Promise<void>;
|
|
77
|
+
};
|