Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@json.ms/www",
"private": true,
"type": "module",
"version": "1.3.3",
"version": "1.3.4",
"scripts": {
"dev": "vite --host",
"build": "run-p type-check \"build-only {@}\" --",
Expand Down
25 changes: 14 additions & 11 deletions src/components/FieldItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -158,18 +158,21 @@ const onCollapsableHeader = (item: any, index: number) => {
let thumbnail: string | false = false;
for (const key in fields.value) {
const fieldItem = fields.value[key];
if (!title && ['string', 'i18n:string', 'i18n', 'date', 'i18n:date'].includes(fieldItem.type)) {
if (isFieldI18n(fieldItem)) {
title = item[key][locale];
} else {
title = item[key];
const path = fieldKey + `[${index}]`;
if (isFieldVisible(fieldItem, path)) {
if (!title && ['string', 'i18n:string', 'i18n', 'date', 'i18n:date'].includes(fieldItem.type)) {
if (isFieldI18n(fieldItem)) {
title = item[key][locale];
} else {
title = item[key];
}
}
if (fieldItem.type === 'file' && typeof item[key] === 'object' && item[key] !== null && item[key].path && item[key].meta && item[key].meta.type.startsWith('image/')) {
thumbnail = serverSettings.publicUrl + item[key].path;
}
if (thumbnail && title) {
break;
}
}
if (fieldItem.type === 'file' && typeof item[key] === 'object' && item[key] !== null && item[key].path && item[key].meta && item[key].meta.type.startsWith('image/')) {
thumbnail = serverSettings.publicUrl + item[key].path;
}
if (thumbnail && title) {
break;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/components/FileFieldItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ const onRemoveFile = (file: IFile) => {
<VideoPlayer
v-else-if="isVideo(file)"
:src="src"
:type="file.meta.type"
:aspect-ratio="(file.meta.width || 1) / (file.meta.height || 1)"
:style="{ float: 'left', width: thumbnailSize(file).width + 'px', height: thumbnailSize(file).height + 'px' }"
controls
Expand Down
20 changes: 13 additions & 7 deletions src/components/FileManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,13 @@ const upload = async (fileList: FileList, type: 'remote' | 'local') => {
}))
}
}
globalStore.showBottomSheet('Uploading file(s)...', null, 'mdi-upload', true);
return Promise.all(promises)
.catch(globalStore.catchError)
.finally(() => uploading.value = false);
.finally(() => {
globalStore.hideBottomSheet();
uploading.value = false
});
}

const remove = () => {
Expand Down Expand Up @@ -464,7 +468,9 @@ watch(() => globalStore.fileManager.visible, () => {
<VideoPlayer
v-else-if="item.meta.type?.startsWith('video')"
:src="blobFileList[item.path || 'unknown'] || (serverSettings.publicUrl + item.path)"
:type="item.meta.type"
:aspect-ratio="(item.meta.width || 1) / (item.meta.height || 1)"
controls
/>
<v-sheet v-else>
<v-responsive :aspect-ratio="16 / 9" class="d-flex align-center justify-center text-center">
Expand Down Expand Up @@ -568,12 +574,12 @@ watch(() => globalStore.fileManager.visible, () => {
</v-list-item>
</v-list>
</v-menu>
<v-checkbox
v-if="!smAndDown"
v-model="showInfo"
label="Show additional info"
hide-details
/>
<!-- <v-checkbox-->
<!-- v-if="!smAndDown"-->
<!-- v-model="showInfo"-->
<!-- label="Show additional info"-->
<!-- hide-details-->
<!-- />-->
<v-spacer />
<v-btn
v-if="canSelect"
Expand Down
2 changes: 1 addition & 1 deletion src/components/IntroductionDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const close = () => {
src="https://www.youtube.com/embed/QbzHaJ3GeJM?si=HsQLg2_cXyt97vv1"
title="JSON.ms Youtube Presentation"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen
/>
Expand Down
36 changes: 20 additions & 16 deletions src/components/JSONms.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,23 @@ const showActionBar = computed((): boolean => {
return ['data'].includes((tab.value || '').toString());
});

const bottomSheetData = ref<{
text: string,
icon?: string,
color?: string,
loading?: boolean,
}>({
text: '',
});
const showBottomSheet = computed((): boolean => {
return downloading.value || userDataLoading.value || structureStates.value.saved;
return globalStore.bottomSheet.visible || structureStates.value.saved;
});
watch(() => downloading.value, () => {
if (downloading.value) {
globalStore.showBottomSheet('Downloading files and generating ZIP file. Please wait...', null, null, true)
} else {
globalStore.hideBottomSheet();
}
});
watch(() => userDataLoading.value, () => {
if (userDataLoading.value) {
globalStore.showBottomSheet('Fetching user data. Please wait...', null, null, true)
} else {
globalStore.hideBottomSheet();
}
});
watch(() => downloading.value, () => bottomSheetData.value = { text: 'Downloading files and generating ZIP file. Please wait...', loading: true });
watch(() => userDataLoading.value, () => bottomSheetData.value = { text: 'Fetching user data. Please wait...', loading: true });

const showEditor = computed({
get(): boolean {
Expand Down Expand Up @@ -168,7 +172,7 @@ const onSaveStructure = () => {
if (canSaveStructure.value) {
modelStore.structure.content = modelStore.temporaryContent || modelStore.structure.content;
saveStructure().then(() => {
bottomSheetData.value = { text: 'Structure saved!', color: 'success', icon: 'mdi-check' };
globalStore.showBottomSheet('Structure saved!', 'success', 'mdi-check');
syncToFolder(modelStore.structure, ['structure', 'default', 'typings', 'settings', 'index']);
const newModel = modelStore.structure;
globalStore.addStructure(newModel);
Expand Down Expand Up @@ -395,7 +399,7 @@ if (globalStore.session.loggedIn) {
disable-resize-watcher
disable-route-watcher
>
<v-card class="w-100 fill-height d-flex flex-column" theme="dark" tile flat>
<v-card class="w-100 fill-height d-flex flex-column" theme="dark" :style="{ left: showEditor ? 0 : '-1px' }" tile flat>
<StructureEditor
ref="structureEditor"
v-model="structure"
Expand Down Expand Up @@ -540,11 +544,11 @@ if (globalStore.session.loggedIn) {
>
<v-card theme="dark" style="margin: 0 auto">
<template #prepend>
<v-progress-circular v-if="bottomSheetData.loading" color="primary" indeterminate class="mr-4" />
<v-icon v-else-if="bottomSheetData.icon" :color="bottomSheetData.color" :icon="bottomSheetData.icon" />
<v-progress-circular v-if="globalStore.bottomSheet.loading" color="primary" indeterminate class="mr-4" />
<v-icon v-else-if="globalStore.bottomSheet.icon" :color="globalStore.bottomSheet.color" :icon="globalStore.bottomSheet.icon" />
</template>
<template #item>
{{ bottomSheetData.text }}
{{ globalStore.bottomSheet.text }}
</template>
</v-card>
</v-bottom-sheet>
Expand Down
45 changes: 26 additions & 19 deletions src/components/SessionPanel.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { useGlobalStore } from '@/stores/global';
import { ref } from 'vue';
import {computed, ref} from 'vue';
import { Services } from '@/services';
import {useModelStore} from '@/stores/model';

Expand Down Expand Up @@ -42,31 +42,38 @@ const logout = () => {
})
})
}

const userInitials = computed((): string => {
return globalStore.session.user?.name.trim().split(' ')
.map(part => part.substring(0, 1))
.join('').substring(0, 2) || '?';
})
</script>

<template>
<v-menu>
<template #activator="{ props }">
<v-btn v-bind="props" :icon="dense" height="40">
<template v-if="globalStore.session.loggedIn">
<v-avatar size="32" color="primary">
<v-img
v-if="globalStore.session.user?.avatar"
:src="globalStore.session.user?.avatar"
alt="Avatar"
>
<template #placeholder>
<div class="d-flex align-center justify-center fill-height">
<v-progress-circular
color="surface"
indeterminate
size="16"
width="1"
/>
</div>
</template>
</v-img>
<strong v-else>{{ globalStore.session.user?.name.substring(0, 1) }}</strong>
<v-avatar size="32" color="secondary">
<!-- <v-img-->
<!-- v-if="globalStore.session.user?.avatar"-->
<!-- :src="globalStore.session.user?.avatar"-->
<!-- alt="Avatar"-->
<!-- >-->
<!-- <template #placeholder>-->
<!-- <div class="d-flex align-center justify-center fill-height">-->
<!-- <v-progress-circular-->
<!-- color="surface"-->
<!-- indeterminate-->
<!-- size="16"-->
<!-- width="1"-->
<!-- />-->
<!-- </div>-->
<!-- </template>-->
<!-- </v-img>-->
<!-- <strong v-else>{{ globalStore.session.user?.name.substring(0, 1) }}</strong>-->
<strong>{{ userInitials }}</strong>
</v-avatar>
<strong v-if="showUsername" class="ml-3">{{ globalStore.session.user?.name }}</strong>
</template>
Expand Down
1 change: 1 addition & 0 deletions src/components/SitePreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ defineExpose({
:user-data="userData"
class="fill-height"
columns
show-tabs
@save="onSaveStructureContent"
@create="onCreateStructure"
@change="onStructureContentChange"
Expand Down
61 changes: 53 additions & 8 deletions src/components/StructureEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import {useTypings} from "@/composables/typings";
import {useModelStore} from "@/stores/model";
import {useSyncing} from "@/composables/syncing";
import {LineCounter, parseDocument} from "yaml";
import {copyToClipboard} from "@/utils";
// import yaml from 'js-yaml';

const emit = defineEmits(['save', 'create', 'change', 'focus', 'blur']);
const structure = defineModel<IStructure>({ required: true });
const { columns = false, userData } = defineProps<{
const { columns = false, showTabs = false, userData } = defineProps<{
columns?: boolean,
showTabs?: boolean,
structureData: IStructureData,
userData: any
}>();
Expand All @@ -42,6 +44,8 @@ const blueprintEditorTypings: Ref<VAceEditorInstance | null> = ref(null);
const blueprintEditorDefault: Ref<VAceEditorInstance | null> = ref(null);
const blueprintTypings = ref('')
const blueprintDefault = ref('')
const disableTypings = ref(false)
const disableDefaultObjects = ref(false)
const blueprintLanguage = ref<'typescript'>('typescript')

const sectionMenu = ref(false);
Expand Down Expand Up @@ -79,8 +83,8 @@ const sections = ref([{
disabled: () => false,
}, {
key: 'blueprints',
icon: 'mdi-ruler-square',
title: "Blueprints",
icon: 'mdi-language-typescript',
title: "Typescript",
subtitle: "TypeScript types and default data",
disabledSubtitle: (): string => 'Must be logged in',
disabled: () => false
Expand Down Expand Up @@ -194,6 +198,22 @@ const onBlur = () => {
emit('blur');
}

const copy = (content: string, type: 'typings' | 'defaults') => {
switch (type) {
case 'typings': disableTypings.value = true; break;
case 'defaults': disableDefaultObjects.value = true; break;
}
copyToClipboard(content);
globalStore.showBottomSheet('Copied to clipboard!', null, 'mdi-clipboard-check-outline');
setTimeout(() => {
globalStore.hideBottomSheet();
switch (type) {
case 'typings': disableTypings.value = false; break;
case 'defaults': disableDefaultObjects.value = false; break;
}
}, 1000);
}

const printAnnotations = (content: string) => {
const instance = structureEditor.value?.getAceInstance();
if (instance) {
Expand Down Expand Up @@ -526,7 +546,16 @@ watch(() => globalStore.userSettings.data, () => {
/>
<div v-else class="d-flex flex-column">
<div v-if="globalStore.uiConfig.structure_menu" class="d-flex align-center pa-1" style="gap: 1rem">
<v-menu v-model="sectionMenu" contained :close-on-content-click="false">
<v-tabs v-if="showTabs" density="compact">
<v-tab
v-for="section in filteredSections"
:key="section.key"
:prepend-icon="section.disabled() ? 'mdi-alert' : section.icon"
:text="section.title"
@click="setSection(section)"
/>
</v-tabs>
<v-menu v-else v-model="sectionMenu" contained :close-on-content-click="false">
<template #activator="{ props }">
<v-btn v-bind="props">
<v-icon :icon="selectedSection?.icon" start />
Expand Down Expand Up @@ -700,11 +729,19 @@ watch(() => globalStore.userSettings.data, () => {
}]"
>
<div v-if="globalStore.userSettings.data.blueprintsIncludeTypings" class="d-flex flex-column" style="flex: 1">
<v-alert tile class="py-4 text-caption">
<div class="d-flex justify-space-between bg-sheet pa-2 text-caption">
<div class="text-truncate">
<strong>Readonly:</strong> Typings are generated automatically.
</div>
</v-alert>
<v-btn
:disabled="disableTypings"
size="x-small"
variant="tonal"
@click="() => copy(blueprintTypings, 'typings')"
>
Copy
</v-btn>
</div>
<v-ace-editor
ref="blueprintEditorTypings"
v-model:value="blueprintTypings"
Expand All @@ -716,11 +753,19 @@ watch(() => globalStore.userSettings.data, () => {
/>
</div>
<div class="d-flex flex-column" style="flex: 1">
<v-alert tile class="py-4 text-caption">
<div class="d-flex justify-space-between bg-sheet pa-2 text-caption">
<div class="text-truncate">
<strong>Readonly:</strong> Default objects are generated automatically.
</div>
</v-alert>
<v-btn
:disabled="disableDefaultObjects"
size="x-small"
variant="tonal"
@click="() => copy(blueprintDefault, 'defaults')"
>
Copy
</v-btn>
</div>
<v-ace-editor
ref="blueprintEditorDefault"
v-model:value="blueprintDefault"
Expand Down
2 changes: 1 addition & 1 deletion src/components/Toolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ watch(() => currentRoute.params.locale, () => {
<template #prepend>
<v-tooltip
v-if="globalStore.uiConfig.toolbar_menu"
text="Sections (CTRL+Q)"
text="Sections (ALT+Q)"
location="bottom"
>
<template #activator="{ props }">
Expand Down
Loading