@omnitend/dashboard-for-laravel 0.4.13 → 0.5.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.
@@ -1,5 +1,16 @@
1
+ import type { Component } from "vue";
2
+
1
3
  /**
2
- * Field types supported by OForm
4
+ * Field types supported by DXForm (and DXField, its per-field renderer).
5
+ *
6
+ * Text-like types render an `<input>`; the remainder render purpose-built
7
+ * controls:
8
+ * - `currency` / `percentage` — numeric input wrapped in an input-group
9
+ * with a symbol affix.
10
+ * - `datetime` — alias for the native `datetime-local` control.
11
+ * - `image` / `file` — file input (`image` additionally shows a preview).
12
+ * - `component` — escape hatch that renders `field.component`.
13
+ * - `repeater` — nested, repeatable sub-form driven by `field.fields`.
3
14
  */
4
15
  export type FieldType =
5
16
  | "text"
@@ -10,11 +21,25 @@ export type FieldType =
10
21
  | "tel"
11
22
  | "date"
12
23
  | "datetime-local"
24
+ | "datetime"
13
25
  | "time"
26
+ | "currency"
27
+ | "percentage"
14
28
  | "textarea"
15
29
  | "select"
16
30
  | "checkbox"
17
- | "radio";
31
+ | "radio"
32
+ | "image"
33
+ | "file"
34
+ | "component"
35
+ | "repeater";
36
+
37
+ /**
38
+ * A value that may be supplied directly or computed from the live form
39
+ * model. Predicates receive the current model so fields can react to
40
+ * other fields (cross-field reactivity).
41
+ */
42
+ export type MaybeFn<TValue> = TValue | ((model: any) => TValue);
18
43
 
19
44
  /**
20
45
  * Option for select or radio fields
@@ -26,7 +51,18 @@ export interface FieldOption extends Record<string, unknown> {
26
51
  }
27
52
 
28
53
  /**
29
- * Field definition for OForm
54
+ * Asynchronously resolves the options for a select/radio field from the
55
+ * current model (e.g. fetch a dependent list). Resolved on mount, and
56
+ * again on model change when `reloadOptionsOnChange` is set.
57
+ */
58
+ export type OptionsLoader = (model: any) => Promise<FieldOption[]>;
59
+
60
+ /**
61
+ * Field definition shared by every form renderer.
62
+ *
63
+ * DXForm and DXTable's edit modal honour `hint`, `span`, `when`,
64
+ * `readonly`, `disabledWhen`, function-valued `label`/`hint`, async
65
+ * options, the `component` escape hatch and nested `repeater` fields.
30
66
  */
31
67
  export interface FieldDefinition {
32
68
  /** Field key (must match form data key) */
@@ -35,8 +71,8 @@ export interface FieldDefinition {
35
71
  /** Field type */
36
72
  type: FieldType;
37
73
 
38
- /** Field label (optional) */
39
- label?: string;
74
+ /** Field label string or a function of the form model */
75
+ label?: MaybeFn<string>;
40
76
 
41
77
  /** Placeholder text (optional) */
42
78
  placeholder?: string;
@@ -47,15 +83,123 @@ export interface FieldDefinition {
47
83
  /** Options for select or radio fields */
48
84
  options?: FieldOption[];
49
85
 
86
+ /**
87
+ * Asynchronously load options for select/radio fields. Takes
88
+ * precedence over `options` once resolved.
89
+ */
90
+ optionsLoader?: OptionsLoader;
91
+
92
+ /** Re-run `optionsLoader` whenever the form model changes. */
93
+ reloadOptionsOnChange?: boolean;
94
+
50
95
  /** Number of rows for textarea (default: 3) */
51
96
  rows?: number;
52
97
 
53
- /** Help text displayed below field */
98
+ /** Step for numeric/currency/percentage inputs */
99
+ step?: number | string;
100
+
101
+ /** Min/max for numeric inputs */
102
+ min?: number | string;
103
+ max?: number | string;
104
+
105
+ /** Symbol shown for `currency` fields (default: the locale's, "£"). */
106
+ currencySymbol?: string;
107
+
108
+ /** `accept` attribute for `image`/`file` inputs (e.g. "image/*"). */
109
+ accept?: string;
110
+
111
+ /** Help text displayed below the field (always visible). */
54
112
  help?: string;
55
113
 
114
+ /**
115
+ * Hint text displayed below the field. Unlike `help`, may be a
116
+ * function of the model for dynamic hints.
117
+ */
118
+ hint?: MaybeFn<string>;
119
+
56
120
  /** CSS class for the form group */
57
121
  class?: string;
58
122
 
59
123
  /** Additional props to pass to the input component */
60
124
  inputProps?: Record<string, any>;
125
+
126
+ /**
127
+ * Render the field full-width with no label wrapper, delegating its
128
+ * content to the `#span(<key>)` slot. Useful for custom blocks.
129
+ */
130
+ span?: boolean;
131
+
132
+ /**
133
+ * Component rendered for `type: "component"` fields. Receives
134
+ * `modelValue`, `field`, `model` props and emits `update:modelValue`.
135
+ */
136
+ component?: Component;
137
+
138
+ /** Sub-field definitions for `type: "repeater"` fields. */
139
+ fields?: FieldDefinition[];
140
+
141
+ /**
142
+ * Default/initial value. Used by `defineForm` to seed form data and by
143
+ * repeaters to seed a freshly-added row's sub-fields. (`defineForm`'s
144
+ * `FormFieldDefinition` re-declares this as required for inference.)
145
+ */
146
+ default?: any;
147
+
148
+ /** Label for a repeater's "add row" button (default: "Add"). */
149
+ addLabel?: string;
150
+
151
+ /** Minimum / maximum number of repeater rows. */
152
+ minItems?: number;
153
+ maxItems?: number;
154
+
155
+ /** Disable the field (static or computed from the model). */
156
+ disabled?: MaybeFn<boolean>;
157
+
158
+ /**
159
+ * Disable the field based on the model. Retained for backwards
160
+ * compatibility with DXTable; prefer `disabled` with a function.
161
+ */
162
+ disabledWhen?: (model: any) => boolean;
163
+
164
+ /**
165
+ * Render the field read-only (static or computed). For controls
166
+ * without a native readonly state (select/checkbox/radio) this is
167
+ * applied as `disabled`.
168
+ */
169
+ readonly?: MaybeFn<boolean>;
170
+
171
+ /**
172
+ * Conditionally show or hide this field. When omitted the field is
173
+ * always visible. Boolean or a function of the form model; evaluated
174
+ * reactively for cross-field conditional fields.
175
+ */
176
+ when?: MaybeFn<boolean>;
177
+
178
+ /**
179
+ * Legacy no-argument visibility predicate. Retained for backwards
180
+ * compatibility; prefer `when`. When both are present, a field is
181
+ * visible only if both pass.
182
+ */
183
+ show?: () => boolean;
184
+ }
185
+
186
+ /**
187
+ * A tab in a tabbed form. Groups a subset of fields and can be shown
188
+ * conditionally or lazily mounted.
189
+ */
190
+ export interface FormTab {
191
+ /** Unique key for this tab */
192
+ key: string;
193
+
194
+ /** Display label (optional, defaults to the key) */
195
+ label?: string;
196
+
197
+ /** Field keys (from the form's fields) to render in this tab */
198
+ fieldKeys: string[];
199
+
200
+ /** Conditional display — boolean or a function of the form model */
201
+ when?: MaybeFn<boolean>;
202
+
203
+ /** Lazily mount tab content until first activated */
204
+ lazy?: boolean;
61
205
  }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Minimal dot-path get/set helpers used for nested form binding
3
+ * (e.g. repeater rows where a field value lives at `lines.0.price`).
4
+ *
5
+ * Paths are dot-separated; numeric segments index into arrays. Setting
6
+ * creates intermediate objects/arrays as needed so a fresh repeater row
7
+ * can be populated without pre-seeding the whole shape.
8
+ */
9
+
10
+ type AnyRecord = Record<string, any>;
11
+
12
+ /** Segments that could pollute Object.prototype if written through. */
13
+ const BLOCKED_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
14
+
15
+ /** Split a dot path into segments, ignoring empty segments. */
16
+ function splitPath(path: string): string[] {
17
+ return path.split(".").filter((segment) => segment.length > 0);
18
+ }
19
+
20
+ /** Read the value at `path` within `target`, or `undefined` if absent. */
21
+ export function getByPath(target: AnyRecord, path: string): any {
22
+ const segments = splitPath(path);
23
+ let current: any = target;
24
+ for (const segment of segments) {
25
+ if (current === null || current === undefined) return undefined;
26
+ current = current[segment];
27
+ }
28
+ return current;
29
+ }
30
+
31
+ /**
32
+ * Write `value` at `path` within `target`, creating intermediate
33
+ * containers. A numeric next-segment creates an array; otherwise an
34
+ * object. Mutates `target` in place (form.data is reactive).
35
+ */
36
+ export function setByPath(target: AnyRecord, path: string, value: any): void {
37
+ const segments = splitPath(path);
38
+ if (segments.length === 0) return;
39
+ // Never write through prototype-polluting segments.
40
+ if (segments.some((segment) => BLOCKED_SEGMENTS.has(segment))) return;
41
+
42
+ let current: any = target;
43
+ for (let index = 0; index < segments.length - 1; index += 1) {
44
+ const segment = segments[index];
45
+ const nextSegment = segments[index + 1];
46
+ const nextIsIndex = /^\d+$/.test(nextSegment);
47
+
48
+ if (
49
+ current[segment] === null ||
50
+ current[segment] === undefined ||
51
+ typeof current[segment] !== "object"
52
+ ) {
53
+ current[segment] = nextIsIndex ? [] : {};
54
+ }
55
+ current = current[segment];
56
+ }
57
+
58
+ current[segments[segments.length - 1]] = value;
59
+ }