@ditojs/admin 2.9.1 → 2.9.3

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": "2.9.1",
3
+ "version": "2.9.3",
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",
@@ -33,9 +33,9 @@
33
33
  "not ie_mob > 0"
34
34
  ],
35
35
  "dependencies": {
36
- "@ditojs/ui": "^2.9.0",
36
+ "@ditojs/ui": "^2.9.3",
37
37
  "@ditojs/utils": "^2.9.0",
38
- "@kyvg/vue3-notification": "^2.9.0",
38
+ "@kyvg/vue3-notification": "^2.9.1",
39
39
  "@lk77/vue3-color": "^3.0.6",
40
40
  "@tiptap/core": "^2.0.3",
41
41
  "@tiptap/extension-blockquote": "^2.0.3",
@@ -62,28 +62,28 @@
62
62
  "codeflask": "^1.4.1",
63
63
  "filesize": "^10.0.7",
64
64
  "filesize-parser": "^1.5.0",
65
- "focus-trap": "^7.4.0",
65
+ "focus-trap": "^7.4.1",
66
66
  "nanoid": "^4.0.2",
67
67
  "sortablejs": "^1.15.0",
68
68
  "tinycolor2": "^1.6.0",
69
69
  "tippy.js": "^6.3.7",
70
70
  "type-fest": "^3.10.0",
71
- "vue": "^3.2.47",
72
- "vue-multiselect": "^3.0.0-beta.1",
73
- "vue-router": "^4.1.6",
71
+ "vue": "^3.3.2",
72
+ "vue-multiselect": "^3.0.0-beta.2",
73
+ "vue-router": "^4.2.0",
74
74
  "vue-upload-component": "^3.1.8"
75
75
  },
76
76
  "devDependencies": {
77
77
  "@ditojs/build": "^2.9.0",
78
- "@vitejs/plugin-vue": "^4.2.1",
79
- "@vue/compiler-sfc": "^3.2.47",
78
+ "@vitejs/plugin-vue": "^4.2.3",
79
+ "@vue/compiler-sfc": "^3.3.2",
80
80
  "pug": "^3.0.2",
81
81
  "sass": "1.62.1",
82
82
  "typescript": "^5.0.4",
83
- "vite": "^4.3.5"
83
+ "vite": "^4.3.7"
84
84
  },
85
85
  "types": "types",
86
- "gitHead": "002f3c713f9d3b10e348d2e9fd07d3a5bea426e6",
86
+ "gitHead": "8855fb3de01a12a456a622cc17c0c03ffdbf7eab",
87
87
  "scripts": {
88
88
  "build": "vite build",
89
89
  "watch": "yarn build --mode 'development' --watch",
package/src/DitoAdmin.js CHANGED
@@ -199,8 +199,8 @@ export default class DitoAdmin {
199
199
  app.config.errorHandler = console.error
200
200
 
201
201
  app.use(VueNotifications, {
202
- name: 'notify',
203
- componentName: 'VueNotifications'
202
+ componentName: 'VueNotifications',
203
+ name: 'notify'
204
204
  })
205
205
 
206
206
  app.directive('resize', ResizeDirective)
@@ -82,6 +82,11 @@ export default DitoComponent.component('DitoClipboard', {
82
82
  },
83
83
 
84
84
  methods: {
85
+ checkClipboardData(clipboardData) {
86
+ const { $schema, ...data } = clipboardData || {}
87
+ return $schema === this.schema.name ? data : null
88
+ },
89
+
85
90
  async getClipboardData(report) {
86
91
  // Use the internal clipboard as fallback.
87
92
  let { clipboardData } = this.appState
@@ -101,12 +106,11 @@ export default DitoComponent.component('DitoClipboard', {
101
106
  }
102
107
  }
103
108
  }
104
- const { $schema, ...data } = clipboardData || {}
105
- return $schema === this.schema.name ? data : null
109
+ return this.checkClipboardData(clipboardData)
106
110
  },
107
111
 
108
112
  async updatePaste() {
109
- this.pasteEnabled = !!this.appState.clipboardData
113
+ this.pasteEnabled = !!this.checkClipboardData(this.appState.clipboardData)
110
114
  if (!this.pasteEnabled && this.appState.agent.chrome) {
111
115
  // See if the clipboard content is valid JSON data that is compatible
112
116
  // with the current target schema, and only then activate the pasting:
@@ -1,23 +1,10 @@
1
1
  <template lang="pug">
2
2
  nav.dito-header
3
- .dito-trail
4
- ul
5
- li(
6
- v-for="(component, index) in trail"
7
- )
8
- template(
9
- v-if="index === trail.length - 1"
10
- )
11
- span(:class="getBreadcrumbClass(component)")
12
- | {{ component.breadcrumb }}
13
- RouterLink.dito-breadcrumb(
14
- v-else
15
- :to="component.path"
16
- )
17
- span(:class="getBreadcrumbClass(component)")
18
- | {{ component.breadcrumb }}
3
+ DitoTrail
19
4
  DitoSpinner(
20
5
  v-if="isLoading"
6
+ :size="spinner?.size"
7
+ :color="spinner?.color"
21
8
  )
22
9
  //- Teleport target for `.dito-schema-header`:
23
10
  .dito-header__teleport
@@ -26,12 +13,9 @@ nav.dito-header
26
13
 
27
14
  <script>
28
15
  import DitoComponent from '../DitoComponent.js'
29
- import DitoSpinner from './DitoSpinner.vue'
30
16
 
31
17
  // @vue/component
32
18
  export default DitoComponent.component('DitoHeader', {
33
- components: { DitoSpinner },
34
-
35
19
  props: {
36
20
  spinner: {
37
21
  type: Object,
@@ -41,34 +25,6 @@ export default DitoComponent.component('DitoHeader', {
41
25
  type: Boolean,
42
26
  default: false
43
27
  }
44
- },
45
-
46
- computed: {
47
- trail() {
48
- return this.appState.routeComponents.filter(
49
- component => !!component.routeRecord
50
- )
51
- }
52
- },
53
-
54
- created() {
55
- const {
56
- size = '8px',
57
- color = '#999'
58
- } = this.spinner || {}
59
- // TODO: This is a hack to set the default props for the DitoSpinner.
60
- // Pass them on through the template instead!
61
- const { props } = DitoSpinner
62
- props.size.default = size
63
- props.color.default = color
64
- },
65
-
66
- methods: {
67
- getBreadcrumbClass(component) {
68
- return {
69
- 'dito-dirty': component.isDirty
70
- }
71
- }
72
28
  }
73
29
  })
74
30
  </script>
@@ -106,60 +62,6 @@ export default DitoComponent.component('DitoHeader', {
106
62
  }
107
63
  }
108
64
 
109
- .dito-trail {
110
- display: flex;
111
- box-sizing: border-box;
112
- height: 3em;
113
-
114
- ul {
115
- display: flex;
116
- }
117
-
118
- li {
119
- white-space: nowrap;
120
- }
121
-
122
- a {
123
- position: relative;
124
- display: block;
125
-
126
- $angle: 33deg;
127
-
128
- &:hover {
129
- span {
130
- color: $color-light;
131
- }
132
- }
133
-
134
- &::before,
135
- &::after {
136
- position: absolute;
137
- content: '';
138
- width: 1px;
139
- height: 0.75em;
140
- right: -0.25em;
141
- background: $color-white;
142
- opacity: 0.5;
143
- }
144
-
145
- &::before {
146
- top: 50%;
147
- transform: rotate($angle);
148
- transform-origin: top;
149
- }
150
-
151
- &::after {
152
- bottom: 50%;
153
- transform: rotate(-$angle);
154
- transform-origin: bottom;
155
- }
156
- }
157
- }
158
-
159
- .dito-spinner {
160
- margin-top: $header-padding-ver;
161
- }
162
-
163
65
  .dito-dirty {
164
66
  &::after {
165
67
  content: '';
@@ -0,0 +1,159 @@
1
+ <template lang="pug">
2
+ .dito-notifications
3
+ .dito-header
4
+ span
5
+ .dito-notifications__inner
6
+ VueNotifications(
7
+ ref="notifications"
8
+ classes="dito-notification"
9
+ position=""
10
+ width=""
11
+ )
12
+ </template>
13
+
14
+ <script>
15
+ import DitoComponent from '../DitoComponent.js'
16
+ import { asArray, stripTags } from '@ditojs/utils'
17
+
18
+ // @vue/component
19
+ export default DitoComponent.component('DitoNotifications', {
20
+ notifications() {
21
+ return this.isMounted && this.$refs.notifications
22
+ },
23
+
24
+ methods: {
25
+ notify({ type = 'info', title, text } = {}) {
26
+ title ||= (
27
+ {
28
+ warning: 'Warning',
29
+ error: 'Error',
30
+ info: 'Information',
31
+ success: 'Success'
32
+ }[type] ||
33
+ 'Notification'
34
+ )
35
+ text = `<p>${
36
+ asArray(text).join('</p> <p>')
37
+ }</p>`.replace(/\n|\r\n|\r/g, '<br>')
38
+ const log = (
39
+ {
40
+ warning: 'warn',
41
+ error: 'error',
42
+ info: 'log',
43
+ success: 'log'
44
+ }[type] ||
45
+ 'error'
46
+ )
47
+ // eslint-disable-next-line no-console
48
+ console[log](stripTags(text))
49
+ const { notifications = true } = this.api
50
+ if (notifications) {
51
+ // Calculate display-duration for the notification based on its content
52
+ // and the setting of the `durationFactor` configuration. It defines the
53
+ // amount of milliseconds multiplied with the amount of characters
54
+ // displayed in the notification, plus 40 (40 + title + message):
55
+ const { durationFactor = 20 } = notifications
56
+ const duration = (40 + text.length + title.length) * durationFactor
57
+ this.$notify({ type, title, text, duration })
58
+ }
59
+ },
60
+
61
+ destroyAll() {
62
+ this.notifications.destroyAll()
63
+ }
64
+ }
65
+ })
66
+ </script>
67
+
68
+ <style lang="scss">
69
+ @use 'sass:color';
70
+ @import '../styles/_imports';
71
+
72
+ @mixin type($background) {
73
+ background: color.adjust($background, $lightness: 5%);
74
+ color: $color-white;
75
+ border-left: 12px solid color.adjust($background, $lightness: -10%);
76
+ }
77
+
78
+ .dito-notifications {
79
+ $notification-width: 300px;
80
+
81
+ flex: 1;
82
+ z-index: $z-index-notifications;
83
+ box-sizing: border-box;
84
+ margin-left: $form-spacing;
85
+ // For the `@container` rule to work:
86
+ container-type: inline-size;
87
+
88
+ .dito-header {
89
+ span {
90
+ padding-left: 0;
91
+ padding-right: 0;
92
+ }
93
+ }
94
+
95
+ &__inner {
96
+ position: relative;
97
+ }
98
+
99
+ .vue-notification-group {
100
+ position: absolute;
101
+ left: 0;
102
+ top: 0;
103
+ width: $notification-width;
104
+
105
+ @container (width < #{$notification-width + $content-padding}) {
106
+ left: unset;
107
+ right: $content-padding;
108
+ }
109
+ }
110
+
111
+ .vue-notification-wrapper {
112
+ overflow: visible;
113
+ }
114
+
115
+ .dito-notification {
116
+ padding: 8px;
117
+ margin: $content-padding 0;
118
+ font-size: inherit;
119
+ color: $color-white;
120
+ border-radius: $border-radius;
121
+ box-shadow: $shadow-window;
122
+
123
+ .notification-title {
124
+ font-weight: bold;
125
+ padding-bottom: 8px;
126
+ }
127
+
128
+ .notification-content {
129
+ overflow: hidden;
130
+ word-break: break-all;
131
+
132
+ p {
133
+ margin: 0;
134
+
135
+ & + p {
136
+ margin-top: 8px;
137
+ }
138
+ }
139
+ }
140
+
141
+ &,
142
+ &.info {
143
+ @include type($color-active);
144
+ }
145
+
146
+ &.success {
147
+ @include type($color-success);
148
+ }
149
+
150
+ &.warning {
151
+ @include type($color-warning);
152
+ }
153
+
154
+ &.error {
155
+ @include type($color-error);
156
+ }
157
+ }
158
+ }
159
+ </style>
@@ -4,11 +4,6 @@
4
4
  :data-agent-platform="appState.agent.platform"
5
5
  :data-agent-version="appState.agent.versionNumber"
6
6
  )
7
- VueNotifications.dito-notifications(
8
- ref="notifications"
9
- position="top right"
10
- classes="dito-notification"
11
- )
12
7
  Transition(name="dito-drag")
13
8
  .dito-drag-overlay(
14
9
  v-if="isDraggingFiles"
@@ -43,12 +38,12 @@
43
38
  @click="rootComponent.login()"
44
39
  )
45
40
  span Login
46
- .dito-fill
41
+ DitoNotifications(ref="notifications")
47
42
  </template>
48
43
 
49
44
  <script>
50
45
  import { delegate as tippyDelegate } from 'tippy.js'
51
- import { asArray, mapConcurrently, stripTags } from '@ditojs/utils'
46
+ import { mapConcurrently } from '@ditojs/utils'
52
47
  import DitoComponent from '../DitoComponent.js'
53
48
  import DomMixin from '../mixins/DomMixin.js'
54
49
  import DitoUser from '../DitoUser.js'
@@ -90,7 +85,7 @@ export default DitoComponent.component('DitoRoot', {
90
85
 
91
86
  computed: {
92
87
  notifications() {
93
- return this.$refs.notifications
88
+ return this.isMounted && this.$refs.notifications
94
89
  },
95
90
 
96
91
  isLoading() {
@@ -239,39 +234,7 @@ export default DitoComponent.component('DitoRoot', {
239
234
  },
240
235
 
241
236
  notify({ type = 'info', title, text } = {}) {
242
- title ||= (
243
- {
244
- warning: 'Warning',
245
- error: 'Error',
246
- info: 'Information',
247
- success: 'Success'
248
- }[type] ||
249
- 'Notification'
250
- )
251
- text = `<p>${
252
- asArray(text).join('</p> <p>')
253
- }</p>`.replace(/\n|\r\n|\r/g, '<br>')
254
- const log = (
255
- {
256
- warning: 'warn',
257
- error: 'error',
258
- info: 'log',
259
- success: 'log'
260
- }[type] ||
261
- 'error'
262
- )
263
- // eslint-disable-next-line no-console
264
- console[log](stripTags(text))
265
- const { notifications = true } = this.api
266
- if (notifications) {
267
- // Calculate display-duration for the notification based on its content
268
- // and the setting of the `durationFactor` configuration. It defines the
269
- // amount of milliseconds multiplied with the amount of characters
270
- // displayed in the notification, plus 40 (40 + title + message):
271
- const { durationFactor = 20 } = notifications
272
- const duration = (40 + text.length + title.length) * durationFactor
273
- this.$notify({ type, title, text, duration })
274
- }
237
+ this.notifications.notify({ type, title, text })
275
238
  },
276
239
 
277
240
  closeNotifications() {
@@ -509,17 +472,6 @@ function addRoutes(router, routes) {
509
472
  }
510
473
  }
511
474
 
512
- .dito-fill {
513
- flex: 1;
514
-
515
- .dito-header {
516
- span {
517
- padding-left: 0;
518
- padding-right: 0;
519
- }
520
- }
521
- }
522
-
523
475
  .dito-account,
524
476
  .dito-login {
525
477
  cursor: pointer;
@@ -757,7 +757,7 @@ export default DitoComponent.component('DitoSchema', {
757
757
  }
758
758
 
759
759
  &:has(> .dito-schema-content + .dito-edit-buttons) {
760
- // Display the edit buttons to the right of the schema:
760
+ // Display the inlined edit buttons to the right of the schema:
761
761
  display: flex;
762
762
  flex-direction: row;
763
763
  align-items: stretch;
@@ -1,15 +1,18 @@
1
1
  <template lang="pug">
2
2
  .dito-spinner(
3
3
  v-show="loading"
4
+ :style="{ '--color': color, '--size': size, '--margin': margin }"
4
5
  )
5
- //- TODO: Convert to BEM
6
- .v-pulse.v-pulse1(:style="[spinnerStyle, spinnerDelay1]")
7
- .v-pulse.v-pulse2(:style="[spinnerStyle, spinnerDelay2]")
8
- .v-pulse.v-pulse3(:style="[spinnerStyle, spinnerDelay3]")
6
+ .dito-spinner__pulse.dito-spinner__pulse1
7
+ .dito-spinner__pulse.dito-spinner__pulse2
8
+ .dito-spinner__pulse.dito-spinner__pulse3
9
9
  </template>
10
10
 
11
11
  <script>
12
- export default {
12
+ import DitoComponent from '../DitoComponent.js'
13
+
14
+ // @vue/component
15
+ export default DitoComponent.component('DitoSpinner', {
13
16
  props: {
14
17
  loading: {
15
18
  type: Boolean,
@@ -17,54 +20,54 @@ export default {
17
20
  },
18
21
  color: {
19
22
  type: String,
20
- default: '#5dc596'
23
+ default: null
21
24
  },
22
25
  size: {
23
26
  type: String,
24
- default: '15px'
27
+ default: null
25
28
  },
26
29
  margin: {
27
30
  type: String,
28
- default: '2px'
29
- },
30
- radius: {
31
- type: String,
32
- default: '100%'
33
- }
34
- },
35
-
36
- data() {
37
- // TODO: Convert to using only classes
38
- return {
39
- spinnerStyle: {
40
- backgroundColor: this.color,
41
- width: this.size,
42
- height: this.size,
43
- margin: this.margin,
44
- borderRadius: this.radius,
45
- display: 'inline-block',
46
- animationName: 'v-pulseStretchDelay',
47
- animationDuration: '0.75s',
48
- animationIterationCount: 'infinite',
49
- animationTimingFunction: 'cubic-bezier(.2,.68,.18,1.08)',
50
- animationFillMode: 'both'
51
- },
52
- spinnerDelay1: {
53
- animationDelay: '0.12s'
54
- },
55
- spinnerDelay2: {
56
- animationDelay: '0.24s'
57
- },
58
- spinnerDelay3: {
59
- animationDelay: '0.36s'
60
- }
31
+ default: null
61
32
  }
62
33
  }
63
- }
34
+ })
64
35
  </script>
65
36
 
66
37
  <style lang="scss">
67
- @keyframes v-pulseStretchDelay {
38
+ .dito-spinner {
39
+ --color: #999999;
40
+ --size: 8px;
41
+ --margin: 2px;
42
+
43
+ display: flex;
44
+ align-items: center;
45
+
46
+ &__pulse {
47
+ display: inline-block;
48
+ background: var(--color);
49
+ width: var(--size);
50
+ height: var(--size);
51
+ margin: var(--margin);
52
+ border-radius: 100%;
53
+ animation: dito-spinner-pulse 0.75s cubic-bezier(0.2, 0.68, 0.18, 1.08) 0s
54
+ infinite both;
55
+ }
56
+
57
+ &__pulse1 {
58
+ animation-delay: 0.12s;
59
+ }
60
+
61
+ &__pulse2 {
62
+ animation-delay: 0.24s;
63
+ }
64
+
65
+ &__pulse3 {
66
+ animation-delay: 0.36s;
67
+ }
68
+ }
69
+
70
+ @keyframes dito-spinner-pulse {
68
71
  0%,
69
72
  80% {
70
73
  transform: scale(1);
@@ -77,7 +80,7 @@ export default {
77
80
  }
78
81
  }
79
82
 
80
- @keyframes v-pulseStretchDelay {
83
+ @keyframes dito-spinner-pulse {
81
84
  0%,
82
85
  80% {
83
86
  transform: scale(1);