@tailor-cms/tce-preview-runtime 0.0.23 → 0.0.28
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 +1 -1
- package/src/App.vue +7 -1
- package/src/components/AppBar.vue +14 -9
- package/src/components/BottomPanel.vue +44 -0
- package/src/components/MainLayout.vue +40 -89
- package/src/components/PreviewPanel.vue +55 -0
- package/src/components/SplashLoader.vue +30 -31
- package/src/state/preview.ts +5 -5
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -17,13 +17,19 @@ onMounted(async () => {
|
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
function timeout(ms: number) {
|
|
21
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
async function getElement() {
|
|
21
25
|
try {
|
|
22
26
|
const response = await api('content-element').json();
|
|
23
27
|
return response || {};
|
|
24
28
|
} catch (error) {
|
|
25
29
|
console.log('Error on element get', error);
|
|
26
|
-
|
|
30
|
+
await timeout(2000);
|
|
31
|
+
console.log('Retrying element get...');
|
|
32
|
+
return getElement();
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
35
|
</script>
|
|
@@ -7,23 +7,28 @@ const { isDark, toggleDark, showEdit, showDisplay, showBothComponents } =
|
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
9
|
<template>
|
|
10
|
-
<v-app-bar :color="isDark ? '
|
|
10
|
+
<v-app-bar :color="isDark ? 'grey-darken-4' : 'grey-lighten-4'" elevation="1">
|
|
11
11
|
<template #prepend>
|
|
12
|
-
<v-app-bar-nav-icon class="ml-
|
|
13
|
-
<
|
|
14
|
-
<img :src="logoUrl" alt="Tailor logo" width="30" />
|
|
15
|
-
</v-avatar>
|
|
12
|
+
<v-app-bar-nav-icon class="ml-2">
|
|
13
|
+
<img :src="logoUrl" alt="Tailor logo" width="24" />
|
|
16
14
|
</v-app-bar-nav-icon>
|
|
17
15
|
</template>
|
|
18
|
-
<v-app-bar-title class="ml-
|
|
16
|
+
<v-app-bar-title class="ml-1 font-weight-bold">
|
|
17
|
+
Content Element Kit
|
|
18
|
+
<span class="pl-1 text-body-2 font-weight-bold">0.0.1</span>
|
|
19
|
+
</v-app-bar-title>
|
|
19
20
|
<v-spacer />
|
|
20
|
-
<v-btn-toggle
|
|
21
|
+
<v-btn-toggle
|
|
22
|
+
:variant="isDark ? 'flat' : 'tonal'"
|
|
23
|
+
class="mr-4"
|
|
24
|
+
density="compact"
|
|
25
|
+
>
|
|
21
26
|
<v-btn @click="showEdit">
|
|
22
27
|
<span>Edit</span>
|
|
23
28
|
<v-icon end>mdi-pencil</v-icon>
|
|
24
29
|
</v-btn>
|
|
25
30
|
<v-btn @click="showDisplay">
|
|
26
|
-
<span>
|
|
31
|
+
<span>Display</span>
|
|
27
32
|
<v-icon end>mdi-fullscreen</v-icon>
|
|
28
33
|
</v-btn>
|
|
29
34
|
<v-btn @click="showBothComponents">
|
|
@@ -31,7 +36,7 @@ const { isDark, toggleDark, showEdit, showDisplay, showBothComponents } =
|
|
|
31
36
|
<v-icon end>mdi-math-norm-box</v-icon>
|
|
32
37
|
</v-btn>
|
|
33
38
|
</v-btn-toggle>
|
|
34
|
-
<v-btn href="https://github.com/tailor-cms/
|
|
39
|
+
<v-btn href="https://github.com/tailor-cms/tce-template" icon>
|
|
35
40
|
<v-icon>mdi-github</v-icon>
|
|
36
41
|
</v-btn>
|
|
37
42
|
<v-btn icon @click.prevent="toggleDark()">
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-show="isLoaded">
|
|
3
|
+
<v-tabs density="compact" hide-slider>
|
|
4
|
+
<v-tab>Server state</v-tab>
|
|
5
|
+
</v-tabs>
|
|
6
|
+
<div class="pa-6">
|
|
7
|
+
<JsonEditor
|
|
8
|
+
v-if="isLoaded"
|
|
9
|
+
:main-menu-bar="false"
|
|
10
|
+
:status-bar="false"
|
|
11
|
+
:value="orderKeys(props.element)"
|
|
12
|
+
mode="text"
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import JsonEditor from 'vue3-ts-jsoneditor';
|
|
20
|
+
|
|
21
|
+
const props = defineProps<{ element: any; isLoaded: Boolean }>();
|
|
22
|
+
|
|
23
|
+
const orderKeys = (val: any) => {
|
|
24
|
+
return [
|
|
25
|
+
'id',
|
|
26
|
+
'uid',
|
|
27
|
+
'type',
|
|
28
|
+
'position',
|
|
29
|
+
'data',
|
|
30
|
+
'meta',
|
|
31
|
+
'refs',
|
|
32
|
+
'contentId',
|
|
33
|
+
'contentSignature',
|
|
34
|
+
'linked',
|
|
35
|
+
'detached',
|
|
36
|
+
'createdAt',
|
|
37
|
+
'updatedAt',
|
|
38
|
+
'deletedAt',
|
|
39
|
+
].reduce((acc: any, key: string) => {
|
|
40
|
+
acc[key] = val[key];
|
|
41
|
+
return acc;
|
|
42
|
+
}, {});
|
|
43
|
+
};
|
|
44
|
+
</script>
|
|
@@ -1,100 +1,55 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { onMounted, ref } from 'vue';
|
|
3
|
-
import JsonEditor from 'vue3-ts-jsoneditor';
|
|
4
3
|
import Split from 'split.js';
|
|
5
4
|
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// const SERVER_PROP_ORDER = [
|
|
11
|
-
// 'id',
|
|
12
|
-
// 'uid',
|
|
13
|
-
// 'type',
|
|
14
|
-
// 'position',
|
|
15
|
-
// 'data',
|
|
16
|
-
// 'meta',
|
|
17
|
-
// 'refs',
|
|
18
|
-
// 'contentId',
|
|
19
|
-
// 'contentSignature',
|
|
20
|
-
// 'linked',
|
|
21
|
-
// 'detached',
|
|
22
|
-
// 'createdAt',
|
|
23
|
-
// 'updatedAt',
|
|
24
|
-
// 'deletedAt',
|
|
25
|
-
// ];
|
|
5
|
+
import BottomPanel from './BottomPanel.vue';
|
|
6
|
+
import PreviewPanel from './PreviewPanel.vue';
|
|
7
|
+
import SplashLoader from './SplashLoader.vue';
|
|
8
|
+
import { useGlobalState } from '../state';
|
|
26
9
|
|
|
27
10
|
const props = defineProps<{ element: any }>();
|
|
28
11
|
|
|
12
|
+
const { isDark } = useGlobalState();
|
|
29
13
|
const isLoaded = ref(false);
|
|
30
|
-
const editPanel = ref(null);
|
|
31
|
-
const displayPanel = ref(null);
|
|
32
|
-
|
|
33
|
-
const { isDark, splitJs } = useGlobalState();
|
|
34
14
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
15
|
+
const runtimeId = import.meta.env.VITE_RUNTIME_ID;
|
|
16
|
+
const bootRegistry = JSON.parse(
|
|
17
|
+
localStorage.getItem('element-kit-boot') || '{}',
|
|
18
|
+
);
|
|
19
|
+
const isFirstBoot = !bootRegistry[runtimeId];
|
|
41
20
|
|
|
42
21
|
onMounted(() => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
22
|
+
// Client/server vertical split
|
|
23
|
+
Split([`#panelTop`, `#panelBottom`], {
|
|
24
|
+
direction: 'vertical',
|
|
25
|
+
sizes: [70, 30],
|
|
26
|
+
});
|
|
27
|
+
setTimeout(
|
|
28
|
+
() => {
|
|
29
|
+
isLoaded.value = true;
|
|
30
|
+
localStorage.setItem(
|
|
31
|
+
'element-kit-boot',
|
|
32
|
+
JSON.stringify({
|
|
33
|
+
...bootRegistry,
|
|
34
|
+
[runtimeId]: true,
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
isFirstBoot ? 17000 : 5000,
|
|
39
|
+
);
|
|
56
40
|
});
|
|
57
41
|
</script>
|
|
58
42
|
|
|
59
43
|
<template>
|
|
60
44
|
<main :class="{ 'dark-theme': isDark }">
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
src="http://localhost:8010"
|
|
70
|
-
title="Edit component container"
|
|
71
|
-
>
|
|
72
|
-
</iframe>
|
|
73
|
-
<iframe
|
|
74
|
-
:id="PANELS.DISPLAY"
|
|
75
|
-
ref="displayPanel"
|
|
76
|
-
frameBorder="0"
|
|
77
|
-
sandbox="allow-scripts"
|
|
78
|
-
src="http://localhost:8020"
|
|
79
|
-
title="Display component container"
|
|
80
|
-
>
|
|
81
|
-
</iframe>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
<div v-show="isLoaded" id="panelBottom" class="panel">
|
|
85
|
-
<v-tabs density="compact" hide-slider>
|
|
86
|
-
<v-tab>Server state</v-tab>
|
|
87
|
-
</v-tabs>
|
|
88
|
-
<div class="pa-5">
|
|
89
|
-
<JsonEditor
|
|
90
|
-
v-if="isLoaded"
|
|
91
|
-
:main-menu-bar="false"
|
|
92
|
-
:status-bar="false"
|
|
93
|
-
:value="props.element"
|
|
94
|
-
mode="text"
|
|
95
|
-
/>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
45
|
+
<SplashLoader v-show="!isLoaded" :is-first-boot="isFirstBoot" />
|
|
46
|
+
<PreviewPanel id="panelTop" :is-loaded="isLoaded" />
|
|
47
|
+
<BottomPanel
|
|
48
|
+
id="panelBottom"
|
|
49
|
+
:element="props.element"
|
|
50
|
+
:is-loaded="isLoaded"
|
|
51
|
+
class="preview-panel"
|
|
52
|
+
/>
|
|
98
53
|
</main>
|
|
99
54
|
</template>
|
|
100
55
|
|
|
@@ -102,8 +57,9 @@ onMounted(() => {
|
|
|
102
57
|
main {
|
|
103
58
|
height: calc(100vh - var(--nav-height));
|
|
104
59
|
|
|
105
|
-
.panel {
|
|
60
|
+
.preview-panel {
|
|
106
61
|
background-color: #eceff1;
|
|
62
|
+
overflow-y: auto;
|
|
107
63
|
}
|
|
108
64
|
}
|
|
109
65
|
|
|
@@ -118,9 +74,9 @@ main {
|
|
|
118
74
|
}
|
|
119
75
|
|
|
120
76
|
.gutter.gutter-vertical {
|
|
121
|
-
cursor: row-resize
|
|
77
|
+
cursor: row-resize;
|
|
122
78
|
background-color: #fff;
|
|
123
|
-
background-image: url('data:image/png;base64,
|
|
79
|
+
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=');
|
|
124
80
|
}
|
|
125
81
|
|
|
126
82
|
.dark-theme {
|
|
@@ -128,10 +84,5 @@ main {
|
|
|
128
84
|
.gutter.gutter-vertical {
|
|
129
85
|
background-color: #333;
|
|
130
86
|
}
|
|
131
|
-
|
|
132
|
-
.jse-theme-dark .cm-content,
|
|
133
|
-
.jse-theme-dark .cm-gutters {
|
|
134
|
-
background-color: #29292f !important;
|
|
135
|
-
}
|
|
136
87
|
}
|
|
137
88
|
</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-show="isLoaded" class="d-flex flex-grow-1 preview-panel">
|
|
3
|
+
<iframe
|
|
4
|
+
:id="PANELS.EDIT"
|
|
5
|
+
ref="editPanel"
|
|
6
|
+
frameBorder="0"
|
|
7
|
+
sandbox="allow-scripts"
|
|
8
|
+
src="http://localhost:8010"
|
|
9
|
+
title="Edit component container"
|
|
10
|
+
>
|
|
11
|
+
</iframe>
|
|
12
|
+
<iframe
|
|
13
|
+
:id="PANELS.DISPLAY"
|
|
14
|
+
ref="displayPanel"
|
|
15
|
+
frameBorder="0"
|
|
16
|
+
sandbox="allow-scripts"
|
|
17
|
+
src="http://localhost:8020"
|
|
18
|
+
title="Display component container"
|
|
19
|
+
>
|
|
20
|
+
</iframe>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { onMounted, ref, watch } from 'vue';
|
|
26
|
+
import Split from 'split.js';
|
|
27
|
+
|
|
28
|
+
import { PANELS, useGlobalState } from '../state';
|
|
29
|
+
|
|
30
|
+
const props = defineProps<{ isLoaded: Boolean }>();
|
|
31
|
+
|
|
32
|
+
const editPanel = ref(null);
|
|
33
|
+
const displayPanel = ref(null);
|
|
34
|
+
const { previewPanelSplit } = useGlobalState();
|
|
35
|
+
|
|
36
|
+
onMounted(() => {
|
|
37
|
+
previewPanelSplit.value = Split([`#${PANELS.EDIT}`, `#${PANELS.DISPLAY}`], {
|
|
38
|
+
minSize: [0, 0],
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
watch(props.isLoaded, (isLoaded) => {
|
|
43
|
+
if (!isLoaded) return;
|
|
44
|
+
reloadPreview();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const reloadPreview = () => {
|
|
48
|
+
/* eslint-disable */
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
editPanel.value.src = editPanel.value.src;
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
displayPanel.value.src = displayPanel.value.src;
|
|
53
|
+
/* eslint-enable */
|
|
54
|
+
};
|
|
55
|
+
</script>
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<transition name="fade">
|
|
3
|
-
<div
|
|
4
|
-
:class="['vue-splash', { 'vue-splash--fixed': fixed }]"
|
|
5
|
-
:style="containerStyle"
|
|
6
|
-
>
|
|
3
|
+
<div :style="{ backgroundColor }" class="splash-loader">
|
|
7
4
|
<div>
|
|
8
|
-
<div class="
|
|
9
|
-
<img :src="logo"
|
|
5
|
+
<div class="splash-loader__anim">
|
|
6
|
+
<img :src="logo" alt="Logo" width="88" />
|
|
10
7
|
</div>
|
|
11
|
-
<div class="
|
|
8
|
+
<div class="splash-loader__text text-body-1">
|
|
12
9
|
<div>Booting Teaching Element Kit....</div>
|
|
10
|
+
<v-progress-linear
|
|
11
|
+
v-if="isFirstBoot"
|
|
12
|
+
:model-value="progress"
|
|
13
|
+
class="my-3"
|
|
14
|
+
/>
|
|
13
15
|
<div class="font-weight-bold">Alpha preview v0.0.1</div>
|
|
14
16
|
</div>
|
|
15
17
|
</div>
|
|
@@ -22,6 +24,10 @@ import logoUrl from '../assets/logo.png';
|
|
|
22
24
|
|
|
23
25
|
export default {
|
|
24
26
|
props: {
|
|
27
|
+
isFirstBoot: {
|
|
28
|
+
type: Boolean,
|
|
29
|
+
required: true,
|
|
30
|
+
},
|
|
25
31
|
logo: {
|
|
26
32
|
type: String,
|
|
27
33
|
default: logoUrl,
|
|
@@ -32,19 +38,23 @@ export default {
|
|
|
32
38
|
},
|
|
33
39
|
backgroundColor: {
|
|
34
40
|
type: String,
|
|
35
|
-
default: '',
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
computed: {
|
|
39
|
-
imageStyle: () => ({ width: '88px' }),
|
|
40
|
-
containerStyle() {
|
|
41
|
-
return {
|
|
42
|
-
backgroundColor: this.backgroundColor || '#fff',
|
|
43
|
-
};
|
|
41
|
+
default: '#fff',
|
|
44
42
|
},
|
|
45
43
|
},
|
|
46
|
-
|
|
44
|
+
data: () => ({
|
|
45
|
+
interval: 0,
|
|
46
|
+
progress: 0,
|
|
47
|
+
}),
|
|
48
|
+
mounted() {
|
|
47
49
|
this.setColor();
|
|
50
|
+
if (!this.isFirstBoot) return;
|
|
51
|
+
this.interval = setInterval(() => {
|
|
52
|
+
this.progress += 1;
|
|
53
|
+
if (this.progress === 100) clearInterval(this.interval);
|
|
54
|
+
}, 160);
|
|
55
|
+
},
|
|
56
|
+
beforeUnmount() {
|
|
57
|
+
clearInterval(this.interval);
|
|
48
58
|
},
|
|
49
59
|
methods: {
|
|
50
60
|
setColor() {
|
|
@@ -57,27 +67,16 @@ export default {
|
|
|
57
67
|
<style lang="scss" scoped>
|
|
58
68
|
$splash-color: var(--splash-color);
|
|
59
69
|
|
|
60
|
-
.
|
|
70
|
+
.splash-loader {
|
|
61
71
|
flex: 1;
|
|
62
72
|
width: 100%;
|
|
63
73
|
height: 100%;
|
|
64
74
|
display: flex;
|
|
65
75
|
align-items: center;
|
|
66
76
|
justify-content: center;
|
|
67
|
-
|
|
68
|
-
&--fixed {
|
|
69
|
-
top: 0;
|
|
70
|
-
left: 0;
|
|
71
|
-
right: 0;
|
|
72
|
-
bottom: 0;
|
|
73
|
-
width: 100%;
|
|
74
|
-
height: 100%;
|
|
75
|
-
position: fixed;
|
|
76
|
-
background-color: white;
|
|
77
|
-
}
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
.
|
|
79
|
+
.splash-loader__anim {
|
|
81
80
|
text-align: center;
|
|
82
81
|
animation: splashAnimation 1.3s infinite;
|
|
83
82
|
|
|
@@ -86,7 +85,7 @@ $splash-color: var(--splash-color);
|
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
.
|
|
88
|
+
.splash-loader__text {
|
|
90
89
|
margin-top: 50px;
|
|
91
90
|
font-size: 1rem;
|
|
92
91
|
text-align: center;
|
package/src/state/preview.ts
CHANGED
|
@@ -14,25 +14,25 @@ export const useGlobalState = createGlobalState(() => {
|
|
|
14
14
|
|
|
15
15
|
// Control panels
|
|
16
16
|
const visiblePanels = ref(['EDIT', 'DISPLAY', 'STATE']);
|
|
17
|
-
const
|
|
17
|
+
const previewPanelSplit = ref();
|
|
18
18
|
const showEdit = () => {
|
|
19
19
|
visiblePanels.value = [PANELS.EDIT];
|
|
20
|
-
if (
|
|
20
|
+
if (previewPanelSplit.value) previewPanelSplit.value?.setSizes([100, 0]);
|
|
21
21
|
};
|
|
22
22
|
const showDisplay = () => {
|
|
23
23
|
visiblePanels.value = [PANELS.DISPLAY];
|
|
24
|
-
if (
|
|
24
|
+
if (previewPanelSplit.value) previewPanelSplit.value?.setSizes([0, 100]);
|
|
25
25
|
};
|
|
26
26
|
const showBothComponents = () => {
|
|
27
27
|
visiblePanels.value = [PANELS.EDIT, PANELS.DISPLAY];
|
|
28
|
-
if (
|
|
28
|
+
if (previewPanelSplit.value) previewPanelSplit.value?.setSizes([50, 50]);
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
return {
|
|
32
32
|
isDark,
|
|
33
33
|
toggleDark,
|
|
34
34
|
visiblePanels,
|
|
35
|
-
|
|
35
|
+
previewPanelSplit,
|
|
36
36
|
showEdit,
|
|
37
37
|
showDisplay,
|
|
38
38
|
showBothComponents,
|