@ditojs/admin 1.13.0 → 1.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/admin",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "type": "module",
5
5
  "description": "Dito.js Admin is a schema based admin interface for Dito.js Server, featuring auto-generated views and forms and built with Vue.js",
6
6
  "repository": "https://github.com/ditojs/dito/tree/master/packages/admin",
@@ -37,8 +37,8 @@
37
37
  "not ie_mob > 0"
38
38
  ],
39
39
  "dependencies": {
40
- "@ditojs/ui": "^1.13.0",
41
- "@ditojs/utils": "^1.13.0",
40
+ "@ditojs/ui": "^1.14.0",
41
+ "@ditojs/utils": "^1.14.0",
42
42
  "codeflask": "^1.4.1",
43
43
  "filesize": "^10.0.5",
44
44
  "filesize-parser": "^1.5.0",
@@ -48,26 +48,26 @@
48
48
  "tiptap-commands": "^1.17.1",
49
49
  "tiptap-extensions": "^1.35.2",
50
50
  "to-pascal-case": "^1.0.0",
51
- "vue": "^2.7.13",
51
+ "vue": "^2.7.14",
52
52
  "vue-color": "^2.8.1",
53
53
  "vue-js-modal": "^2.0.1",
54
54
  "vue-multiselect": "^2.1.6",
55
55
  "vue-notification": "^1.3.20",
56
56
  "vue-router": "^3.6.5",
57
57
  "vue-spinner": "^1.0.4",
58
- "vue-template-compiler": "^2.7.13",
59
- "vue-upload-component": "^2.8.22",
58
+ "vue-template-compiler": "^2.7.14",
59
+ "vue-upload-component": "^2.8.23",
60
60
  "vuedraggable": "^2.24.3"
61
61
  },
62
62
  "devDependencies": {
63
- "@ditojs/build": "^1.13.0",
63
+ "@ditojs/build": "^1.14.0",
64
64
  "pug": "^3.0.2",
65
- "sass": "1.55.0",
66
- "type-fest": "^3.1.0",
67
- "typescript": "^4.8.4",
68
- "vite": "^3.2.2",
65
+ "sass": "1.56.1",
66
+ "type-fest": "^3.2.0",
67
+ "typescript": "^4.9.3",
68
+ "vite": "^3.2.4",
69
69
  "vite-plugin-vue2": "^2.0.2"
70
70
  },
71
71
  "types": "types",
72
- "gitHead": "b65d7d15871b0d11579565df92780536ef2926da"
72
+ "gitHead": "ba197ae5254deb657b2c3b5dab7a851f488e022a"
73
73
  }
package/src/DitoAdmin.js CHANGED
@@ -3,7 +3,9 @@ import VueModal from 'vue-js-modal'
3
3
  import VueRouter from 'vue-router'
4
4
  import VueNotifications from 'vue-notification'
5
5
  import {
6
- isString, isAbsoluteUrl, merge, hyphenate, camelize, defaultFormats
6
+ isString, isArray, asArray, isAbsoluteUrl,
7
+ merge, hyphenate, camelize,
8
+ defaultFormats
7
9
  } from '@ditojs/utils'
8
10
  import * as components from './components/index.js'
9
11
  import * as types from './types/index.js'
@@ -234,16 +236,41 @@ async function request(api, {
234
236
  return response
235
237
  }
236
238
 
239
+ function isApiUrl(api, url) {
240
+ return !isAbsoluteUrl(url) || url.startsWith(api.url)
241
+ }
242
+
237
243
  function getApiUrl(api, { url, query }) {
238
244
  if (!isAbsoluteUrl(url)) {
239
- // Use same approach as axios `combineURLs()` to join baseURL with path:
240
- url = `${api.url.replace(/\/+$/, '')}/${url.replace(/^\/+/, '')}`
245
+ url = combineUrls(api.url, url)
241
246
  }
242
247
  // Support optional query parameters, to be are added to the URL.
243
- const search = query && new URLSearchParams(query).toString()
248
+ const search = formatQuery(query)
244
249
  return search ? `${url}?${search}` : url
245
250
  }
246
251
 
247
- function isApiUrl(api, url) {
248
- return !isAbsoluteUrl(url) || url.startsWith(api.url)
252
+ function combineUrls(baseUrl, relativeUrl) {
253
+ // Use same approach as axios `combineURLs()` to join baseUrl & relativeUrl:
254
+ return `${baseUrl.replace(/\/+$/, '')}/${relativeUrl.replace(/^\/+/, '')}`
255
+ }
256
+
257
+ function formatQuery(query) {
258
+ const entries = query
259
+ ? isArray(query)
260
+ ? query
261
+ : Object.entries(query)
262
+ : []
263
+ return new URLSearchParams(
264
+ // Expand array values into multiple entries under the same key, so
265
+ // `formatQuery({ foo: [1, 2], bar: 3 })` => 'foo=1&foo=2&bar=3'.
266
+ entries.reduce(
267
+ (entries, [key, value]) => {
268
+ for (const val of asArray(value)) {
269
+ entries.push([key, val])
270
+ }
271
+ return entries
272
+ },
273
+ []
274
+ )
275
+ ).toString()
249
276
  }
@@ -288,8 +288,7 @@ export default DitoComponent.component('dito-form', {
288
288
  // @override ResourceMixin.setupData()
289
289
  setupData() {
290
290
  if (this.isCreating) {
291
- this.createdData = this.createdData ||
292
- this.createData(this.schema, this.type)
291
+ this.createdData ||= this.createData(this.schema, this.type)
293
292
  } else {
294
293
  this.ensureData()
295
294
  }
@@ -163,7 +163,7 @@ export function processComponent(api, schema, name, routes, level) {
163
163
  export function processRouteSchema(api, schema, name) {
164
164
  // Used for view and source schemas, see SourceMixin
165
165
  schema.name = name
166
- schema.path = schema.path || api.normalizePath(name)
166
+ schema.path ||= api.normalizePath(name)
167
167
  }
168
168
 
169
169
  export async function processSchemaComponents(api, schema, routes, level) {
package/types/index.d.ts CHANGED
@@ -7,7 +7,6 @@ import {
7
7
  NumberFormat,
8
8
  TimeFormat
9
9
  } from '@ditojs/utils'
10
- import { AxiosResponse as Response } from 'axios'
11
10
  import { RequireAtLeastOne, SetOptional } from 'type-fest'
12
11
  import Vue, { VueConstructor } from 'vue'
13
12
 
@@ -21,7 +20,7 @@ export interface DitoGlobal {
21
20
  base?: string
22
21
  settings?: Record<string, any>
23
22
  }
24
- export type PerformRequest = <T>({
23
+ export type RequestMethod = <T>({
25
24
  url,
26
25
  method,
27
26
  data,
@@ -39,7 +38,9 @@ export type PerformRequest = <T>({
39
38
  params: ConstructorParameters<typeof URLSearchParams>[0]
40
39
  query: ConstructorParameters<typeof URLSearchParams>[0]
41
40
  headers: Record<string, string>
42
- }) => Promise<Response<T>>
41
+ }) => Promise<RequestMethodResponse<T>>
42
+
43
+ export type RequestMethodResponse<T> = Response & { data: T }
43
44
 
44
45
  export interface ApiResource {
45
46
  type: string
@@ -61,7 +62,7 @@ export interface ApiConfig {
61
62
  date?: DateFormat
62
63
  time?: TimeFormat
63
64
  }
64
- request?: PerformRequest
65
+ request?: RequestMethod
65
66
  /**
66
67
  * Whether to display admin notifications.
67
68
  *
@@ -74,7 +75,7 @@ export interface ApiConfig {
74
75
  * The amount of milliseconds multiplied with the amount of characters
75
76
  * displayed in the notification, plus 40 (40 + title + message).
76
77
  * @defaultValue `20`
77
- **/
78
+ */
78
79
  durationFactor: number
79
80
  }
80
81
  cors?: {
@@ -214,7 +215,11 @@ export interface SchemaTypeMixin<$Item> {
214
215
  * depend on its contents or as 'fill' to fill left over space. A line will
215
216
  * contain multiple components until their widths exceed 100%.
216
217
  */
217
- width?: OrItemAccessor<$Item, {}, 'auto' | 'fill' | `${number}%` | `${number}/${number}` | number>
218
+ width?: OrItemAccessor<
219
+ $Item,
220
+ {},
221
+ 'auto' | 'fill' | `${number}%` | `${number}/${number}` | number
222
+ >
218
223
 
219
224
  /**
220
225
  * Whether the component is visible.
@@ -879,76 +884,83 @@ export type ColumnSchema<$Item = any> = {
879
884
 
880
885
  export type ResolvableForm<$Item = any> = Resolvable<Form<$Item>>
881
886
 
882
- export type ListSchema<$Item = { [key: string]: any }> = SchemaSourceMixin<$Item> &
883
- BaseSchema<$Item> & {
884
- /**
885
- * The type of the view
886
- */
887
- type: 'list'
888
- /**
889
- * The form.
890
- */
891
- form?: ResolvableForm
892
- /**
893
- * The forms.
894
- */
895
- forms?: {
896
- [key: string]: ResolvableForm
897
- }
898
- /**
899
- * The label given to the items. If no itemLabel is given, the default is
900
- * the 'name' property of the item, followed by label of the form of the
901
- * view (plus item id) and other defaults.
902
- */
903
- itemLabel?: OrItemAccessor<any, {}, string>
904
- /**
905
- * The columns displayed in the table. While columns can be supplied as an
906
- * array where each entry is the name of a property of the item, it is
907
- * usually beneficial to assign an object with further options to the
908
- * columns property.
909
- */
910
- columns?: Record<string, ColumnSchema<$Item>> | (keyof $Item)[]
911
- /**
912
- * Scope names as defined on the model. When set, the admin renders a set of
913
- * scope buttons, allowing the user to switch between them while editing.
914
- */
915
- scopes?:
916
- | string[]
917
- | {
918
- [scopeName: string]:
919
- | {
920
- label?: string
921
- hint?: string
922
- defaultScope?: boolean
923
- }
924
- | string
925
- }
926
- /**
927
- * Default scope name as defined on the model.
928
- */
929
- scope?: string
930
-
931
- // TODO: document filters
932
- filters?: {
933
- [k: string]:
934
- | {
935
- label?: string
936
- filter: 'text'
937
- /**
938
- * @defaultValue `['contains']`
939
- */
940
- operators?: ('contains' | 'equals' | 'starts-with' | 'ends-with')[]
941
- }
942
- | {
943
- label?: string
944
- filter: 'date-range'
945
- }
887
+ export type ListSchema<$Item = { [key: string]: any }> =
888
+ SchemaSourceMixin<$Item> &
889
+ BaseSchema<$Item> & {
890
+ /**
891
+ * The type of the view
892
+ */
893
+ type: 'list'
894
+ /**
895
+ * The form.
896
+ */
897
+ form?: ResolvableForm
898
+ /**
899
+ * The forms.
900
+ */
901
+ forms?: {
902
+ [key: string]: ResolvableForm
903
+ }
904
+ /**
905
+ * The label given to the items. If no itemLabel is given, the default is
906
+ * the 'name' property of the item, followed by label of the form of the
907
+ * view (plus item id) and other defaults.
908
+ */
909
+ itemLabel?: OrItemAccessor<any, {}, string>
910
+ /**
911
+ * The columns displayed in the table. While columns can be supplied as an
912
+ * array where each entry is the name of a property of the item, it is
913
+ * usually beneficial to assign an object with further options to the
914
+ * columns property.
915
+ */
916
+ columns?: Record<string, ColumnSchema<$Item>> | (keyof $Item)[]
917
+ /**
918
+ * Scope names as defined on the model. When set, the admin renders a set
919
+ * of scope buttons, allowing the user to switch between them while
920
+ * editing.
921
+ */
922
+ scopes?:
923
+ | string[]
946
924
  | {
947
- label?: string
948
- components: Components<any>
925
+ [scopeName: string]:
926
+ | {
927
+ label?: string
928
+ hint?: string
929
+ defaultScope?: boolean
930
+ }
931
+ | string
949
932
  }
933
+ /**
934
+ * Default scope name as defined on the model.
935
+ */
936
+ scope?: string
937
+
938
+ // TODO: document filters
939
+ filters?: {
940
+ [k: string]:
941
+ | {
942
+ label?: string
943
+ filter: 'text'
944
+ /**
945
+ * @defaultValue `['contains']`
946
+ */
947
+ operators?: (
948
+ | 'contains'
949
+ | 'equals'
950
+ | 'starts-with'
951
+ | 'ends-with'
952
+ )[]
953
+ }
954
+ | {
955
+ label?: string
956
+ filter: 'date-range'
957
+ }
958
+ | {
959
+ label?: string
960
+ components: Components<any>
961
+ }
962
+ }
950
963
  }
951
- }
952
964
 
953
965
  export type OrItemAccessor<
954
966
  $Item = any,
@@ -1013,7 +1025,7 @@ export type DitoContext<$Item = any> = {
1013
1025
 
1014
1026
  // Helper Methods
1015
1027
 
1016
- request<T extends any>(options: {
1028
+ request<T>(options: {
1017
1029
  /**
1018
1030
  * Allows caching of loaded data on two levels:
1019
1031
  * - 'global': cache globally, for the entire admin session
@@ -1044,7 +1056,7 @@ export type DitoContext<$Item = any> = {
1044
1056
  }
1045
1057
 
1046
1058
  export type View<$Item = any> = {
1047
- resource?: Form['resource'];
1059
+ resource?: Form['resource']
1048
1060
  clipboard?: Form['clipboard']
1049
1061
  } & (
1050
1062
  | InputSchema<$Item>
@@ -1167,7 +1179,7 @@ export class DitoAdmin<
1167
1179
 
1168
1180
  // TODO: options and return type
1169
1181
  register(type: OrArrayOf<string>, options: any): any
1170
- request: PerformRequest
1182
+ request: RequestMethod
1171
1183
  }
1172
1184
  export type HTTPVerb = 'get' | 'post' | 'put' | 'delete' | 'patch'
1173
1185