-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add new LSB navigation components and router item state managem…
…ent (#5104) * feat: add new LSB navigation components and router item state management Signed-off-by: Wanjin Noh <[email protected]> * build(pinia): update pinia to version 2.2.7 Signed-off-by: Wanjin Noh <[email protected]> --------- Signed-off-by: Wanjin Noh <[email protected]>
- Loading branch information
Showing
9 changed files
with
369 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
apps/web/src/common/modules/navigations/new-lsb/LSBContainer.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<template> | ||
<aside> | ||
<div class="pt-4 pb-8 px-2"> | ||
<slot /> | ||
</div> | ||
</aside> | ||
</template> | ||
|
9 changes: 9 additions & 0 deletions
9
apps/web/src/common/modules/navigations/new-lsb/LSBDivider.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<script setup lang="ts"> | ||
import { PDivider } from '@cloudforet/mirinae'; | ||
</script> | ||
|
||
<template> | ||
<div class="pt-1 pb-3 px-2 my-2"> | ||
<p-divider /> | ||
</div> | ||
</template> |
83 changes: 83 additions & 0 deletions
83
apps/web/src/common/modules/navigations/new-lsb/LSBRouterButton.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<script setup lang="ts"> | ||
import type { TranslateResult } from 'vue-i18n'; | ||
import type { Location } from 'vue-router'; | ||
import { PLazyImg, PI } from '@cloudforet/mirinae'; | ||
import { useLsbRouterItemState } from '@/common/modules/navigations/new-lsb/composables/use-lsb-router-item-state'; | ||
import type { LSBIcon } from '@/common/modules/navigations/new-lsb/type'; | ||
const props = defineProps<{ | ||
label?: TranslateResult; | ||
icon?: LSBIcon; | ||
imgIcon?: string; | ||
to?: Location; | ||
currentPath?: string; | ||
openNewTab?: boolean; | ||
}>(); | ||
const { | ||
isSelected, | ||
iconName, | ||
iconColor, | ||
imgIconUrl, | ||
} = useLsbRouterItemState(props); | ||
</script> | ||
|
||
<template> | ||
<router-link ref="itemEl" | ||
class="lsb-router-button inline-flex items-center w-full h-8 justify-between px-2 border border-transparent outline-none rounded-md text-gray-800" | ||
:class="[{'selected': isSelected}]" | ||
:target="props.openNewTab ? '_blank' : '_self'" | ||
:to="props.to ? props.to : {}" | ||
@click.native="$event.stopImmediatePropagation()" | ||
> | ||
<slot name="before-text" | ||
v-bind="props" | ||
/> | ||
<div ref="textEl" | ||
class="inline-flex items-center gap-1 overflow-hidden whitespace-no-wrap" | ||
> | ||
<p-i v-if="props.icon" | ||
:name="iconName" | ||
:color="iconColor" | ||
width="1rem" | ||
height="1rem" | ||
class="flex-shrink-0" | ||
/> | ||
<p-lazy-img | ||
v-if="props.imgIcon" | ||
:src="imgIconUrl" | ||
width="1rem" | ||
height="1rem" | ||
class="flex-shrink-0" | ||
/> | ||
<span class="text-label-md"> | ||
<slot> | ||
{{ props.label }} | ||
</slot> | ||
</span> | ||
<slot name="after-text" | ||
v-bind="props" | ||
/> | ||
</div> | ||
<slot name="right-extra" | ||
v-bind="props" | ||
/> | ||
</router-link> | ||
</template> | ||
|
||
<style scoped lang="postcss"> | ||
.lsb-router-button { | ||
&:focus, &:focus-within, &:active { | ||
@apply bg-white border-secondary1; | ||
box-shadow: 0 0 0 2px rgba(theme('colors.secondary1'), 0.2); | ||
} | ||
&.selected { | ||
@apply bg-blue-200; | ||
} | ||
&:hover { | ||
@apply bg-blue-100 cursor-pointer; | ||
} | ||
} | ||
</style> |
151 changes: 151 additions & 0 deletions
151
apps/web/src/common/modules/navigations/new-lsb/LSBRouterItem.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
<script setup lang="ts"> | ||
import { useElementSize } from '@vueuse/core'; | ||
import type { Ref } from 'vue'; | ||
import { | ||
computed, reactive, ref, | ||
} from 'vue'; | ||
import type { TranslateResult } from 'vue-i18n'; | ||
import type { Location } from 'vue-router'; | ||
import { PI, PLazyImg, PTooltip } from '@cloudforet/mirinae'; | ||
import type { MenuId } from '@/lib/menu/config'; | ||
import BetaMark from '@/common/components/marks/BetaMark.vue'; | ||
import NewMark from '@/common/components/marks/NewMark.vue'; | ||
import UpdateMark from '@/common/components/marks/UpdateMark.vue'; | ||
import FavoriteButton from '@/common/modules/favorites/favorite-button/FavoriteButton.vue'; | ||
import { FAVORITE_TYPE, type FavoriteOptions } from '@/common/modules/favorites/favorite-button/type'; | ||
import { useLsbRouterItemState } from '@/common/modules/navigations/new-lsb/composables/use-lsb-router-item-state'; | ||
import type { HighlightTagType, LSBIcon } from '@/common/modules/navigations/new-lsb/type'; | ||
interface Props { | ||
id: string; | ||
label?: TranslateResult; | ||
icon?: LSBIcon; | ||
imgIcon?: string; | ||
to?: Location; | ||
currentPath?: string; | ||
openNewTab?: boolean; | ||
highlightTag?: HighlightTagType; | ||
favoriteOptions?: FavoriteOptions; | ||
hideFavorite?: boolean; | ||
isAdminMode?: boolean; | ||
index?: number | string; | ||
} | ||
const props = defineProps<Props>(); | ||
const itemEl = ref<HTMLElement | null>(null); | ||
const textEl = ref<HTMLElement | null>(null); | ||
const state = reactive({ | ||
itemWidth: computed<Ref<number>>(() => useElementSize(itemEl.value).width), | ||
textWidth: computed<Ref<number>>(() => useElementSize(textEl.value).width), | ||
hoveredItem: '' as MenuId | string, | ||
isEllipsis: computed<boolean>(() => state.hoveredItem === props.id && (state.itemWidth.value - 20 === state.textWidth.value)), | ||
}); | ||
const getIsHovered = (itemId?: MenuId | string) => state.hoveredItem && state.hoveredItem === itemId; | ||
const { | ||
isSelected, | ||
iconName, | ||
iconColor, | ||
imgIconUrl, | ||
} = useLsbRouterItemState(props); | ||
</script> | ||
|
||
<template> | ||
<router-link ref="itemEl" | ||
class="lsb-router-item inline-flex items-center w-full h-8 justify-between px-2 border border-transparent outline-none rounded-md text-label-md text-gray-800" | ||
:class="[{'selected': isSelected, 'is-hide-favorite': props.hideFavorite}]" | ||
:target="props.openNewTab ? '_blank' : '_self'" | ||
:to="props.to ? props.to : {}" | ||
@click.native="$event.stopImmediatePropagation()" | ||
@mouseenter.native="state.hoveredItem = props.id" | ||
@mouseleave.native="state.hoveredItem = ''" | ||
> | ||
<slot name="before-text" | ||
v-bind="props" | ||
/> | ||
<div ref="textEl" | ||
class="inline-flex items-center overflow-hidden whitespace-no-wrap" | ||
> | ||
<p-i v-if="props.icon" | ||
:name="iconName" | ||
:color="iconColor" | ||
width="1rem" | ||
height="1rem" | ||
class="flex-shrink-0 ml-1" | ||
/> | ||
<p-lazy-img | ||
v-if="props.imgIcon" | ||
:src="imgIconUrl" | ||
width="1rem" | ||
height="1rem" | ||
class="flex-shrink-0 ml-1" | ||
/> | ||
<slot> | ||
<div class="item-text overflow-hidden whitespace-no-wrap"> | ||
<p-tooltip v-if="state.isEllipsis" | ||
position="bottom-start" | ||
:contents="props.label" | ||
> | ||
{{ props.label }} | ||
</p-tooltip> | ||
<span v-else>{{ props.label }}</span> | ||
</div> | ||
</slot> | ||
<slot name="after-text" | ||
v-bind="props" | ||
/> | ||
<span class="mark-wrapper h-full"> | ||
<new-mark v-if="props.highlightTag === 'new'" /> | ||
<update-mark v-else-if="props.highlightTag === 'update'" /> | ||
<beta-mark v-else-if="props.highlightTag === 'beta'" /> | ||
</span> | ||
</div> | ||
<slot name="right-extra" | ||
v-bind="props" | ||
/> | ||
<favorite-button | ||
v-if="!hideFavorite && !isAdminMode" | ||
:item-id="props.favoriteOptions?.id ? props.favoriteOptions.id : props.id" | ||
:favorite-type="props.favoriteOptions?.type ? props.favoriteOptions.type : FAVORITE_TYPE.MENU" | ||
:visible-active-case-only="!getIsHovered(props.id)" | ||
scale="0.8" | ||
class="flex-shrink-0 ml-1" | ||
/> | ||
</router-link> | ||
</template> | ||
|
||
<style lang="postcss" scoped> | ||
.lsb-router-item { | ||
&:focus, &:focus-within, &:active { | ||
@apply bg-white border-secondary1; | ||
box-shadow: 0 0 0 2px rgba(theme('colors.secondary1'), 0.2); | ||
} | ||
&.selected { | ||
@apply bg-blue-200; | ||
} | ||
&.is-hide-favorite { | ||
.favorite-button { | ||
@apply hidden; | ||
} | ||
&:hover { | ||
.favorite-button { | ||
@apply block; | ||
} | ||
} | ||
} | ||
&:hover { | ||
@apply bg-blue-100 cursor-pointer; | ||
} | ||
.item-text { | ||
text-overflow: ellipsis; | ||
} | ||
.mark-wrapper { | ||
margin-top: -0.25rem; | ||
} | ||
} | ||
</style> |
38 changes: 38 additions & 0 deletions
38
apps/web/src/common/modules/navigations/new-lsb/LSBTopTitle.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<script setup lang="ts"> | ||
import type { Location } from 'vue-router'; | ||
import { | ||
PLazyImg, | ||
} from '@cloudforet/mirinae'; | ||
import { assetUrlConverter } from '@/lib/helper/asset-helper'; | ||
const props = defineProps<{ | ||
label?: string; | ||
icon?: string; | ||
subText?: string; | ||
visibleAddButton?: boolean; | ||
addButtonLink?: Location; | ||
}>(); | ||
</script> | ||
|
||
<template> | ||
<div class=" flex items-center gap-1 h-8 mt-1 pb-2 px-2 text-gray-800 font-bold text-label-md"> | ||
<div class="flex items-center h-6"> | ||
<p-lazy-img | ||
v-if="props.icon" | ||
:src="assetUrlConverter(props.icon)" | ||
width="1.5rem" | ||
height="1.5rem" | ||
class="icon rounded flex-shrink-0" | ||
/> | ||
<span> | ||
<slot>{{ props.label }}</slot> | ||
<span v-if="props.subText" | ||
class="font-normal" | ||
>{{ props.subText }}</span> | ||
</span> | ||
</div> | ||
</div> | ||
</template> |
50 changes: 50 additions & 0 deletions
50
apps/web/src/common/modules/navigations/new-lsb/composables/use-lsb-router-item-state.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import type { UnwrapRef, Ref } from 'vue'; | ||
import { computed } from 'vue'; | ||
import type { Location } from 'vue-router'; | ||
import { useRouter } from 'vue-router/composables'; | ||
|
||
import { assetUrlConverter } from '@/lib/helper/asset-helper'; | ||
|
||
import type { LSBIcon } from '@/common/modules/navigations/new-lsb/type'; | ||
|
||
interface UseLsbRouterItemProps { | ||
to?: Ref<Readonly<Location>>; | ||
currentPath?: Ref<Readonly<string>>; | ||
icon?: Ref<Readonly<LSBIcon>>; | ||
imgIcon?: Ref<Readonly<string>>; | ||
} | ||
export const useLsbRouterItemState = (props: UnwrapRef<UseLsbRouterItemProps>) => { | ||
const router = useRouter(); | ||
|
||
const isSelected = computed<boolean>(() => { | ||
let currentPath = props.currentPath; | ||
if (!currentPath || !props.to) return false; | ||
|
||
const resolved = router.resolve(props.to); | ||
if (!resolved) return false; | ||
|
||
if (currentPath.indexOf('?') > 0) { | ||
currentPath = currentPath.slice(0, currentPath.indexOf('?')); | ||
} | ||
const resolvedHref = resolved.href; | ||
return currentPath === resolvedHref; | ||
}); | ||
const iconName = computed<string>(() => { | ||
if (!props.icon) return ''; | ||
if (typeof props.icon === 'string') return props.icon; | ||
return props.icon.name; | ||
}); | ||
const iconColor = computed<string>(() => { | ||
if (!props.icon) return 'inherit'; | ||
if (typeof props.icon === 'string') return 'inherit'; | ||
return props.icon.color || 'inherit'; | ||
}); | ||
const imgIconUrl = computed<string>(() => (props.imgIcon ? assetUrlConverter(props.imgIcon) : '')); | ||
|
||
return { | ||
isSelected, | ||
iconName, | ||
iconColor, | ||
imgIconUrl, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export type LSBIcon = string | { name: string; color?: string; }; | ||
export type HighlightTagType = 'new' | 'beta' | 'update'; |
Oops, something went wrong.