Skip to content

Commit

Permalink
feat: main menu (#243)
Browse files Browse the repository at this point in the history
* refactor: split global and roadiz type utils

* docs: fix comment

* feat(VRoadizLink): more generic types

* feat(roadiz): add plaholder types

* feat: add VMainMenu

* feat: add VMainMenu

* chore: comment empty style block
  • Loading branch information
manuelodelain authored Jan 16, 2025
1 parent 74913a0 commit 960d1a0
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 29 deletions.
9 changes: 4 additions & 5 deletions components/molecules/VRoadizLink/VRoadizLink.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<script lang="ts">
import type { RoadizDocument } from '@roadiz/types'
import type { RoadizDocument, RoadizNodesSources } from '@roadiz/types'
import { h, type PropType } from 'vue'
import type { NuxtLinkProps } from '#app/components/nuxt-link'
import { NuxtLink } from '#components'
import type { ReachableItem } from '~/types/app'
export const vRoadizLinkProps = {
label: [String, Boolean],
reference: [Array, Object] as PropType<ReachableItem[] | ReachableItem>,
reference: [Array, Object] as PropType<RoadizNodesSources[] | RoadizNodesSources>,
url: String,
document: Object as PropType<RoadizDocument>,
nuxtLinkProps: Object as PropType<NuxtLinkProps>,
Expand All @@ -27,7 +26,7 @@ export default defineComponent({
const runtimeConfig = useRuntimeConfig()
const siteUrl = runtimeConfig?.public?.site.url
const attributes = computed(() => {
const attributes = computed<Record<string, unknown>>(() => {
const defaultAttrs = { ...attrs, ...props.nuxtLinkProps }
// Download
Expand All @@ -52,7 +51,7 @@ export default defineComponent({
}
// Internal link
const internalUrl = props.url && isInternalURL(props.url, siteUrl) ? props.url : reference.value && isInternalURL(reference.value.url, siteUrl) ? reference.value.url : undefined
const internalUrl = props.url && isInternalURL(props.url, siteUrl) ? props.url : reference.value?.url && isInternalURL(reference.value.url, siteUrl) ? reference.value.url : undefined
if (internalUrl) {
// Prevent NuxtLink to add rel attrs if it is absolute internal url
Expand Down
101 changes: 101 additions & 0 deletions components/organisms/VMainMenu/Default.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script setup lang="ts">
import { http, HttpResponse } from 'msw'
import type { CommonContent } from '~/types/api'
const menuIsOpen = ref(true)
useMockRequest(http.get('*/common_content', () => {
return HttpResponse.json({
menus: {
mainMenuWalker: {
'@id': '1',
'@type': 'menu',
'item': {
'@id': 'menu 1',
'@type': 'menu',
'title': 'Main menu',
},
'children': [
{
'@id': 'child-1',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSMenuLink',
'title': 'Item 1',
'linkExternalUrl': 'https://example.com',
},
'children': [],
},
{
'@id': 'child-2',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSMenuLink',
'title': 'Item 2',
'linkInternalReference': [
{
'@id': '1',
'@type': 'NSPage',
'title': 'Page 1',
'url': '/page-1',
},
],
},
'children': [],
},
{
'@id': 'child-3',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSNeutral',
'title': 'Item 3',
},
'children': [
{
'@id': 'sub-child-1',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSMenuLink',
'title': 'Sub item 1',
'linkExternalUrl': 'https://example.com',
},
'children': [],
},
{
'@id': 'sub-child-2',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSMenuLink',
'title': 'Sub item 2',
'linkInternalReference': [
{
'@id': '1',
'@type': 'NSPage',
'title': 'Page 2',
'url': '/page-2',
},
],
},
'children': [],
},
],
},
],
},
},
} satisfies CommonContent)
}))
</script>

<template>
<NuxtStory layout="fullscreen">
<ClientOnly>
<VMainMenu v-model:open="menuIsOpen" />
</ClientOnly>
</NuxtStory>
</template>
105 changes: 105 additions & 0 deletions components/organisms/VMainMenu/Toggle.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<script lang="ts" setup>
import { http, HttpResponse } from 'msw'
import type { CommonContent } from '~/types/api'
const menuIsOpen = ref(false)
useMockRequest(http.get('*/common_content', () => {
return HttpResponse.json({
menus: {
mainMenuWalker: {
'@id': '1',
'@type': 'menu',
'item': {
'@id': 'menu 1',
'@type': 'menu',
'title': 'Main menu',
},
'children': [
{
'@id': 'child-1',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSMenuLink',
'title': 'Item 1',
'linkExternalUrl': 'https://example.com',
},
'children': [],
},
{
'@id': 'child-2',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSMenuLink',
'title': 'Item 2',
'linkInternalReference': [
{
'@id': '1',
'@type': 'NSPage',
'title': 'Page 1',
'url': '/page-1',
},
],
},
'children': [],
},
{
'@id': 'child-3',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSNeutral',
'title': 'Item 3',
},
'children': [
{
'@id': 'sub-child-1',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSMenuLink',
'title': 'Sub item 1',
'linkExternalUrl': 'https://example.com',
},
'children': [],
},
{
'@id': 'sub-child-2',
'@type': 'NSMenuLink',
'item': {
'@id': '1',
'@type': 'NSMenuLink',
'title': 'Sub item 2',
'linkInternalReference': [
{
'@id': '1',
'@type': 'NSPage',
'title': 'Page 2',
'url': '/page-2',
},
],
},
'children': [],
},
],
},
],
},
},
} satisfies CommonContent)
}))
</script>

<template>
<NuxtStory layout="fullscreen">
<VButton
filled
@click="menuIsOpen = !menuIsOpen"
>
Open
</VButton>
<VMainMenu v-model:open="menuIsOpen" />
</NuxtStory>
</template>
56 changes: 56 additions & 0 deletions components/organisms/VMainMenu/VMainMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<script lang="ts" setup>
import type { NSMenuLink } from '~/types/roadiz'
const isOpen = defineModel<boolean>('open')
const { mainMenuWalker } = await useCommonContent()
const route = useRoute()
watch(
() => route.fullPath,
() => {
isOpen.value = false
},
)
</script>

<template>
<VModal
v-model="isOpen"
align="right"
>
<nav>
<ul v-if="mainMenuWalker?.children">
<li
v-for="child in mainMenuWalker.children"
:key="child.item?.['@id']"
>
<VRoadizLink
v-if="(child.item as NSMenuLink)?.linkExternalUrl || (child.item as NSMenuLink)?.linkInternalReference"
:url="(child.item as NSMenuLink)?.linkExternalUrl"
:reference="(child.item as NSMenuLink)?.linkInternalReference"
>
{{ child.item?.title }}
</VRoadizLink>
<span v-else>{{ child.item?.title }}</span>
<ul v-if="child.children">
<li
v-for="subChild in child.children"
:key="subChild.item?.['@id']"
>
<VRoadizLink
:url="(subChild.item as NSMenuLink)?.linkExternalUrl"
:reference="(subChild.item as NSMenuLink)?.linkInternalReference"
>
{{ subChild.item?.title }}
</VRoadizLink>
</li>
</ul>
</li>
</ul>
</nav>
</VModal>
</template>

<!-- <style lang="scss" module> -->
<!-- </style> -->
7 changes: 4 additions & 3 deletions types/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import type {
RoadizNodesSourcesHead,
RoadizWebResponse,
} from '@roadiz/types'
import type { MenuNodeType } from '~/types/app'
import type { RoadizWalkerKnown } from '~/utils/types'

import type { RoadizWalkerKnown } from '~/utils/roadiz/types'
import type { NSMenu, NSMenuLink } from '~/types/roadiz'

interface HydraError {
'@context': string
Expand Down Expand Up @@ -38,7 +39,7 @@ type CommonContentMenuKey = 'mainMenuWalker' | string
interface CommonContent {
home?: RoadizNodesSources
head?: RoadizNodesSourcesHead
menus?: Record<CommonContentMenuKey, RoadizWalkerKnown<NSMenu, MenuNodeType>>
menus?: Record<CommonContentMenuKey, RoadizWalkerKnown<NSMenu, NSMenuLink | NSMenu>>
errorPage?: RoadizWalkerKnown<NSPage>
}

Expand Down
8 changes: 0 additions & 8 deletions types/app.d.ts

This file was deleted.

1 change: 0 additions & 1 deletion types/json-schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { JSONSchema4 } from 'json-schema'

export interface JsonSchemaExtended extends JSONSchema4 {
widget?: string

enum_titles?: string[]
attr?: Record<string, string | boolean>
}
16 changes: 13 additions & 3 deletions types/roadiz.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
/*
* This is an automated Roadiz interface declaration file.
* This file content should be replaced entirely with new types definitions exported from Roadiz.
*
* RoadizNodesSources, RoadizDocument and other mentioned types are part of
* roadiz/abstract-api-client package which must be installed in your project.
* @roadiz/types package which must be installed in your project.
*
* @see https://github.com/roadiz/abstract-api-client
* @see https://github.com/roadiz/types#readme
*
* Roadiz CMS node-types interfaces
*
* @see https://docs.roadiz.io/en/latest/developer/nodes-system/intro.html#what-is-a-node-type
*/

import type { RoadizNodesSources } from '@roadiz/types'

export type NSMenu = RoadizNodesSources

export interface NSMenuLink extends RoadizNodesSources {
linkExternalUrl?: string
linkInternalReference?: Array<RoadizNodesSources>
}
8 changes: 8 additions & 0 deletions utils/roadiz/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ export function isBlogListingEntity(entity: JsonLdObject): boolean {
return isEntityType(entity, 'BlogPostContainer')
}

export function isMenuLinkEntity(entity: JsonLdObject): boolean {
return isEntityType(entity, 'MenuLink')
}

export function isMenuEntity(entity: JsonLdObject): boolean {
return isEntityType(entity, 'Menu')
}

// BLOCKS
export function isContentBlock(entity: JsonLdObject): boolean {
return isEntityType(entity, 'ContentBlock')
Expand Down
6 changes: 6 additions & 0 deletions utils/roadiz/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { JsonLdObject, RoadizNodesSources } from '@roadiz/types'

export interface RoadizWalkerKnown<Item = RoadizNodesSources, Child = RoadizNodesSources> extends JsonLdObject {
item: Item
children: RoadizWalkerKnown<Child>[]
}
11 changes: 2 additions & 9 deletions utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
// ROADIZ
import type { RoadizNodesSources, JsonLdObject } from '@roadiz/types'

export interface RoadizWalkerKnown<T = RoadizNodesSources, C = RoadizNodesSources> extends JsonLdObject {
item: T
children: RoadizWalkerKnown<C>[]
}

// COMMONS
export type Writeable<T> = { -readonly [P in keyof T]: T[P] }

export type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never

export type ValueOf<T> = T extends unknown[] ? T[number] : T[keyof T]

0 comments on commit 960d1a0

Please sign in to comment.