Skip to content

Commit 22240d7

Browse files
authored
Merge branch 'main' into feat/dev-ex
2 parents 9293297 + cca347a commit 22240d7

36 files changed

+1170
-852
lines changed

src/app/src/app.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { StudioFeature } from './types'
55
import { defineShortcuts } from '#imports'
66
import { useRouter } from 'vue-router'
77
8-
const { host, ui, isReady, documentTree } = useStudio()
8+
const { host, ui, isReady, context } = useStudio()
99
const router = useRouter()
1010
1111
defineShortcuts({
@@ -31,7 +31,7 @@ function detectActiveDocuments() {
3131
}
3232
3333
async function editContentFile(id: string) {
34-
await documentTree.selectItemById(id)
34+
await context.activeTree.value.selectItemById(id)
3535
ui.open(StudioFeature.Content)
3636
}
3737

src/app/src/components/AppHeader.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ const items: TabsItem[] = [
1010
{
1111
label: 'Content',
1212
value: 'content',
13+
to: '/content',
1314
},
1415
{
1516
label: 'Media',
1617
value: 'media',
18+
to: '/media',
1719
},
1820
]
1921

src/app/src/components/content/ContentEditor.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const props = defineProps({
1717
},
1818
})
1919
20-
const { documentTree } = useStudio()
20+
const { context } = useStudio()
2121
2222
const document = computed<DatabasePageItem>({
2323
get() {
@@ -50,7 +50,7 @@ const document = computed<DatabasePageItem>({
5050
return
5151
}
5252
53-
documentTree.draft.update(props.draftItem.id, {
53+
context.activeTree.value.draft.update(props.draftItem.id, {
5454
...toRaw(document.value as DatabasePageItem),
5555
...toRaw(value),
5656
})

src/app/src/components/modal/ModalConfirmAction.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ const handleConfirm = async () => {
5858
color: 'success',
5959
})
6060
}
61-
catch {
61+
catch (e) {
62+
console.error(e)
63+
6264
toast.add({
6365
title: errorMessageMap[props.actionId],
6466
color: 'error',

src/app/src/components/shared/item/ItemActionsToolbar.vue

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,38 @@
11
<script setup lang="ts">
2-
import { computed } from 'vue'
2+
import { computed, ref } from 'vue'
33
import { computeActionItems } from '../../../utils/context'
44
import { useStudio } from '../../../composables/useStudio'
5+
import { type StudioAction, StudioItemActionId } from '../../../types'
6+
import { mediaFileExtensions } from '../../../utils/media'
57
68
const { context } = useStudio()
9+
const fileInputRef = ref<HTMLInputElement>()
710
811
const item = computed(() => context.activeTree.value.currentItem.value)
912
const actions = computed(() => {
1013
return computeActionItems(context.itemActions.value, item.value)
1114
})
15+
16+
const handleFileSelection = (event: Event) => {
17+
const target = event.target as HTMLInputElement
18+
19+
if (target.files && target.files.length > 0) {
20+
context.itemActionHandler[StudioItemActionId.UploadMedia]({
21+
parentFsPath: item.value.fsPath,
22+
files: Array.from(target.files),
23+
})
24+
target.value = ''
25+
}
26+
}
27+
28+
const actionHandler = (action: StudioAction) => {
29+
if (action.id === StudioItemActionId.UploadMedia) {
30+
fileInputRef.value?.click()
31+
return
32+
}
33+
34+
action.handler!(item.value)
35+
}
1236
</script>
1337

1438
<template>
@@ -24,8 +48,17 @@ const actions = computed(() => {
2448
size="sm"
2549
color="neutral"
2650
variant="ghost"
27-
@click="action.handler!(item)"
51+
@click="actionHandler(action)"
2852
/>
2953
</UTooltip>
54+
55+
<input
56+
ref="fileInputRef"
57+
type="file"
58+
multiple
59+
:accept="mediaFileExtensions.map(ext => `.${ext}`).join(', ')"
60+
class="hidden"
61+
@change="handleFileSelection"
62+
>
3063
</div>
3164
</template>

src/app/src/components/shared/item/ItemCard.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { PropType } from 'vue'
44
import { computed } from 'vue'
55
import { Image } from '@unpic/vue'
66
import { titleCase } from 'scule'
7-
import { COLOR_UI_STATUS_MAP } from '../../../utils/tree'
7+
import { COLOR_UI_STATUS_MAP, TreeRootId } from '../../../utils/tree'
88
import { getFileIcon } from '../../../utils/file'
99
1010
const props = defineProps({
@@ -14,12 +14,13 @@ const props = defineProps({
1414
},
1515
})
1616
17+
const isMedia = computed(() => props.item.id.startsWith(TreeRootId.Media))
1718
const isFolder = computed(() => props.item.type === 'directory')
1819
const name = computed(() => titleCase(props.item.name))
1920
2021
const itemExtensionIcon = computed(() => props.item.preview ? '' : getFileIcon(props.item.id))
2122
22-
const imageSrc = computed(() => props.item.preview || '')
23+
const imageSrc = computed(() => props.item.routePath || '')
2324
2425
// ring-(--ui-success) ring-(--ui-info) ring-(--ui-warning) ring-(--ui-error) ring-(--ui-neutral)
2526
const statusRingColor = computed(() => props.item.status ? `ring-(--ui-${COLOR_UI_STATUS_MAP[props.item.status]})` : 'ring-(--ui-border) hover:ring-(--ui-border-accented)')

src/app/src/components/shared/item/ItemCardForm.vue

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { computed, reactive, type PropType } from 'vue'
33
import * as z from 'zod'
4-
import { type CreateFileParams, type CreateFolderParams, type RenameFileParams, type StudioAction, type TreeItem, ContentFileExtension } from '../../../types'
4+
import { type CreateFileParams, type CreateFolderParams, type RenameFileParams, type StudioAction, type TreeItem, ContentFileExtension, MediaFileExtension } from '../../../types'
55
import { joinURL, withLeadingSlash, withoutLeadingSlash } from 'ufo'
66
import { contentFileExtensions } from '../../../utils/content'
77
import { useStudio } from '../../../composables/useStudio'
@@ -10,6 +10,8 @@ import { stripNumericPrefix } from '../../../utils/string'
1010
import { defineShortcuts } from '#imports'
1111
import { upperFirst } from 'scule'
1212
import { getFileExtension } from '../../../utils/file'
13+
import { TreeRootId } from '../../../utils/tree'
14+
import { mediaFileExtensions } from '../../../utils/media'
1315
1416
const { context } = useStudio()
1517
@@ -35,8 +37,16 @@ const props = defineProps({
3537
})
3638
3739
const originalName = computed(() => props.renamedItem?.name || '')
40+
const isMedia = computed(() => props.renamedItem?.id.startsWith(TreeRootId.Media))
3841
const originalExtension = computed(() => {
3942
const ext = getFileExtension(props.renamedItem?.id || '')
43+
if (isMedia.value) {
44+
if (ext && mediaFileExtensions.includes(ext as MediaFileExtension)) {
45+
return ext as MediaFileExtension
46+
}
47+
return null
48+
}
49+
4050
if (ext && contentFileExtensions.includes(ext as ContentFileExtension)) {
4151
return ext as ContentFileExtension
4252
}
@@ -49,7 +59,7 @@ const schema = z.object({
4959
.min(1, 'Name cannot be empty')
5060
.refine((name: string) => !name.endsWith('.'), 'Name cannot end with "."')
5161
.refine((name: string) => !name.startsWith('/'), 'Name cannot start with "/"'),
52-
extension: z.optional(z.enum(ContentFileExtension)),
62+
extension: z.enum([...Object.values(ContentFileExtension), ...Object.values(MediaFileExtension)]).nullish(),
5363
})
5464
5565
type Schema = z.output<typeof schema>
@@ -216,7 +226,8 @@ function onSubmit() {
216226
</template>
217227
<USelect
218228
v-model="state.extension"
219-
:items="contentFileExtensions"
229+
:items="isMedia ? mediaFileExtensions : contentFileExtensions"
230+
:disabled="isMedia"
220231
variant="soft"
221232
class="w-18"
222233
/>

src/app/src/composables/useContext.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
type StudioActionInProgress,
1212
type CreateFolderParams,
1313
StudioItemActionId,
14+
type DatabaseItem,
1415
} from '../types'
1516
import { oneStepActions, STUDIO_ITEM_ACTION_DEFINITIONS, twoStepActions } from '../utils/context'
1617
import { useModal } from './useModal'
@@ -94,12 +95,12 @@ export const useContext = createSharedComposable((
9495
[StudioItemActionId.CreateDocument]: async (params: CreateFileParams) => {
9596
const { fsPath, content } = params
9697
const document = await host.document.create(fsPath, content)
97-
const draftItem = await activeTree.value.draft.create(document)
98+
const draftItem = await activeTree.value.draft.create(document as DatabaseItem)
9899
await activeTree.value.selectItemById(draftItem.id)
99100
},
100-
[StudioItemActionId.UploadMedia]: async ({ directory, files }: UploadMediaParams) => {
101+
[StudioItemActionId.UploadMedia]: async ({ parentFsPath, files }: UploadMediaParams) => {
101102
for (const file of files) {
102-
await (activeTree.value.draft as ReturnType<typeof useDraftMedias>).upload(directory, file)
103+
await (activeTree.value.draft as ReturnType<typeof useDraftMedias>).upload(parentFsPath, file)
103104
}
104105
},
105106
[StudioItemActionId.RevertItem]: async (item: TreeItem) => {
@@ -128,7 +129,7 @@ export const useContext = createSharedComposable((
128129
},
129130
[StudioItemActionId.DuplicateItem]: async (item: TreeItem) => {
130131
const draftItem = await activeTree.value.draft.duplicate(item.id)
131-
await activeTree.value.selectItemById(draftItem.id)
132+
await activeTree.value.selectItemById(draftItem!.id)
132133
},
133134
}
134135

0 commit comments

Comments
 (0)