Skip to content

Commit 3032212

Browse files
committed
up
1 parent c068e09 commit 3032212

25 files changed

+730
-420
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { computed, type PropType, toRaw } from 'vue'
33
import { decompressTree } from '@nuxt/content/runtime'
44
import type { MarkdownRoot } from '@nuxt/content'
5-
import type { DatabasePageItem, DraftItem } from '../../types'
5+
import { DraftStatus, type DatabasePageItem, type DraftItem } from '../../types'
66
import { useStudio } from '../../composables/useStudio'
77
88
const props = defineProps({
@@ -25,10 +25,13 @@ const document = computed<DatabasePageItem>({
2525
return {} as DatabasePageItem
2626
}
2727
28+
if (props.draftItem.status === DraftStatus.Deleted) {
29+
return props.draftItem.original as DatabasePageItem
30+
}
31+
2832
const dbItem = props.draftItem.modified as DatabasePageItem
2933
3034
let result: DatabasePageItem
31-
// TODO: check mdcRoot and markdownRoot types with Ahad
3235
if (dbItem.body?.type === 'minimark') {
3336
result = {
3437
...props.draftItem.modified as DatabasePageItem,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { computeActionItems, computeActionParams } from '../../../utils/context'
2+
import { computeActionItems } from '../../../utils/context'
33
import { computed, type PropType } from 'vue'
44
import type { TreeItem } from '../../../types'
55
import { useStudio } from '../../../composables/useStudio'
@@ -17,7 +17,7 @@ const props = defineProps({
1717
const actions = computed<DropdownMenuItem[]>(() => {
1818
return computeActionItems(context.itemActions.value, props.item).map(action => ({
1919
...action,
20-
onSelect: () => action.handler?.(computeActionParams(action.id, { item: props.item })),
20+
onSelect: () => action.handler!(props.item),
2121
}))
2222
})
2323
</script>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const actions = computed(() => {
2424
size="sm"
2525
color="neutral"
2626
variant="ghost"
27-
@click="action.handler!(item.id)"
27+
@click="action.handler!(item)"
2828
/>
2929
</UTooltip>
3030
</div>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script setup lang="ts">
2-
import { COLOR_UI_STATUS_MAP } from '../../../utils/draft'
3-
import type { DraftStatus } from '../../../types/draft'
2+
import { COLOR_UI_STATUS_MAP } from '../../../utils/tree'
3+
import type { TreeStatus } from '../../../types'
44
import type { PropType } from 'vue'
55
66
defineProps({
77
status: {
8-
type: String as PropType<DraftStatus>,
8+
type: String as PropType<TreeStatus>,
99
required: true,
1010
},
1111
})

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
import type { BreadcrumbItem } from '@nuxt/ui/components/Breadcrumb.vue.d.ts'
33
import type { DropdownMenuItem } from '@nuxt/ui/components/DropdownMenu.vue.d.ts'
44
import { computed, unref } from 'vue'
5-
import type { TreeItem } from '../../../types'
5+
import { type TreeItem, TreeStatus } from '../../../types'
66
import { useStudio } from '../../../composables/useStudio'
77
import { findParentFromId, ROOT_ITEM } from '../../../utils/tree'
88
import { FEATURE_DISPLAY_MAP } from '../../../utils/context'
9-
import { DraftStatus } from '../../../types/draft'
109
1110
const { context } = useStudio()
1211
@@ -87,7 +86,7 @@ const items = computed<BreadcrumbItem[]>(() => {
8786
</template>
8887
</UBreadcrumb>
8988
<ItemBadge
90-
v-if="currentItem.status && currentItem.status !== DraftStatus.Opened"
89+
v-if="currentItem.status && currentItem.status !== TreeStatus.Opened"
9190
:status="currentItem.status"
9291
/>
9392
</div>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<script setup lang="ts">
2-
import type { TreeItem } from '../../../types'
2+
import { TreeStatus, type TreeItem } from '../../../types'
33
import type { PropType } from 'vue'
44
import { computed } from 'vue'
55
import { Image } from '@unpic/vue'
66
import { titleCase } from 'scule'
7-
import { COLOR_STATUS_MAP } from '../../../utils/draft'
7+
import { COLOR_STATUS_MAP } from '../../../utils/tree'
88
99
const props = defineProps({
1010
item: {
@@ -90,7 +90,7 @@ const statusRingColor = computed(() => props.item.status ? `ring-${COLOR_STATUS_
9090
/>
9191
<h3
9292
class="text-sm font-semibold truncate"
93-
:class="props.item.status === 'deleted' && 'line-through'"
93+
:class="props.item.status === TreeStatus.Deleted && 'line-through'"
9494
>
9595
{{ name }}
9696
</h3>

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { computed, reactive, type PropType } from 'vue'
33
import { Image } from '@unpic/vue'
44
import * as z from 'zod'
55
import type { FormSubmitEvent } from '@nuxt/ui'
6-
import { type StudioAction, type TreeItem, ContentFileExtension } from '../../../types'
6+
import { type CreateFileParams, type CreateFolderParams, type RenameFileParams, type StudioAction, type TreeItem, ContentFileExtension } from '../../../types'
77
import { joinURL, withLeadingSlash } from 'ufo'
88
import { contentFileExtensions } from '../../../utils/content'
99
import { useStudio } from '../../../composables/useStudio'
@@ -14,7 +14,7 @@ const { context } = useStudio()
1414
1515
const props = defineProps({
1616
actionId: {
17-
type: String as PropType<StudioItemActionId>,
17+
type: String as PropType<StudioItemActionId.CreateDocument | StudioItemActionId.CreateFolder | StudioItemActionId.RenameItem>,
1818
required: true,
1919
},
2020
parentItem: {
@@ -76,11 +76,29 @@ const tooltipText = computed(() => {
7676
function onSubmit(_event: FormSubmitEvent<Schema>) {
7777
const fsPath = joinURL(props.parentItem.fsPath, `${state.name}.${state.extension}`)
7878
79-
action.value.handler!({
80-
routePath: routePath.value,
81-
fsPath,
82-
content: `New ${state.name} file`,
83-
})
79+
let params: CreateFileParams | CreateFolderParams | RenameFileParams
80+
switch (props.actionId) {
81+
case StudioItemActionId.CreateDocument:
82+
params = {
83+
routePath: routePath.value,
84+
fsPath,
85+
content: `New ${state.name} file`,
86+
}
87+
break
88+
case StudioItemActionId.CreateFolder:
89+
params = {
90+
fsPath,
91+
}
92+
break
93+
case StudioItemActionId.RenameItem:
94+
params = {
95+
id: props.renamedItem.id,
96+
newFsPath: joinURL(props.parentItem.fsPath, `${state.name}.${state.extension}`),
97+
}
98+
break
99+
}
100+
101+
action.value.handler!(params)
84102
}
85103
</script>
86104

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const filteredTree = computed(() => {
3737
<li v-if="showForm">
3838
<ItemCardForm
3939
:parent-item="context.activeTree.value.currentItem.value"
40-
:action-id="context.actionInProgress.value!.id"
40+
:action-id="context.actionInProgress.value!.id as never"
4141
:renamed-item="context.actionInProgress.value!.item"
4242
/>
4343
</li>

src/app/src/composables/useContext.ts

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { createSharedComposable } from '@vueuse/core'
22
import { computed, ref } from 'vue'
33
import {
4+
type RenameFileParams,
5+
type TreeItem,
46
type UploadMediaParams,
57
type CreateFileParams,
68
type StudioHost,
79
type StudioAction,
810
type ActionHandlerParams,
911
type StudioActionInProgress,
12+
type CreateFolderParams,
1013
StudioItemActionId,
1114
} from '../types'
1215
import { oneStepActions, STUDIO_ITEM_ACTION_DEFINITIONS, twoStepActions } from '../utils/context'
@@ -33,13 +36,13 @@ export const useContext = createSharedComposable((
3336
})
3437

3538
const itemActions = computed<StudioAction[]>(() => {
36-
return STUDIO_ITEM_ACTION_DEFINITIONS.map(action => ({
39+
return STUDIO_ITEM_ACTION_DEFINITIONS.map(<K extends StudioItemActionId>(action: StudioAction<K>) => ({
3740
...action,
38-
handler: async (args) => {
41+
handler: async (args: ActionHandlerParams[K]) => {
42+
// Two steps actions need to be already in progress to be executed
3943
if (actionInProgress.value?.id === action.id) {
40-
// Two steps actions need to be already in progress to be executed
4144
if (twoStepActions.includes(action.id)) {
42-
await itemActionHandler[action.id](args as never)
45+
await itemActionHandler[action.id](args)
4346
unsetActionInProgress()
4447
return
4548
}
@@ -49,22 +52,31 @@ export const useContext = createSharedComposable((
4952
}
5053
}
5154

52-
actionInProgress.value = { id: action.id, item: args.item }
55+
actionInProgress.value = { id: action.id }
56+
57+
if (action.id === StudioItemActionId.RenameItem) {
58+
if (activeTree.value.currentItem.value) {
59+
const itemToRename = args as TreeItem
60+
await activeTree.value.selectParentById(itemToRename.id)
61+
actionInProgress.value.item = itemToRename
62+
}
63+
}
5364

5465
// One step actions can be executed immediately
5566
if (oneStepActions.includes(action.id)) {
56-
await itemActionHandler[action.id](args as never)
67+
await itemActionHandler[action.id](args)
5768
unsetActionInProgress()
5869
}
5970
},
6071
}))
6172
})
6273

6374
const itemActionHandler: { [K in StudioItemActionId]: (args: ActionHandlerParams[K]) => Promise<void> } = {
64-
[StudioItemActionId.CreateFolder]: async (args: string) => {
65-
alert(`create folder ${args}`)
75+
[StudioItemActionId.CreateFolder]: async (params: CreateFolderParams) => {
76+
alert(`create folder in ${params.fsPath}`)
6677
},
67-
[StudioItemActionId.CreateDocument]: async ({ fsPath, routePath, content }: CreateFileParams) => {
78+
[StudioItemActionId.CreateDocument]: async (params: CreateFileParams) => {
79+
const { fsPath, routePath, content } = params
6880
const document = await host.document.create(fsPath, routePath, content)
6981
const draftItem = await activeTree.value.draft.create(document)
7082
await activeTree.value.selectItemById(draftItem.id)
@@ -74,23 +86,23 @@ export const useContext = createSharedComposable((
7486
await (activeTree.value.draft as ReturnType<typeof useDraftMedias>).upload(directory, file)
7587
}
7688
},
77-
[StudioItemActionId.RevertItem]: async (id: string) => {
78-
modal.openConfirmActionModal(id, StudioItemActionId.RevertItem, async () => {
79-
await activeTree.value.draft.revert(id)
89+
[StudioItemActionId.RevertItem]: async (item: TreeItem) => {
90+
modal.openConfirmActionModal(item.id, StudioItemActionId.RevertItem, async () => {
91+
await activeTree.value.draft.revert(item.id)
8092
})
8193
},
82-
[StudioItemActionId.RenameItem]: async ({ id, newNameWithExtension }: { id: string, newNameWithExtension: string }) => {
83-
alert(`rename file ${id} ${newNameWithExtension}`)
94+
[StudioItemActionId.RenameItem]: async (params: TreeItem | RenameFileParams) => {
95+
const { id, newFsPath } = params as RenameFileParams
96+
await activeTree.value.draft.rename(id, newFsPath)
8497
},
85-
[StudioItemActionId.DeleteItem]: async (id: string) => {
86-
modal.openConfirmActionModal(id, StudioItemActionId.DeleteItem, async () => {
87-
const ids: string[] = findDescendantsFileItemsFromId(activeTree.value.root.value, id).map(item => item.id)
98+
[StudioItemActionId.DeleteItem]: async (item: TreeItem) => {
99+
modal.openConfirmActionModal(item.id, StudioItemActionId.DeleteItem, async () => {
100+
const ids: string[] = findDescendantsFileItemsFromId(activeTree.value.root.value, item.id).map(item => item.id)
88101
await activeTree.value.draft.remove(ids)
89-
await activeTree.value.selectParentById(id)
90102
})
91103
},
92-
[StudioItemActionId.DuplicateItem]: async (id: string) => {
93-
const draftItem = await activeTree.value.draft.duplicate(id)
104+
[StudioItemActionId.DuplicateItem]: async (item: TreeItem) => {
105+
const draftItem = await activeTree.value.draft.duplicate(item.id)
94106
await activeTree.value.selectItemById(draftItem.id)
95107
},
96108
}
@@ -103,7 +115,6 @@ export const useContext = createSharedComposable((
103115
activeTree,
104116
itemActions,
105117
actionInProgress,
106-
107118
unsetActionInProgress,
108119
itemActionHandler,
109120
}

src/app/src/composables/useDraftDocuments.ts

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { DatabaseItem, DraftItem, StudioHost, GithubFile, DatabasePageItem,
55
import { DraftStatus } from '../types/draft'
66
import type { useGit } from './useGit'
77
import { generateContentFromDocument } from '../utils/content'
8-
import { getDraftStatus, findDescendantsFromId } from '../utils/draft'
8+
import { findDescendantsFromId, getUpdatedDraftStatus } from '../utils/draft'
99
import { createSharedComposable } from '@vueuse/core'
1010
import { useHooks } from './useHooks'
1111
import { stripNumericPrefix } from '../utils/string'
@@ -35,7 +35,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
3535
// return item
3636
}
3737

38-
async function create(document: DatabaseItem, status: DraftStatus = DraftStatus.Created) {
38+
async function create(document: DatabaseItem, status: DraftStatus = DraftStatus.Created, original?: DatabaseItem) {
3939
const existingItem = list.value.find(item => item.id === document.id)
4040
if (existingItem) {
4141
throw new Error(`Draft file already exists for document ${document.id}`)
@@ -47,7 +47,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
4747
const item: DraftItem<DatabaseItem> = {
4848
id: document.id,
4949
fsPath,
50-
original: document,
50+
original: original || document,
5151
githubFile,
5252
status,
5353
modified: document,
@@ -69,7 +69,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
6969
}
7070

7171
const oldStatus = existingItem.status
72-
existingItem.status = getDraftStatus(document, existingItem.original)
72+
existingItem.status = getUpdatedDraftStatus(document, existingItem.original)
7373
existingItem.modified = document
7474

7575
await storage.setItem(id, existingItem)
@@ -170,7 +170,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
170170
host.app.requestRerender()
171171
}
172172

173-
async function rename(id: string, newNameWithExtension: string) {
173+
async function rename(id: string, newFsPath: string) {
174174
let currentDbItem: DatabaseItem = await host.document.get(id)
175175
if (!currentDbItem) {
176176
throw new Error(`Database item not found for document ${id}`)
@@ -181,21 +181,16 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
181181
currentDbItem = currentDraftItem.modified as DatabasePageItem
182182
}
183183

184-
const newNameWithoutExtension = newNameWithExtension.split('.').slice(0, -1).join('.')
185-
const newId = `${currentDbItem.id.split('/').slice(0, -1).join('/')}/${newNameWithExtension}`
186-
const newPath = `${currentDbItem.path!.split('/').slice(0, -1).join('/')}/${newNameWithExtension}`
187-
const newStem = `${currentDbItem.stem.split('/').slice(0, -1).join('/')}/${newNameWithoutExtension}`
188-
const newExtension = newNameWithExtension.split('.').pop()!
189-
190-
const newDbItem: DatabaseItem = {
191-
...currentDbItem,
192-
id: newId,
193-
path: newPath,
194-
stem: newStem,
195-
extension: newExtension,
196-
}
184+
const nameWithoutExtension = newFsPath.split('/').pop()!.split('.').slice(0, -1).join('.')
185+
const newRoutePath = `${currentDbItem.path!.split('/').slice(0, -1).join('/')}/${nameWithoutExtension}`
186+
const content = await generateContentFromDocument(currentDbItem)
187+
188+
// Delete renamed draft item
189+
await remove([id])
197190

198-
return await update(id, newDbItem)
191+
// Create new draft item
192+
const newDbItem = await host.document.create(newFsPath, newRoutePath, content!)
193+
return await create(newDbItem, DraftStatus.Created, currentDbItem)
199194
}
200195

201196
async function duplicate(id: string): Promise<DraftItem<DatabaseItem>> {
@@ -221,7 +216,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
221216

222217
const newDbItem = await host.document.create(newFsPath, newRoutePath, currentContent)
223218

224-
return await create(newDbItem, DraftStatus.Created)
219+
return await create(newDbItem)
225220
}
226221

227222
async function load() {
@@ -254,11 +249,6 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
254249
}
255250

256251
function select(draftItem: DraftItem<DatabaseItem> | null) {
257-
// TODO: Handle editor with deleted file
258-
if (draftItem?.status === DraftStatus.Deleted) {
259-
return
260-
}
261-
262252
current.value = draftItem
263253
}
264254

0 commit comments

Comments
 (0)