11<script setup lang="ts">
22import { computed , onMounted , onUnmounted , ref , watch } from ' vue'
3- import { computeItemActions , oneStepActions } from ' ../../../utils/context'
43import { useStudio } from ' ../../../composables/useStudio'
54import type { StudioAction } from ' ../../../types'
6- import { StudioItemActionId } from ' ../../../types'
5+ import { StudioItemActionId , TreeStatus } from ' ../../../types'
76import { MEDIA_EXTENSIONS } from ' ../../../utils/file'
7+ import type { DropdownMenuItem } from ' @nuxt/ui/runtime/components/DropdownMenu.vue.d.ts'
88import { useI18n } from ' vue-i18n'
99
10- const { context } = useStudio ()
10+ const { context, gitProvider } = useStudio ()
1111const { t } = useI18n ()
1212const fileInputRef = ref <HTMLInputElement >()
1313const toolbarRef = ref <HTMLElement >()
@@ -23,44 +23,25 @@ watch(context.actionInProgress, (action) => {
2323
2424const item = computed (() => context .activeTree .value .currentItem .value )
2525
26- const getActionTooltip = (action : StudioAction <StudioItemActionId >, isPending : boolean ) => {
27- if (isPending ) {
28- const verb = action .id .split (' -' )[0 ]
29- return t (' studio.actions.confirmAction' , { action: t (` studio.actions.verbs.${verb } ` , verb ) })
30- }
31- return action .tooltip ? t (action .tooltip ) : t (action .label )
32- }
33-
34- const actions = computed (() => {
35- const hasPendingAction = pendingAction .value !== null
36- const hasLoadingAction = loadingAction .value !== null
37-
38- return computeItemActions (context .itemActions .value , item .value , context .currentFeature .value ).map ((action ) => {
39- const isOneStepAction = oneStepActions .includes (action .id )
40- const isPending = pendingAction .value ?.id === action .id
41- const isLoading = loadingAction .value ?.id === action .id
42- const isDeleteAction = action .id === StudioItemActionId .DeleteItem
43-
44- let icon = action .icon
45- if (isLoading ) {
46- icon = ' i-ph-circle-notch'
47- }
48- else if (isPending ) {
49- icon = isDeleteAction ? ' i-ph-x' : ' i-ph-check'
26+ const extraActions = computed <DropdownMenuItem []>(() => {
27+ const actions: DropdownMenuItem [] = []
28+
29+ if (item .value .type === ' file' && item .value .status !== TreeStatus .Created ) {
30+ const providerInfo = gitProvider .api .getRepositoryInfo ()
31+ const provider = providerInfo .provider
32+ const feature = context .currentFeature .value
33+
34+ if (feature ) {
35+ actions .push ({
36+ label: t (` studio.actions.labels.openGitProvider ` , { providerName: gitProvider .name }),
37+ icon: provider === ' gitlab' ? ' i-simple-icons:gitlab' : ' i-simple-icons:github' ,
38+ to: gitProvider .api .getFileUrl (feature , item .value .fsPath ),
39+ target: ' _blank' ,
40+ })
5041 }
42+ }
5143
52- return {
53- ... action ,
54- color: isPending ? (isDeleteAction ? ' error' : ' secondary' ) : ' neutral' ,
55- variant: isPending ? ' soft' : ' ghost' ,
56- icon ,
57- tooltip: getActionTooltip (action , isPending ),
58- disabled: (hasPendingAction && ! isPending ) || hasLoadingAction ,
59- isOneStepAction ,
60- isPending ,
61- isLoading ,
62- }
63- })
44+ return actions
6445})
6546
6647const handleFileSelection = (event : Event ) => {
@@ -75,49 +56,6 @@ const handleFileSelection = (event: Event) => {
7556 }
7657}
7758
78- const actionHandler = (action : StudioAction <StudioItemActionId > & { isPending? : boolean , isOneStepAction? : boolean , isLoading? : boolean }, event : Event ) => {
79- // Stop propagation to prevent click outside handler from triggering
80- event .stopPropagation ()
81-
82- // Don't allow action if already loading
83- if (action .isLoading ) {
84- return
85- }
86-
87- if (action .id === StudioItemActionId .UploadMedia ) {
88- fileInputRef .value ?.click ()
89- return
90- }
91-
92- const targetItem = item .value
93-
94- // For two-steps actions, execute without confirmation
95- if (! action .isOneStepAction ) {
96- if (action .id === StudioItemActionId .RenameItem ) {
97- // Navigate to parent since rename form is displayed in the parent tree
98- context .activeTree .value .selectParentByFsPath (targetItem .fsPath )
99- }
100-
101- action .handler !(targetItem )
102- return
103- }
104-
105- // Second click on pending action - execute it
106- if (action .isPending ) {
107- loadingAction .value = action
108- action .handler !(targetItem )
109- pendingAction .value = null
110- }
111- // Click on different action while one is pending - cancel pending state
112- else if (pendingAction .value !== null ) {
113- pendingAction .value = null
114- }
115- // First click - enter pending state
116- else {
117- pendingAction .value = action
118- }
119- }
120-
12159const handleClickOutside = () => {
12260 if (pendingAction .value !== null && loadingAction .value === null ) {
12361 pendingAction .value = null
@@ -138,23 +76,10 @@ onUnmounted(() => {
13876 ref =" toolbarRef"
13977 class =" flex items-center -mr-1"
14078 >
141- <UTooltip
142- v-for =" action in actions"
143- :key =" action.id"
144- :text =" action.tooltip"
145- :open =" action.isPending ? true : undefined"
146- >
147- <UButton
148- :key =" action.id"
149- :icon =" action.icon"
150- :disabled =" action.disabled"
151- size =" sm"
152- :color =" action.color as never"
153- :variant =" action.variant as never"
154- :loading =" action.isLoading"
155- @click =" actionHandler(action, $event)"
156- />
157- </UTooltip >
79+ <ItemActionsDropdown
80+ :item =" item"
81+ :extra-actions =" extraActions"
82+ />
15883
15984 <input
16085 ref =" fileInputRef"
0 commit comments