@radio-garden/ditojs-admin 2.85.2-0.5067ad799
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/README.md +180 -0
- package/dist/dito-admin.css +1 -0
- package/dist/dito-admin.es.js +12106 -0
- package/dist/dito-admin.umd.js +7 -0
- package/package.json +96 -0
- package/src/DitoAdmin.js +293 -0
- package/src/DitoComponent.js +34 -0
- package/src/DitoContext.js +318 -0
- package/src/DitoTypeComponent.js +42 -0
- package/src/DitoUser.js +12 -0
- package/src/appState.js +12 -0
- package/src/components/DitoAccount.vue +60 -0
- package/src/components/DitoAffix.vue +68 -0
- package/src/components/DitoAffixes.vue +200 -0
- package/src/components/DitoButtons.vue +80 -0
- package/src/components/DitoClipboard.vue +186 -0
- package/src/components/DitoContainer.vue +374 -0
- package/src/components/DitoCreateButton.vue +146 -0
- package/src/components/DitoDialog.vue +242 -0
- package/src/components/DitoDraggable.vue +117 -0
- package/src/components/DitoEditButtons.vue +135 -0
- package/src/components/DitoErrors.vue +83 -0
- package/src/components/DitoForm.vue +521 -0
- package/src/components/DitoFormInner.vue +26 -0
- package/src/components/DitoFormNested.vue +17 -0
- package/src/components/DitoHeader.vue +84 -0
- package/src/components/DitoLabel.vue +200 -0
- package/src/components/DitoMenu.vue +186 -0
- package/src/components/DitoNavigation.vue +40 -0
- package/src/components/DitoNotifications.vue +170 -0
- package/src/components/DitoPagination.vue +42 -0
- package/src/components/DitoPane.vue +334 -0
- package/src/components/DitoPanel.vue +256 -0
- package/src/components/DitoPanels.vue +61 -0
- package/src/components/DitoRoot.vue +524 -0
- package/src/components/DitoSchema.vue +846 -0
- package/src/components/DitoSchemaInlined.vue +97 -0
- package/src/components/DitoScopes.vue +76 -0
- package/src/components/DitoSidebar.vue +50 -0
- package/src/components/DitoSpinner.vue +95 -0
- package/src/components/DitoTableCell.vue +64 -0
- package/src/components/DitoTableHead.vue +121 -0
- package/src/components/DitoTabs.vue +103 -0
- package/src/components/DitoTrail.vue +124 -0
- package/src/components/DitoTreeItem.vue +420 -0
- package/src/components/DitoUploadFile.vue +199 -0
- package/src/components/DitoVNode.vue +14 -0
- package/src/components/DitoView.vue +143 -0
- package/src/components/index.js +42 -0
- package/src/directives/resize.js +83 -0
- package/src/index.js +1 -0
- package/src/mixins/ContextMixin.js +68 -0
- package/src/mixins/DataMixin.js +131 -0
- package/src/mixins/DitoMixin.js +591 -0
- package/src/mixins/DomMixin.js +29 -0
- package/src/mixins/EmitterMixin.js +158 -0
- package/src/mixins/ItemMixin.js +144 -0
- package/src/mixins/LoadingMixin.js +23 -0
- package/src/mixins/NumberMixin.js +118 -0
- package/src/mixins/OptionsMixin.js +304 -0
- package/src/mixins/PulldownMixin.js +63 -0
- package/src/mixins/ResourceMixin.js +398 -0
- package/src/mixins/RouteMixin.js +190 -0
- package/src/mixins/SchemaParentMixin.js +33 -0
- package/src/mixins/SortableMixin.js +49 -0
- package/src/mixins/SourceMixin.js +734 -0
- package/src/mixins/TextMixin.js +26 -0
- package/src/mixins/TypeMixin.js +280 -0
- package/src/mixins/ValidationMixin.js +119 -0
- package/src/mixins/ValidatorMixin.js +57 -0
- package/src/mixins/ValueMixin.js +31 -0
- package/src/styles/_base.scss +17 -0
- package/src/styles/_button.scss +191 -0
- package/src/styles/_imports.scss +3 -0
- package/src/styles/_info.scss +19 -0
- package/src/styles/_layout.scss +19 -0
- package/src/styles/_pulldown.scss +38 -0
- package/src/styles/_scroll.scss +13 -0
- package/src/styles/_settings.scss +88 -0
- package/src/styles/_table.scss +223 -0
- package/src/styles/_tippy.scss +45 -0
- package/src/styles/style.scss +9 -0
- package/src/types/DitoTypeButton.vue +143 -0
- package/src/types/DitoTypeCheckbox.vue +27 -0
- package/src/types/DitoTypeCheckboxes.vue +65 -0
- package/src/types/DitoTypeCode.vue +199 -0
- package/src/types/DitoTypeColor.vue +272 -0
- package/src/types/DitoTypeComponent.vue +31 -0
- package/src/types/DitoTypeComputed.vue +50 -0
- package/src/types/DitoTypeDate.vue +99 -0
- package/src/types/DitoTypeLabel.vue +23 -0
- package/src/types/DitoTypeList.vue +364 -0
- package/src/types/DitoTypeMarkup.vue +700 -0
- package/src/types/DitoTypeMultiselect.vue +522 -0
- package/src/types/DitoTypeNumber.vue +66 -0
- package/src/types/DitoTypeObject.vue +136 -0
- package/src/types/DitoTypePanel.vue +18 -0
- package/src/types/DitoTypeProgress.vue +40 -0
- package/src/types/DitoTypeRadio.vue +45 -0
- package/src/types/DitoTypeSection.vue +80 -0
- package/src/types/DitoTypeSelect.vue +133 -0
- package/src/types/DitoTypeSlider.vue +66 -0
- package/src/types/DitoTypeSpacer.vue +11 -0
- package/src/types/DitoTypeSwitch.vue +40 -0
- package/src/types/DitoTypeText.vue +101 -0
- package/src/types/DitoTypeTextarea.vue +48 -0
- package/src/types/DitoTypeTreeList.vue +193 -0
- package/src/types/DitoTypeUpload.vue +503 -0
- package/src/types/index.js +30 -0
- package/src/utils/SchemaGraph.js +147 -0
- package/src/utils/accessor.js +75 -0
- package/src/utils/agent.js +47 -0
- package/src/utils/data.js +92 -0
- package/src/utils/filter.js +266 -0
- package/src/utils/math.js +14 -0
- package/src/utils/options.js +48 -0
- package/src/utils/path.js +5 -0
- package/src/utils/resource.js +44 -0
- package/src/utils/route.js +53 -0
- package/src/utils/schema.js +1121 -0
- package/src/utils/type.js +81 -0
- package/src/utils/uid.js +15 -0
- package/src/utils/units.js +5 -0
- package/src/validators/_creditcard.js +6 -0
- package/src/validators/_decimals.js +11 -0
- package/src/validators/_domain.js +6 -0
- package/src/validators/_email.js +6 -0
- package/src/validators/_hostname.js +6 -0
- package/src/validators/_integer.js +6 -0
- package/src/validators/_max.js +6 -0
- package/src/validators/_min.js +6 -0
- package/src/validators/_password.js +5 -0
- package/src/validators/_range.js +6 -0
- package/src/validators/_required.js +9 -0
- package/src/validators/_url.js +6 -0
- package/src/validators/index.js +12 -0
- package/src/verbs.js +17 -0
- package/types/index.d.ts +3298 -0
- package/types/tests/admin.test-d.ts +27 -0
- package/types/tests/component-buttons.test-d.ts +44 -0
- package/types/tests/component-list.test-d.ts +159 -0
- package/types/tests/component-misc.test-d.ts +137 -0
- package/types/tests/component-object.test-d.ts +69 -0
- package/types/tests/component-section.test-d.ts +174 -0
- package/types/tests/component-select.test-d.ts +107 -0
- package/types/tests/components.test-d.ts +81 -0
- package/types/tests/context.test-d.ts +31 -0
- package/types/tests/fixtures.ts +24 -0
- package/types/tests/form.test-d.ts +109 -0
- package/types/tests/instance.test-d.ts +20 -0
- package/types/tests/schema-features.test-d.ts +402 -0
- package/types/tests/variance.test-d.ts +125 -0
- package/types/tests/view.test-d.ts +146 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
<template lang="pug">
|
|
2
|
+
.dito-root(
|
|
3
|
+
:data-agent-browser="appState.agent.browser"
|
|
4
|
+
:data-agent-platform="appState.agent.platform"
|
|
5
|
+
:data-agent-version="appState.agent.versionNumber"
|
|
6
|
+
)
|
|
7
|
+
Transition(name="dito-drag")
|
|
8
|
+
.dito-drag-overlay(
|
|
9
|
+
v-if="isDraggingFiles"
|
|
10
|
+
)
|
|
11
|
+
TransitionGroup(name="dito-dialog")
|
|
12
|
+
DitoDialog(
|
|
13
|
+
v-for="(dialog, key) in dialogs"
|
|
14
|
+
:key="key"
|
|
15
|
+
:components="dialog.components"
|
|
16
|
+
:buttons="dialog.buttons"
|
|
17
|
+
:promise="dialog.promise"
|
|
18
|
+
:data="dialog.data"
|
|
19
|
+
:settings="dialog.settings"
|
|
20
|
+
@remove="removeDialog(key)"
|
|
21
|
+
)
|
|
22
|
+
DitoNavigation
|
|
23
|
+
main.dito-page.dito-scroll-parent(
|
|
24
|
+
v-resize="onResizePage"
|
|
25
|
+
:class="pageClasses"
|
|
26
|
+
)
|
|
27
|
+
DitoHeader(
|
|
28
|
+
:spinner="options.spinner"
|
|
29
|
+
:isLoading="isLoading"
|
|
30
|
+
)
|
|
31
|
+
RouterView
|
|
32
|
+
DitoSidebar
|
|
33
|
+
DitoAccount(
|
|
34
|
+
v-if="user"
|
|
35
|
+
)
|
|
36
|
+
a.dito-login(
|
|
37
|
+
v-else-if="allowLogin"
|
|
38
|
+
@click="rootComponent.login()"
|
|
39
|
+
)
|
|
40
|
+
span Login
|
|
41
|
+
DitoNotifications(ref="notifications")
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script>
|
|
45
|
+
import { delegate as tippyDelegate } from 'tippy.js'
|
|
46
|
+
import { mapConcurrently } from '@ditojs/utils'
|
|
47
|
+
import DitoComponent from '../DitoComponent.js'
|
|
48
|
+
import DomMixin from '../mixins/DomMixin.js'
|
|
49
|
+
import DitoUser from '../DitoUser.js'
|
|
50
|
+
import DitoView from '../components/DitoView.vue'
|
|
51
|
+
import DitoDialog from './DitoDialog.vue'
|
|
52
|
+
import {
|
|
53
|
+
processView,
|
|
54
|
+
resolveViews,
|
|
55
|
+
processSchemaComponents
|
|
56
|
+
} from '../utils/schema.js'
|
|
57
|
+
|
|
58
|
+
// @vue/component
|
|
59
|
+
export default DitoComponent.component('DitoRoot', {
|
|
60
|
+
mixins: [DomMixin],
|
|
61
|
+
components: { DitoDialog },
|
|
62
|
+
|
|
63
|
+
provide() {
|
|
64
|
+
return {
|
|
65
|
+
$views: () => this.resolvedViews
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
props: {
|
|
70
|
+
unresolvedViews: { type: [Object, Function, Promise], required: true },
|
|
71
|
+
options: { type: Object, default: () => ({}) }
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
data() {
|
|
75
|
+
return {
|
|
76
|
+
resolvedViews: {},
|
|
77
|
+
removeRoutes: null,
|
|
78
|
+
dialogs: {},
|
|
79
|
+
pageWidth: 0,
|
|
80
|
+
loadingCount: 0,
|
|
81
|
+
allowLogin: false,
|
|
82
|
+
isDraggingFiles: false
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
computed: {
|
|
87
|
+
notifications() {
|
|
88
|
+
return this.isMounted && this.$refs.notifications
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
isLoading() {
|
|
92
|
+
return this.loadingCount > 0
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
pageClasses() {
|
|
96
|
+
const prefix = 'dito-page'
|
|
97
|
+
// NOTE: Keep synced with $content-width in SCSS:
|
|
98
|
+
const contentWidth = 900
|
|
99
|
+
return [
|
|
100
|
+
this.appState.pageClass,
|
|
101
|
+
{
|
|
102
|
+
[`${prefix}--width-80`]: this.pageWidth <= contentWidth * 0.8,
|
|
103
|
+
[`${prefix}--width-60`]: this.pageWidth <= contentWidth * 0.6
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
created() {
|
|
110
|
+
this.appState.title = document.title || 'Dito.js Admin'
|
|
111
|
+
// With hot-reloading, it looks like destroyed hooks aren't always called
|
|
112
|
+
// for route components so reset the array of registered components instead.
|
|
113
|
+
this.appState.routeComponents = []
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
async mounted() {
|
|
117
|
+
this.setupDragAndDrop()
|
|
118
|
+
|
|
119
|
+
tippyDelegate(this.$el, {
|
|
120
|
+
target: '.dito-info',
|
|
121
|
+
theme: 'info',
|
|
122
|
+
animation: 'shift-away-subtle',
|
|
123
|
+
interactive: true,
|
|
124
|
+
delay: 250,
|
|
125
|
+
zIndex: 1,
|
|
126
|
+
appendTo: node => node.closest('.dito-pane'),
|
|
127
|
+
onShow: instance => instance.setContent(instance.reference.dataset.info)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// Clear the label marked as active on all mouse and keyboard events, except
|
|
131
|
+
// the ones that DitoLabel itself intercepts.
|
|
132
|
+
this.domOn(document, {
|
|
133
|
+
click: event => {
|
|
134
|
+
if (!event.target.closest('.dito-label')) {
|
|
135
|
+
this.appState.activeLabel = null
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
keyup: event => {
|
|
140
|
+
if (event.code === 'Tab') {
|
|
141
|
+
this.appState.activeLabel = null
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
this.allowLogin = false
|
|
148
|
+
if (await this.fetchUser()) {
|
|
149
|
+
await this.resolveViews()
|
|
150
|
+
} else {
|
|
151
|
+
await this.login()
|
|
152
|
+
}
|
|
153
|
+
} catch (err) {
|
|
154
|
+
console.error(err)
|
|
155
|
+
}
|
|
156
|
+
this.allowLogin = true
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
methods: {
|
|
160
|
+
setupDragAndDrop() {
|
|
161
|
+
// This code only happens the visual effects around dragging and dropping
|
|
162
|
+
// files into a `DitoTypeUpload` component. The actual uploading is
|
|
163
|
+
// handled by the `DitoTypeUpload` component itself.
|
|
164
|
+
|
|
165
|
+
let dragCount = 0
|
|
166
|
+
let uploads = []
|
|
167
|
+
|
|
168
|
+
const toggleDropTargetClass = enabled => {
|
|
169
|
+
for (const upload of uploads) {
|
|
170
|
+
upload
|
|
171
|
+
.closest('.dito-container')
|
|
172
|
+
.classList.toggle('dito-drop-target', enabled)
|
|
173
|
+
}
|
|
174
|
+
if (!enabled) {
|
|
175
|
+
uploads = []
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const setDraggingFiles = enabled => {
|
|
180
|
+
this.isDraggingFiles = enabled
|
|
181
|
+
if (enabled) {
|
|
182
|
+
toggleDropTargetClass(true)
|
|
183
|
+
} else {
|
|
184
|
+
setTimeout(() => toggleDropTargetClass(false), 150)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.domOn(document, {
|
|
189
|
+
dragenter: event => {
|
|
190
|
+
if (!dragCount && event.dataTransfer) {
|
|
191
|
+
uploads = document.querySelectorAll('.dito-upload')
|
|
192
|
+
const hasUploads = uploads.length > 0
|
|
193
|
+
event.dataTransfer.effectAllowed = hasUploads ? 'copy' : 'none'
|
|
194
|
+
if (hasUploads) {
|
|
195
|
+
setDraggingFiles(true)
|
|
196
|
+
} else {
|
|
197
|
+
event.preventDefault()
|
|
198
|
+
event.stopPropagation()
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
dragCount++
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
dragleave: event => {
|
|
206
|
+
dragCount--
|
|
207
|
+
if (!dragCount && event.dataTransfer) {
|
|
208
|
+
setDraggingFiles(false)
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
dragover: event => {
|
|
213
|
+
if (event.dataTransfer) {
|
|
214
|
+
const canDrop = event.target.closest(
|
|
215
|
+
'.dito-container:has(.dito-upload)'
|
|
216
|
+
)
|
|
217
|
+
event.dataTransfer.dropEffect = canDrop ? 'copy' : 'none'
|
|
218
|
+
if (!canDrop) {
|
|
219
|
+
event.preventDefault()
|
|
220
|
+
event.stopPropagation()
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
drop: event => {
|
|
226
|
+
dragCount = 0
|
|
227
|
+
if (event.dataTransfer) {
|
|
228
|
+
setDraggingFiles(false)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
notify({ type = 'info', title, text, error, duration } = {}) {
|
|
235
|
+
this.notifications.notify({ type, title, text, error, duration })
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
closeNotifications() {
|
|
239
|
+
this.notifications.destroyAll()
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
registerLoading(isLoading) {
|
|
243
|
+
this.loadingCount += isLoading ? 1 : -1
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
showDialog({ components, buttons, data, settings }) {
|
|
247
|
+
// Shows a dito-dialog component and wraps it in a promise so that the
|
|
248
|
+
// buttons in the dialog can use `dialog.resolve()` and `dialog.reject()`
|
|
249
|
+
// to close the modal dialog and resolve / reject the promise at once.
|
|
250
|
+
return new Promise(
|
|
251
|
+
async (resolve, reject) => {
|
|
252
|
+
// Process components to resolve async schemas.
|
|
253
|
+
const routes = []
|
|
254
|
+
await processSchemaComponents(
|
|
255
|
+
this.api,
|
|
256
|
+
{ type: 'dialog', components },
|
|
257
|
+
routes,
|
|
258
|
+
0
|
|
259
|
+
)
|
|
260
|
+
if (routes.length > 0) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
'Dialogs do not support components that produce routes'
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
const key = `dialog-${++dialogId}`
|
|
266
|
+
this.dialogs[key] = {
|
|
267
|
+
components,
|
|
268
|
+
buttons,
|
|
269
|
+
data,
|
|
270
|
+
settings,
|
|
271
|
+
promise: { resolve, reject }
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
removeDialog(key) {
|
|
278
|
+
delete this.dialogs[key]
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
async login() {
|
|
282
|
+
this.allowLogin = true
|
|
283
|
+
const {
|
|
284
|
+
additionalComponents,
|
|
285
|
+
redirectAfterLogin
|
|
286
|
+
} = this.options.login || {}
|
|
287
|
+
const loginData = await this.showDialog({
|
|
288
|
+
components: {
|
|
289
|
+
username: {
|
|
290
|
+
type: 'text',
|
|
291
|
+
autofocus: true
|
|
292
|
+
},
|
|
293
|
+
password: {
|
|
294
|
+
type: 'password'
|
|
295
|
+
},
|
|
296
|
+
...additionalComponents
|
|
297
|
+
},
|
|
298
|
+
// NOTE: Login must come before cancel in DOM order so that password
|
|
299
|
+
// managers (e.g. 1Password) target the submit button instead of cancel.
|
|
300
|
+
// DitoDialog uses CSS order to visually place cancel first.
|
|
301
|
+
buttons: {
|
|
302
|
+
login: {
|
|
303
|
+
type: 'submit',
|
|
304
|
+
text: 'Login'
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
cancel: {
|
|
308
|
+
type: 'button',
|
|
309
|
+
text: 'Cancel'
|
|
310
|
+
// NOTE: The click event is added in DitoDialog.buttonSchemas()
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
if (loginData) {
|
|
315
|
+
try {
|
|
316
|
+
const response = await this.sendRequest({
|
|
317
|
+
resource: this.api.users.login,
|
|
318
|
+
data: loginData,
|
|
319
|
+
internal: true
|
|
320
|
+
})
|
|
321
|
+
if (redirectAfterLogin) {
|
|
322
|
+
location.replace(redirectAfterLogin)
|
|
323
|
+
} else {
|
|
324
|
+
this.setUser(response.data.user)
|
|
325
|
+
await this.resolveViews()
|
|
326
|
+
}
|
|
327
|
+
} catch (err) {
|
|
328
|
+
const error = err.response?.data?.error || err
|
|
329
|
+
this.notify({
|
|
330
|
+
type: 'error',
|
|
331
|
+
error,
|
|
332
|
+
title: 'Authentication Error',
|
|
333
|
+
text: error
|
|
334
|
+
})
|
|
335
|
+
this.login()
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
navigateHome() {
|
|
341
|
+
return this.navigate('/')
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
async logout() {
|
|
345
|
+
try {
|
|
346
|
+
const response = await this.sendRequest({
|
|
347
|
+
resource: this.api.users.logout,
|
|
348
|
+
internal: true
|
|
349
|
+
})
|
|
350
|
+
if (response.data.success) {
|
|
351
|
+
this.setUser(null)
|
|
352
|
+
this.navigateHome()
|
|
353
|
+
}
|
|
354
|
+
} catch (err) {
|
|
355
|
+
console.error(err)
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
async fetchUser() {
|
|
360
|
+
let user = null
|
|
361
|
+
try {
|
|
362
|
+
const response = await this.sendRequest({
|
|
363
|
+
resource: this.api.users.session,
|
|
364
|
+
internal: true
|
|
365
|
+
})
|
|
366
|
+
user = response.data.user || null
|
|
367
|
+
} catch (err) {
|
|
368
|
+
const error = err.response?.data?.error || err
|
|
369
|
+
this.notify({
|
|
370
|
+
type: 'error',
|
|
371
|
+
error,
|
|
372
|
+
title: 'Authentication Error',
|
|
373
|
+
text: error
|
|
374
|
+
})
|
|
375
|
+
}
|
|
376
|
+
this.setUser(user)
|
|
377
|
+
return user
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
setUser(user) {
|
|
381
|
+
this.appState.user = (
|
|
382
|
+
user &&
|
|
383
|
+
Object.setPrototypeOf(user, DitoUser.prototype)
|
|
384
|
+
)
|
|
385
|
+
// Clear resolved views when user is logged out.
|
|
386
|
+
if (!user) {
|
|
387
|
+
this.resolvedViews = {}
|
|
388
|
+
this.navigateHome()
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
async ensureUser() {
|
|
393
|
+
if (!(await this.fetchUser())) {
|
|
394
|
+
await this.login()
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
async resolveViews() {
|
|
399
|
+
try {
|
|
400
|
+
this.resolvedViews = await resolveViews(this.unresolvedViews)
|
|
401
|
+
} catch (error) {
|
|
402
|
+
if (!error.request) {
|
|
403
|
+
console.error(error)
|
|
404
|
+
}
|
|
405
|
+
return this.login()
|
|
406
|
+
}
|
|
407
|
+
// Collect all routes from the root schema components
|
|
408
|
+
const routes = await mapConcurrently(
|
|
409
|
+
Object.entries(this.resolvedViews),
|
|
410
|
+
([name, schema]) => processView(DitoView, this.api, schema, name)
|
|
411
|
+
)
|
|
412
|
+
// Now that the routes are loaded, replace all existing routes with the
|
|
413
|
+
// new routes, and restore the current path.
|
|
414
|
+
const { fullPath } = this.$route
|
|
415
|
+
this.removeRoutes?.()
|
|
416
|
+
this.removeRoutes = addRoutes(this.$router, [
|
|
417
|
+
{
|
|
418
|
+
name: 'root',
|
|
419
|
+
path: '/',
|
|
420
|
+
components: {}
|
|
421
|
+
},
|
|
422
|
+
...routes.flat()
|
|
423
|
+
])
|
|
424
|
+
this.$router.replace(fullPath)
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
onResizePage({ contentRect: { width } }) {
|
|
428
|
+
this.pageWidth = width
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
let dialogId = 0
|
|
434
|
+
|
|
435
|
+
function addRoutes(router, routes) {
|
|
436
|
+
const removers = []
|
|
437
|
+
for (const route of routes) {
|
|
438
|
+
removers.push(
|
|
439
|
+
router.addRoute(route)
|
|
440
|
+
)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return () => {
|
|
444
|
+
for (const remove of removers) {
|
|
445
|
+
remove()
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
</script>
|
|
450
|
+
|
|
451
|
+
<style lang="scss">
|
|
452
|
+
@import '../styles/style';
|
|
453
|
+
|
|
454
|
+
.dito-app,
|
|
455
|
+
.dito-root {
|
|
456
|
+
width: 100%;
|
|
457
|
+
height: 100%;
|
|
458
|
+
display: flex;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.dito-page {
|
|
462
|
+
--max-content-width: #{$content-width};
|
|
463
|
+
--max-page-width: calc(var(--max-content-width) + 2 * #{$content-padding});
|
|
464
|
+
|
|
465
|
+
flex: 0 1 var(--max-page-width);
|
|
466
|
+
background: $content-color-background;
|
|
467
|
+
min-width: 0%;
|
|
468
|
+
max-width: var(--max-page-width);
|
|
469
|
+
overflow: visible; // For .dito-header full-width background.
|
|
470
|
+
|
|
471
|
+
&--wide {
|
|
472
|
+
--max-content-width: #{$content-width-wide};
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.dito-account,
|
|
477
|
+
.dito-login {
|
|
478
|
+
cursor: pointer;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.dito-drag-overlay {
|
|
482
|
+
position: fixed;
|
|
483
|
+
top: 0;
|
|
484
|
+
left: 0;
|
|
485
|
+
width: 100%;
|
|
486
|
+
height: 100%;
|
|
487
|
+
z-index: $z-index-drag-overlay;
|
|
488
|
+
background: rgb(0, 0, 0, 0.25);
|
|
489
|
+
pointer-events: none;
|
|
490
|
+
backdrop-filter: blur(8px);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.dito-drop-target {
|
|
494
|
+
--shadow-alpha: 0.25;
|
|
495
|
+
|
|
496
|
+
background: $content-color-background;
|
|
497
|
+
border-radius: $border-radius;
|
|
498
|
+
z-index: $z-index-drag-overlay + 1;
|
|
499
|
+
filter: drop-shadow(0 4px 8px rgb(0, 0, 0, var(--shadow-alpha)));
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.dito-drag-enter-active,
|
|
503
|
+
.dito-drag-leave-active {
|
|
504
|
+
$duration: 0.15s;
|
|
505
|
+
|
|
506
|
+
transition:
|
|
507
|
+
opacity $duration,
|
|
508
|
+
backdrop-filter $duration;
|
|
509
|
+
|
|
510
|
+
~ * .dito-drop-target {
|
|
511
|
+
transition: filter $duration;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.dito-drag-enter-from,
|
|
516
|
+
.dito-drag-leave-to {
|
|
517
|
+
opacity: 0;
|
|
518
|
+
backdrop-filter: blur(0);
|
|
519
|
+
|
|
520
|
+
~ * .dito-drop-target {
|
|
521
|
+
--shadow-alpha: 0;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
</style>
|