@ditojs/admin 2.85.2 → 2.87.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/types/index.d.ts CHANGED
@@ -17,97 +17,206 @@ declare global {
17
17
 
18
18
  export default DitoAdmin
19
19
  export interface DitoGlobal {
20
+ /** Global API configuration shared across the admin. */
20
21
  api?: ApiConfig
22
+ /** Base URL path for the admin application. */
21
23
  base?: string
24
+ /** Arbitrary settings accessible via `dito.settings`. */
22
25
  settings?: Record<string, any>
23
26
  }
24
- export type RequestMethod = <T>({
25
- url,
26
- method,
27
- data,
28
- query,
29
- params,
30
- headers
31
- }: {
27
+
28
+ /**
29
+ * A function that performs an HTTP request and returns the parsed
30
+ * response. Used by the admin to communicate with the Dito.js
31
+ * server API.
32
+ *
33
+ * @template T The expected type of the response data.
34
+ */
35
+ export type RequestMethod = <T>(options: {
36
+ /** The URL to send the request to. */
32
37
  url: string
33
38
  /**
39
+ * The HTTP method to use.
40
+ *
34
41
  * @default 'get'
35
42
  */
36
- method: HTTPVerb
37
- data: any
38
- query: Record<string, string | string[]> | [string, string][]
39
- headers: Record<string, string>
43
+ method?: HTTPMethod
44
+ /** Request body payload. */
45
+ data?: unknown
46
+ /** URL query parameters. */
47
+ query?:
48
+ | Record<string, string | number | (string | number)[]>
49
+ | [string, string | number][]
50
+ | null
51
+ /** Additional HTTP headers to include. */
52
+ headers?: Record<string, string> | null
53
+ /** Abort signal to cancel the request. */
54
+ signal?: AbortSignal | null
40
55
  }) => Promise<RequestMethodResponse<T>>
41
56
 
57
+ /**
58
+ * The response returned by {@link RequestMethod}, extending the
59
+ * standard `Response` with a parsed `data` property.
60
+ *
61
+ * @template T The type of the parsed response data.
62
+ */
42
63
  export type RequestMethodResponse<T> = Response & { data: T }
43
64
 
65
+ /**
66
+ * Describes a resource in the Dito.js server API. Resources can be
67
+ * nested via {@link ApiResource.parent} to build hierarchical API
68
+ * paths. The {@link ApiResource.type} determines how the path is
69
+ * constructed:
70
+ *
71
+ * - `'collection'` — uses `path` directly (e.g. `users`)
72
+ * - `'member'` — appends `id` to the path (e.g. `users/5`)
73
+ * - `'upload'` — builds an upload path under the parent collection
74
+ * (e.g. `users/upload/avatar`)
75
+ *
76
+ * Paths are built recursively through the `parent` chain, so a
77
+ * member nested under a collection produces paths like
78
+ * `users/1/comments/3`.
79
+ */
44
80
  export interface ApiResource {
45
- type: string
81
+ /**
82
+ * Determines how the resource path is constructed.
83
+ *
84
+ * @see {@link ApiResource} for the path-building rules per type.
85
+ */
86
+ type: LiteralUnion<'collection' | 'member' | 'upload'>
87
+ /**
88
+ * URL path segment for this resource. If it starts with `/`, it is
89
+ * treated as absolute and parent nesting is skipped. Otherwise it
90
+ * is appended to the parent's resolved path.
91
+ */
46
92
  path?: string
93
+ /** HTTP method for the resource request. */
94
+ method?: HTTPMethod
95
+ /**
96
+ * Identifier of a specific resource item, used when
97
+ * {@link ApiResource.type} is `'member'` to append the id to the
98
+ * resolved path.
99
+ */
100
+ id?: string | number
101
+ /**
102
+ * Parent resource for nested API paths. The parent's path is
103
+ * resolved first and this resource's path is appended to it,
104
+ * enabling hierarchical URLs.
105
+ */
47
106
  parent?: ApiResource
107
+ /** Query parameters appended to the request URL. */
108
+ query?: Record<string, string | number | (string | number)[]>
48
109
  }
49
110
 
111
+ /**
112
+ * Configuration for the admin's API layer. Merged from three
113
+ * sources in order of priority: the constructor `api` parameter,
114
+ * `dito.api` (passed from `AdminController` on the server), and
115
+ * built-in defaults.
116
+ */
50
117
  export interface ApiConfig {
51
118
  /**
52
- * The base url to use for api requests.
119
+ * The base path for the admin UI, as passed from
120
+ * `AdminController`.
121
+ *
122
+ * @defaultValue `'/'`
123
+ */
124
+ base?: string
125
+ /**
126
+ * The base URL prepended to all API requests. Relative request
127
+ * URLs are combined with this value.
53
128
  */
54
129
  url?: string
55
130
  /**
56
- * @defaultValue 'en-US'
131
+ * Locale used for date, time, and number formatting throughout
132
+ * the admin.
133
+ *
134
+ * @defaultValue `'en-US'`
57
135
  */
58
136
  locale?: string
137
+ /**
138
+ * Default format options for numbers, dates, and times. Merged
139
+ * with the built-in `defaultFormats` from `@ditojs/utils`.
140
+ * Individual schemas can override these on a per-component basis.
141
+ */
59
142
  formats?: {
60
143
  number?: NumberFormat
61
144
  date?: DateFormat
62
145
  time?: TimeFormat
63
146
  }
147
+ /**
148
+ * The function used to perform HTTP requests. Defaults to a
149
+ * built-in `fetch` wrapper that applies {@link ApiConfig.headers},
150
+ * {@link ApiConfig.cors}, and {@link ApiConfig.getApiUrl}
151
+ * automatically for API URLs.
152
+ */
64
153
  request?: RequestMethod
65
154
  /**
66
- * Whether to display admin notifications.
155
+ * Controls admin notification toasts. Set to `false` to disable
156
+ * all notifications, or provide an object to customize display
157
+ * duration.
67
158
  *
68
- * @default `true`
159
+ * @defaultValue `true`
69
160
  */
70
161
  notifications?:
71
162
  | boolean
72
163
  | {
73
164
  /**
74
- * The amount of milliseconds multiplied with the amount of characters
75
- * displayed in the notification, plus 40 (40 + title + message).
165
+ * Milliseconds per character used to calculate notification
166
+ * display duration. The formula is:
167
+ * `(40 + text.length + title.length) * durationFactor`.
168
+ *
76
169
  * @defaultValue `20`
77
170
  */
78
171
  durationFactor: number
79
172
  }
173
+ /**
174
+ * CORS settings applied to API requests (where
175
+ * {@link ApiConfig.isApiUrl} returns `true`).
176
+ */
80
177
  cors?: {
81
178
  /**
82
- * Whether cross-site `Access-Control` requests are made using credentials.
179
+ * Enables cross-site requests with credentials
180
+ * (`credentials: 'include'`).
83
181
  */
84
182
  credentials: boolean
85
183
  }
86
184
  /**
87
- * Setting normalizePaths to `true` sets `api.normalizePath` to hyphenate
88
- * camelized strings and `api.denormalizePath` to do the opposite.
185
+ * When `true`, sets {@link ApiConfig.normalizePath} to
186
+ * `hyphenate` (camelCase to kebab-case) and
187
+ * {@link ApiConfig.denormalizePath} to `camelize` (kebab-case to
188
+ * camelCase). Used for automatic path conversion between JS
189
+ * naming conventions and URL paths.
89
190
  *
90
- * @default Defaults to Application.config.app.normalizePaths and then
91
- * `false` when missing.
191
+ * @defaultValue Inherits from
192
+ * `Application.config.app.normalizePaths`, falling back
193
+ * to `false`.
92
194
  */
93
195
  normalizePaths?: boolean
94
196
  /**
95
- * @default When `api.normalizePaths = true` (plural),
96
- * `require('@ditojs/utils').hyphenate` is used for path normalization.
97
- * Otherwise paths are left unchanged.
197
+ * Converts a camelCase path segment to its URL form. Applied
198
+ * when building schema routes and upload paths.
199
+ *
200
+ * @defaultValue `hyphenate` when {@link ApiConfig.normalizePaths}
201
+ * is `true`, otherwise an identity function.
98
202
  */
99
203
  normalizePath?: (path: string) => string
100
204
  /**
101
- * @default When `api.normalizePaths = true` (plural),
102
- * `require('@ditojs/utils').camelize` is used for path denormalization.
103
- * Otherwise paths are left unchanged.
205
+ * Converts a URL path segment back to its camelCase form.
206
+ *
207
+ * @defaultValue `camelize` when {@link ApiConfig.normalizePaths}
208
+ * is `true`, otherwise an identity function.
104
209
  */
105
210
  denormalizePath?: (path: string) => string
106
211
  /**
107
- * Auth resources
212
+ * Authentication resource configuration. The `path` defines the
213
+ * user collection endpoint, and `login`, `logout`, and `session`
214
+ * are nested as child resources under it (e.g. `users/login`).
108
215
  */
109
216
  users?: {
217
+ /** The collection path for the user model. */
110
218
  path: string
219
+ /** Login endpoint. */
111
220
  login?: {
112
221
  /**
113
222
  * @defaultValue `'login'`
@@ -116,8 +225,9 @@ export interface ApiConfig {
116
225
  /**
117
226
  * @defaultValue `'post'`
118
227
  */
119
- method?: HTTPVerb
228
+ method?: HTTPMethod
120
229
  }
230
+ /** Logout endpoint. */
121
231
  logout?: {
122
232
  /**
123
233
  * @defaultValue `'logout'`
@@ -126,8 +236,12 @@ export interface ApiConfig {
126
236
  /**
127
237
  * @defaultValue `'post'`
128
238
  */
129
- method?: HTTPVerb
239
+ method?: HTTPMethod
130
240
  }
241
+ /**
242
+ * Session endpoint for checking an existing authenticated
243
+ * session on page load.
244
+ */
131
245
  session?: {
132
246
  /**
133
247
  * @defaultValue `'session'`
@@ -136,46 +250,112 @@ export interface ApiConfig {
136
250
  /**
137
251
  * @defaultValue `'get'`
138
252
  */
139
- method?: HTTPVerb
253
+ method?: HTTPMethod
140
254
  }
141
255
  }
142
256
  /**
143
- * Optionally override resource path handlers.
257
+ * Custom resource path handlers, keyed by resource type. Merged
258
+ * with the built-in handlers (`any`, `default`, `collection`,
259
+ * `member`, `upload`). Each handler receives an
260
+ * {@link ApiResource} and returns the resolved path string.
144
261
  */
145
262
  resources?: Record<string, (resource: ApiResource | string) => string>
146
263
 
147
264
  /**
148
- * Optionally override / extend headers
149
- * @defaultValue `{
150
- * 'Content-Type': 'application/json'
151
- * }`
265
+ * HTTP headers included in all API requests (where
266
+ * {@link ApiConfig.isApiUrl} returns `true`). Merged with the
267
+ * default `Content-Type: application/json` header, and can be
268
+ * further overridden per-request.
269
+ *
270
+ * @defaultValue `{ 'Content-Type': 'application/json' }`
152
271
  */
153
272
  headers?: Record<string, string>
154
273
 
155
274
  /**
156
- * Configures how urls passed to `DitoAdmin.request` are checked to see if
157
- * they are an API request.
158
- *
159
- * By default (if `api.request` is not overridden) API requests include
160
- * `api.url` as their base url, `api.headers` in their headers. If
161
- * `api.cors.credentials` is set to `true`, cross-site `Access-Control`
162
- * requests are made using credentials.
275
+ * Returns the full API URL for a given request configuration.
276
+ * Prepends {@link ApiConfig.url} to relative URLs and appends
277
+ * formatted query parameters.
278
+ */
279
+ getApiUrl?: (options: {
280
+ url: string
281
+ query?:
282
+ | Record<string, string | number | (string | number)[]>
283
+ | [string, string | number][]
284
+ | null
285
+ }) => string
286
+
287
+ /**
288
+ * Returns `true` if the given URL is an API URL. A URL is
289
+ * considered an API URL if it is not absolute or if it starts
290
+ * with {@link ApiConfig.url}. When `true`, the request includes
291
+ * {@link ApiConfig.headers} and respects
292
+ * {@link ApiConfig.cors} settings.
163
293
  */
164
- isApiRequest?: (url: string) => boolean
294
+ isApiUrl?: (url: string) => boolean
295
+
296
+ /**
297
+ * Default schema property values per component type. During
298
+ * schema processing, defaults for a matching type are applied
299
+ * to the schema: top-level properties that are `undefined` are
300
+ * set directly, while existing object properties are
301
+ * deep-merged. When a function is provided, it receives the
302
+ * schema and can return defaults or modify it directly.
303
+ *
304
+ * @example
305
+ * ```js
306
+ * defaults: {
307
+ * multiselect: {
308
+ * selectable: true
309
+ * }
310
+ * }
311
+ * ```
312
+ */
313
+ defaults?: Record<
314
+ string,
315
+ | Record<string, any>
316
+ | ((schema: Component) => Record<string, any> | void)
317
+ >
165
318
  }
166
319
 
167
320
  export interface BaseSchema<$Item>
168
321
  extends SchemaDitoMixin<$Item>,
169
322
  SchemaTypeMixin<$Item> {
323
+ /**
324
+ * Value used when the field's key is absent from the
325
+ * data object.
326
+ */
170
327
  default?: OrItemAccessor<$Item>
328
+ /**
329
+ * Computes and sets the field value reactively. If
330
+ * the callback returns `undefined`, the current value
331
+ * is preserved.
332
+ */
171
333
  compute?: ItemAccessor<$Item>
334
+ /**
335
+ * Additional reactive data properties merged into
336
+ * the component's data scope.
337
+ */
172
338
  data?: OrItemAccessor<$Item, {}, Record<string, any>>
339
+ /**
340
+ * Custom CSS class(es) added to the component's container
341
+ * element. Accepts a string or an object of class-boolean
342
+ * pairs.
343
+ */
344
+ class?: string | Record<string, boolean>
345
+ /**
346
+ * Whether the component type omits surrounding spacing.
347
+ *
348
+ * @internal Set as a static option on component type
349
+ * definitions, not configured in schemas.
350
+ */
173
351
  omitSpacing?: boolean
174
- break?: 'before' | 'after'
352
+ /**
353
+ * Forces a layout line break before, after, or on
354
+ * both sides of the component.
355
+ */
356
+ break?: 'before' | 'after' | 'both'
175
357
  }
176
358
 
177
- // TODO: finish off DitoMixin docs
178
- // (methods / computed / watch / events / `on[A-Z]`-style callbacks)
179
359
  export interface SchemaDitoMixin<$Item> {
180
360
  /**
181
361
  * Only displays the component if the schema accessor returns `true`
@@ -188,6 +368,7 @@ export interface SchemaDitoMixin<$Item> {
188
368
  * the component is rendered.
189
369
  */
190
370
  rules?: {
371
+ /** Override whether the field is required. */
191
372
  required?: boolean
192
373
  }
193
374
  }
@@ -200,7 +381,51 @@ export type ItemEventHandler<$Item = any> = (
200
381
  itemParams: DitoContext<$Item>
201
382
  ) => void | false
202
383
 
203
- export interface SchemaTypeMixin<$Item> {
384
+ export type OpenEventHandler<$Item = any> = (
385
+ itemParams: DitoContext<$Item> & { open: boolean }
386
+ ) => void | false
387
+
388
+ export type ErrorEventHandler<$Item = any> = (
389
+ itemParams: DitoContext<$Item> & { error: Error }
390
+ ) => void | false
391
+
392
+ /**
393
+ * Event handlers shared by component schemas, views,
394
+ * and forms.
395
+ */
396
+ export interface SchemaEvents<$Item = any> {
397
+ /**
398
+ * Fires when the schema's component tree is set up.
399
+ * @see {@link SchemaFields.onInitialize}
400
+ */
401
+ initialize?: ItemEventHandler<$Item>
402
+ /**
403
+ * Fires when the schema component is unmounted.
404
+ * @see {@link SchemaFields.onDestroy}
405
+ */
406
+ destroy?: ItemEventHandler<$Item>
407
+ /**
408
+ * Fires after data has been fetched from the API.
409
+ * @see {@link SchemaFields.onLoad}
410
+ */
411
+ load?: ItemEventHandler<$Item>
412
+ /**
413
+ * Fires after a value change is committed.
414
+ * @see {@link SchemaFields.onChange}
415
+ */
416
+ change?: ItemEventHandler<$Item>
417
+ }
418
+
419
+ export interface SchemaOpen<$Item = any> {
420
+ /**
421
+ * Called when a collapsible schema section is
422
+ * toggled open or closed. The `open` property on
423
+ * the context indicates the new state.
424
+ */
425
+ onOpen?: OpenEventHandler<$Item>
426
+ }
427
+
428
+ export interface SchemaTypeMixin<$Item> extends SchemaFields<$Item> {
204
429
  /**
205
430
  * The label of the component.
206
431
  *
@@ -228,9 +453,11 @@ export interface SchemaTypeMixin<$Item> {
228
453
  visible?: OrItemAccessor<$Item, {}, boolean>
229
454
 
230
455
  /**
456
+ * Whether to exclude the field's value from the processed data
457
+ * sent to the server.
458
+ *
231
459
  * @defaultValue `false`
232
460
  */
233
- // TODO: document exclude
234
461
  exclude?: OrItemAccessor<$Item, {}, boolean>
235
462
 
236
463
  /**
@@ -259,10 +486,24 @@ export interface SchemaTypeMixin<$Item> {
259
486
  clearable?: OrItemAccessor<$Item, {}, boolean>
260
487
 
261
488
  /**
262
- * Specifies a short hint intended to aid the user with data entry when the
263
- * input has no value.
489
+ * Specifies a short hint intended to aid the user
490
+ * with data entry when the input has no value. Set
491
+ * to `false` to disable the placeholder, or `true`
492
+ * to use the component's auto-generated default
493
+ * (e.g. multiselect generates one based on
494
+ * `searchable` and `taggable`).
495
+ */
496
+ placeholder?: OrItemAccessor<$Item, {}, string | boolean>
497
+
498
+ /**
499
+ * Info text displayed near the component label.
264
500
  */
265
- placeholder?: OrItemAccessor<$Item, {}, any>
501
+ info?: OrItemAccessor<$Item, {}, string>
502
+
503
+ /**
504
+ * The maximum allowed length of the input value.
505
+ */
506
+ maxLength?: OrItemAccessor<$Item, {}, number>
266
507
 
267
508
  /**
268
509
  * Whether the input field should have autocomplete enabled.
@@ -270,37 +511,166 @@ export interface SchemaTypeMixin<$Item> {
270
511
  autocomplete?: OrItemAccessor<$Item, {}, 'on' | 'off'>
271
512
 
272
513
  /**
273
- * Specifies a function which changes the item value into another format,
274
- * before it is passed to the component.
514
+ * Specifies a function which transforms the stored
515
+ * value into a display format before it is passed
516
+ * to the component for rendering.
275
517
  */
276
518
  format?: ItemAccessor<$Item, {}, any>
519
+ /**
520
+ * Whether the component is disabled.
521
+ *
522
+ * @defaultValue `false`
523
+ */
277
524
  disabled?: OrItemAccessor<$Item, {}, boolean>
278
525
 
279
526
  /**
280
- * Specifies a function which parses the component value when it changes,
281
- *
527
+ * Specifies a function which parses the component
528
+ * value when it changes, transforming the raw
529
+ * input before it is stored.
282
530
  */
283
- parse?: ItemAccessor<$Item, any>
531
+ parse?: ItemAccessor<$Item, {}>
284
532
 
285
- // TODO: document process
286
- process?: OrItemAccessor<$Item>
533
+ /**
534
+ * Specifies a function which processes the field value after
535
+ * type-specific processing but before exclusion, allowing
536
+ * custom value transformation before sending data to the
537
+ * server. Can also modify sibling data via `processedItem`
538
+ * in the context, even when `exclude` is `true`.
539
+ */
540
+ process?: ItemAccessor<$Item, {}>
287
541
 
288
- // TODO: document name
542
+ /**
543
+ * The property key used to identify the field in data objects.
544
+ * Auto-assigned from the component's key if not provided.
545
+ */
289
546
  name?: string
290
547
 
548
+ /**
549
+ * Called when the input element receives focus.
550
+ */
291
551
  onFocus?: ItemEventHandler<$Item>
552
+ /**
553
+ * Called when the input element loses focus.
554
+ */
292
555
  onBlur?: ItemEventHandler<$Item>
293
- onChange?: ItemEventHandler<$Item>
556
+ /**
557
+ * Called on every keystroke or value modification. Unlike
558
+ * {@link BaseSchema.onChange}, fires synchronously.
559
+ */
294
560
  onInput?: ItemEventHandler<$Item>
295
- events?: {
561
+ /**
562
+ * Grouped event handlers, equivalent to the `on[A-Z]`-style
563
+ * callbacks on the schema (e.g. {@link BaseSchema.onFocus}).
564
+ *
565
+ * @see {@link SchemaFields.onInitialize} and other `on`-
566
+ * prefixed properties for per-event documentation.
567
+ */
568
+ events?: SchemaEvents<$Item> & {
569
+ /** @see {@link BaseSchema.onFocus} */
296
570
  focus?: ItemEventHandler<$Item>
571
+ /** @see {@link BaseSchema.onBlur} */
297
572
  blur?: ItemEventHandler<$Item>
298
- change?: ItemEventHandler<$Item>
573
+ /** @see {@link BaseSchema.onInput} */
299
574
  input?: ItemEventHandler<$Item>
300
575
  }
301
576
  }
302
577
 
303
578
  export interface SchemaSourceMixin<$Item> {
579
+ /**
580
+ * The form schema used for editing items.
581
+ */
582
+ form?: ResolvableForm<$Item>
583
+ /**
584
+ * Multiple form schemas keyed by item type, for
585
+ * sources with polymorphic content.
586
+ */
587
+ forms?: {
588
+ [key: string]: ResolvableForm
589
+ }
590
+ /**
591
+ * The label given to items. If no itemLabel is given,
592
+ * the default is the 'name' property of the item,
593
+ * followed by the label of the form (plus item id)
594
+ * and other defaults.
595
+ */
596
+ itemLabel?:
597
+ | OrItemAccessor<
598
+ $Item,
599
+ {},
600
+ | string
601
+ | {
602
+ text?: string
603
+ prefix?: string
604
+ suffix?: string
605
+ }
606
+ >
607
+ | false
608
+ /**
609
+ * The columns displayed in the table. Can be an array
610
+ * of property names or an object with column schemas.
611
+ */
612
+ columns?: Columns<$Item> | (keyof $Item)[]
613
+ /**
614
+ * Scope names as defined on the model. When set, the
615
+ * admin renders scope buttons allowing the user to
616
+ * switch between them while editing.
617
+ */
618
+ scopes?:
619
+ | string[]
620
+ | {
621
+ [scopeName: string]:
622
+ | {
623
+ label?: string
624
+ hint?: string
625
+ defaultScope?: boolean
626
+ }
627
+ | string
628
+ }
629
+ /**
630
+ * Use a Vue component to render the items.
631
+ */
632
+ component?: Resolvable<VueComponent>
633
+ /**
634
+ * Custom render function for items.
635
+ */
636
+ render?: ItemAccessor<$Item, {}, string>
637
+ /**
638
+ * Maximum nesting depth for nested sources.
639
+ *
640
+ * @defaultValue `1`
641
+ */
642
+ maxDepth?: number
643
+ /** Buttons for the source. */
644
+ buttons?: Buttons<$Item>
645
+ /**
646
+ * Whether to wrap primitive values in objects.
647
+ */
648
+ wrapPrimitives?: boolean
649
+ /**
650
+ * URL path for the source items.
651
+ */
652
+ path?: string
653
+ /**
654
+ * The property name used as the item's unique
655
+ * identifier.
656
+ *
657
+ * @defaultValue `'id'`
658
+ */
659
+ idKey?: string
660
+ /**
661
+ * Whether the data is stored under its own key in the parent item.
662
+ * When `true`, an "address" source stores data at `item.address`.
663
+ * When `false`, its fields are stored directly on `item`.
664
+ *
665
+ * @defaultValue `true`
666
+ */
667
+ nested?: boolean
668
+ /**
669
+ * Inline form components. When defined, items are
670
+ * edited inline rather than navigating to a separate
671
+ * page.
672
+ */
673
+ components?: Components<$Item>
304
674
  /**
305
675
  * The number of items displayed per page. When not provided, all items are
306
676
  * rendered.
@@ -308,8 +678,15 @@ export interface SchemaSourceMixin<$Item> {
308
678
  * @defaultValue `false`
309
679
  */
310
680
  paginate?: OrItemAccessor<$Item, {}, number>
311
- // TODO: document inlined
312
681
  /**
682
+ * The default page number for paginated sources.
683
+ */
684
+ page?: number
685
+ /**
686
+ * Whether to display items inline in an expandable form rather
687
+ * than navigating to a separate page. Also implicitly `true`
688
+ * when `components` is defined on the schema.
689
+ *
313
690
  * @defaultValue `false`
314
691
  */
315
692
  inlined?: OrItemAccessor<$Item, {}, boolean>
@@ -347,21 +724,42 @@ export interface SchemaSourceMixin<$Item> {
347
724
  * @defaultValue `null`
348
725
  */
349
726
  collapsible?: OrItemAccessor<$Item, {}, boolean | null>
727
+ /** Whether the inlined form is collapsed. */
728
+ collapsed?: OrItemAccessor<$Item, {}, boolean>
350
729
  /**
351
- * Whether an inlined form is collapsed.
730
+ * Default sort configuration.
731
+ */
732
+ defaultSort?:
733
+ | string
734
+ | { key: string; order: 'asc' | 'desc' }
735
+ /**
736
+ * Default scope name as defined on the model.
737
+ */
738
+ defaultScope?: string
739
+ /**
740
+ * Resource configuration for loading data from the
741
+ * API.
352
742
  */
353
- collapsed?: OrItemAccessor<$Item, {}, boolean>
354
743
  resource?: Resource
744
+ /**
745
+ * The name of a view to reference for form schemas and
746
+ * editing navigation.
747
+ */
748
+ view?: string
355
749
  }
356
750
 
357
751
  export type SchemaOptionsOption<$Value> =
358
752
  | { label: string; value: $Value }
359
753
  | $Value
360
754
  export type SchemaOptions<$Item, $Option = any> =
361
- | SchemaOptionsOption<$Option[]>
755
+ | SchemaOptionsOption<$Option>[]
362
756
  | {
363
757
  /**
364
758
  * The function which is called to load the options.
759
+ * Can be a double-function: the outer function
760
+ * receives the `DitoContext` and returns an inner
761
+ * function that is called to fetch the actual data,
762
+ * enabling reactive dependency tracking.
365
763
  */
366
764
  data?: OrItemAccessor<
367
765
  $Item,
@@ -377,23 +775,112 @@ export type SchemaOptions<$Item, $Option = any> =
377
775
  */
378
776
  label?: keyof $Option | ItemAccessor<$Item, { option: $Option }, string>
379
777
  /**
380
- * Either the key of the option property which should be treated as
381
- * the value or a function returning the option value.
778
+ * Either the key of the option property which should be
779
+ * treated as the value or a function returning the option
780
+ * value.
382
781
  *
383
- * @defaultValue `'value'` when no label is supplied and the options are
384
- * objects
782
+ * @defaultValue `'id'` when `relate` is set, otherwise
783
+ * `'value'` when the options are objects.
385
784
  */
386
- // TODO: when relate is set, the default value is 'id'
387
785
  value?: keyof $Option | ItemAccessor<$Item, { option: $Option }>
388
786
  /**
389
787
  * The key of the option property which should used to group the options.
390
788
  */
391
789
  groupBy?: keyof $Option
790
+ /**
791
+ * Custom equality function for comparing options.
792
+ */
793
+ equals?: (
794
+ a: $Option,
795
+ b: $Option
796
+ ) => boolean
797
+ /**
798
+ * Relative path to resolve options from data
799
+ * within the same form. Uses filesystem-style
800
+ * path notation: `..` navigates up, `/` navigates
801
+ * into nested properties.
802
+ *
803
+ * @example Sibling data
804
+ * ```js
805
+ * // Form data: { tags: [...], selectedTags: [...] }
806
+ * selectedTags: {
807
+ * type: 'checkboxes',
808
+ * relate: true,
809
+ * options: { dataPath: '../tags' }
810
+ * }
811
+ * ```
812
+ *
813
+ * @example Parent data
814
+ * ```js
815
+ * // Root data: { categories: [...], items: [{ category: ... }] }
816
+ * // Inside a form for items:
817
+ * category: {
818
+ * type: 'select',
819
+ * options: { dataPath: '../../../categories' }
820
+ * }
821
+ * ```
822
+ */
823
+ dataPath?: string
392
824
  }
393
825
 
394
826
  export interface SchemaOptionsMixin<$Item, $Option = any> {
827
+ /** The available options for selection. */
395
828
  options?: SchemaOptions<$Item, $Option>
829
+ /**
830
+ * When true, treats options as related objects. The
831
+ * full object is used during editing, but only the
832
+ * `id` reference is stored on save.
833
+ */
396
834
  relate?: boolean
835
+ /**
836
+ * The property key used as the identifier when
837
+ * relating options. Only relevant when `relate` is
838
+ * `true`.
839
+ *
840
+ * @defaultValue `'id'`
841
+ *
842
+ * Note: Only `'id'` is currently supported.
843
+ */
844
+ relateBy?: string
845
+ /**
846
+ * The key of the option property which should be used to
847
+ * group the options.
848
+ */
849
+ groupBy?: OrItemAccessor<$Item, {}, string>
850
+ /**
851
+ * When defined, a search input field will be added to allow
852
+ * searching for specific options.
853
+ */
854
+ search?:
855
+ | ItemAccessor<$Item, { query: string }, OrPromiseOf<$Option[]>>
856
+ | {
857
+ /**
858
+ * Filters options based on the search `query`
859
+ * available in the context. Returns matching
860
+ * options, optionally as a promise.
861
+ */
862
+ filter?: ItemAccessor<$Item, { query: string }, OrPromiseOf<$Option[]>>
863
+ /**
864
+ * Debounce config for the filter. Delays
865
+ * invocation until input pauses.
866
+ */
867
+ debounce?:
868
+ | number
869
+ | {
870
+ delay: number
871
+ immediate?: boolean
872
+ }
873
+ }
874
+ /**
875
+ * Whether the selected option can be edited by navigating
876
+ * to it.
877
+ */
878
+ editable?: OrItemAccessor<$Item, {}, boolean>
879
+ /**
880
+ * The name of a view to reference for form schemas and
881
+ * editing navigation.
882
+ */
883
+ view?: string
397
884
  }
398
885
 
399
886
  export interface SchemaNumberMixin<$Item> {
@@ -420,120 +907,320 @@ export interface SchemaNumberMixin<$Item> {
420
907
  * The amount of decimals to round to.
421
908
  */
422
909
  decimals?: OrItemAccessor<$Item, {}, number>
910
+ /** Validation rules for numeric constraints. */
423
911
  rules?: Omit<SchemaNumberMixin<$Item>, 'rules'> & {
912
+ /** Restrict the value to whole numbers. */
424
913
  integer?: boolean
425
914
  }
426
915
  }
427
916
 
428
- export type ComponentSchema<$Item = any> = BaseSchema<$Item> & {
429
- type: 'component'
917
+ export interface SchemaTextMixin<$Item> {
430
918
  /**
431
- * Use a Vue component to render the component. The component is specified
432
- * like this: import(...).
919
+ * Whether to trim whitespace from the value.
433
920
  */
434
- component: Resolvable<VueComponent>
921
+ trim?: OrItemAccessor<$Item, {}, boolean>
435
922
  }
436
923
 
437
- export type InputSchema<$Item = any> = BaseSchema<$Item> & {
924
+ export type SchemaAffix =
925
+ | string
926
+ | {
927
+ /** The component type name for the affix. */
928
+ type?: string
929
+ /** Plain text content for the affix. */
930
+ text?: string
931
+ /** Raw HTML content for the affix. */
932
+ html?: string
933
+ /** Conditionally display the affix. */
934
+ if?: OrItemAccessor<any, {}, boolean>
935
+ }
936
+
937
+ export interface SchemaAffixMixin<$Item> {
438
938
  /**
439
- * The type of the component.
939
+ * Prefix content displayed before the input.
440
940
  */
441
- type:
442
- | 'text'
443
- | 'email'
444
- | 'url'
445
- | 'hostname'
446
- | 'domain'
447
- | 'tel'
448
- | 'password'
449
- | 'creditcard'
450
- | 'computed'
451
- rules?: {
452
- text?: boolean
453
- email?: boolean
454
- url?: boolean
455
- hostname?: boolean
456
- domain?: boolean
457
- // TODO: check why there is no 'tel' validation
458
- // tel: boolean,
459
- password?: boolean
460
- creditcard?: boolean
461
- }
941
+ prefix?: OrArrayOf<SchemaAffix>
942
+ /**
943
+ * Suffix content displayed after the input.
944
+ */
945
+ suffix?: OrArrayOf<SchemaAffix>
462
946
  }
463
947
 
464
- export type DateSchema<$Item = any> = BaseSchema<$Item> & {
948
+ export interface SchemaDataMixin<$Item> {
465
949
  /**
466
- * The type of the component.
950
+ * Data source for the component.
467
951
  */
468
- type: 'date' | 'datetime' | 'time'
952
+ data?: OrItemAccessor<$Item, {}, any>
469
953
  /**
470
- * @defaultValue `En/US`
954
+ * Path to retrieve data from parent context.
471
955
  */
472
- locale?: string
473
- dateFormat?: OrItemAccessor<$Item, {}, DateFormat>
956
+ dataPath?: string
474
957
  }
475
958
 
476
- export type ButtonSchema<
477
- $Item,
478
- $EventHandler = ItemEventHandler<$Item>
479
- > = BaseSchema<$Item> & {
959
+ /**
960
+ * Properties shared by all schemas that support custom
961
+ * instance members and lifecycle events — component
962
+ * schemas, views, and forms.
963
+ */
964
+ export interface SchemaFields<$Item> {
480
965
  /**
481
- * The type of the component.
966
+ * Methods accessible via `this` from event handlers,
967
+ * computed properties, watchers, and templates. Unlike
968
+ * event handlers, methods do not receive a context
969
+ * parameter — use `this.context` to access it.
482
970
  */
483
- type: 'button' | 'submit'
484
- closeForm?: OrItemAccessor<$Item, {}, boolean>
485
- text?: OrItemAccessor<$Item, {}, string>
486
- resource?: Resource
487
- onClick?: $EventHandler
488
- onSuccess?: $EventHandler
489
- onError?: $EventHandler
490
- events?: {
491
- click?: $EventHandler
492
- success?: $EventHandler
493
- error?: $EventHandler
494
- }
495
- }
971
+ methods?: Record<string, (...args: any[]) => any> &
972
+ ThisType<DitoComponentInstance<$Item>>
496
973
 
497
- export type SwitchSchema<$Item = any> = BaseSchema<$Item> & {
498
974
  /**
499
- * The type of the component.
975
+ * Computed properties defined on the component. Can be a
976
+ * getter function or a `{ get, set }` object for
977
+ * read-write computed properties. Getters and setters
978
+ * are called with the component as `this`. Like
979
+ * {@link SchemaFields.methods}, use `this.context` to
980
+ * access the context.
500
981
  */
501
- type: 'switch'
502
- labels?: {
503
- /**
504
- * The displayed label when the switch is checked.
505
- *
506
- * @defaultValue `'on'`
507
- */
508
- checked?: string
509
- /**
510
- * The displayed label when the switch is unchecked.
511
- *
512
- * @defaultValue `'off'`
513
- */
514
- unchecked?: string
515
- }
516
- }
517
-
518
- export type NumberSchema<$Item = any> = SchemaNumberMixin<$Item> &
519
- BaseSchema<$Item> & {
520
- /**
521
- * The type of the component.
522
- */
523
- type: 'number' | 'integer'
982
+ computed?: Record<
983
+ string,
984
+ | ((this: DitoComponentInstance<$Item>) => any)
985
+ | {
986
+ get: (this: DitoComponentInstance<$Item>) => any
987
+ set: (
988
+ this: DitoComponentInstance<$Item>,
989
+ value: any
990
+ ) => void
991
+ }
992
+ >
993
+
994
+ /**
995
+ * Watchers on data properties or expression paths. Keys
996
+ * are either component names (resolved to
997
+ * `data.${name}`) or arbitrary expression paths. Can be
998
+ * a plain handler function or an object with `handler`,
999
+ * `deep`, and `immediate` options.
1000
+ */
1001
+ watch?:
1002
+ | WatchHandlers<$Item>
1003
+ | ((
1004
+ this: DitoComponentInstance<$Item>
1005
+ ) => WatchHandlers<$Item>)
1006
+
1007
+ /**
1008
+ * Panel schemas rendered in the sidebar. Panels share
1009
+ * the parent's data unless their schema defines its
1010
+ * own `data` property.
1011
+ */
1012
+ panels?: Record<string, PanelSchema<$Item>>
1013
+
1014
+ /**
1015
+ * Called when the schema's component tree is set up.
1016
+ */
1017
+ onInitialize?: ItemEventHandler<$Item>
1018
+ /**
1019
+ * Called once when the schema component is unmounted
1020
+ * from the DOM.
1021
+ */
1022
+ onDestroy?: ItemEventHandler<$Item>
1023
+ /**
1024
+ * Called after data has been fetched from the API.
1025
+ * Fires after all reactive updates have propagated.
1026
+ */
1027
+ onLoad?: ItemEventHandler<$Item>
1028
+ /**
1029
+ * Called after a value change is committed. Fires
1030
+ * after all reactive updates have propagated. Bubbles
1031
+ * to parent schemas — return `false` to stop
1032
+ * propagation.
1033
+ */
1034
+ onChange?: ItemEventHandler<$Item>
1035
+ }
1036
+
1037
+ /** @deprecated Use {@link SchemaFields} instead. */
1038
+ export type SchemaSetupMixin<$Item = any> = SchemaFields<$Item>
1039
+
1040
+ /**
1041
+ * Properties shared by route-level schemas
1042
+ * ({@link ViewSchema} and {@link Form}).
1043
+ */
1044
+ export interface SchemaRoute<$Item = any>
1045
+ extends SchemaFields<$Item>,
1046
+ SchemaOpen<$Item> {
1047
+ /**
1048
+ * Renders with reduced spacing.
1049
+ *
1050
+ * @defaultValue `false`
1051
+ */
1052
+ compact?: boolean
1053
+ /**
1054
+ * Resource configuration for loading data from
1055
+ * the API.
1056
+ */
1057
+ resource?: Resource
1058
+ /**
1059
+ * Enables copy/paste for the data. Set to `true`
1060
+ * for default behavior or provide custom
1061
+ * copy/paste handlers.
1062
+ */
1063
+ clipboard?: ClipboardConfig
1064
+ /**
1065
+ * Additional reactive data properties merged into
1066
+ * the component's data scope.
1067
+ */
1068
+ data?: OrItemAccessor<$Item, {}, Record<string, any>>
1069
+ /**
1070
+ * Whether to use a wider content area.
1071
+ *
1072
+ * @defaultValue `false`
1073
+ */
1074
+ wide?: OrItemAccessor<$Item, {}, boolean>
1075
+ /**
1076
+ * Custom breadcrumb text shown in page navigation.
1077
+ *
1078
+ * @defaultValue `${breadcrumbPrefix} ${label}`
1079
+ */
1080
+ breadcrumb?: string
1081
+ buttons?: Buttons<$Item>
1082
+ /**
1083
+ * Conditionally display this view or form.
1084
+ */
1085
+ if?: OrItemAccessor<$Item, {}, boolean>
1086
+ }
1087
+
1088
+ /** @deprecated Use {@link SchemaRoute} instead. */
1089
+ export type SchemaRouteMixin<$Item = any> = SchemaRoute<$Item>
1090
+
1091
+ export interface ComponentSchema<$Item = any> extends BaseSchema<$Item> {
1092
+ type: 'component'
1093
+ /**
1094
+ * Use a Vue component to render the component. The component is specified
1095
+ * like this: import(...).
1096
+ */
1097
+ component: Resolvable<VueComponent>
1098
+ }
1099
+
1100
+ export interface InputSchema<$Item = any>
1101
+ extends BaseSchema<$Item>,
1102
+ SchemaTextMixin<$Item>,
1103
+ SchemaAffixMixin<$Item> {
1104
+ /**
1105
+ * The type of the component.
1106
+ */
1107
+ type:
1108
+ | 'text'
1109
+ | 'email'
1110
+ | 'url'
1111
+ | 'hostname'
1112
+ | 'domain'
1113
+ | 'tel'
1114
+ | 'password'
1115
+ | 'creditcard'
1116
+ rules?: {
1117
+ text?: boolean
1118
+ email?: boolean
1119
+ url?: boolean
1120
+ hostname?: boolean
1121
+ domain?: boolean
1122
+ password?: boolean
1123
+ creditcard?: boolean
1124
+ }
1125
+ }
1126
+
1127
+ export interface DateSchema<$Item = any>
1128
+ extends BaseSchema<$Item>,
1129
+ SchemaAffixMixin<$Item> {
1130
+ /**
1131
+ * The type of the component.
1132
+ */
1133
+ type: 'date' | 'datetime' | 'time'
1134
+ /**
1135
+ * @defaultValue `En/US`
1136
+ */
1137
+ locale?: string
1138
+ /**
1139
+ * Format configuration for date/time display.
1140
+ */
1141
+ formats?: OrItemAccessor<
1142
+ $Item,
1143
+ {},
1144
+ {
1145
+ date?: DateFormat
1146
+ time?: TimeFormat
1147
+ }
1148
+ >
1149
+ /**
1150
+ * @deprecated Use `formats` instead.
1151
+ */
1152
+ dateFormat?: OrItemAccessor<$Item, {}, DateFormat>
1153
+ }
1154
+
1155
+ export interface ButtonSchema<$Item = any>
1156
+ extends BaseSchema<$Item>,
1157
+ SchemaAffixMixin<$Item> {
1158
+ /**
1159
+ * The type of the component.
1160
+ */
1161
+ type: 'button' | 'submit'
1162
+ closeForm?: OrItemAccessor<$Item, {}, boolean>
1163
+ text?: OrItemAccessor<$Item, {}, string>
1164
+ resource?: Resource
1165
+ onClick?: ItemEventHandler<$Item>
1166
+ onSuccess?: ItemEventHandler<$Item>
1167
+ onError?: ErrorEventHandler<$Item>
1168
+ events?: {
1169
+ click?: ItemEventHandler<$Item>
1170
+ success?: ItemEventHandler<$Item>
1171
+ error?: ErrorEventHandler<$Item>
524
1172
  }
1173
+ }
525
1174
 
526
- export type SliderSchema<$Item = any> = SchemaNumberMixin<$Item> &
527
- BaseSchema<$Item> & {
1175
+ export interface SwitchSchema<$Item = any> extends BaseSchema<$Item> {
1176
+ /**
1177
+ * The type of the component.
1178
+ */
1179
+ type: 'switch'
1180
+ labels?: {
528
1181
  /**
529
- * The type of the component.
1182
+ * The displayed label when the switch is checked.
1183
+ *
1184
+ * @defaultValue `'on'`
1185
+ */
1186
+ checked?: string
1187
+ /**
1188
+ * The displayed label when the switch is unchecked.
1189
+ *
1190
+ * @defaultValue `'off'`
530
1191
  */
531
- type: 'slider'
532
- // TODO: document what the input SliderSchema option does
533
- input?: OrItemAccessor<$Item>
1192
+ unchecked?: string
534
1193
  }
1194
+ }
1195
+
1196
+ export interface NumberSchema<$Item = any>
1197
+ extends SchemaNumberMixin<$Item>,
1198
+ BaseSchema<$Item>,
1199
+ SchemaAffixMixin<$Item> {
1200
+ /**
1201
+ * The type of the component.
1202
+ */
1203
+ type: 'number' | 'integer'
1204
+ }
1205
+
1206
+ export interface SliderSchema<$Item = any>
1207
+ extends SchemaNumberMixin<$Item>,
1208
+ BaseSchema<$Item> {
1209
+ /**
1210
+ * The type of the component.
1211
+ */
1212
+ type: 'slider'
1213
+ /**
1214
+ * Whether to show a number input alongside the slider.
1215
+ *
1216
+ * @defaultValue `true`
1217
+ */
1218
+ input?: OrItemAccessor<$Item, {}, boolean>
1219
+ }
535
1220
 
536
- export type TextareaSchema<$Item = any> = BaseSchema<$Item> & {
1221
+ export interface TextareaSchema<$Item = any>
1222
+ extends BaseSchema<$Item>,
1223
+ SchemaTextMixin<$Item> {
537
1224
  /**
538
1225
  * The type of the component.
539
1226
  */
@@ -541,7 +1228,7 @@ export type TextareaSchema<$Item = any> = BaseSchema<$Item> & {
541
1228
  /**
542
1229
  * Whether the input element is resizable.
543
1230
  */
544
- resizable?: boolean
1231
+ resizable?: OrItemAccessor<$Item, {}, boolean>
545
1232
  /**
546
1233
  * The amount of visible lines.
547
1234
  *
@@ -550,7 +1237,7 @@ export type TextareaSchema<$Item = any> = BaseSchema<$Item> & {
550
1237
  lines?: number
551
1238
  }
552
1239
 
553
- export type CodeSchema<$Item = any> = BaseSchema<$Item> & {
1240
+ export interface CodeSchema<$Item = any> extends BaseSchema<$Item> {
554
1241
  /**
555
1242
  * The type of the component.
556
1243
  */
@@ -560,22 +1247,26 @@ export type CodeSchema<$Item = any> = BaseSchema<$Item> & {
560
1247
  *
561
1248
  * @defaultValue `js`
562
1249
  */
563
- language?: string
1250
+ language?: OrItemAccessor<$Item, {}, string>
564
1251
  /**
565
1252
  * The indent size.
566
1253
  *
567
1254
  * @defaultValue `2`
568
1255
  */
569
- indentSize?: number
1256
+ indentSize?: OrItemAccessor<$Item, {}, number>
570
1257
  /**
571
1258
  * The amount of visible lines.
572
1259
  *
573
1260
  * @defaultValue `3`
574
1261
  */
575
- lines?: number
1262
+ lines?: OrItemAccessor<$Item, {}, number>
1263
+ /**
1264
+ * Whether the input element is resizable.
1265
+ */
1266
+ resizable?: OrItemAccessor<$Item, {}, boolean>
576
1267
  }
577
1268
 
578
- export type MarkupSchema<$Item = any> = BaseSchema<$Item> & {
1269
+ export interface MarkupSchema<$Item = any> extends BaseSchema<$Item> {
579
1270
  /**
580
1271
  * The type of the component.
581
1272
  */
@@ -599,7 +1290,13 @@ export type MarkupSchema<$Item = any> = BaseSchema<$Item> & {
599
1290
  */
600
1291
  lines?: number
601
1292
 
602
- // TODO: document enableRules
1293
+ /**
1294
+ * Whether TipTap input and paste rules are enabled. When
1295
+ * `true`, both input and paste rules are active. Can also be
1296
+ * an object to control input and paste rules independently.
1297
+ *
1298
+ * @defaultValue `false`
1299
+ */
603
1300
  enableRules?: OrItemAccessor<
604
1301
  $Item,
605
1302
  {},
@@ -609,6 +1306,11 @@ export type MarkupSchema<$Item = any> = BaseSchema<$Item> & {
609
1306
  paste: boolean
610
1307
  }
611
1308
  >
1309
+ /**
1310
+ * Whether Enter creates a hard break instead of a new
1311
+ * paragraph.
1312
+ */
1313
+ hardBreak?: boolean
612
1314
  marks?: {
613
1315
  bold?: boolean
614
1316
  italic?: boolean
@@ -616,6 +1318,8 @@ export type MarkupSchema<$Item = any> = BaseSchema<$Item> & {
616
1318
  strike?: boolean
617
1319
  small?: boolean
618
1320
  code?: boolean
1321
+ subscript?: boolean
1322
+ superscript?: boolean
619
1323
  link?: boolean
620
1324
  }
621
1325
  nodes?: {
@@ -628,24 +1332,31 @@ export type MarkupSchema<$Item = any> = BaseSchema<$Item> & {
628
1332
  }
629
1333
  tools?: {
630
1334
  history?: boolean
1335
+ footnotes?: boolean
631
1336
  }
632
1337
  }
633
1338
 
634
- export type LabelSchema<$Item = any> = BaseSchema<$Item> & {
1339
+ export interface LabelSchema<$Item = any> extends BaseSchema<$Item> {
635
1340
  /**
636
1341
  * The type of the component.
637
1342
  */
638
1343
  type: 'label'
639
1344
  }
640
1345
 
641
- export type HiddenSchema<$Item = any> = BaseSchema<$Item> & {
1346
+ /**
1347
+ * A non-visible component that includes a computed or
1348
+ * derived value in the form data without rendering it.
1349
+ */
1350
+ export interface HiddenSchema<$Item = any>
1351
+ extends BaseSchema<$Item>,
1352
+ SchemaDataMixin<$Item> {
642
1353
  /**
643
1354
  * The type of the component.
644
1355
  */
645
1356
  type: 'hidden'
646
1357
  }
647
1358
 
648
- export type UploadSchema<$Item = any> = BaseSchema<$Item> & {
1359
+ export interface UploadSchema<$Item = any> extends BaseSchema<$Item> {
649
1360
  /**
650
1361
  * The type of the component.
651
1362
  */
@@ -679,103 +1390,262 @@ export type UploadSchema<$Item = any> = BaseSchema<$Item> & {
679
1390
  * @see {@link https://github.com/patrickkettner/filesize-parser/blob/master/test.js String Examples}
680
1391
  */
681
1392
  maxSize?: string | number
682
- // TODO: UploadSchema draggable type
683
- draggable?: boolean
1393
+ /**
1394
+ * Whether uploaded files can be reordered by dragging.
1395
+ *
1396
+ * @defaultValue `false`
1397
+ */
1398
+ draggable?: OrItemAccessor<$Item, {}, boolean>
684
1399
  /**
685
1400
  * Whether files can be deleted.
686
1401
  */
687
- deletable?: boolean
1402
+ deletable?: OrItemAccessor<$Item, {}, boolean>
1403
+ /**
1404
+ * Custom render function for file display.
1405
+ */
1406
+ render?: ItemAccessor<$Item, {}, string>
1407
+ /**
1408
+ * Whether to display thumbnails for uploaded files, or
1409
+ * thumbnail size.
1410
+ */
1411
+ thumbnails?: OrItemAccessor<$Item, {}, boolean | string>
1412
+ /**
1413
+ * URL or function returning URL for file thumbnails.
1414
+ */
1415
+ thumbnailUrl?: OrItemAccessor<$Item, {}, string>
1416
+ /**
1417
+ * URL or function returning URL for file downloads.
1418
+ */
1419
+ downloadUrl?: OrItemAccessor<$Item, {}, string>
1420
+ }
1421
+
1422
+ export interface MultiselectSchema<$Item = any, $Option = any>
1423
+ extends BaseSchema<$Item>,
1424
+ SchemaOptionsMixin<$Item, $Option>,
1425
+ SchemaAffixMixin<$Item> {
1426
+ /**
1427
+ * The type of the component.
1428
+ */
1429
+ type: 'multiselect'
1430
+ /**
1431
+ * Whether more than one option can be selected.
1432
+ *
1433
+ * @defaultValue `false`
1434
+ */
1435
+ multiple?: boolean
1436
+ /**
1437
+ * Whether to enable a search input field in the dropdown
1438
+ * for filtering options.
1439
+ *
1440
+ * @defaultValue `false`
1441
+ */
1442
+ searchable?: boolean
1443
+ /**
1444
+ * Whether the dropdown stays open after selecting an option,
1445
+ * useful for making multiple selections without repeated
1446
+ * opening.
1447
+ *
1448
+ * @defaultValue `false`
1449
+ */
1450
+ stayOpen?: boolean
1451
+ /**
1452
+ * Whether users can create new options by typing and
1453
+ * pressing Enter, adding custom tags not in the options
1454
+ * list.
1455
+ *
1456
+ * @defaultValue `false`
1457
+ */
1458
+ taggable?: boolean
1459
+ }
1460
+
1461
+ export interface SelectSchema<$Item = any>
1462
+ extends BaseSchema<$Item>,
1463
+ SchemaOptionsMixin<$Item>,
1464
+ SchemaAffixMixin<$Item> {
1465
+ /**
1466
+ * The type of the component.
1467
+ */
1468
+ type: 'select'
1469
+ }
1470
+
1471
+ export interface RadioSchema<$Item = any>
1472
+ extends BaseSchema<$Item>,
1473
+ SchemaOptionsMixin<$Item> {
1474
+ /**
1475
+ * The type of the component.
1476
+ */
1477
+ type: 'radio'
1478
+ /**
1479
+ * @defaultValue `'vertical'`
1480
+ */
1481
+ layout?: 'horizontal' | 'vertical'
1482
+ }
1483
+
1484
+ type SectionContent<$Data> = {
1485
+ /** The section's field components. */
1486
+ components?: Components<$Data>
1487
+ /**
1488
+ * A form schema for the section's content. Use this
1489
+ * instead of `components` to get form-level options
1490
+ * like `label`, `tabs`, and `mutate`.
1491
+ */
1492
+ form?: ResolvableForm<$Data>
1493
+ /**
1494
+ * Display several schemas in different tabs
1495
+ * within the section.
1496
+ */
1497
+ tabs?: Record<
1498
+ string,
1499
+ Omit<Form<$Data>, 'tabs' | 'type'> & {
1500
+ type: 'tab'
1501
+ defaultTab?: OrItemAccessor<$Data, {}, boolean>
1502
+ }
1503
+ >
688
1504
  }
689
1505
 
690
- export type MultiselectSchema<$Item = any, $Option = any> = BaseSchema<$Item> &
691
- SchemaOptionsMixin<$Item, $Option> & {
1506
+ /**
1507
+ * A visual grouping of components within a form.
1508
+ *
1509
+ * By default, the section's components read and write fields
1510
+ * on the parent item directly. When `nested` is `true`, the
1511
+ * section's fields are stored under their own key on the
1512
+ * parent item instead (e.g. `item.address`).
1513
+ *
1514
+ * For non-nested sections, declare the key as `never` in
1515
+ * your item type. For nested sections, declare it as the
1516
+ * object type of the nested data (see {@link Components}).
1517
+ *
1518
+ * @template $Item The parent item type, used for callbacks
1519
+ * like `label` and `collapsible`.
1520
+ * @template $Nested The type of the section's own data.
1521
+ * For nested sections this is the value type at the
1522
+ * section's key (e.g. `Address`). For non-nested sections
1523
+ * this defaults to `$Item`.
1524
+ *
1525
+ * @example Non-nested section (fields stored on parent item)
1526
+ * ```ts
1527
+ * type Item = {
1528
+ * title: string
1529
+ * details: never // UI-only key
1530
+ * }
1531
+ *
1532
+ * const components: Components<Item> = {
1533
+ * details: {
1534
+ * type: 'section',
1535
+ * components: {
1536
+ * title: { type: 'text' }
1537
+ * }
1538
+ * }
1539
+ * }
1540
+ * ```
1541
+ *
1542
+ * @example Nested section (fields stored at `item.address`)
1543
+ * ```ts
1544
+ * type Address = { street: string; city: string }
1545
+ *
1546
+ * type Item = {
1547
+ * title: string
1548
+ * address: Address // data key for nested section
1549
+ * }
1550
+ *
1551
+ * const components: Components<Item> = {
1552
+ * address: {
1553
+ * type: 'section',
1554
+ * nested: true,
1555
+ * components: {
1556
+ * street: { type: 'text' },
1557
+ * city: { type: 'text' }
1558
+ * }
1559
+ * }
1560
+ * }
1561
+ * ```
1562
+ */
1563
+ export type SectionSchema<$Item = any, $Nested = $Item> = BaseSchema<$Item> &
1564
+ SchemaOpen<$Item> & {
692
1565
  /**
693
1566
  * The type of the component.
694
1567
  */
695
- type: 'multiselect'
696
- /**
697
- * Whether more than one option can be selected.
698
- *
699
- * @defaultValue `false`
700
- */
701
- multiple?: boolean
702
- // TODO: document searchable
703
- /**
704
- * @defaultValue `false`
705
- */
706
- searchable?: boolean
707
- // TODO: document stayOpen
708
- /**
709
- * @defaultValue `false`
710
- */
711
- stayOpen?: boolean
1568
+ type: 'section'
712
1569
  /**
713
- * When defined, a search input field will be added to allow searching for
714
- * specific options.
1570
+ * Multiple form schemas keyed by item type, for
1571
+ * sections with polymorphic content.
715
1572
  */
716
- search?: {
717
- filter?: ItemAccessor<$Item, { query: string }, OrPromiseOf<$Option[]>>
718
- debounce?:
719
- | number
720
- | {
721
- delay: number
722
- immediate?: boolean
723
- }
1573
+ forms?: {
1574
+ [key: string]: ResolvableForm
724
1575
  }
725
1576
  /**
1577
+ * Renders the section with reduced spacing.
1578
+ *
726
1579
  * @defaultValue `false`
727
1580
  */
728
- // TODO: document taggable
729
- taggable?: boolean
730
- }
731
-
732
- export type SelectSchema<$Item = any> = BaseSchema<$Item> &
733
- SchemaOptionsMixin<$Item> & {
1581
+ compact?: boolean
734
1582
  /**
735
- * The type of the component.
1583
+ * Enables copy/paste for the section's data.
736
1584
  */
737
- type: 'select'
738
- }
739
-
740
- export type RadioSchema<$Item = any> = BaseSchema<$Item> &
741
- SchemaOptionsMixin<$Item> & {
1585
+ clipboard?: ClipboardConfig
742
1586
  /**
743
- * The type of the component.
1587
+ * Whether the section can be collapsed.
744
1588
  */
745
- type: 'radio'
1589
+ collapsible?: OrItemAccessor<$Item, {}, boolean>
1590
+ /** Whether the section is collapsed. */
1591
+ collapsed?: OrItemAccessor<$Item, {}, boolean>
746
1592
  /**
747
- * @defaultValue `'vertical'`
1593
+ * Grouped event handlers.
748
1594
  */
749
- layout?: 'horizontal' | 'vertical'
750
- }
1595
+ events?: {
1596
+ /**
1597
+ * Called when a collapsible section is toggled
1598
+ * open or closed.
1599
+ */
1600
+ open?: OpenEventHandler<$Item>
1601
+ }
1602
+ } & (
1603
+ | ({
1604
+ /**
1605
+ * Whether the data is stored under its own key
1606
+ * on the parent item. When `true`, an "address"
1607
+ * section stores data at `item.address`. When
1608
+ * `false` or omitted, the section's fields are
1609
+ * stored directly on the parent item.
1610
+ *
1611
+ * @defaultValue `false`
1612
+ */
1613
+ nested?: false
1614
+ } & SectionContent<$Item>)
1615
+ | ({
1616
+ /**
1617
+ * Whether the data is stored under its own key
1618
+ * on the parent item. When `true`, an "address"
1619
+ * section stores data at `item.address`. When
1620
+ * `false` or omitted, the section's fields are
1621
+ * stored directly on the parent item.
1622
+ *
1623
+ * @defaultValue `false`
1624
+ */
1625
+ nested: true
1626
+ } & SectionContent<$Nested>)
1627
+ )
751
1628
 
752
- export type SectionSchema<$Item = any> = BaseSchema<$Item> & {
1629
+ export interface CheckboxSchema<$Item = any> extends BaseSchema<$Item> {
753
1630
  /**
754
1631
  * The type of the component.
755
1632
  */
756
- type: 'section'
757
- components?: Components<$Item>
1633
+ type: 'checkbox'
758
1634
  }
759
1635
 
760
- export type CheckboxSchema<$Item = any> = BaseSchema<$Item> & {
1636
+ export interface CheckboxesSchema<$Item = any>
1637
+ extends BaseSchema<$Item>,
1638
+ SchemaOptionsMixin<$Item> {
761
1639
  /**
762
1640
  * The type of the component.
763
1641
  */
764
- type: 'checkbox'
1642
+ type: 'checkboxes'
1643
+ /**
1644
+ * @defaultValue `'vertical'`
1645
+ */
1646
+ layout?: 'horizontal' | 'vertical'
765
1647
  }
766
1648
 
767
- export type CheckboxesSchema<$Item = any> = BaseSchema<$Item> &
768
- SchemaOptionsMixin<$Item> & {
769
- /**
770
- * The type of the component.
771
- */
772
- type: 'checkboxes'
773
- /**
774
- * @defaultValue `'vertical'`
775
- */
776
- layout?: 'horizontal' | 'vertical'
777
- }
778
-
779
1649
  export type ColorFormat =
780
1650
  | 'rgb'
781
1651
  | 'prgb'
@@ -787,7 +1657,9 @@ export type ColorFormat =
787
1657
  | 'name'
788
1658
  | 'hsl'
789
1659
  | 'hsv'
790
- export type ColorSchema<$Item = any> = BaseSchema<$Item> & {
1660
+ export interface ColorSchema<$Item = any>
1661
+ extends BaseSchema<$Item>,
1662
+ SchemaAffixMixin<$Item> {
791
1663
  /**
792
1664
  * The type of the component.
793
1665
  */
@@ -803,10 +1675,9 @@ export type ColorSchema<$Item = any> = BaseSchema<$Item> & {
803
1675
  */
804
1676
  alpha?: OrItemAccessor<$Item, {}, boolean>
805
1677
  /**
806
- * @defaultValue true
807
- */
808
- // TODO: document inputs
809
- /**
1678
+ * Whether to display input fields for manual color value
1679
+ * entry (e.g. RGB, HSL) in the color picker.
1680
+ *
810
1681
  * @defaultValue `true`
811
1682
  */
812
1683
  inputs?: OrItemAccessor<$Item, {}, boolean>
@@ -818,7 +1689,7 @@ export type ColorSchema<$Item = any> = BaseSchema<$Item> & {
818
1689
  presets?: OrItemAccessor<$Item, {}, string[]>
819
1690
  }
820
1691
 
821
- export type ColumnSchema<$Item = any> = {
1692
+ export type ColumnSchema<$Item = any, $Value = any> = {
822
1693
  /**
823
1694
  * The label of the column.
824
1695
  * @defaultValue The labelized column key.
@@ -838,7 +1709,7 @@ export type ColumnSchema<$Item = any> = {
838
1709
  * If the column is sortable, the column is sorted by value and not by
839
1710
  * rendered name.
840
1711
  */
841
- render?: ItemAccessor<$Item, {}, string>
1712
+ render?: ItemAccessor<$Item, { value: $Value }, string | null | undefined>
842
1713
  /**
843
1714
  * The provided string is applied to the class property of the column
844
1715
  * cell html elements.
@@ -854,91 +1725,69 @@ export type ColumnSchema<$Item = any> = {
854
1725
  * first column to specify `defaultSort`.
855
1726
  */
856
1727
  defaultSort?: 'asc' | 'desc'
857
- /**
858
- * Only displays the column if the item accessor returns `true`
859
- */
860
- if?: ItemAccessor<$Item, {}, boolean>
1728
+ /** Whether to display the column. */
1729
+ if?: OrItemAccessor<$Item, {}, boolean>
861
1730
  }
862
1731
 
1732
+ /**
1733
+ * A form that can be provided directly, as a record of named forms
1734
+ * (e.g. a module namespace from `import()`), wrapped in a promise,
1735
+ * or returned from a function.
1736
+ */
863
1737
  export type ResolvableForm<$Item = any> = Resolvable<Form<$Item>>
864
1738
 
865
- export type ListSchema<$Item = { [key: string]: any }> =
866
- SchemaSourceMixin<$Item> &
867
- BaseSchema<$Item> & {
868
- /**
869
- * The type of the view
870
- */
871
- type: 'list'
872
- /**
873
- * The form.
874
- */
875
- form?: ResolvableForm
876
- /**
877
- * The forms.
878
- */
879
- forms?: {
880
- [key: string]: ResolvableForm
881
- }
882
- /**
883
- * The label given to the items. If no itemLabel is given, the default is
884
- * the 'name' property of the item, followed by label of the form of the
885
- * view (plus item id) and other defaults.
886
- */
887
- itemLabel?: OrItemAccessor<any, {}, string>
888
- /**
889
- * The columns displayed in the table. While columns can be supplied as an
890
- * array where each entry is the name of a property of the item, it is
891
- * usually beneficial to assign an object with further options to the
892
- * columns property.
893
- */
894
- columns?: Record<string, ColumnSchema<$Item>> | (keyof $Item)[]
895
- /**
896
- * Scope names as defined on the model. When set, the admin renders a set
897
- * of scope buttons, allowing the user to switch between them while
898
- * editing.
899
- */
900
- scopes?:
901
- | string[]
902
- | {
903
- [scopeName: string]:
904
- | {
905
- label?: string
906
- hint?: string
907
- defaultScope?: boolean
908
- }
909
- | string
910
- }
911
- /**
912
- * Default scope name as defined on the model.
913
- */
914
- scope?: string
1739
+ export interface ListSchema<$Item = { [key: string]: any }>
1740
+ extends SchemaSourceMixin<$Item>,
1741
+ BaseSchema<$Item>,
1742
+ SchemaOpen<$Item> {
1743
+ /**
1744
+ * The type of the component.
1745
+ */
1746
+ type: 'list'
915
1747
 
916
- // TODO: document filters
917
- filters?: {
918
- [k: string]:
919
- | {
920
- label?: string
921
- filter: 'text'
922
- /**
923
- * @defaultValue `['contains']`
924
- */
925
- operators?: (
926
- | 'contains'
927
- | 'equals'
928
- | 'starts-with'
929
- | 'ends-with'
930
- )[]
931
- }
932
- | {
933
- label?: string
934
- filter: 'date-range'
935
- }
936
- | {
937
- label?: string
938
- components: Components<any>
939
- }
940
- }
941
- }
1748
+ /**
1749
+ * Filter definitions that render a filter panel above the
1750
+ * list, allowing users to filter displayed items. Each
1751
+ * filter can be a text filter with operators, a date-range
1752
+ * filter, or a custom filter with components.
1753
+ */
1754
+ filters?: {
1755
+ /**
1756
+ * Whether the filters panel header sticks to
1757
+ * the top of the scroll container.
1758
+ */
1759
+ sticky?: boolean
1760
+ [k: string]:
1761
+ | {
1762
+ label?: string
1763
+ filter: 'text'
1764
+ /**
1765
+ * @defaultValue `['contains']`
1766
+ */
1767
+ operators?: ('contains' | 'equals' | 'starts-with' | 'ends-with')[]
1768
+ }
1769
+ | {
1770
+ label?: string
1771
+ filter: 'date-range'
1772
+ }
1773
+ | {
1774
+ label?: string
1775
+ components: Components
1776
+ }
1777
+ | boolean
1778
+ | undefined
1779
+ }
1780
+ /**
1781
+ * Grouped event handlers.
1782
+ */
1783
+ events?: {
1784
+ /**
1785
+ * Called when a collapsible inlined list is
1786
+ * toggled open or closed.
1787
+ */
1788
+ open?: OpenEventHandler<$Item>
1789
+ }
1790
+ }
942
1791
 
943
1792
  export type OrItemAccessor<
944
1793
  $Item = any,
@@ -946,87 +1795,186 @@ export type OrItemAccessor<
946
1795
  $ReturnValue = any
947
1796
  > = ItemAccessor<$Item, $Params, $ReturnValue> | $ReturnValue
948
1797
 
1798
+ /**
1799
+ * Merges two object types into a single flat mapped type.
1800
+ * Unlike `A & B`, this preserves contextual typing for
1801
+ * callback parameters in complex intersections.
1802
+ */
1803
+ type Merge<A, B> = {
1804
+ [K in keyof A | keyof B]: K extends keyof B
1805
+ ? B[K]
1806
+ : K extends keyof A
1807
+ ? A[K]
1808
+ : never
1809
+ }
1810
+
949
1811
  export type ItemAccessor<
950
1812
  $Item = any,
951
1813
  $Params extends {} = {},
952
1814
  $ReturnValue = any
953
- > = (params: DitoContext<$Item> & $Params) => $ReturnValue
1815
+ > = (params: Merge<DitoContext<$Item>, $Params>) => $ReturnValue
954
1816
 
955
1817
  export type DitoContext<$Item = any> = {
956
1818
  /**
957
- * `nested` is `true` when the data-path points a value inside an item, and
958
- * `false` when it points to the item itself.
1819
+ * `true` when the data-path points to a value inside
1820
+ * an item, `false` when it points to the item itself.
959
1821
  */
960
1822
  nested: boolean
1823
+ /** The current value of the component. */
961
1824
  value: any
1825
+ /** Full dot-separated path to the current data. */
962
1826
  dataPath: string
1827
+ /** The property name of the current component. */
963
1828
  name: string
964
- index: any
965
- itemDataPath: any
966
- parentItemDataPath: any
967
- itemIndex: any
968
- parentItemIndex: any
969
- item: $Item
970
- /**
971
- * NOTE: `parentItem` isn't the closest data parent to `item`, it's the
972
- * closest parent that isn't an array, e.g. for relations or nested JSON
973
- * data. This is why the term `item` was chosen over `data`, e.g. VS the
974
- * use of `parentData` in server-sided validation, which is the closest
975
- * parent. If needed, we could expose this data here too, as we can do all
976
- * sorts of data processing with `rootData` and `dataPath`.
1829
+ /** The index within a list, if applicable. */
1830
+ index: number | null
1831
+ /**
1832
+ * Data path of the closest item ancestor.
1833
+ */
1834
+ itemDataPath: string
1835
+ /**
1836
+ * Data path of the parent item ancestor.
1837
+ */
1838
+ parentItemDataPath: string
1839
+ /** Index of the closest item in its list. */
1840
+ itemIndex: number | null
1841
+ /** Index of the parent item in its list. */
1842
+ parentItemIndex: number | null
1843
+ /** The current data item. */
1844
+ item: OmitNever<$Item>
1845
+ /**
1846
+ * NOTE: `parentItem` isn't the closest data parent to `item`,
1847
+ * it's the closest parent that isn't an array, e.g. for
1848
+ * relations or nested JSON data. This is why the term `item`
1849
+ * was chosen over `data`, e.g. VS the use of `parentData` in
1850
+ * server-sided validation, which is the closest parent.
977
1851
  */
978
1852
  parentItem: any
1853
+ /** The top-level root data item. */
979
1854
  rootItem: any
1855
+ /**
1856
+ * The cloned data being prepared for server
1857
+ * submission. Available during `process` callbacks,
1858
+ * allowing modification of sibling fields.
1859
+ */
980
1860
  processedItem: any
1861
+ /**
1862
+ * The root-level cloned data being prepared for
1863
+ * server submission.
1864
+ */
1865
+ processedRootItem: any
1866
+ /**
1867
+ * The form's current data processed for clipboard
1868
+ * copy/paste operations.
1869
+ */
981
1870
  clipboardItem: any
1871
+ /** The currently authenticated user. */
982
1872
  user: {
983
1873
  roles?: string[]
984
1874
  hasRole(...roles: string[]): boolean
985
1875
  }
1876
+ /** The admin API configuration. */
986
1877
  api: ApiConfig
987
- views: any
1878
+ /** The schema definition for the current component. */
1879
+ schema: Component | null
1880
+ /** All registered top-level views. */
1881
+ views: Record<string, View>
1882
+ /** All views flattened into a single record. */
1883
+ flattenedViews: Record<string, ViewSchema>
1884
+ /** Display label of the current item. */
988
1885
  itemLabel: string | null
1886
+ /** Display label of the current form. */
989
1887
  formLabel: string | null
990
- component: any
991
- schemaComponent: VueComponent | null
992
- formComponent: any
993
- viewComponent: any
994
- dialogComponent: any
995
- panelComponent: VueComponent | null
996
- resourceComponent: VueComponent | null
997
- sourceComponent: VueComponent | null
1888
+ /** The current Vue component instance. */
1889
+ component: DitoComponentInstanceBase | null
1890
+ /**
1891
+ * The nearest ancestor `DitoSchema` component that
1892
+ * manages this field's layout.
1893
+ */
1894
+ schemaComponent: DitoSchemaInstance | null
1895
+ /** The nearest ancestor form component. */
1896
+ formComponent: DitoFormInstance
1897
+ /** The nearest ancestor view component. */
1898
+ viewComponent: DitoViewInstance | null
1899
+ /** The nearest ancestor dialog component. */
1900
+ dialogComponent: DitoComponentInstanceBase | null
1901
+ /** The nearest ancestor panel component. */
1902
+ panelComponent: DitoComponentInstanceBase | null
1903
+ /** The nearest ancestor resource component. */
1904
+ resourceComponent:
1905
+ | DitoFormInstance
1906
+ | DitoSourceInstance
1907
+ | null
1908
+ /** The nearest ancestor source component. */
1909
+ sourceComponent: DitoSourceInstance | null
1910
+ /** The currently focused option in a select. */
998
1911
  option: any
1912
+ /** All available options in a select. */
999
1913
  options: any
1000
- query: string
1001
- error: any | null
1914
+ /**
1915
+ * Whether a pulldown/select is currently open.
1916
+ */
1917
+ open: boolean | undefined
1918
+ /**
1919
+ * The current search term in select components.
1920
+ */
1921
+ searchTerm: string | undefined
1922
+ /**
1923
+ * Whether a button request is currently running.
1924
+ */
1925
+ isRunning: boolean
1926
+ /** Current URL query parameters. */
1927
+ query: Record<string, string | (string | null)[]>
1928
+ /**
1929
+ * The error object, populated in button `error`
1930
+ * event handler contexts.
1931
+ */
1932
+ error: unknown
1933
+ /**
1934
+ * Whether the event handler already called
1935
+ * `context.notify()`, used to suppress the default
1936
+ * notification for button events.
1937
+ */
1002
1938
  wasNotified: boolean
1003
1939
 
1004
1940
  // Helper Methods
1005
1941
 
1942
+ /** Performs an API request with optional caching. */
1006
1943
  request<T>(options: {
1007
1944
  /**
1008
1945
  * Allows caching of loaded data on two levels:
1009
1946
  * - 'global': cache globally, for the entire admin session
1010
- * - 'local': cache locally within the closest route component that is
1011
- * associated with a resource and loads its own data.
1947
+ * - 'local': cache locally within the closest route
1948
+ * component that is associated with a resource and loads
1949
+ * its own data.
1012
1950
  */
1013
1951
  cache?: 'local' | 'global'
1014
1952
  url: string
1015
1953
  /**
1016
1954
  * @default 'get'
1017
1955
  */
1018
- method?: HTTPVerb
1019
- query?: Record<string, string | string[]> | [string, string][]
1020
- data?: any
1956
+ method?: HTTPMethod
1957
+ query?:
1958
+ | Record<string, string | number | (string | number)[]>
1959
+ | [string, string | number][]
1960
+ data?: unknown
1021
1961
  resource?: Resource
1022
1962
  }): Promise<T>
1963
+ /** Formats values using locale-aware formatters. */
1023
1964
  format: typeof utilsFormat
1965
+ /** Navigates to a route programmatically. */
1024
1966
  navigate: VueRouter['push']
1967
+ /**
1968
+ * Triggers a file download from the given URL or
1969
+ * options.
1970
+ */
1025
1971
  download: {
1026
1972
  (url: string): void
1027
1973
  (options: { url: string; filename: string }): void
1028
1974
  }
1029
- getResourceUrl: any
1975
+ /** Returns the full URL for a given resource. */
1976
+ getResourceUrl(resource: Resource): string
1977
+ /** Displays a notification to the user. */
1030
1978
  notify(options: {
1031
1979
  type?: LiteralUnion<'warning' | 'error' | 'info' | 'success'>
1032
1980
  title?: string
@@ -1034,21 +1982,950 @@ export type DitoContext<$Item = any> = {
1034
1982
  }): void
1035
1983
  }
1036
1984
 
1037
- export type View<$Item = any> =
1038
- | {
1039
- type: 'view'
1040
- resource?: Form['resource']
1041
- clipboard?: Form['clipboard']
1042
- component?: Component<$Item>
1985
+ /**
1986
+ * The `this` type inside schema `methods`, `computed`,
1987
+ * and `watch` handlers. Unlike the context passed to
1988
+ * schema accessors like `if` and `label`, this is the
1989
+ * live component instance.
1990
+ *
1991
+ * @template $Item The data item type.
1992
+ * @template $Members Additional schema-defined methods
1993
+ * and computed properties available on `this`.
1994
+ */
1995
+ export type DitoComponentInstance<
1996
+ $Item = any,
1997
+ $Members extends Record<string, any> = {}
1998
+ > = DitoComponentInstanceBase<$Item> & $Members
1999
+
2000
+ export interface DitoComponentInstanceBase<$Item = any> extends EmitterMixin {
2001
+ // -- Data access (ValueMixin, ContextMixin, TypeMixin) --
2002
+
2003
+ /** The current value of the component (getter/setter). */
2004
+ value: any
2005
+ /** The current data item. */
2006
+ item: OmitNever<$Item>
2007
+ /**
2008
+ * The closest parent item that isn't an array (e.g.
2009
+ * for relations or nested JSON data).
2010
+ */
2011
+ parentItem: any
2012
+ /** The top-level root data item. */
2013
+ rootItem: any
2014
+ /**
2015
+ * The cloned data being prepared for server
2016
+ * submission. Available during `process` callbacks.
2017
+ */
2018
+ processedItem: any
2019
+ /** The root-level cloned data for server submission. */
2020
+ processedRootItem: any
2021
+ /** The component's data object. */
2022
+ data: Record<string, any>
2023
+ /** Parent data object, or `null` if same as data. */
2024
+ parentData: Record<string, any> | null
2025
+ /** The property name of the current component. */
2026
+ name: string
2027
+ /** Full dot-separated path to the current data. */
2028
+ dataPath: string
2029
+ /** The schema definition for the current component. */
2030
+ schema: Component
2031
+ /** The component type from the schema. */
2032
+ type: string
2033
+
2034
+ // -- State (TypeMixin, ValidationMixin) --
2035
+
2036
+ /** Whether the component currently has focus. */
2037
+ focused: boolean
2038
+ /** The value after applying `schema.parse()`. */
2039
+ parsedValue: any
2040
+ /** Whether the field has been focused at least once. */
2041
+ isTouched: boolean
2042
+ /** Whether the field value has been modified. */
2043
+ isDirty: boolean
2044
+ /** Whether the field is currently valid. */
2045
+ isValid: boolean
2046
+ /** Whether validation has been run on the field. */
2047
+ isValidated: boolean
2048
+ /** Validation error messages, or `null`. */
2049
+ errors: string[] | null
2050
+ /** Whether the field has validation errors. */
2051
+ hasErrors: boolean
2052
+ /** Whether async data is currently being loaded. */
2053
+ isLoading: boolean
2054
+ /**
2055
+ * Sets the loading state. Optionally propagates
2056
+ * to the root or view component.
2057
+ */
2058
+ setLoading(
2059
+ isLoading: boolean,
2060
+ options?: { updateRoot?: boolean; updateView?: boolean }
2061
+ ): void
2062
+ /**
2063
+ * Whether the component works with data not yet
2064
+ * persisted to the server.
2065
+ */
2066
+ isTransient: boolean
2067
+ /** Whether the component is mounted. */
2068
+ isMounted: boolean
2069
+ /** Whether the component tree is populated. */
2070
+ isPopulated: boolean
2071
+ /**
2072
+ * Whether this component loads its own data from
2073
+ * a resource.
2074
+ */
2075
+ providesData: boolean
2076
+ /** The schema of the source component. */
2077
+ sourceSchema: Component | null
2078
+ /** Action verb labels (create, save, delete, etc.). */
2079
+ verbs: Record<string, string>
2080
+
2081
+ // -- Resolved schema accessors (TypeMixin) --
2082
+
2083
+ /** Resolved `schema.visible` value. */
2084
+ visible: boolean
2085
+ /** Resolved `schema.exclude` value. */
2086
+ exclude: boolean
2087
+ /** Resolved `schema.required` value. */
2088
+ required: boolean
2089
+ /** Resolved `schema.readonly` value. */
2090
+ readonly: boolean
2091
+ /** Resolved `schema.disabled` value. */
2092
+ disabled: boolean
2093
+ /** Resolved `schema.clearable` value. */
2094
+ clearable: boolean
2095
+ /** Resolved `schema.autofocus` value. */
2096
+ autofocus: boolean
2097
+ /** Resolved `schema.placeholder` value. */
2098
+ placeholder: string | undefined
2099
+ /** Resolved `schema.info` value. */
2100
+ info: string | null
2101
+ /** Resolved `schema.maxLength` value. */
2102
+ maxLength: number | undefined
2103
+ /** Resolved `schema.autocomplete` value. */
2104
+ autocomplete: string | undefined
2105
+
2106
+ // -- Component hierarchy (DitoMixin) --
2107
+
2108
+ /** The `DitoContext` for this component. */
2109
+ context: DitoContext<$Item>
2110
+ /** The nearest `DitoSchema` managing layout. */
2111
+ schemaComponent: DitoSchemaInstance
2112
+ /** The nearest ancestor form component. */
2113
+ formComponent: DitoFormInstance
2114
+ /** The nearest ancestor view component. */
2115
+ viewComponent: DitoViewInstance | null
2116
+ /** The nearest ancestor dialog component. */
2117
+ dialogComponent: DitoComponentInstanceBase | null
2118
+ /** The nearest ancestor panel component. */
2119
+ panelComponent: DitoComponentInstanceBase | null
2120
+ /** The nearest ancestor resource component. */
2121
+ resourceComponent: DitoFormInstance | DitoSourceInstance
2122
+ /** The nearest ancestor source component. */
2123
+ sourceComponent: DitoSourceInstance
2124
+ /**
2125
+ * The nearest route component (form or view) in the
2126
+ * ancestor chain.
2127
+ */
2128
+ routeComponent: DitoFormInstance | DitoViewInstance | null
2129
+ /**
2130
+ * The first parent route component that provides and
2131
+ * loads its own data from the API.
2132
+ */
2133
+ dataComponent: DitoFormInstance | DitoViewInstance | null
2134
+ /** The parent schema component. */
2135
+ parentSchemaComponent: DitoComponentInstanceBase | null
2136
+ /** The parent form component. */
2137
+ parentFormComponent: DitoComponentInstanceBase | null
2138
+ /** The root component instance. */
2139
+ rootComponent: DitoComponentInstanceBase | null
2140
+ /** The nearest ancestor tab component. */
2141
+ tabComponent: DitoComponentInstanceBase | null
2142
+ /** The parent route component. */
2143
+ parentRouteComponent:
2144
+ | DitoFormInstance
2145
+ | DitoViewInstance
2146
+ | null
2147
+ /** The parent resource component. */
2148
+ parentResourceComponent:
2149
+ | DitoFormInstance
2150
+ | DitoSourceInstance
2151
+ | null
2152
+
2153
+ // -- Environment (DitoMixin) --
2154
+
2155
+ /** The currently authenticated user. */
2156
+ user: DitoContext['user']
2157
+ /** The admin API configuration. */
2158
+ api: ApiConfig
2159
+ /** Current locale from the API config. */
2160
+ locale: string
2161
+ /** All registered top-level views. */
2162
+ views: Record<string, View>
2163
+ /** All views flattened into a single record. */
2164
+ flattenedViews: Record<string, ViewSchema>
2165
+ /** Data from the first parent route that loads data. */
2166
+ rootData: any
2167
+
2168
+ // -- Actions (DitoMixin) --
2169
+
2170
+ /** Performs an API request with optional caching. */
2171
+ request: DitoContext['request']
2172
+ /** Formats values using locale-aware formatters. */
2173
+ format: DitoContext['format']
2174
+ /** Navigates to a route programmatically. */
2175
+ navigate: DitoContext['navigate']
2176
+ /** Triggers a file download. */
2177
+ download: DitoContext['download']
2178
+ /** Displays a notification to the user. */
2179
+ notify: DitoContext['notify']
2180
+ /** Returns the full URL for a given resource. */
2181
+ getResourceUrl: DitoContext['getResourceUrl']
2182
+ /**
2183
+ * Sends an HTTP request to the API.
2184
+ */
2185
+ sendRequest(options: {
2186
+ method?: HTTPMethod
2187
+ url?: string
2188
+ resource?: Resource
2189
+ query?:
2190
+ | Record<string, string | number | (string | number)[]>
2191
+ | [string, string | number][]
2192
+ data?: unknown
2193
+ signal?: AbortSignal
2194
+ internal?: boolean
2195
+ }): Promise<unknown>
2196
+ /**
2197
+ * Shows a modal dialog and returns a promise that
2198
+ * resolves with the dialog result.
2199
+ */
2200
+ showDialog(options: {
2201
+ components?: Components
2202
+ buttons?: Buttons<any>
2203
+ data?: Record<string, any>
2204
+ settings?: Record<string, any>
2205
+ }): Promise<unknown>
2206
+ /**
2207
+ * Resolves a schema value by key or data-path,
2208
+ * with optional type coercion and default.
2209
+ */
2210
+ getSchemaValue(
2211
+ keyOrDataPath: string,
2212
+ options?: {
2213
+ type?: Function | Function[]
2214
+ default?: any
2215
+ schema?: Component
2216
+ context?: DitoContext
2217
+ callback?: boolean
2218
+ }
2219
+ ): any
2220
+ /** Returns a human-readable label for a schema. */
2221
+ getLabel(
2222
+ schema: Component | null,
2223
+ name?: string
2224
+ ): string
2225
+ /**
2226
+ * Returns a Vue Router location object with the
2227
+ * given query params and the current hash
2228
+ * preserved (for tab navigation).
2229
+ */
2230
+ getQueryLink(
2231
+ query: Record<string, any>
2232
+ ): { query: Record<string, any>; hash: string }
2233
+ /**
2234
+ * Returns `true` if the schema's `if` accessor
2235
+ * evaluates to `true` (or is absent).
2236
+ */
2237
+ shouldRenderSchema(
2238
+ schema?: Component | null
2239
+ ): boolean
2240
+ /**
2241
+ * Returns the resolved `visible` value for a
2242
+ * schema. Defaults to `true`.
2243
+ */
2244
+ shouldShowSchema(
2245
+ schema?: Component | null
2246
+ ): boolean
2247
+ /**
2248
+ * Returns the resolved `disabled` value for a
2249
+ * schema. Defaults to `false`.
2250
+ */
2251
+ shouldDisableSchema(
2252
+ schema?: Component | null
2253
+ ): boolean
2254
+ /**
2255
+ * Emits a schema event by name (e.g. `'change'`,
2256
+ * `'focus'`). Calls the matching `on[Event]`
2257
+ * handler and the `events[event]` handler on the
2258
+ * schema. Returns the handler result, or
2259
+ * `undefined` if no handler was found. The event
2260
+ * bubbles to parent schemas unless the handler
2261
+ * returns `false`.
2262
+ *
2263
+ * @param event The event name to emit.
2264
+ * @param options.context Custom context properties
2265
+ * merged into the `DitoContext` passed to the
2266
+ * event handler.
2267
+ */
2268
+ emitEvent(
2269
+ event: string,
2270
+ options?: {
2271
+ context?: DitoContext
1043
2272
  }
2273
+ ): Promise<any>
2274
+ /**
2275
+ * Emits a schema event on the nearest schema
2276
+ * component (rather than `this`). Used by form
2277
+ * and resource components to emit lifecycle
2278
+ * events like `'load'` and `'submit'`.
2279
+ */
2280
+ emitSchemaEvent(
2281
+ event: string,
2282
+ params?: Record<string, any>
2283
+ ): Promise<any>
2284
+ /** Converts a camelCase name to a human-readable label. */
2285
+ labelize(name: string): string
2286
+ /** Gets a value from the component store. */
2287
+ getStore(key: string): any
2288
+ /** Sets a value in the component store. */
2289
+ setStore(key: string, value: any): any
2290
+ /** Removes a value from the component store. */
2291
+ removeStore(key: string): void
2292
+
2293
+ // -- Actions (TypeMixin) --
2294
+
2295
+ /** Focuses the component and scrolls it into view. */
2296
+ focus(): Promise<void>
2297
+ /** Blurs the component. */
2298
+ blur(): void
2299
+ /** Clears the value, blurs, and triggers onChange. */
2300
+ clear(): void
2301
+ /** Scrolls the component into view. */
2302
+ scrollIntoView(): Promise<void>
2303
+
2304
+ // -- Actions (ValidationMixin) --
2305
+
2306
+ /**
2307
+ * Validates the field. Returns `true` if valid.
2308
+ * When `notify` is `true` (default), updates state
2309
+ * and emits errors.
2310
+ */
2311
+ validate(notify?: boolean): boolean
2312
+ /** Validates without notifying (shorthand). */
2313
+ verify(): boolean
2314
+ /** Marks the field as touched and clears errors. */
2315
+ markTouched(): void
2316
+ /** Marks the field as dirty and resets validation. */
2317
+ markDirty(): void
2318
+ /** Resets all validation state. */
2319
+ resetValidation(): void
2320
+ /** Adds an error message to the errors array. */
2321
+ addError(error: string, addLabel?: boolean): void
2322
+ /**
2323
+ * Shows server-side validation errors on the
2324
+ * component. Returns `true` if errors were shown.
2325
+ */
2326
+ showValidationErrors(
2327
+ errors: { message: string }[],
2328
+ focus: boolean
2329
+ ): boolean
2330
+ /** Returns a copy of the current errors, or `null`. */
2331
+ getErrors(): string[] | null
2332
+ /** Clears all validation errors. */
2333
+ clearErrors(): void
2334
+ /** Closes all notification toasts. */
2335
+ closeNotifications(): void
2336
+ }
2337
+
2338
+ export interface EmitterMixin {
2339
+ /** Registers one or more event listeners. */
2340
+ on(
2341
+ event:
2342
+ | string
2343
+ | string[]
2344
+ | Record<string, Function>,
2345
+ callback?: Function
2346
+ ): this
2347
+ /** Registers a one-time event listener. */
2348
+ once(event: string, callback: Function): this
2349
+ /** Removes event listener(s). */
2350
+ off(
2351
+ event?:
2352
+ | string
2353
+ | string[]
2354
+ | Record<string, Function>,
2355
+ callback?: Function
2356
+ ): this
2357
+ /** Emits an event asynchronously (queued). */
2358
+ emit(event: string, ...args: any[]): Promise<any>
2359
+ /** Returns `true` if listeners exist for the event(s). */
2360
+ hasListeners(event: string | string[]): boolean
2361
+ /** Re-emits events from this component on the target. */
2362
+ delegate(
2363
+ event: string | string[],
2364
+ target: EmitterMixin
2365
+ ): this
2366
+ }
2367
+
2368
+ export interface DitoFormInstance<$Item = any>
2369
+ extends DitoComponentInstanceBase<$Item> {
2370
+ /**
2371
+ * Whether this form is creating a new item
2372
+ * (`true`) or editing an existing one (`false`).
2373
+ */
2374
+ isCreating: boolean
2375
+
2376
+ /**
2377
+ * Submits the form data to the API. Returns
2378
+ * `true` on success, `false` if validation
2379
+ * fails or the request errors.
2380
+ */
2381
+ submit(
2382
+ button?: DitoComponentInstanceBase,
2383
+ options?: {
2384
+ validate?: boolean
2385
+ closeForm?: boolean
2386
+ }
2387
+ ): Promise<boolean>
2388
+
2389
+ /**
2390
+ * Cancels the form and navigates to the parent
2391
+ * route.
2392
+ */
2393
+ cancel(): Promise<void>
2394
+
2395
+ /**
2396
+ * Closes the form and navigates to the parent
2397
+ * route.
2398
+ */
2399
+ close(): Promise<void>
2400
+
2401
+ /**
2402
+ * Validates all fields in the form. Optionally
2403
+ * filter fields with a match pattern. Returns
2404
+ * `true` if all matched fields are valid.
2405
+ */
2406
+ validateAll(
2407
+ match?:
2408
+ | string
2409
+ | string[]
2410
+ | RegExp
2411
+ | ((field: string) => boolean),
2412
+ notify?: boolean
2413
+ ): boolean
2414
+
2415
+ /**
2416
+ * Like {@link validateAll} but without
2417
+ * triggering error notifications.
2418
+ */
2419
+ verifyAll(
2420
+ match?:
2421
+ | string
2422
+ | string[]
2423
+ | RegExp
2424
+ | ((field: string) => boolean)
2425
+ ): boolean
2426
+
2427
+ // -- Resource & route (ResourceMixin, RouteMixin) --
2428
+
2429
+ /** Display label for this form. */
2430
+ label: string
2431
+ /** Breadcrumb text for navigation. */
2432
+ breadcrumb: string
2433
+ /** Prefix prepended to the breadcrumb label. */
2434
+ breadcrumbPrefix: string
2435
+ /** Whether this is the last route in the hierarchy. */
2436
+ isLastRoute: boolean
2437
+ /** Whether this route is nested inside another route. */
2438
+ isNestedRoute: boolean
2439
+ /** Zero-based depth of this route in the hierarchy. */
2440
+ routeLevel: number
2441
+
2442
+ /**
2443
+ * The resolved API resource for this component,
2444
+ * or `null` if none is configured.
2445
+ */
2446
+ resource: Resource | null
2447
+ /**
2448
+ * Whether loaded data is available on this
2449
+ * component.
2450
+ */
2451
+ hasData: boolean
2452
+ /**
2453
+ * Reloads data from the API without clearing
2454
+ * the existing data first.
2455
+ */
2456
+ reloadData(): void
2457
+ /**
2458
+ * Ensures data is loaded: reloads if data
2459
+ * exists, otherwise loads fresh.
2460
+ */
2461
+ ensureData(): void
2462
+ /** Clears the loaded data. */
2463
+ clearData(): void
2464
+ /** Sets the component's loaded data directly. */
2465
+ setData(data: any): void
2466
+ /**
2467
+ * Creates a new data object with default values
2468
+ * from the schema. Optionally sets a `type`
2469
+ * property for polymorphic forms.
2470
+ */
2471
+ createData(
2472
+ schema: Component,
2473
+ type?: string
2474
+ ): Record<string, any>
2475
+ /**
2476
+ * The route parameter value for this route
2477
+ * component (e.g. an item id or `'create'`).
2478
+ */
2479
+ param: string | number | null
2480
+ /**
2481
+ * Whether the form directly mutates the
2482
+ * parent's data instead of working on a copy.
2483
+ */
2484
+ isMutating: boolean
2485
+ /**
2486
+ * Builds a child route path relative to this
2487
+ * route component's path.
2488
+ */
2489
+ getChildPath(path: string): string
2490
+ }
2491
+
2492
+ export interface DitoViewInstance<$Item = any>
2493
+ extends DitoComponentInstanceBase<$Item> {
2494
+ /** Always `true` for view components. */
2495
+ isView: true
2496
+
2497
+ // -- Route (RouteMixin) --
2498
+
2499
+ /** Display label for this view. */
2500
+ label: string
2501
+ /** Breadcrumb text for navigation. */
2502
+ breadcrumb: string
2503
+ /** Prefix prepended to the breadcrumb label. */
2504
+ breadcrumbPrefix: string
2505
+ /** Whether this is the last route in the hierarchy. */
2506
+ isLastRoute: boolean
2507
+ /** Whether this route is nested inside another route. */
2508
+ isNestedRoute: boolean
2509
+ /** Zero-based depth of this route in the hierarchy. */
2510
+ routeLevel: number
2511
+ /**
2512
+ * The route parameter value for this route
2513
+ * component (e.g. an item id or `'create'`).
2514
+ */
2515
+ param: string | number | null
2516
+ /**
2517
+ * Whether the view directly mutates the
2518
+ * parent's data instead of working on a copy.
2519
+ */
2520
+ isMutating: boolean
2521
+ /**
2522
+ * Builds a child route path relative to this
2523
+ * route component's path.
2524
+ */
2525
+ getChildPath(path: string): string
2526
+
2527
+ // -- Data & schema (DitoView) --
2528
+
2529
+ /** The view's reactive data object. */
2530
+ data: Record<string, any>
2531
+ /** The resolved view schema definition. */
2532
+ viewSchema: ViewSchema
2533
+ /**
2534
+ * Whether this view contains a single inlined
2535
+ * source component (renders without a table header).
2536
+ */
2537
+ isSingleComponentView: boolean
2538
+ /**
2539
+ * Whether this view loads its own data from
2540
+ * a resource.
2541
+ */
2542
+ providesData: boolean
2543
+ /** Sets the view's data directly. */
2544
+ setData(data: any): void
2545
+
2546
+ // -- Validation (ValidatorMixin) --
2547
+
2548
+ /**
2549
+ * Validates all fields in the view.
2550
+ * Optionally filter fields with a match pattern.
2551
+ * Returns `true` if all matched fields are valid.
2552
+ */
2553
+ validateAll(
2554
+ match?:
2555
+ | string
2556
+ | string[]
2557
+ | RegExp
2558
+ | ((field: string) => boolean),
2559
+ notify?: boolean
2560
+ ): boolean
2561
+
2562
+ /**
2563
+ * Like {@link validateAll} but without
2564
+ * triggering error notifications.
2565
+ */
2566
+ verifyAll(
2567
+ match?:
2568
+ | string
2569
+ | string[]
2570
+ | RegExp
2571
+ | ((field: string) => boolean)
2572
+ ): boolean
2573
+ }
2574
+
2575
+ export interface DitoSchemaInstance<$Item = any>
2576
+ extends DitoComponentInstanceBase<$Item> {
2577
+ /**
2578
+ * Validates all fields in the schema.
2579
+ * Optionally filter fields with a match pattern
2580
+ * (string, string[], RegExp, or function).
2581
+ * Returns `true` if all matched fields are
2582
+ * valid.
2583
+ */
2584
+ validateAll(
2585
+ match?:
2586
+ | string
2587
+ | string[]
2588
+ | RegExp
2589
+ | ((field: string) => boolean),
2590
+ notify?: boolean
2591
+ ): boolean
2592
+
2593
+ /**
2594
+ * Like {@link validateAll} but without
2595
+ * triggering error notifications.
2596
+ */
2597
+ verifyAll(
2598
+ match?:
2599
+ | string
2600
+ | string[]
2601
+ | RegExp
2602
+ | ((field: string) => boolean)
2603
+ ): boolean
2604
+ }
2605
+
2606
+ export interface DitoSourceInstance<$Item = any>
2607
+ extends DitoComponentInstanceBase<$Item> {
2608
+ // -- Data access (SourceMixin) --
2609
+
2610
+ /** The list data array (getter/setter). */
2611
+ listData: any[]
2612
+ /** The object data (getter/setter). */
2613
+ objectData: Record<string, any> | null
2614
+ /** Total number of items (for pagination). */
2615
+ total: number
2616
+ /** Current query parameters (getter/setter). */
2617
+ query: Record<string, any>
2618
+ /** Whether this source manages a list. */
2619
+ isListSource: boolean
2620
+ /** Whether this source manages an object. */
2621
+ isObjectSource: boolean
2622
+ /** Whether the source renders inline. */
2623
+ isInlined: boolean
2624
+ /**
2625
+ * Nesting depth of sources (0 for top-level,
2626
+ * increments for nested sources).
2627
+ */
2628
+ sourceDepth: number
2629
+ /** The URL path for this source. */
2630
+ path: string
2631
+
2632
+ // -- Resolved schema accessors (SourceMixin) --
2633
+
2634
+ /** Whether new items can be created. */
2635
+ creatable: boolean
2636
+ /** Whether existing items can be edited. */
2637
+ editable: boolean
2638
+ /** Whether items can be deleted. */
2639
+ deletable: boolean
2640
+ /** Whether items can be reordered by dragging. */
2641
+ draggable: boolean
2642
+ /** Whether inlined forms are collapsible. */
2643
+ collapsible: boolean
2644
+ /** Whether inlined forms are collapsed. */
2645
+ collapsed: boolean
2646
+ /** Resolved pagination page size. */
2647
+ paginate: number | undefined
2648
+ /** Maximum nesting depth for nested sources. */
2649
+ maxDepth: number
2650
+ /** Whether the source renders in compact mode. */
2651
+ isCompact: boolean
2652
+
2653
+ // -- Schema introspection (SourceMixin) --
2654
+
2655
+ /** Resolved column definitions. */
2656
+ columns: Record<string, ColumnSchema> | null
2657
+ /** Resolved scope definitions. */
2658
+ scopes: Record<string, any> | null
2659
+ /** Resolved form definitions. */
2660
+ forms: Form[]
2661
+ /** Resolved button schemas. */
2662
+ buttonSchemas: Record<string, ButtonSchema<any>>
2663
+
2664
+ // -- Item operations (SourceMixin) --
2665
+
2666
+ /**
2667
+ * Creates a new data item with defaults from the
2668
+ * schema. Optionally sets a `type` property for
2669
+ * polymorphic forms.
2670
+ */
2671
+ createItem(
2672
+ schema: Component,
2673
+ type?: string
2674
+ ): Record<string, any>
2675
+ /** Removes an item from the list data locally. */
2676
+ removeItem(item: any, index: number): void
2677
+ /**
2678
+ * Deletes an item via the API and removes it
2679
+ * from the list.
2680
+ */
2681
+ deleteItem(item: any, index: number): void
2682
+ /**
2683
+ * Navigates to a component identified by its
2684
+ * data path.
2685
+ */
2686
+ navigateToComponent(
2687
+ dataPath: string,
2688
+ onComplete?: Function
2689
+ ): Promise<boolean>
2690
+ /**
2691
+ * Navigates to the route component associated
2692
+ * with a data path.
2693
+ */
2694
+ navigateToRouteComponent(
2695
+ dataPath: string,
2696
+ onComplete?: Function
2697
+ ): Promise<boolean>
2698
+
2699
+ // -- Resource (ResourceMixin) --
2700
+
2701
+ /**
2702
+ * The resolved API resource for this component,
2703
+ * or `null` if none is configured.
2704
+ */
2705
+ resource: Resource | null
2706
+ /**
2707
+ * Whether loaded data is available on this
2708
+ * component.
2709
+ */
2710
+ hasData: boolean
2711
+ /**
2712
+ * Reloads data from the API without clearing
2713
+ * the existing data first.
2714
+ */
2715
+ reloadData(): void
2716
+ /**
2717
+ * Ensures data is loaded: reloads if data
2718
+ * exists, otherwise loads fresh.
2719
+ */
2720
+ ensureData(): void
2721
+ /** Clears the loaded data. */
2722
+ clearData(): void
2723
+ /** Sets the component's loaded data directly. */
2724
+ setData(data: any): void
2725
+ /**
2726
+ * Creates a new data object with default values
2727
+ * from the schema. Optionally sets a `type`
2728
+ * property for polymorphic forms.
2729
+ */
2730
+ createData(
2731
+ schema: Component,
2732
+ type?: string
2733
+ ): Record<string, any>
2734
+ }
2735
+
2736
+ export interface MenuSchema<$Item = any> {
2737
+ type: 'menu'
2738
+ /**
2739
+ * The label shown in the navigation menu.
2740
+ */
2741
+ label?: string
2742
+ /**
2743
+ * The name of the menu group.
2744
+ *
2745
+ * @defaultValue Camelized from `label`.
2746
+ */
2747
+ name?: string
2748
+ /** Sub-views shown as menu items under this group. */
2749
+ items: Record<string, OrPromiseOf<View<$Item>>>
2750
+ }
2751
+
2752
+ export type ClipboardConfig =
2753
+ | boolean
1044
2754
  | {
1045
- type: 'view'
1046
- resource?: Form['resource']
1047
- clipboard?: Form['clipboard']
1048
- components?: Components<$Item>
2755
+ copy?: (context: DitoContext) => unknown
2756
+ paste?: (context: DitoContext) => unknown
1049
2757
  }
1050
2758
 
1051
- export type Component<$Item = any> =
2759
+ export type View<$Item = any> =
2760
+ | ViewSchema<$Item>
2761
+ | MenuSchema<$Item>
2762
+
2763
+ export interface ViewSchema<$Item = any> extends SchemaRoute<$Item> {
2764
+ type: 'view'
2765
+ /**
2766
+ * The label shown in the navigation menu.
2767
+ *
2768
+ * @defaultValue The title-cased view name.
2769
+ */
2770
+ label?: string
2771
+ /**
2772
+ * The URL path for the view.
2773
+ */
2774
+ path?: string
2775
+ /**
2776
+ * The name of the view.
2777
+ */
2778
+ name?: string
2779
+ /**
2780
+ * Display several schemas in different tabs within
2781
+ * the view.
2782
+ */
2783
+ tabs?: Record<
2784
+ string,
2785
+ Omit<ViewSchema<$Item>, 'tabs' | 'type'> & {
2786
+ type: 'tab'
2787
+ /**
2788
+ * Whether this tab is selected by default.
2789
+ */
2790
+ defaultTab?: OrItemAccessor<$Item, {}, boolean>
2791
+ }
2792
+ >
2793
+ /**
2794
+ * Grouped event handlers, equivalent to the
2795
+ * `on[A-Z]`-style callbacks on the view schema
2796
+ * (e.g. {@link ViewSchema.onOpen}).
2797
+ *
2798
+ * @see {@link SchemaFields.onInitialize} and
2799
+ * other `on`-prefixed properties for per-event
2800
+ * documentation.
2801
+ */
2802
+ events?: SchemaEvents<$Item> & {
2803
+ /**
2804
+ * Called when a collapsible schema section is
2805
+ * toggled open or closed.
2806
+ */
2807
+ open?: OpenEventHandler<$Item>
2808
+ }
2809
+ component?: Component<$Item>
2810
+ components?: Components<$Item>
2811
+ }
2812
+
2813
+ /**
2814
+ * A non-visible component that computes a value and
2815
+ * stores it in the form data.
2816
+ */
2817
+ export interface ComputedSchema<$Item = any>
2818
+ extends BaseSchema<$Item>,
2819
+ SchemaDataMixin<$Item> {
2820
+ /**
2821
+ * The type of the component.
2822
+ */
2823
+ type: 'computed'
2824
+ }
2825
+
2826
+ /**
2827
+ * A non-visible component that fetches external data
2828
+ * and stores it in the form data.
2829
+ */
2830
+ export interface DataSchema<$Item = any>
2831
+ extends BaseSchema<$Item>,
2832
+ SchemaDataMixin<$Item> {
2833
+ /**
2834
+ * The type of the component.
2835
+ */
2836
+ type: 'data'
2837
+ }
2838
+
2839
+ export interface ObjectSchema<$Item = { [key: string]: any }>
2840
+ extends SchemaSourceMixin<$Item>,
2841
+ BaseSchema<$Item>,
2842
+ SchemaOpen<$Item> {
2843
+ /**
2844
+ * The type of the component.
2845
+ */
2846
+ type: 'object'
2847
+ /**
2848
+ * Grouped event handlers.
2849
+ */
2850
+ events?: {
2851
+ /**
2852
+ * Called when a collapsible inlined object is
2853
+ * toggled open or closed.
2854
+ */
2855
+ open?: OpenEventHandler<$Item>
2856
+ }
2857
+ }
2858
+
2859
+ interface TreeSchema<$Item, $Type extends 'tree-list' | 'tree-object'>
2860
+ extends SchemaSourceMixin<$Item>,
2861
+ BaseSchema<$Item> {
2862
+ /**
2863
+ * The type of the component.
2864
+ */
2865
+ type: $Type
2866
+ /**
2867
+ * Nested tree schema describing the recursive
2868
+ * children of each node. The `name` property
2869
+ * identifies which data property holds the
2870
+ * children array.
2871
+ */
2872
+ children?: Omit<TreeListSchema<$Item>, 'type'> & {
2873
+ name: string
2874
+ }
2875
+ /**
2876
+ * Properties schema for tree nodes.
2877
+ */
2878
+ properties?: Record<string, Component<$Item>>
2879
+ /**
2880
+ * Whether child nodes are expanded by default.
2881
+ */
2882
+ open?: boolean
2883
+ }
2884
+
2885
+ export type TreeListSchema<$Item = any> = TreeSchema<$Item, 'tree-list'>
2886
+ export type TreeObjectSchema<$Item = any> = TreeSchema<$Item, 'tree-object'>
2887
+ export type PanelSchema<$Item = any> = BaseSchema<$Item> & {
2888
+ /**
2889
+ * The type of the component.
2890
+ */
2891
+ type: 'panel'
2892
+ /**
2893
+ * The components within the panel.
2894
+ */
2895
+ components?: Components<$Item>
2896
+ /** Buttons rendered at the bottom of the panel. */
2897
+ buttons?: Buttons<$Item>
2898
+ /**
2899
+ * Buttons rendered in the panel header, next to the
2900
+ * title. Displayed at a smaller size.
2901
+ */
2902
+ panelButtons?: Buttons<$Item>
2903
+ /**
2904
+ * Whether the panel header sticks to the top of the
2905
+ * scroll container.
2906
+ *
2907
+ * @defaultValue `false`
2908
+ */
2909
+ sticky?: OrItemAccessor<$Item, {}, boolean>
2910
+ }
2911
+
2912
+ export interface SpacerSchema<$Item = any> extends BaseSchema<$Item> {
2913
+ /**
2914
+ * The type of the component.
2915
+ */
2916
+ type: 'spacer'
2917
+ }
2918
+
2919
+ export interface ProgressSchema<$Item = any>
2920
+ extends SchemaNumberMixin<$Item>,
2921
+ BaseSchema<$Item> {
2922
+ /**
2923
+ * The type of the component.
2924
+ */
2925
+ type: 'progress'
2926
+ }
2927
+
2928
+ type NonSectionComponent<$Item = any> =
1052
2929
  | InputSchema<$Item>
1053
2930
  | RadioSchema<$Item>
1054
2931
  | CheckboxSchema<$Item>
@@ -1068,13 +2945,127 @@ export type Component<$Item = any> =
1068
2945
  | DateSchema<$Item>
1069
2946
  | ComponentSchema<$Item>
1070
2947
  | LabelSchema<$Item>
1071
- | SectionSchema<$Item>
1072
2948
  | HiddenSchema<$Item>
2949
+ | ObjectSchema<$Item>
2950
+ | TreeListSchema<$Item>
2951
+ | TreeObjectSchema<$Item>
2952
+ | ComputedSchema<$Item>
2953
+ | DataSchema<$Item>
2954
+ | SpacerSchema<$Item>
2955
+ | ProgressSchema<$Item>
1073
2956
 
1074
- export type Components<$Item = any> =
1075
- | Component<$Item>[]
1076
- | {
1077
- [$name: string]: Component<$Item>
2957
+ export type Component<$Item = any> =
2958
+ | NonSectionComponent<$Item>
2959
+ | SectionSchema<$Item>
2960
+
2961
+ /**
2962
+ * Source components (list, object, tree) that contain nested
2963
+ * items with their own item type.
2964
+ */
2965
+ export type SourceComponent<$Item = any> =
2966
+ | ListSchema<$Item>
2967
+ | ObjectSchema<$Item>
2968
+ | TreeListSchema<$Item>
2969
+ | TreeObjectSchema<$Item>
2970
+
2971
+ /**
2972
+ * Strips properties with `never` values from a type.
2973
+ */
2974
+ type OmitNever<T> = {
2975
+ [K in keyof T as [T[K]] extends [never] ? never : K]: T[K]
2976
+ } & {}
2977
+
2978
+ /**
2979
+ * Defines the components for a form or view.
2980
+ *
2981
+ * When you provide an `$Item` type, each component key must match
2982
+ * a property on that type, and callbacks receive a typed `item`
2983
+ * whose type depends on that property:
2984
+ *
2985
+ * - **Regular data fields** (e.g. `title: string`): callbacks
2986
+ * receive the full parent item type.
2987
+ * - **Array fields** (e.g. `entries: Entry[]`): nested list or
2988
+ * tree components use the array element type (`Entry`).
2989
+ * - **UI-only keys** (e.g. `viewButton: never`): for components
2990
+ * like buttons, spacers, or sections that exist only in the UI
2991
+ * and are not actual data fields. Declare these keys as `never`.
2992
+ * They are omitted from `item` in callbacks.
2993
+ *
2994
+ * Only keys defined in `$Item` are allowed. Unknown keys are a
2995
+ * type error.
2996
+ *
2997
+ * @example
2998
+ * ```ts
2999
+ * type Entry = { id: number; title: string }
3000
+ *
3001
+ * type Item = {
3002
+ * title: string
3003
+ * entries: Entry[]
3004
+ * viewButton: never // UI-only key for a button
3005
+ * details: never // UI-only key for a section
3006
+ * }
3007
+ *
3008
+ * const components: Components<Item> = {
3009
+ * // regular data field — callbacks receive Item
3010
+ * title: { type: 'text' },
3011
+ *
3012
+ * // array field — callbacks receive Entry, not Item
3013
+ * entries: {
3014
+ * type: 'list',
3015
+ * form: {
3016
+ * type: 'form',
3017
+ * components: {
3018
+ * title: {
3019
+ * type: 'text',
3020
+ * // item is typed as Entry, not Item
3021
+ * onChange({ item }) { console.log(item.title) }
3022
+ * }
3023
+ * }
3024
+ * }
3025
+ * },
3026
+ *
3027
+ * // UI-only key — item omits viewButton and details
3028
+ * viewButton: {
3029
+ * type: 'button',
3030
+ * events: {
3031
+ * click({ item }) { ... }
3032
+ * }
3033
+ * },
3034
+ *
3035
+ * // UI-only key — sections group fields from the parent item
3036
+ * details: {
3037
+ * type: 'section',
3038
+ * components: {
3039
+ * title: { type: 'text' }
3040
+ * }
3041
+ * }
3042
+ * }
3043
+ * ```
3044
+ */
3045
+ export type Components<$Item = any> = 0 extends 1 & $Item
3046
+ ? Record<string, Component>
3047
+ : {
3048
+ [K in keyof $Item]?: [$Item[K]] extends [never]
3049
+ ? NonSectionComponent<$Item> | SectionSchema<$Item>
3050
+ : $Item[K] extends (infer E)[]
3051
+ ? E extends Record<string, any>
3052
+ ? NonSectionComponent<E>
3053
+ : NonSectionComponent<$Item>
3054
+ : $Item[K] extends Record<string, any>
3055
+ ?
3056
+ | NonSectionComponent<$Item>
3057
+ | SectionSchema<$Item, $Item[K]>
3058
+ :
3059
+ | NonSectionComponent<$Item>
3060
+ | SectionSchema<$Item>
3061
+ }
3062
+
3063
+ export type Columns<$Item = any> = 0 extends 1 & $Item
3064
+ ? { [key: string]: ColumnSchema | undefined }
3065
+ : {
3066
+ [K in keyof $Item]?: ColumnSchema<$Item, $Item[K]>
3067
+ } & {
3068
+ [key: string]: ColumnSchema<$Item> | undefined
1078
3069
  }
1079
3070
 
1080
3071
  export type Buttons<$Item> = Record<
@@ -1082,7 +3073,7 @@ export type Buttons<$Item> = Record<
1082
3073
  SetOptional<ButtonSchema<$Item>, 'type'>
1083
3074
  >
1084
3075
 
1085
- export type Form<$Item = any> = {
3076
+ export interface Form<$Item = any> extends SchemaRoute<$Item> {
1086
3077
  type: 'form'
1087
3078
 
1088
3079
  /**
@@ -1094,101 +3085,214 @@ export type Form<$Item = any> = {
1094
3085
  */
1095
3086
  label?: OrItemAccessor<$Item, {}, string | boolean>
1096
3087
  /**
3088
+ * Whether the form directly mutates the parent data instead
3089
+ * of working on a copy.
3090
+ *
1097
3091
  * @defaultValue `false`
1098
3092
  */
1099
- compact?: boolean
1100
- resource?: Resource
3093
+ mutate?: boolean
3094
+ /**
3095
+ * The property name used as the item's unique identifier.
3096
+ *
3097
+ * @defaultValue `'id'`
3098
+ */
3099
+ idKey?: string
1101
3100
  /**
1102
3101
  * Display several forms in different tabs within the form.
1103
3102
  */
1104
3103
  tabs?: Record<
1105
3104
  string,
1106
3105
  Omit<Form<$Item>, 'tabs' | 'type'> & {
3106
+ type: 'tab'
3107
+ /**
3108
+ * Whether this tab is selected by default.
3109
+ */
1107
3110
  defaultTab?: OrItemAccessor<$Item, {}, boolean>
1108
3111
  }
1109
3112
  >
1110
- // TODO: document components
3113
+ /** The form's field components. */
1111
3114
  components?: Components<$Item>
1112
- // TODO: document clipboard
1113
- clipboard?:
1114
- | boolean
1115
- | {
1116
- copy?: (...args: any[]) => any
1117
- paste?: (...args: any[]) => any
1118
- }
1119
- buttons?: Buttons<$Item>
1120
- if?: OrItemAccessor<$Item, {}, boolean>
3115
+ /**
3116
+ * Grouped event handlers, equivalent to the
3117
+ * `on[A-Z]`-style callbacks on the form schema
3118
+ * (e.g. {@link Form.onOpen}).
3119
+ *
3120
+ * @see {@link SchemaFields.onInitialize} and
3121
+ * other `on`-prefixed properties for per-event
3122
+ * documentation.
3123
+ */
3124
+ events?: SchemaEvents<$Item> & {
3125
+ /**
3126
+ * Called when a collapsible schema section is
3127
+ * toggled open or closed.
3128
+ */
3129
+ open?: OpenEventHandler<$Item>
3130
+ /**
3131
+ * Called after a new item is successfully
3132
+ * created and persisted.
3133
+ */
3134
+ create?: ItemEventHandler<$Item>
3135
+ /**
3136
+ * Called after an existing item is successfully
3137
+ * submitted and persisted.
3138
+ */
3139
+ submit?: ItemEventHandler<$Item>
3140
+ /**
3141
+ * Called when a submit request fails. The
3142
+ * `error` property on the context contains the
3143
+ * request error.
3144
+ */
3145
+ error?: ErrorEventHandler<$Item>
3146
+ }
3147
+ /**
3148
+ * Called after a new item is successfully
3149
+ * created and persisted.
3150
+ */
3151
+ onCreate?: ItemEventHandler<$Item>
3152
+ /**
3153
+ * Called after an existing item is successfully
3154
+ * submitted and persisted.
3155
+ */
3156
+ onSubmit?: ItemEventHandler<$Item>
3157
+ /**
3158
+ * Called when a submit request fails. The
3159
+ * `error` property on the context contains the
3160
+ * request error.
3161
+ */
3162
+ onError?: ErrorEventHandler<$Item>
1121
3163
  }
1122
3164
 
1123
3165
  export type Resource =
1124
3166
  | string
1125
3167
  | RequireAtLeastOne<{
1126
3168
  path?: string
1127
- method?: HTTPVerb
1128
- // TODO: type Resource['data']
1129
- data?: any
3169
+ method?: HTTPMethod
3170
+ data?: unknown
1130
3171
  }>
1131
3172
 
1132
3173
  export class DitoAdmin<
1133
- $Views extends Record<string, any> = Record<string, OrPromiseOf<View>>
3174
+ $Views extends Record<string, OrPromiseOf<View>> = Record<
3175
+ string,
3176
+ OrPromiseOf<View>
3177
+ >
1134
3178
  > {
3179
+ /** The DOM element the admin is mounted to. */
3180
+ el: Element
3181
+ /** The resolved API configuration. */
1135
3182
  api: ApiConfig
1136
- // TODO: finish off Vue types
1137
- root: VueComponent
3183
+ /** The Vue application instance. */
3184
+ app: import('vue').App
3185
+ /** Additional options passed at construction. */
3186
+ options: Record<string, any>
3187
+
1138
3188
  constructor(
1139
3189
  element: Element | string,
1140
3190
  options?: {
1141
- // `dito` contains the base and api settings passed from `AdminController`
3191
+ // `dito` contains the base and api settings passed from
3192
+ // `AdminController`
1142
3193
  dito?: DitoGlobal
1143
3194
  api?: ApiConfig
1144
3195
  views: OrFunctionReturning<OrPromiseOf<$Views>>
1145
- // TODO: options rest type
1146
- // ...options: any
3196
+ login?: {
3197
+ additionalComponents?: Components
3198
+ redirectAfterLogin?: string
3199
+ }
3200
+ /**
3201
+ * Controls the loading spinner displayed in the
3202
+ * admin header. Set to `null` to disable it.
3203
+ */
3204
+ spinner?: {
3205
+ size?: string
3206
+ color?: string
3207
+ } | null
3208
+ [key: string]: any
1147
3209
  }
1148
3210
  )
1149
3211
 
1150
- // TODO: options and return type
1151
- register(type: OrArrayOf<string>, options: any): any
1152
- request: RequestMethod
3212
+ /**
3213
+ * Registers a custom component type with the admin.
3214
+ */
3215
+ register(
3216
+ type: OrArrayOf<string>,
3217
+ options: Record<string, unknown>
3218
+ ): VueComponent
1153
3219
  }
1154
- export type HTTPVerb = 'get' | 'post' | 'put' | 'delete' | 'patch'
3220
+ export type HTTPMethod =
3221
+ | 'get'
3222
+ | 'head'
3223
+ | 'post'
3224
+ | 'put'
3225
+ | 'delete'
3226
+ | 'patch'
3227
+ | 'options'
3228
+ | 'trace'
3229
+ | 'connect'
3230
+ /** @deprecated Use `HTTPMethod` instead. */
3231
+ export type HTTPVerb = HTTPMethod
1155
3232
 
1156
3233
  export type SchemaByType<$Item = any> = {
1157
- button: ButtonSchema<$Item>
1158
- checkbox: CheckboxSchema<$Item>
1159
- checkboxes: CheckboxesSchema<$Item>
1160
- code: CodeSchema<$Item>
1161
- color: ColorSchema<$Item>
1162
- component: ComponentSchema<$Item>
1163
- date: DateSchema<$Item>
1164
- list: ListSchema<$Item>
1165
- markup: MarkupSchema<$Item>
1166
- multiselect: MultiselectSchema<$Item>
1167
- number: NumberSchema<$Item>
1168
- radio: RadioSchema<$Item>
1169
- select: SelectSchema<$Item>
1170
- slider: SliderSchema<$Item>
1171
- switch: SwitchSchema<$Item>
1172
- text: InputSchema<$Item>
1173
- textarea: TextareaSchema<$Item>
1174
- upload: UploadSchema<$Item>
1175
- label: LabelSchema<$Item>
1176
- section: SectionSchema<$Item>
1177
- hidden: HiddenSchema<$Item>
1178
- unknown: never
3234
+ 'button': ButtonSchema<$Item>
3235
+ 'checkbox': CheckboxSchema<$Item>
3236
+ 'checkboxes': CheckboxesSchema<$Item>
3237
+ 'code': CodeSchema<$Item>
3238
+ 'color': ColorSchema<$Item>
3239
+ 'component': ComponentSchema<$Item>
3240
+ 'computed': ComputedSchema<$Item>
3241
+ 'creditcard': InputSchema<$Item>
3242
+ 'data': DataSchema<$Item>
3243
+ 'date': DateSchema<$Item>
3244
+ 'datetime': DateSchema<$Item>
3245
+ 'domain': InputSchema<$Item>
3246
+ 'email': InputSchema<$Item>
3247
+ 'hostname': InputSchema<$Item>
3248
+ 'integer': NumberSchema<$Item>
3249
+ 'list': ListSchema<$Item>
3250
+ 'markup': MarkupSchema<$Item>
3251
+ 'multiselect': MultiselectSchema<$Item>
3252
+ 'number': NumberSchema<$Item>
3253
+ 'object': ObjectSchema<$Item>
3254
+ 'password': InputSchema<$Item>
3255
+ 'progress': ProgressSchema<$Item>
3256
+ 'radio': RadioSchema<$Item>
3257
+ 'select': SelectSchema<$Item>
3258
+ 'slider': SliderSchema<$Item>
3259
+ 'spacer': SpacerSchema<$Item>
3260
+ 'submit': ButtonSchema<$Item>
3261
+ 'switch': SwitchSchema<$Item>
3262
+ 'tel': InputSchema<$Item>
3263
+ 'text': InputSchema<$Item>
3264
+ 'textarea': TextareaSchema<$Item>
3265
+ 'time': DateSchema<$Item>
3266
+ 'tree-list': TreeListSchema<$Item>
3267
+ 'tree-object': TreeObjectSchema<$Item>
3268
+ 'upload': UploadSchema<$Item>
3269
+ 'url': InputSchema<$Item>
3270
+ 'label': LabelSchema<$Item>
3271
+ 'section': SectionSchema<$Item>
3272
+ 'hidden': HiddenSchema<$Item>
3273
+ 'unknown': never
1179
3274
  }
1180
3275
 
1181
- type OrRecordOf<T> = T | Record<string, T>
1182
- type OrPromiseOf<T> = T | Promise<T>
1183
- type OrFunctionReturning<T> = (() => T) | T
1184
- type OrArrayOf<T> = T | T[]
1185
- type Resolvable<T> = OrFunctionReturning<OrPromiseOf<OrRecordOf<T>>>
1186
-
1187
- // https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
1188
- type AnyGate<
1189
- $CheckType,
1190
- $TypeWhenNotAny,
1191
- $TypeWhenAny = $CheckType
1192
- > = 0 extends 1 & $CheckType ? $TypeWhenAny : $TypeWhenNotAny
3276
+ export type OrRecordOf<T> = T | Record<string, T>
3277
+ export type OrPromiseOf<T> = T | Promise<T>
3278
+ export type OrFunctionReturning<T> = (() => T) | T
3279
+ export type OrArrayOf<T> = T | T[]
3280
+ export type Resolvable<T> = OrFunctionReturning<OrPromiseOf<OrRecordOf<T>>>
3281
+
3282
+ type WatchHandler<$Item> = (
3283
+ this: DitoComponentInstance<$Item>,
3284
+ value: any,
3285
+ oldValue: any
3286
+ ) => void
3287
+
3288
+ type WatchEntry<$Item> =
3289
+ | WatchHandler<$Item>
3290
+ | {
3291
+ handler: WatchHandler<$Item>
3292
+ deep?: boolean
3293
+ immediate?: boolean
3294
+ }
3295
+
3296
+ type WatchHandlers<$Item> = Record<string, WatchEntry<$Item>>
1193
3297
 
1194
3298
  type LiteralUnion<T extends U, U = string> = T | (U & Record<never, never>)