mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-09 11:17:43 +02:00
Redesign card nav header (#10107)
* Redesign card nav header Signed-off-by: Artem Savchenko <armisav@gmail.com> * Add comments Signed-off-by: Artem Savchenko <armisav@gmail.com> --------- Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
@@ -442,7 +442,9 @@ export function createModel (builder: Builder): void {
|
||||
componentProps: {
|
||||
_class: card.class.CardSpace,
|
||||
icon: view.icon.List,
|
||||
label: core.string.Spaces
|
||||
label: core.string.Spaces,
|
||||
createLabel: card.string.CreateSpace,
|
||||
createComponent: card.component.CreateSpace
|
||||
},
|
||||
position: 'top'
|
||||
}
|
||||
@@ -468,7 +470,7 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
]
|
||||
},
|
||||
navHeaderComponent: card.component.NewCardHeader
|
||||
navHeaderActions: card.component.CardHeaderButton
|
||||
},
|
||||
card.app.Card
|
||||
)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AccountRole, getCurrentAccount, hasAccountRole, Ref } from '@hcengineering/core'
|
||||
import { ButtonIcon, getCurrentLocation, IconAdd, location, Menu, navigate, showPopup } from '@hcengineering/ui'
|
||||
|
||||
import { MasterTag } from '@hcengineering/card'
|
||||
import card from '../../plugin'
|
||||
import CreateSpace from './CreateSpace.svelte'
|
||||
import CreateCardPopup from '../CreateCardPopup.svelte'
|
||||
|
||||
const me = getCurrentAccount()
|
||||
|
||||
let pressed: boolean = false
|
||||
|
||||
$: _class = $location.path[4] as Ref<MasterTag>
|
||||
|
||||
async function navigateToCard (cardId: string): Promise<void> {
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[3] = cardId
|
||||
loc.path.length = 4
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
async function handleCreateCard (): Promise<void> {
|
||||
showPopup(CreateCardPopup, { type: _class }, 'center', async (result) => {
|
||||
if (result != null && result !== '') {
|
||||
await navigateToCard(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function newTeamspace (): Promise<void> {
|
||||
showPopup(CreateSpace, {}, 'top')
|
||||
}
|
||||
|
||||
const globalActions = hasAccountRole(me, AccountRole.User)
|
||||
? [
|
||||
{
|
||||
label: card.string.CreateCard,
|
||||
icon: IconAdd,
|
||||
action: handleCreateCard
|
||||
},
|
||||
{
|
||||
label: card.string.CreateSpace,
|
||||
icon: IconAdd,
|
||||
action: newTeamspace
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
label: card.string.CreateCard,
|
||||
icon: IconAdd,
|
||||
action: handleCreateCard
|
||||
}
|
||||
]
|
||||
|
||||
function addButtonClicked (ev: MouseEvent): void {
|
||||
pressed = true
|
||||
showPopup(Menu, { actions: globalActions }, ev.target as HTMLElement, () => {
|
||||
pressed = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<ButtonIcon icon={IconAdd} hasMenu {pressed} kind={'primary'} size={'small'} on:click={addButtonClicked} />
|
||||
@@ -1,130 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AccountRole, getCurrentAccount, hasAccountRole, Ref, Space } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import {
|
||||
Button,
|
||||
ButtonWithDropdown,
|
||||
getCurrentLocation,
|
||||
IconAdd,
|
||||
IconDropdown,
|
||||
Loading,
|
||||
location,
|
||||
navigate,
|
||||
SelectPopupValueType,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
|
||||
import { MasterTag } from '@hcengineering/card'
|
||||
import card from '../../plugin'
|
||||
import CreateSpace from './CreateSpace.svelte'
|
||||
import { createCard } from '../../utils'
|
||||
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
|
||||
const query = createQuery()
|
||||
const me = getCurrentAccount()
|
||||
|
||||
let loading = true
|
||||
let hasSpace = false
|
||||
query.query(
|
||||
card.class.CardSpace,
|
||||
{ archived: false, members: me.uuid },
|
||||
(res) => {
|
||||
hasSpace = res.length > 0
|
||||
loading = false
|
||||
},
|
||||
{ limit: 1, projection: { _id: 1 } }
|
||||
)
|
||||
|
||||
$: _class = $location.path[4] as Ref<MasterTag>
|
||||
|
||||
async function handleCreateCard (): Promise<void> {
|
||||
if (_class === undefined || currentSpace === undefined) return
|
||||
const _id = await createCard(_class, currentSpace)
|
||||
const loc = getCurrentLocation()
|
||||
|
||||
loc.path[3] = _id
|
||||
loc.path.length = 4
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
async function newTeamspace (): Promise<void> {
|
||||
showPopup(CreateSpace, {}, 'top')
|
||||
}
|
||||
|
||||
async function dropdownItemSelected (res?: SelectPopupValueType['id']): Promise<void> {
|
||||
if (res === 'card') {
|
||||
await handleCreateCard()
|
||||
} else if (res === 'space') {
|
||||
await newTeamspace()
|
||||
}
|
||||
}
|
||||
|
||||
const dropdownItems = hasAccountRole(me, AccountRole.User)
|
||||
? [
|
||||
{ id: 'card', label: card.string.CreateCard },
|
||||
{ id: 'space', label: card.string.CreateSpace }
|
||||
]
|
||||
: [{ id: 'card', label: card.string.CreateCard }]
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
<Loading shrink />
|
||||
{:else if hasAccountRole(getCurrentAccount(), AccountRole.User) || hasSpace}
|
||||
<div class="antiNav-subheader">
|
||||
{#if hasAccountRole(getCurrentAccount(), AccountRole.User)}
|
||||
{#if hasSpace && currentSpace !== undefined && _class !== undefined}
|
||||
<ButtonWithDropdown
|
||||
icon={IconAdd}
|
||||
justify={'left'}
|
||||
kind={'primary'}
|
||||
label={card.string.CreateCard}
|
||||
on:click={handleCreateCard}
|
||||
mainButtonId={'new-document'}
|
||||
dropdownIcon={IconDropdown}
|
||||
{dropdownItems}
|
||||
on:dropdown-selected={(ev) => {
|
||||
void dropdownItemSelected(ev.detail)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
id={'new-teamspace'}
|
||||
icon={IconAdd}
|
||||
label={card.string.CreateSpace}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
kind={'primary'}
|
||||
gap={'large'}
|
||||
on:click={newTeamspace}
|
||||
/>
|
||||
{/if}
|
||||
{:else if hasSpace}
|
||||
<Button
|
||||
id={'new-document'}
|
||||
icon={IconAdd}
|
||||
label={card.string.CreateCard}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
kind={'primary'}
|
||||
gap={'large'}
|
||||
disabled={currentSpace === undefined || _class === undefined}
|
||||
on:click={handleCreateCard}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -51,13 +51,14 @@ import CardRefPresenter from './components/CardRefPresenter.svelte'
|
||||
import ChangeType from './components/ChangeType.svelte'
|
||||
import CreateCardButton from './components/CreateCardButton.svelte'
|
||||
import CardArrayEditor from './components/CardArrayEditor.svelte'
|
||||
import NewCardHeader from './components/navigator/NewCardHeader.svelte'
|
||||
import SpacePresenter from './components/navigator/SpacePresenter.svelte'
|
||||
import TypesNavigator from './components/navigator/TypesNavigator.svelte'
|
||||
import LabelsPresenter from './components/LabelsPresenter.svelte'
|
||||
import RolesSection from './components/settings/RolesSection.svelte'
|
||||
import EditRole from './components/settings/EditRole.svelte'
|
||||
import CardWidget from './components/CardWidget.svelte'
|
||||
import CreateSpace from './components/navigator/CreateSpace.svelte'
|
||||
import CardHeaderButton from './components/navigator/CardHeaderButton.svelte'
|
||||
|
||||
// Card Sections
|
||||
import AttachmentsCardSection from './components/sections/AttachmentsSection.svelte'
|
||||
@@ -110,7 +111,6 @@ export default async (): Promise<Resources> => ({
|
||||
ChangeType,
|
||||
CreateCardButton,
|
||||
CardArrayEditor,
|
||||
NewCardHeader,
|
||||
SpacePresenter,
|
||||
TypesNavigator,
|
||||
LabelsPresenter,
|
||||
@@ -122,7 +122,9 @@ export default async (): Promise<Resources> => ({
|
||||
CardTagColored,
|
||||
CardTagsColored,
|
||||
CardIcon,
|
||||
CardFeedView
|
||||
CardFeedView,
|
||||
CreateSpace,
|
||||
CardHeaderButton
|
||||
},
|
||||
sectionComponent: {
|
||||
AttachmentsSection: AttachmentsCardSection,
|
||||
|
||||
@@ -44,14 +44,15 @@ export default mergeIds(cardId, card, {
|
||||
CardRefPresenter: '' as AnyComponent,
|
||||
ChangeType: '' as AnyComponent,
|
||||
CreateCardButton: '' as AnyComponent,
|
||||
NewCardHeader: '' as AnyComponent,
|
||||
CreateSpace: '' as AnyComponent,
|
||||
SpacePresenter: '' as AnyComponent,
|
||||
TypesNavigator: '' as AnyComponent,
|
||||
RolesSection: '' as AnyComponent,
|
||||
EditRole: '' as AnyComponent,
|
||||
CardWidget: '' as AnyComponent,
|
||||
CardWidgetTab: '' as AnyComponent,
|
||||
CreateCard: '' as AnyComponent
|
||||
CreateCard: '' as AnyComponent,
|
||||
CardHeaderButton: '' as AnyComponent
|
||||
},
|
||||
function: {
|
||||
CardFactory: '' as Resource<(props?: Record<string, any>) => Promise<Ref<Doc> | undefined>>
|
||||
|
||||
@@ -978,7 +978,18 @@
|
||||
>
|
||||
<div class="antiPanel-wrap__content hulyNavPanel-container">
|
||||
{#if currentApplication}
|
||||
<NavHeader label={currentApplication.label} />
|
||||
<NavHeader label={currentApplication.label}>
|
||||
{#if currentApplication.navHeaderActions != null}
|
||||
<Component
|
||||
is={currentApplication.navHeaderActions}
|
||||
props={{
|
||||
currentSpace,
|
||||
currentSpecial,
|
||||
currentFragment
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</NavHeader>
|
||||
{#if currentApplication.navHeaderComponent}
|
||||
<Component
|
||||
is={currentApplication.navHeaderComponent}
|
||||
|
||||
@@ -50,7 +50,10 @@ export interface Application extends Doc {
|
||||
// Component will be displayed in case navigator model is not defined, or nothing is selected in navigator model
|
||||
component?: AnyComponent
|
||||
|
||||
// Component to display below the navigator header
|
||||
navHeaderComponent?: AnyComponent
|
||||
// Component to display in the actions area of the navigator header
|
||||
navHeaderActions?: AnyComponent
|
||||
accessLevel?: AccountRole
|
||||
navFooterComponent?: AnyComponent
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user