@ditojs/admin 1.10.0 → 1.11.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.10.0",
3
+ "version": "1.11.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",
@@ -25,7 +25,7 @@
25
25
  "prepare": "yarn build"
26
26
  },
27
27
  "engines": {
28
- "node": ">= 16.0.0",
28
+ "node": ">= 18.0.0",
29
29
  "yarn": ">= 1.0.0"
30
30
  },
31
31
  "browserslist": [
@@ -35,11 +35,10 @@
35
35
  "not ie_mob > 0"
36
36
  ],
37
37
  "dependencies": {
38
- "@ditojs/ui": "^1.10.0",
39
- "@ditojs/utils": "^1.10.0",
40
- "axios": "^0.27.2",
38
+ "@ditojs/ui": "^1.11.0",
39
+ "@ditojs/utils": "^1.11.0",
41
40
  "codeflask": "^1.4.1",
42
- "filesize": "^9.0.11",
41
+ "filesize": "^10.0.5",
43
42
  "filesize-parser": "^1.5.0",
44
43
  "nanoid": "^4.0.0",
45
44
  "tinycolor2": "^1.4.2",
@@ -47,23 +46,23 @@
47
46
  "tiptap-commands": "^1.17.1",
48
47
  "tiptap-extensions": "^1.35.2",
49
48
  "to-pascal-case": "^1.0.0",
50
- "vue": "^2.7.8",
49
+ "vue": "^2.7.13",
51
50
  "vue-color": "^2.8.1",
52
- "vue-js-modal": "^1.3.35",
51
+ "vue-js-modal": "^2.0.1",
53
52
  "vue-multiselect": "^2.1.6",
54
53
  "vue-notification": "^1.3.20",
55
- "vue-router": "^3.5.4",
54
+ "vue-router": "^3.6.5",
56
55
  "vue-spinner": "^1.0.4",
57
- "vue-template-compiler": "^2.7.8",
56
+ "vue-template-compiler": "^2.7.13",
58
57
  "vue-upload-component": "^2.8.22",
59
58
  "vuedraggable": "^2.24.3"
60
59
  },
61
60
  "devDependencies": {
62
- "@ditojs/build": "^1.10.0",
61
+ "@ditojs/build": "^1.11.0",
63
62
  "pug": "^3.0.2",
64
- "sass": "1.53.0",
65
- "vite": "^3.0.2",
63
+ "sass": "1.55.0",
64
+ "vite": "^3.2.2",
66
65
  "vite-plugin-vue2": "^2.0.2"
67
66
  },
68
- "gitHead": "0b044d649e0fe1bcbcd3603bcdc9c6168058fb6d"
67
+ "gitHead": "cc2a838ccf15a75f435844f3087698c4efaf44e4"
69
68
  }
package/src/DitoAdmin.js CHANGED
@@ -2,7 +2,6 @@ import Vue from 'vue'
2
2
  import VueModal from 'vue-js-modal'
3
3
  import VueRouter from 'vue-router'
4
4
  import VueNotifications from 'vue-notification'
5
- import axios from 'axios'
6
5
  import {
7
6
  isString, isAbsoluteUrl, merge, hyphenate, camelize, defaultFormats
8
7
  } from '@ditojs/utils'
@@ -11,6 +10,7 @@ import * as types from './types/index.js'
11
10
  import DitoRoot from './components/DitoRoot.vue'
12
11
  import TypeComponent from './TypeComponent.js'
13
12
  import { getResource } from './utils/resource.js'
13
+ import { deprecate } from './utils/deprecate.js'
14
14
  import verbs from './verbs.js'
15
15
 
16
16
  Vue.config.productionTip = false
@@ -37,25 +37,14 @@ export default class DitoAdmin {
37
37
 
38
38
  // Setup default api setttings:
39
39
  api.locale ||= 'en-US'
40
-
41
40
  api.formats = merge({}, defaultFormats, api.formats)
42
-
43
- api.request ||= options => this.request(options)
44
-
41
+ api.request ||= options => request(api, options)
42
+ api.getApiUrl ||= path => getApiUrl(api, path)
43
+ api.isApiRequest ||= url => isApiRequest(api, url)
45
44
  // Setting `api.normalizePaths = true (plural) sets both:
46
45
  // `api.normalizePath = hyphenate` and `api.denormalizePath = camelize`
47
- api.normalizePath = (
48
- api.normalizePath ||
49
- api.normalizePaths
50
- ? hyphenate
51
- : val => val
52
- )
53
- api.denormalizePath = (
54
- api.denormalizePath ||
55
- api.normalizePaths
56
- ? camelize
57
- : val => val
58
- )
46
+ api.normalizePath ||= api.normalizePaths ? hyphenate : val => val
47
+ api.denormalizePath ||= api.normalizePaths ? camelize : val => val
59
48
 
60
49
  // Allow the configuration of all auth resources, like so:
61
50
  // api.users = {
@@ -140,10 +129,6 @@ export default class DitoAdmin {
140
129
  ...api.headers
141
130
  }
142
131
 
143
- api.isApiRequest = api.isApiRequest || function(url) {
144
- return !isAbsoluteUrl(url) || url.startsWith(api.url)
145
- }
146
-
147
132
  if (isString(el)) {
148
133
  el = document.querySelector(el)
149
134
  }
@@ -202,26 +187,62 @@ export default class DitoAdmin {
202
187
  register(type, options) {
203
188
  return TypeComponent.register(type, options)
204
189
  }
190
+ }
205
191
 
206
- request({
207
- url,
208
- method = 'get',
209
- data = null,
210
- params = null,
211
- headers = null
212
- }) {
213
- const isApiRequest = this.api.isApiRequest(url)
214
- return axios.request({
215
- url,
216
- method,
217
- params,
218
- ...(data && { data }),
219
- baseURL: isApiRequest ? this.api.url : null,
220
- headers: {
221
- ...(isApiRequest && this.api.headers),
222
- ...headers
223
- },
224
- withCredentials: isApiRequest && !!this.api.cors?.credentials
225
- })
192
+ class RequestError extends Error {
193
+ constructor(response) {
194
+ super(`Request failed with status code: ${response.status} (${response.statusText})`)
195
+ this.response = response
196
+ }
197
+ }
198
+
199
+ async function request(api, {
200
+ url,
201
+ method = 'get',
202
+ // TODO: `params` was deprecated in favor of `query` on 2022-11-01, remove
203
+ // once not in use anywhere anymore.
204
+ params = null,
205
+ query = params || null,
206
+ headers = null,
207
+ data = null
208
+ }) {
209
+ if (params) {
210
+ deprecate(`request.params is deprecated. Use action.method and action.path instead.`)
226
211
  }
212
+
213
+ const isApiRequest = api.isApiRequest(url)
214
+ if (isApiRequest && !isAbsoluteUrl(url)) {
215
+ url = api.getApiUrl(url)
216
+ }
217
+
218
+ const search = query && new URLSearchParams(query).toString()
219
+ if (search) {
220
+ url = `${url}?${search}`
221
+ }
222
+
223
+ const response = await fetch(url, {
224
+ method: method.toUpperCase(),
225
+ ...(data && { body: JSON.stringify(data) }),
226
+ headers: {
227
+ ...(isApiRequest && api.headers),
228
+ ...headers
229
+ },
230
+ credentials: isApiRequest && api.cors?.credentials
231
+ ? 'include'
232
+ : 'same-origin'
233
+ })
234
+ response.data = await response.json()
235
+ if (!response.ok) {
236
+ throw new RequestError(response)
237
+ }
238
+ return response
239
+ }
240
+
241
+ function getApiUrl(api, path) {
242
+ // Use same approach as axios `combineURLs()` to join baseURL with path:
243
+ return `${api.url.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`
244
+ }
245
+
246
+ function isApiRequest(api, url) {
247
+ return !isAbsoluteUrl(url) || url.startsWith(api.url)
227
248
  }
@@ -1,6 +1,5 @@
1
1
  <template lang="pug">
2
2
  .dito-root
3
- modals-container
4
3
  notifications.dito-notifications(
5
4
  ref="notifications"
6
5
  position="top right"
@@ -8,7 +8,6 @@ import DitoContext from '../DitoContext.js'
8
8
  import EmitterMixin from './EmitterMixin.js'
9
9
  import { isMatchingType, convertType } from '../utils/type.js'
10
10
  import { getResource, getMemberResource } from '../utils/resource.js'
11
- import { deprecate } from '../utils/deprecate.js'
12
11
 
13
12
  // @vue/component
14
13
  export default {
@@ -294,35 +293,22 @@ export default {
294
293
 
295
294
  getResourceUrl(resource) {
296
295
  const path = this.getResourcePath(resource)
297
- let url = null
298
- if (path) {
299
- // Use same approach as axios internally to join baseURL with path:
300
- url = `${
301
- this.api.url.replace(/\/+$/, '')
302
- }/${
303
- path.replace(/^\/+/, '')
304
- }`
305
- // Support optional query parameters, which are added to the URL:
306
- const { query } = resource
307
- if (query) {
308
- const params = Object.entries(query).map(
309
- ([key, value]) =>
310
- `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
311
- )
312
- url = `${url}?${params.join('&')}`
313
- }
314
- }
315
- return url
296
+ if (!path) return null
297
+ const url = this.api.getApiUrl(path)
298
+ // Support optional query parameters, to be are added to the URL.
299
+ const { query } = resource
300
+ const search = query && new URLSearchParams(query).toString()
301
+ return search ? `${url}?${search}` : url
316
302
  },
317
303
 
318
- async sendRequest({ method, url, resource, data, params, internal }) {
319
- url = url || this.getResourcePath(resource)
320
- method = method || resource?.method
304
+ async sendRequest({ method, url, resource, query, data, internal }) {
305
+ url ||= this.getResourcePath(resource)
306
+ method ||= resource?.method
321
307
  const checkUser = !internal && this.api.isApiRequest(url)
322
308
  if (checkUser) {
323
309
  await this.rootComponent.ensureUser()
324
310
  }
325
- const response = await this.api.request({ method, url, data, params })
311
+ const response = await this.api.request({ method, url, data, query })
326
312
  // Detect change of the own user, and fetch it again if it was changed.
327
313
  if (
328
314
  checkUser &&
@@ -371,11 +357,6 @@ export default {
371
357
  return res
372
358
  },
373
359
 
374
- load(options) {
375
- deprecate('load() is deprecated. Use request() instead.')
376
- return this.request(options)
377
- },
378
-
379
360
  format(value, {
380
361
  locale = this.api.locale,
381
362
  defaults = this.api.formats,
@@ -102,7 +102,7 @@ export default {
102
102
  text = item[key]
103
103
  }
104
104
  const hadLabel = !!text
105
- text = text || ''
105
+ text ||= ''
106
106
  // If no label was found so far, try to produce one from theindex.
107
107
  if (!text) {
108
108
  // Always use extended style when auto-generating labels from index/id:
@@ -193,8 +193,8 @@ export default {
193
193
  },
194
194
 
195
195
  requestData() {
196
- const params = this.queryParams
197
- this.handleRequest({ method: 'get', params }, (err, response) => {
196
+ const query = this.queryParams
197
+ this.handleRequest({ method: 'get', query }, (err, response) => {
198
198
  if (err) {
199
199
  if (response) {
200
200
  const { data } = response
@@ -226,15 +226,15 @@ export default {
226
226
  async handleRequest({
227
227
  method,
228
228
  resource = this.resource,
229
- data,
230
- params
229
+ query,
230
+ data
231
231
  }, callback) {
232
232
  const loadingOptions = {
233
233
  updateRoot: true, // Display spinner in header when loading in resources
234
234
  updateView: this.isInView // Notify view of loading for view components
235
235
  }
236
236
  this.setLoading(true, loadingOptions)
237
- const request = { method, resource, data, params }
237
+ const request = { method, resource, data, query }
238
238
  try {
239
239
  const response = await this.sendRequest(request)
240
240
  // Pass both request and response to the callback, so they can be
@@ -307,7 +307,7 @@ export default {
307
307
  } = {}) {
308
308
  return new Promise(resolve => {
309
309
  this.handleRequest(
310
- { method, data, resource },
310
+ { method, resource, data },
311
311
  async (err, response) => {
312
312
  const data = response?.data
313
313
  if (err) {
@@ -79,7 +79,7 @@ export default {
79
79
  // at the root in `setData()`, but here instead.
80
80
  data = this.unwrapListData(data) || data
81
81
  }
82
- data = data || []
82
+ data ||= []
83
83
  const { wrapPrimitives } = this
84
84
  if (wrapPrimitives) {
85
85
  if (this.unwrappingPrimitives) {
package/src/utils/uid.js CHANGED
@@ -5,7 +5,7 @@ let uid = 0
5
5
  export function getUid(object, itemId) {
6
6
  let id = uidMap.get(object)
7
7
  if (!id) {
8
- id = id = itemId || `@${++uid}`
8
+ id = itemId || `@${++uid}`
9
9
  uidMap.set(object, id)
10
10
  }
11
11
  return id
@@ -1,4 +1,4 @@
1
- import filesize from 'filesize'
1
+ import { filesize } from 'filesize'
2
2
 
3
3
  export function formatFileSize(size) {
4
4
  return filesize(size, { base: 10 })