@ -134,6 +134,10 @@ const enableEditMode = () => {
}
}
const updateProjectTitle = async ( ) => {
const updateProjectTitle = async ( ) => {
if ( tempTitle . value ) {
tempTitle . value = tempTitle . value . trim ( )
}
if ( ! tempTitle . value ) return
if ( ! tempTitle . value ) return
try {
try {
@ -431,22 +435,8 @@ const onTableIdCopy = async () => {
'hover:bg-gray-200' : ! ( activeProjectId === base . id && baseViewOpen ) ,
'hover:bg-gray-200' : ! ( activeProjectId === base . id && baseViewOpen ) ,
} "
} "
: data - testid = "`nc-sidebar-base-title-${base.title}`"
: data - testid = "`nc-sidebar-base-title-${base.title}`"
class = "nc-sidebar-node base-title-node h-7.25 flex-grow rounded-md group flex items-center w-full pr-1"
class = "nc-sidebar-node base-title-node h-7.25 flex-grow rounded-md group flex items-center w-full pr-1 pl-1.5 "
>
>
< NcButton
v - e = "['c:base:expand']"
type = "text"
size = "xxsmall"
class = "nc-sidebar-node-btn nc-sidebar-expand ml-0.75 !xs:visible"
@ click = "onProjectClick(base, true, true)"
>
< GeneralIcon
icon = "triangleFill"
class = "group-hover:visible cursor-pointer transform transition-transform duration-500 h-1.5 w-1.75 rotate-90 !xs:visible"
: class = "{ '!rotate-180': base.isExpanded, '!visible': isOptionsOpen }"
/ >
< / NcButton >
< div class = "flex items-center mr-1" @click ="onProjectClick(base)" >
< div class = "flex items-center mr-1" @click ="onProjectClick(base)" >
< div class = "flex items-center select-none w-6 h-full" >
< div class = "flex items-center select-none w-6 h-full" >
< a -spin v -if = " base.isLoading " class = "!ml-1.25 !flex !flex-row !items-center !my-0.5 w-8" :indicator ="indicator" / >
< a -spin v -if = " base.isLoading " class = "!ml-1.25 !flex !flex-row !items-center !my-0.5 w-8" :indicator ="indicator" / >
@ -469,7 +459,7 @@ const onTableIdCopy = async () => {
v - if = "editMode"
v - if = "editMode"
ref = "input"
ref = "input"
v - model = "tempTitle"
v - model = "tempTitle"
class = "flex-grow leading-1 outline-0 ring-none capitalize !text-inherit !bg-transparent w-4/5 "
class = "flex-grow leading-1 outline-0 ring-none capitalize !text-inherit !bg-transparent flex-1 mr-4 "
: class = "{ 'text-black font-semibold': activeProjectId === base.id && baseViewOpen && !isMobileMode }"
: class = "{ 'text-black font-semibold': activeProjectId === base.id && baseViewOpen && !isMobileMode }"
@ click . stop
@ click . stop
@ keyup . enter = "updateProjectTitle"
@ keyup . enter = "updateProjectTitle"
@ -478,154 +468,176 @@ const onTableIdCopy = async () => {
/ >
/ >
< NcTooltip
< NcTooltip
v - else
v - else
class = "nc-sidebar-node-title capitalize text-ellipsis overflow-hidden select-none"
class = "nc-sidebar-node-title capitalize text-ellipsis overflow-hidden select-none flex-1 "
: style = "{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
: style = "{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
: class = "{ 'text-black font-semibold': activeProjectId === base.id && baseViewOpen }"
: class = "{ 'text-black font-semibold': activeProjectId === base.id && baseViewOpen }"
show - on - truncate - only
show - on - truncate - only
@ click = "onProjectClick(base)"
>
>
< template # title > { { base . title } } < / template >
< template # title > { { base . title } } < / template >
< span @click ="onProjectClick(base)" >
< span >
{ { base . title } }
{ { base . title } }
< / span >
< / span >
< / NcTooltip >
< / NcTooltip >
< div : class = "{ 'flex flex-grow h-full': !editMode }" @click ="onProjectClick(base)" > < / div >
< NcDropdown v-if ="!isSharedBase" v-model:visible="isOptionsOpen" :trigger="['click']" >
< template v-if ="!editMode" >
< NcButton
< NcDropdown v-if ="!isSharedBase" v-model:visible="isOptionsOpen" :trigger="['click']" >
v - e = "['c:base:options']"
< NcButton
class = "nc-sidebar-node-btn"
v - e = "['c:base:options']"
: class = "{ '!text-black !opacity-100': isOptionsOpen }"
class = "nc-sidebar-node-btn"
data - testid = "nc-sidebar-context-menu"
: class = "{ '!text-black !opacity-100 !inline-block': isOptionsOpen }"
type = "text"
data - testid = "nc-sidebar-context-menu"
size = "xxsmall"
type = "text"
@ click . stop
size = "xxsmall"
>
@ click . stop
< GeneralIcon icon = "threeDotHorizontal" class = "text-xl w-4.75" / >
< / NcButton >
< template # overlay >
< NcMenu
class = "nc-scrollbar-md"
: style = " {
maxHeight : '70vh' ,
overflow : 'overlay' ,
} "
: data - testid = "`nc-sidebar-base-${base.title}-options`"
@ click = "isOptionsOpen = false"
>
>
< template v-if ="!isSharedBase" >
< GeneralIcon icon = "threeDotHorizontal" class = "text-xl w-4.75" / >
< NcMenuItem v-if ="isUIAllowed('baseRename')" data-testid="nc-sidebar-project-rename" @click="enableEditMode" >
< / NcButton >
< div v-e ="['c:base:rename']" class="flex gap-2 items-center" >
< template # overlay >
< GeneralIcon icon = "rename" class = "group-hover:text-black" / >
< NcMenu
{ { $t ( 'general.rename' ) } }
class = "nc-scrollbar-md"
< / div >
: style = " {
< / NcMenuItem >
maxHeight : '70vh' ,
overflow : 'overlay' ,
} "
: data - testid = "`nc-sidebar-base-${base.title}-options`"
@ click = "isOptionsOpen = false"
>
< template v-if ="!isSharedBase" >
< NcMenuItem v-if ="isUIAllowed('baseRename')" data-testid="nc-sidebar-project-rename" @click="enableEditMode" >
< div v-e ="['c:base:rename']" class="flex gap-2 items-center" >
< GeneralIcon icon = "rename" class = "group-hover:text-black" / >
{ { $t ( 'general.rename' ) } }
< / div >
< / NcMenuItem >
< NcMenuItem
v - if = "isUIAllowed('baseDuplicate', { roles: [stringifyRolesObj(orgRoles), baseRole].join() })"
data - testid = "nc-sidebar-base-duplicate"
@ click = "duplicateProject(base)"
>
< div v-e ="['c:base:duplicate']" class="flex gap-2 items-center" >
< GeneralIcon icon = "duplicate" class = "text-gray-700" / >
{ { $t ( 'general.duplicate' ) } }
< / div >
< / NcMenuItem >
< NcDivider v -if = " [ ' baseDuplicate ' , ' baseRename ' ] .some ( ( permission ) = > isUIAllowed ( permission ) ) " / >
<!-- Copy Project Info -- >
< NcMenuItem
v - if = "!isEeUI"
key = "copy"
data - testid = "nc-sidebar-base-copy-base-info"
@ click . stop = "copyProjectInfo"
>
< div v-e ="['c:base:copy-proj-info']" class="flex gap-2 items-center" >
< GeneralIcon icon = "copy" class = "group-hover:text-black" / >
{ { $t ( 'activity.account.projInfo' ) } }
< / div >
< / NcMenuItem >
<!-- ERD View -- >
< NcMenuItem
v - if = "base?.sources?.[0]?.enabled"
key = "erd"
data - testid = "nc-sidebar-base-relations"
@ click = "openErdView(base?.sources?.[0])"
>
< div v-e ="['c:base:erd']" class="flex gap-2 items-center" >
< GeneralIcon icon = "erd" / >
{ { $t ( 'title.relations' ) } }
< / div >
< / NcMenuItem >
<!-- Swagger : Rest APIs -- >
< NcMenuItem
v - if = "isUIAllowed('apiDocs')"
key = "api"
data - testid = "nc-sidebar-base-rest-apis"
@ click . stop = "
( ) => {
$e ( 'c:base:api-docs' )
openLink ( ` /api/v2/meta/bases/ ${ base . id } /swagger ` , appInfo . ncSiteUrl )
}
"
>
< div v-e ="['c:base:api-docs']" class="flex gap-2 items-center" >
< GeneralIcon icon = "snippet" class = "group-hover:text-black !max-w-3.9" / >
{ { $t ( 'activity.account.swagger' ) } }
< / div >
< / NcMenuItem >
< / template >
< NcMenuItem
< template v-if ="base?.sources?.[0]?.enabled && showBaseOption" >
v - if = "isUIAllowed('baseDuplicate', { roles: [stringifyRolesObj(orgRoles), baseRole].join() })"
< NcDivider / >
data - testid = "nc-sidebar-base-duplicate"
< DashboardTreeViewBaseOptions v -model :base ="base" :source ="base.sources[0]" / >
@ click = "duplicateProject(base)"
< / template >
>
< div v-e ="['c:base:duplicate']" class="flex gap-2 items-center" >
< GeneralIcon icon = "duplicate" class = "text-gray-700" / >
{ { $t ( 'general.duplicate' ) } }
< / div >
< / NcMenuItem >
< NcDivider v -if = " [ ' baseDuplicate ' , ' baseRename ' ] .some ( ( permission ) = > isUIAllowed ( permission ) ) " / >
< NcDivider v -if = " [ ' baseMiscSettings ' , ' baseDelete ' ] .some ( ( permission ) = > isUIAllowed ( permission ) ) " / >
<!-- Copy Project Info -- >
< NcMenuItem
< NcMenuItem
v - if = "!isEeUI"
v - if = "isUIAllowed('baseMiscSettings')"
key = "copy"
key = "teamAndSettings"
data - testid = "nc-sidebar-base-copy-base-info"
data - testid = "nc-sidebar-base-settings"
@ click . stop = "copyProjectInfo"
class = "nc-sidebar-base-base-settings"
@ click = "toggleDialog(true, 'teamAndAuth', undefined, base.id)"
>
>
< div v-e ="['c:base:copy-proj-info']" class="flex gap-2 items-center" >
< div v-e ="['c:base:settings ']" class="flex gap-2 items-center" >
< GeneralIcon icon = "copy" class = "group-hover:text-black" / >
< GeneralIcon icon = "settings " class = "group-hover:text-black" / >
{ { $t ( 'activity.account.projInfo' ) } }
{ { $t ( 'activity.settings ' ) } }
< / div >
< / div >
< / NcMenuItem >
< / NcMenuItem >
<!-- ERD View -- >
< NcMenuItem
< NcMenuItem
v - if = "base?.sources?.[0]?.enabled"
v - if = "isUIAllowed('baseDelete', { roles: [stringifyRolesObj(orgRoles), baseRole].join() })"
key = "erd"
data - testid = "nc-sid eba r-base- delete "
data - testid = "nc-sidebar-base-relations"
class = "!text-red-500 !hover:bg-red-50 "
@ click = "openErdView(base?.sources?.[0])"
@ click = "projectDelete "
>
>
< div v-e ="['c:base:erd']" class="flex gap-2 items-center" >
< div class = "flex gap-2 items-center" >
< GeneralIcon icon = "erd" / >
< GeneralIcon icon = "delete" class = "w-4 " / >
{ { $t ( 'title.relations' ) } }
{ { $t ( 'general.delete ' ) } }
< / div >
< / div >
< / NcMenuItem >
< / NcMenuItem >
< / NcMenu >
< / template >
< / NcDropdown >
<!-- Swagger : Rest APIs -- >
< NcButton
< NcMenuItem
v - if = "isUIAllowed('tableCreate', { roles: baseRole })"
v - if = "isUIAllowed('apiDocs')"
v - e = "['c:base:create-table']"
key = "api"
: disabled = "!base?.sources?.[0]?.enabled"
data - testid = "nc-sidebar-base-rest-apis"
class = "nc-sidebar-node-btn"
@ click . stop = "
size = "xxsmall"
( ) => {
type = "text"
$e ( 'c:base:api-docs' )
data - testid = "nc-sidebar-add-base-entity"
openLink ( ` /api/v2/meta/bases/ ${ base . id } /swagger ` , appInfo . ncSiteUrl )
: class = " {
}
'!text-black !inline-block !opacity-100' : isAddNewProjectChildEntityLoading ,
"
'!inline-block !opacity-100' : isOptionsOpen ,
>
} "
< div v-e ="['c:base:api-docs']" class="flex gap-2 items-center" >
: loading = "isAddNewProjectChildEntityLoading"
< GeneralIcon icon = "snippet" class = "group-hover:text-black !max-w-3.9" / >
@ click . stop = "addNewProjectChildEntity"
{ { $t ( 'activity.account.swagger' ) } }
>
< / div >
< GeneralIcon icon = "plus" class = "text-xl leading-5" style = "-webkit-text-stroke: 0.15px" / >
< / NcMenuItem >
< / NcButton >
< / template >
< template v-if ="base?.sources?.[0]?.enabled && showBaseOption" >
< NcDivider / >
< DashboardTreeViewBaseOptions v -model :base ="base" :source ="base.sources[0]" / >
< / template >
< NcDivider v -if = " [ ' baseMiscSettings ' , ' baseDelete ' ] .some ( ( permission ) = > isUIAllowed ( permission ) ) " / >
< NcMenuItem
< NcButton
v - if = "isUIAllowed('baseMiscSettings')"
v - e = "['c:base:expand']"
key = "teamAndSettings"
type = "text"
data - testid = "nc-sidebar-base-settings"
size = "xxsmall"
class = "nc-sidebar-base-base-settings"
class = "nc-sidebar-node-btn nc-sidebar-expand !xs:opacity-100"
@ click = "toggleDialog(true, 'teamAndAuth', undefined, base.id)"
: class = " {
>
'!opacity-100' : isOptionsOpen ,
< div v-e ="['c:base:settings']" class="flex gap-2 items-center" >
} "
< GeneralIcon icon = "settings" class = "group-hover:text-black" / >
@ click = "onProjectClick(base, true, true)"
{ { $t ( 'activity.settings' ) } }
>
< / div >
< GeneralIcon
< / NcMenuItem >
icon = "chevronDown"
< NcMenuItem
class = "group-hover:visible cursor-pointer transform transition-transform duration-500 rotate-270"
v - if = "isUIAllowed('baseDelete', { roles: [stringifyRolesObj(orgRoles), baseRole].join() })"
: class = "{ '!rotate-180': base.isExpanded }"
data - testid = "nc-sidebar-base-delete"
/ >
class = "!text-red-500 !hover:bg-red-50"
< / NcButton >
@ click = "projectDelete"
< / template >
>
< div class = "flex gap-2 items-center" >
< GeneralIcon icon = "delete" class = "w-4" / >
{ { $t ( 'general.delete' ) } }
< / div >
< / NcMenuItem >
< / NcMenu >
< / template >
< / NcDropdown >
< NcButton
v - if = "isUIAllowed('tableCreate', { roles: baseRole })"
v - e = "['c:base:create-table']"
: disabled = "!base?.sources?.[0]?.enabled"
class = "nc-sidebar-node-btn"
size = "xxsmall"
type = "text"
data - testid = "nc-sidebar-add-base-entity"
: class = "{ '!text-black !visible': isAddNewProjectChildEntityLoading, '!visible': isOptionsOpen }"
: loading = "isAddNewProjectChildEntityLoading"
@ click . stop = "addNewProjectChildEntity"
>
< GeneralIcon icon = "plus" class = "text-xl leading-5" style = "-webkit-text-stroke: 0.15px" / >
< / NcButton >
< / div >
< / div >
< / div >
< / div >
@ -658,25 +670,29 @@ const onTableIdCopy = async () => {
ghost
ghost
>
>
< template # expandIcon = "{ isActive }" >
< template # expandIcon = "{ isActive }" >
< div
< NcButton
class = "nc-sidebar-expand nc-sidebar-node-btn flex flex-row items-center -mt-2 xs:(mt-3 border-1 border-gray-200 px-2.25 py-0.5 rounded-md !mr-0.25)"
v - e = "['c:external:base:expand']"
type = "text"
size = "xxsmall"
class = "nc-sidebar-node-btn nc-sidebar-expand !xs:opacity-100"
: class = "{ '!opacity-100 !inline-block': isBasesOptionsOpen[source!.id!] }"
>
>
< GeneralIcon
< GeneralIcon
icon = "triangleFill"
icon = "chevronDown "
class = "nc-sidebar-source-node-btns -mt-0.75 invisible xs:visible cursor-pointer transform transition-transform duration-500 h-1.5 w-1.5 text-gray-500 rotate-90"
class = "flex-none cursor-pointer transform transition-transform duration-500 rotate-27 0"
: class = "{ '!rotate-180': isActive }"
: class = "{ '!rotate-180': isActive }"
/ >
/ >
< / div >
< / NcButton >
< / template >
< / template >
< a -collapse -panel :key ="`collapse-${source.id}`" >
< a -collapse -panel :key ="`collapse-${source.id}`" >
< template # header >
< template # header >
< div class = "nc-sidebar-node min-w-20 w-full flex flex-row group py-0.25" >
< div class = "nc-sidebar-node min-w-20 w-full h-full flex flex-row group py-0.25 pr-6.5 !mr-0 " >
< div
< div
v - if = "sourceIndex === 0"
v - if = "sourceIndex === 0"
class = "source-context flex items-center gap-2 text-gray-800 nc-sidebar-node-title"
class = "source-context flex items-center gap-2 text-gray-800 nc-sidebar-node-title"
@ contextmenu = "setMenuContext('source', source)"
@ contextmenu = "setMenuContext('source', source)"
>
>
< GeneralBaseLogo class = "min-w-4 !xs:(min-w-4.25 w-4.25 text-sm)" / >
< GeneralBaseLogo class = "flex-none min-w-4 !xs:(min-w-4.25 w-4.25 text-sm)" / >
{ { $t ( 'general.default' ) } }
{ { $t ( 'general.default' ) } }
< / div >
< / div >
< div
< div
@ -684,22 +700,29 @@ const onTableIdCopy = async () => {
class = "source-context flex flex-grow items-center gap-1.75 text-gray-800 min-w-1/20 max-w-full"
class = "source-context flex flex-grow items-center gap-1.75 text-gray-800 min-w-1/20 max-w-full"
@ contextmenu = "setMenuContext('source', source)"
@ contextmenu = "setMenuContext('source', source)"
>
>
< GeneralBaseLogo class = "min-w-4 !xs:(min-w-4.25 w-4.25 text-sm)" / >
< GeneralBaseLogo
< div
class = "flex-none min-w-4 !xs:(min-w-4.25 w-4.25 text-sm) !text-gray-600 !group-hover:text-gray-800"
: data - testid = "`nc-sidebar-base-${source.alias}`"
/ >
class = "nc-sidebar-node-title flex capitalize text-ellipsis overflow-hidden select-none"
< NcTooltip
class = "nc-sidebar-node-title capitalize text-ellipsis overflow-hidden select-none"
: style = "{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
: style = "{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
: class = " {
'text-black font-semibold' : activeProjectId === base . id && baseViewOpen && ! isMobileMode ,
} "
show - on - truncate - only
>
>
{ { source . alias || '' } }
< template # title > { { source . alias || '' } } < / template >
< / div >
< span :data-testid ="`nc-sidebar-base-${source.alias}`" >
< a -tooltip class = "xs:(hidden)" >
{ { source . alias || '' } }
< / span >
< / NcTooltip >
< NcTooltip class = "xs:(hidden) flex items-center mr-1" >
< template # title > { { $t ( 'objects.externalDb' ) } } < / template >
< template # title > { { $t ( 'objects.externalDb' ) } } < / template >
< div >
< GeneralIcon icon = "info" class = "text-gray-400 -mt-0.5 hover:text-gray-700 mr-1" / >
< GeneralIcon icon = "info" class = "flex-none text-gray-400 hover:text-gray-700 mr-1" / >
< / div >
< / NcTooltip >
< / a - t o o l t i p >
< / div >
< / div >
< div class = "flex flex-row items-center gap-x-0.25 w-12.25 " >
< div class = "flex flex-row items-center gap-x-0.25" >
< NcDropdown
< NcDropdown
: visible = "isBasesOptionsOpen[source!.id!]"
: visible = "isBasesOptionsOpen[source!.id!]"
: trigger = "['click']"
: trigger = "['click']"
@ -708,7 +731,7 @@ const onTableIdCopy = async () => {
< NcButton
< NcButton
v - e = "['c:source:options']"
v - e = "['c:source:options']"
class = "nc-sidebar-node-btn"
class = "nc-sidebar-node-btn"
: class = "{ '!text-black !opacity-100': isBasesOptionsOpen[source!.id!] }"
: class = "{ '!text-black !opacity-100 !inline-block ': isBasesOptionsOpen[source!.id!] }"
type = "text"
type = "text"
size = "xxsmall"
size = "xxsmall"
@ click . stop = "isBasesOptionsOpen[source!.id!] = !isBasesOptionsOpen[source!.id!]"
@ click . stop = "isBasesOptionsOpen[source!.id!] = !isBasesOptionsOpen[source!.id!]"
@ -743,6 +766,7 @@ const onTableIdCopy = async () => {
type = "text"
type = "text"
size = "xxsmall"
size = "xxsmall"
class = "nc-sidebar-node-btn"
class = "nc-sidebar-node-btn"
: class = "{ '!opacity-100 !inline-block': isBasesOptionsOpen[source!.id!] }"
@ click . stop = "openTableCreateDialog(sourceIndex)"
@ click . stop = "openTableCreateDialog(sourceIndex)"
>
>
< GeneralIcon icon = "plus" class = "text-xl leading-5" style = "-webkit-text-stroke: 0.15px" / >
< GeneralIcon icon = "plus" class = "text-xl leading-5" style = "-webkit-text-stroke: 0.15px" / >
@ -770,7 +794,7 @@ const onTableIdCopy = async () => {
< NcMenu
< NcMenu
class = "!py-0 rounded text-sm"
class = "!py-0 rounded text-sm"
: class = " {
: class = " {
'!min-w-70 ' : contextMenuTarget . type === 'table' ,
'!min-w-62.5 ' : contextMenuTarget . type === 'table' ,
} "
} "
>
>
< template v-if ="contextMenuTarget.type === 'base' && base.type === 'database'" > < / template >
< template v-if ="contextMenuTarget.type === 'base' && base.type === 'database'" > < / template >
@ -845,7 +869,11 @@ const onTableIdCopy = async () => {
< style lang = "scss" scoped >
< style lang = "scss" scoped >
: deep ( . ant - collapse - header ) {
: deep ( . ant - collapse - header ) {
@ apply ! mx - 0 ! pl - 8.75 ! xs : ( pl - 8 ) ! pr - 0.5 ! py - 0.5 hover : bg - gray - 200 xs : ( hover : bg - gray - 50 ) ! rounded - md ;
@ apply ! mx - 0 ! pl - 8.75 h - 7.1 ! xs : ( pl - 7 h - [ 3 rem ] ) ! pr - 0.5 ! py - 0 hover : bg - gray - 200 xs : ( hover : bg - gray - 50 ) ! rounded - md ;
. ant - collapse - arrow {
@ apply ! right - 1 ! xs : ( flex - none border - 1 border - gray - 200 w - 6.5 h - 6.5 mr - 1 ) ;
}
}
}
: deep ( . ant - collapse - item ) {
: deep ( . ant - collapse - item ) {
@ -856,7 +884,13 @@ const onTableIdCopy = async () => {
@ apply ! px - 0 ! pb - 0 ! pt - 0.25 ;
@ apply ! px - 0 ! pb - 0 ! pt - 0.25 ;
}
}
: deep ( . ant - collapse - header : hover . nc - sidebar - source - node - btns ) {
: deep ( . ant - collapse - header : hover ) {
@ apply visible ;
. nc - sidebar - node - btn {
@ apply ! opacity - 100 ! inline - block ;
& : not ( . nc - sidebar - expand ) {
@ apply ! xs : hidden ;
}
}
}
}
< / style >
< / style >