From ac5a60f8405f53f0719f49dfeb263e384d23939b Mon Sep 17 00:00:00 2001 From: brunoschadeck Date: Wed, 17 Apr 2024 10:48:48 +0200 Subject: [PATCH 01/42] feat: adds safe triangles for navigation item --- .../src/components/navigation-item/model.ts | 2 + .../navigation-item/navigation-item.lite.tsx | 105 ++++++++++++++++++ .../navigation-item/navigation-item.scss | 26 +++-- .../src/utils/navigation-item.tsx | 64 ++++++++++- 4 files changed, 187 insertions(+), 10 deletions(-) diff --git a/packages/components/src/components/navigation-item/model.ts b/packages/components/src/components/navigation-item/model.ts index 1a83710a600..c8943e76a23 100644 --- a/packages/components/src/components/navigation-item/model.ts +++ b/packages/components/src/components/navigation-item/model.ts @@ -56,6 +56,8 @@ export interface DBNavigationItemDefaultState { * Internal state property to show/hide sub-navigation button */ hasSubNavigation?: boolean; + startMouseFollow: () => void; + refOnMouseMove?: (event: MouseEvent) => void; } export type DBNavigationItemState = DBNavigationItemDefaultState & diff --git a/packages/components/src/components/navigation-item/navigation-item.lite.tsx b/packages/components/src/components/navigation-item/navigation-item.lite.tsx index c8b56c182cc..e1125aed48e 100644 --- a/packages/components/src/components/navigation-item/navigation-item.lite.tsx +++ b/packages/components/src/components/navigation-item/navigation-item.lite.tsx @@ -1,5 +1,6 @@ import { onMount, + onUnMount, onUpdate, Show, Slot, @@ -12,6 +13,7 @@ import { DBButton } from '../button'; import { cls, uuid, visibleInVX, visibleInVY } from '../../utils'; import { DEFAULT_BACK } from '../../shared/constants'; import { ClickEvent } from '../../shared/model'; +import { windows } from 'rimraf'; useMetadata({ isAttachedToShadowDom: true @@ -27,6 +29,7 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { hasSubNavigation: true, isSubNavigationExpanded: false, subNavigationId: 'sub-navigation-' + uuid(), + refOnMouseMove: undefined, handleClick: (event: ClickEvent) => { if (props.onClick) { props.onClick(event); @@ -39,11 +42,113 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { handleBackClick: (event: ClickEvent) => { event.stopPropagation(); state.isSubNavigationExpanded = false; + }, + startMouseFollow: () => { + const subMenu = Array.from(ref.children).find((child) => + child.classList.contains('db-sub-navigation') + ); + + const subMenuElement = subMenu + ? (subMenu as HTMLElement) + : undefined; + + if (!document || !ref.parentElement || !subMenuElement) { + return; + } + + const itemRect = ref.getBoundingClientRect(); + const subMenuHeight = subMenuElement.getBoundingClientRect().height; + const parentElementWidth = + ref.parentElement.getBoundingClientRect().width; + const padding = (parentElementWidth - itemRect.width) / 2; + + // the triangle has the width of the sub-navigation, current nav-item can be wider. + // so the width of the triangle must be adapted to the possibly wider nav-item. + ref.style.setProperty( + '--db-navigation-item-inline-size', + `${parentElementWidth}px` + ); + + const getTriangleTipX = (mouseX: number): number => { + if (subMenuElement.getAttribute('data-outside-vx')) { + // vertical flipped triangle needs an inverted x pos + return itemRect.width - mouseX; + } + + // triangle stops shrinking from 75% x pos + return Math.min(mouseX, itemRect.width * 0.75); + }; + + const getTriangleTipY = (mouseY: number): number => { + // padding must be added to the y pos of the tip so that the y pos matches the cursor + const mouseYLimited = + Math.max(Math.min(mouseY, itemRect.height), 0) + padding; + + if (subMenuElement.getAttribute('data-outside-vy')) { + // add offset to tip y pos to match corrected sub-navigation y pos + return ( + mouseYLimited + + (subMenuHeight - padding * 2 - itemRect.height) + ); + } + + return mouseYLimited; + }; + + const onMouseMove = (event: MouseEvent) => { + const mouseX = event.clientX - itemRect.left; + const mouseY = event.clientY - itemRect.top; + + const tipX = getTriangleTipX(mouseX); + const tipY = getTriangleTipY(mouseY); + + const tipUpperPos = `${tipX}px ${tipY + padding}px`; + const tipLowerPos = `${tipX}px ${tipY - padding}px`; + + ref.style.setProperty( + '--db-navigation-item-clip-path', + `polygon(${tipUpperPos}, ${tipLowerPos}, 100% 0, 100% 100%)` + ); + }; + + ref.addEventListener('mouseover', () => { + document.addEventListener('mousemove', onMouseMove); + }); + + ref.addEventListener('mouseout', () => { + document.removeEventListener('mousemove', onMouseMove); + }); } }); + onUpdate(() => { + if (typeof state.refOnMouseMove === 'function') { + console.log('ON UPDATE'); + // ref.addEventListener('mouseover', () => { + // document.addEventListener('mousemove', onMouseMove); + // }); + // + // ref.addEventListener('mouseout', () => { + // document.removeEventListener('mousemove', onMouseMove); + // }); + } + }, [state.refOnMouseMove]); + onMount(() => { state.initialized = true; + + // mouse follow for safe triangle should not be added to root nav items + if (ref.parentElement.classList.contains('db-sub-navigation')) { + setTimeout(state.startMouseFollow, 1000); + } + }); + + onUnMount(() => { + console.log('ON UNMOUNT', state.refOnMouseMove); + + if (document && state.refOnMouseMove) { + // document.removeEventListener('mousemove', state.refOnMouseMove); + } }); onUpdate(() => { diff --git a/packages/components/src/components/navigation-item/navigation-item.scss b/packages/components/src/components/navigation-item/navigation-item.scss index e0ffba70f3e..8939fc2a7f3 100644 --- a/packages/components/src/components/navigation-item/navigation-item.scss +++ b/packages/components/src/components/navigation-item/navigation-item.scss @@ -240,19 +240,31 @@ &[data-outside-vy="true"] { transform: translate(-100%, -100%); } + + &::before { + inset-block-start: auto; + inset-block-end: 0; + transform: translateX(100%) scaleX(-1); + } } &::before { content: ""; position: absolute; - padding: variables.$db-spacing-fixed-xs; - // #{$db-spacing-fixed-3xs} for box shadow - inset-block-start: #{variables.$db-spacing-fixed-3xs}; - inset-inline-start: calc( - -1 * #{variables.$db-spacing-fixed-xs} - ); + inset-block-start: 0; + inset-inline-start: 0; block-size: 100%; - inline-size: variables.$db-spacing-fixed-xs; + inline-size: var(--db-navigation-item-inline-size, 100%); + background: color-mix( + in hsl, + transparent 40%, + #{colors.$db-brand-0} + ); + transform: translateX(-100%); + clip-path: var( + --db-navigation-item-clip-path, + polygon(10% 30px, 100% 0, 100% 100%) + ); } &::after { diff --git a/showcases/react-showcase/src/utils/navigation-item.tsx b/showcases/react-showcase/src/utils/navigation-item.tsx index 7b356f8125a..256d9670ab8 100644 --- a/showcases/react-showcase/src/utils/navigation-item.tsx +++ b/showcases/react-showcase/src/utils/navigation-item.tsx @@ -116,17 +116,75 @@ export const NAVIGATION_ITEMS: NavigationItem[] = [ path: '03', label: '03 Data-Input', subNavigation: getSortedNavigationItems([ - { path: 'input', label: 'Input', component: }, + { + path: 'input', + label: 'Input', + subNavigation: getSortedNavigationItems([ + { + path: 'link', + label: 'Link', + component: + }, + { + path: 'button', + label: 'Button', + component: + } + ]) + }, { path: 'textarea', label: 'Textarea', - component: + subNavigation: getSortedNavigationItems([ + { + path: 'link', + label: 'Link', + component: + }, + { + path: 'button', + label: 'Button', + component: + } + ]) }, { path: 'radio', label: 'Radio', component: }, { path: 'checkbox', label: 'Checkbox', - component: + subNavigation: getSortedNavigationItems([ + { + path: 'link', + label: 'Link', + component: + }, + { + path: 'button', + label: 'Button', + component: + }, + { + path: 'link', + label: 'Link', + component: + }, + { + path: 'button', + label: 'Button', + subNavigation: getSortedNavigationItems([ + { + path: 'link', + label: 'Link', + component: + }, + { + path: 'button', + label: 'Button', + component: + } + ]) + } + ]) }, { path: 'select', label: 'Select', component: } ]) From d56da33160c63343c3f011bff080d6ab46ea506d Mon Sep 17 00:00:00 2001 From: brunoschadeck Date: Thu, 18 Apr 2024 09:26:16 +0200 Subject: [PATCH 02/42] feat: adds visibility handling on hover --- .../src/components/navigation-item/model.ts | 17 +- .../navigation-item/navigation-item.lite.tsx | 211 ++++++++++-------- .../navigation-item/navigation-item.scss | 7 +- 3 files changed, 143 insertions(+), 92 deletions(-) diff --git a/packages/components/src/components/navigation-item/model.ts b/packages/components/src/components/navigation-item/model.ts index c8943e76a23..7dc7e2ca007 100644 --- a/packages/components/src/components/navigation-item/model.ts +++ b/packages/components/src/components/navigation-item/model.ts @@ -56,8 +56,21 @@ export interface DBNavigationItemDefaultState { * Internal state property to show/hide sub-navigation button */ hasSubNavigation?: boolean; - startMouseFollow: () => void; - refOnMouseMove?: (event: MouseEvent) => void; + subNavigation?: HTMLMenuElement; + tryInitSubNavigationHandling: () => void; + addMouseListeners: () => void; + onMouseMove: (event: MouseEvent) => void; + tryUpdateSubNavigationOffset: () => void; + updateSubNavigationState: () => void; + // cacheSafeTriangleData: () => void; + triangleData?: { + itemRect: DOMRect; + parentElementWidth: number; + subNavigationHeight: number; + padding: number; + }; + outsideVX: null | string; + outsideVY: null | string; } export type DBNavigationItemState = DBNavigationItemDefaultState & diff --git a/packages/components/src/components/navigation-item/navigation-item.lite.tsx b/packages/components/src/components/navigation-item/navigation-item.lite.tsx index e1125aed48e..7e59c4c0d6f 100644 --- a/packages/components/src/components/navigation-item/navigation-item.lite.tsx +++ b/packages/components/src/components/navigation-item/navigation-item.lite.tsx @@ -10,10 +10,9 @@ import { } from '@builder.io/mitosis'; import { DBNavigationItemProps, DBNavigationItemState } from './model'; import { DBButton } from '../button'; -import { cls, uuid, visibleInVX, visibleInVY } from '../../utils'; +import { cls, handleDataOutside, uuid } from '../../utils'; import { DEFAULT_BACK } from '../../shared/constants'; import { ClickEvent } from '../../shared/model'; -import { windows } from 'rimraf'; useMetadata({ isAttachedToShadowDom: true @@ -28,8 +27,12 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { hasAreaPopup: false, hasSubNavigation: true, isSubNavigationExpanded: false, + subNavigation: undefined, subNavigationId: 'sub-navigation-' + uuid(), - refOnMouseMove: undefined, + triangleData: undefined, + outsideVX: null, + outsideVY: null, + handleClick: (event: ClickEvent) => { if (props.onClick) { props.onClick(event); @@ -39,28 +42,41 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { state.isSubNavigationExpanded = true; } }, + handleBackClick: (event: ClickEvent) => { event.stopPropagation(); state.isSubNavigationExpanded = false; }, - startMouseFollow: () => { - const subMenu = Array.from(ref.children).find((child) => - child.classList.contains('db-sub-navigation') - ); - const subMenuElement = subMenu - ? (subMenu as HTMLElement) - : undefined; + tryUpdateSubNavigationOffset: () => { + if (state.hasSubNavigation && state.subNavigation) { + handleDataOutside(state.subNavigation); + console.log( + 'TEST', + state.subNavigation.getAttribute('data-outside-vx') + ); + state.outsideVX = + state.subNavigation.getAttribute('data-outside-vx'); + state.outsideVY = + state.subNavigation.getAttribute('data-outside-vy'); + } + }, - if (!document || !ref.parentElement || !subMenuElement) { + tryInitSubNavigationHandling: () => { + if ( + !ref || + !ref.parentElement || + !state.hasSubNavigation || + !state.hasAreaPopup || + !ref.parentElement.classList.contains('db-sub-navigation') || + ref.closest('.db-drawer') + ) { return; } const itemRect = ref.getBoundingClientRect(); - const subMenuHeight = subMenuElement.getBoundingClientRect().height; const parentElementWidth = ref.parentElement.getBoundingClientRect().width; - const padding = (parentElementWidth - itemRect.width) / 2; // the triangle has the width of the sub-navigation, current nav-item can be wider. // so the width of the triangle must be adapted to the possibly wider nav-item. @@ -69,87 +85,136 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { `${parentElementWidth}px` ); + state.triangleData = { + itemRect, + parentElementWidth, + subNavigationHeight: + state.subNavigation.getBoundingClientRect().height, + padding: (parentElementWidth - itemRect.width) / 2 + }; + }, + + addMouseListeners: () => { + ref.addEventListener('mouseenter', () => { + state.tryUpdateSubNavigationOffset(); + + document.removeEventListener('mousemove', state.onMouseMove); + document.addEventListener('mousemove', state.onMouseMove); + }); + + ref.addEventListener('mouseleave', () => { + // state.triangleData = null; + document.removeEventListener('mousemove', state.onMouseMove); + }); + }, + + onMouseMove: (event: MouseEvent) => { + if (!state.triangleData || !state.subNavigation) { + return; + } + const getTriangleTipX = (mouseX: number): number => { - if (subMenuElement.getAttribute('data-outside-vx')) { + if ( + state.subNavigation.getAttribute('data-outside-vx') === + 'right' + ) { // vertical flipped triangle needs an inverted x pos - return itemRect.width - mouseX; + return state.triangleData.itemRect.width - mouseX; } + console.log(mouseX, state.triangleData.itemRect.width); + // triangle stops shrinking from 75% x pos - return Math.min(mouseX, itemRect.width * 0.75); + return Math.min( + mouseX, + state.triangleData.itemRect.width * 0.75 + ); }; const getTriangleTipY = (mouseY: number): number => { // padding must be added to the y pos of the tip so that the y pos matches the cursor const mouseYLimited = - Math.max(Math.min(mouseY, itemRect.height), 0) + padding; + Math.max( + Math.min(mouseY, state.triangleData.itemRect.height), + 0 + ) + state.triangleData.padding; - if (subMenuElement.getAttribute('data-outside-vy')) { + if ( + state.subNavigation.getAttribute('data-outside-vy') === + 'bottom' + ) { // add offset to tip y pos to match corrected sub-navigation y pos return ( mouseYLimited + - (subMenuHeight - padding * 2 - itemRect.height) + (state.triangleData.subNavigationHeight - + state.triangleData.padding * 2 - + state.triangleData.itemRect.height) ); } return mouseYLimited; }; - const onMouseMove = (event: MouseEvent) => { - const mouseX = event.clientX - itemRect.left; - const mouseY = event.clientY - itemRect.top; + const mouseX = event.clientX - state.triangleData.itemRect.left; + const mouseY = event.clientY - state.triangleData.itemRect.top; - const tipX = getTriangleTipX(mouseX); - const tipY = getTriangleTipY(mouseY); + const tipX = getTriangleTipX(mouseX); + const tipY = getTriangleTipY(mouseY); - const tipUpperPos = `${tipX}px ${tipY + padding}px`; - const tipLowerPos = `${tipX}px ${tipY - padding}px`; + const tipUpperPos = `${tipX}px ${tipY + state.triangleData.padding}px`; + const tipLowerPos = `${tipX}px ${tipY - state.triangleData.padding}px`; - ref.style.setProperty( - '--db-navigation-item-clip-path', - `polygon(${tipUpperPos}, ${tipLowerPos}, 100% 0, 100% 100%)` - ); - }; + ref.style.setProperty( + '--db-navigation-item-clip-path', + `polygon(${tipUpperPos}, ${tipLowerPos}, 100% 0, 100% 100%)` + ); + }, - ref.addEventListener('mouseover', () => { - document.addEventListener('mousemove', onMouseMove); - }); + updateSubNavigationState: () => { + if (props.areaPopup !== undefined) { + state.hasAreaPopup = props.areaPopup; + state.hasSubNavigation = state.hasAreaPopup; + return; + } - ref.addEventListener('mouseout', () => { - document.removeEventListener('mousemove', onMouseMove); - }); - } - }); + if (state.initialized && document && state.subNavigationId) { + const subNavigationSlot = document?.getElementById( + state.subNavigationId + ) as HTMLMenuElement; - onUpdate(() => { - if (typeof state.refOnMouseMove === 'function') { - console.log('ON UPDATE'); - // ref.addEventListener('mouseover', () => { - // document.addEventListener('mousemove', onMouseMove); - // }); - // - // ref.addEventListener('mouseout', () => { - // document.removeEventListener('mousemove', onMouseMove); - // }); + if (subNavigationSlot) { + if (subNavigationSlot.children?.length > 0) { + state.hasAreaPopup = true; + state.subNavigation = subNavigationSlot; + } else { + state.hasSubNavigation = false; + } + } + } } - }, [state.refOnMouseMove]); + }); onMount(() => { state.initialized = true; + }); - // mouse follow for safe triangle should not be added to root nav items - if (ref.parentElement.classList.contains('db-sub-navigation')) { - setTimeout(state.startMouseFollow, 1000); + onUnMount(() => { + if (document && state.onMouseMove) { + document.removeEventListener('mousemove', state.onMouseMove); } }); - onUnMount(() => { - console.log('ON UNMOUNT', state.refOnMouseMove); + onUpdate(() => { + state.tryInitSubNavigationHandling(); + }, [ref, state.hasSubNavigation, state.hasAreaPopup]); + + onUpdate(() => { + console.log('<<<< state.triangleData', state.triangleData); - if (document && state.refOnMouseMove) { - // document.removeEventListener('mousemove', state.refOnMouseMove); + if (state.triangleData) { + state.addMouseListeners(); } - }); + }, [ref, state.triangleData]); onUpdate(() => { if (props.subNavigationExpanded !== undefined) { @@ -158,35 +223,7 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { }, [props.subNavigationExpanded]); onUpdate(() => { - if (props.areaPopup !== undefined) { - state.hasAreaPopup = props.areaPopup; - state.hasSubNavigation = state.hasAreaPopup; - } else if (state.initialized && document && state.subNavigationId) { - const subNavigationSlot = document?.getElementById( - state.subNavigationId - ) as HTMLMenuElement; - - if (subNavigationSlot) { - const children = subNavigationSlot.children; - if (children?.length > 0) { - state.hasAreaPopup = true; - if (!visibleInVX(subNavigationSlot)) { - subNavigationSlot.setAttribute( - 'data-outside-vx', - 'true' - ); - } - if (!visibleInVY(subNavigationSlot)) { - subNavigationSlot.setAttribute( - 'data-outside-vy', - 'true' - ); - } - } else { - state.hasSubNavigation = false; - } - } - } + state.updateSubNavigationState(); }, [state.initialized, props.areaPopup]); // jscpd:ignore-end diff --git a/packages/components/src/components/navigation-item/navigation-item.scss b/packages/components/src/components/navigation-item/navigation-item.scss index 8939fc2a7f3..fe967edf7fa 100644 --- a/packages/components/src/components/navigation-item/navigation-item.scss +++ b/packages/components/src/components/navigation-item/navigation-item.scss @@ -224,26 +224,27 @@ ); inset-inline-start: calc(100% + #{variables.$db-spacing-fixed-xs}); - &[data-outside-vy="true"] { + &[data-outside-vy="bottom"] { inset-block-start: calc( 100% + #{variables.$db-spacing-fixed-md} ); transform: translateY(-100%); } - &[data-outside-vx="true"] { + &[data-outside-vx="right"] { transform: translateX(-100%); inset-inline-start: calc( -1 * #{variables.$db-spacing-fixed-xs} ); - &[data-outside-vy="true"] { + &[data-outside-vy="bottom"] { transform: translate(-100%, -100%); } &::before { inset-block-start: auto; inset-block-end: 0; + // inline-size: calc(var(--db-navigation-item-inline-size, 100%) - #{variables.$db-spacing-fixed-xs} - 2px); transform: translateX(100%) scaleX(-1); } } From 403ce8cd697f212a994491ab700cb6d2c1c44d1f Mon Sep 17 00:00:00 2001 From: brunoschadeck Date: Thu, 18 Apr 2024 15:24:03 +0200 Subject: [PATCH 03/42] feat: adds react and partly angular support --- .../scripts/post-build/components.js | 16 ++ .../src/components/navigation-item/model.ts | 19 ++- .../navigation-item/navigation-item.lite.tsx | 154 +++++++++++------- packages/components/src/utils/index.ts | 24 +++ .../src/utils/navigation-items.ts | 42 ++++- 5 files changed, 190 insertions(+), 65 deletions(-) diff --git a/packages/components/scripts/post-build/components.js b/packages/components/scripts/post-build/components.js index 646d109eb72..0e88cce5abc 100644 --- a/packages/components/scripts/post-build/components.js +++ b/packages/components/scripts/post-build/components.js @@ -118,6 +118,22 @@ const getComponents = () => [ }, { name: 'navigation-item', + overwrites: { + angular: [ + { + from: 'subNavigation = null;', + to: 'subNavigation: HTMLMenuElement | null = null;' + }, + { + from: 'triangleData = null;', + to: 'triangleData: DBNavigationItemTriangleData | null = null;' + }, + { + from: 'DBNavigationItemProps,', + to: 'DBNavigationItemProps, DBNavigationItemTriangleData,' + } + ] + }, config: { angular: { directives: [{ name: 'NavigationContent' }] diff --git a/packages/components/src/components/navigation-item/model.ts b/packages/components/src/components/navigation-item/model.ts index 7dc7e2ca007..94f1dde5604 100644 --- a/packages/components/src/components/navigation-item/model.ts +++ b/packages/components/src/components/navigation-item/model.ts @@ -9,6 +9,7 @@ import { NavigationBackButtonProps, WidthProps } from '../../shared/model'; +import { TestClass } from '../../utils'; export interface DBNavigationItemDefaultProps { /** @@ -46,6 +47,15 @@ export type DBNavigationItemProps = DBNavigationItemDefaultProps & WidthProps & NavigationBackButtonProps; +export interface DBNavigationItemTriangleData { + itemRect: DOMRect; + parentElementWidth: number; + subNavigationHeight: number; + padding: number; + outsideVX: null | string; + outsideVY: null | string; +} + export interface DBNavigationItemDefaultState { handleBackClick: (event: ClickEvent) => void; hasAreaPopup: boolean; @@ -63,12 +73,9 @@ export interface DBNavigationItemDefaultState { tryUpdateSubNavigationOffset: () => void; updateSubNavigationState: () => void; // cacheSafeTriangleData: () => void; - triangleData?: { - itemRect: DOMRect; - parentElementWidth: number; - subNavigationHeight: number; - padding: number; - }; + triangleData: DBNavigationItemTriangleData | null; + onMouseEnter: () => void; + onMouesLeave: () => void; outsideVX: null | string; outsideVY: null | string; } diff --git a/packages/components/src/components/navigation-item/navigation-item.lite.tsx b/packages/components/src/components/navigation-item/navigation-item.lite.tsx index 7e59c4c0d6f..71b99a5ae80 100644 --- a/packages/components/src/components/navigation-item/navigation-item.lite.tsx +++ b/packages/components/src/components/navigation-item/navigation-item.lite.tsx @@ -10,7 +10,7 @@ import { } from '@builder.io/mitosis'; import { DBNavigationItemProps, DBNavigationItemState } from './model'; import { DBButton } from '../button'; -import { cls, handleDataOutside, uuid } from '../../utils'; +import { cls, handleDataOutside, TestClass, uuid } from '../../utils'; import { DEFAULT_BACK } from '../../shared/constants'; import { ClickEvent } from '../../shared/model'; @@ -27,9 +27,9 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { hasAreaPopup: false, hasSubNavigation: true, isSubNavigationExpanded: false, - subNavigation: undefined, + subNavigation: null, subNavigationId: 'sub-navigation-' + uuid(), - triangleData: undefined, + triangleData: null, outsideVX: null, outsideVY: null, @@ -51,14 +51,10 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { tryUpdateSubNavigationOffset: () => { if (state.hasSubNavigation && state.subNavigation) { handleDataOutside(state.subNavigation); - console.log( - 'TEST', - state.subNavigation.getAttribute('data-outside-vx') - ); - state.outsideVX = - state.subNavigation.getAttribute('data-outside-vx'); - state.outsideVY = - state.subNavigation.getAttribute('data-outside-vy'); + // state.outsideVX = + // state.subNavigation.getAttribute('data-outside-vx'); + // state.outsideVY = + // state.subNavigation.getAttribute('data-outside-vy'); } }, @@ -74,6 +70,43 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { return; } + // const itemRect = ref.getBoundingClientRect(); + const parentElementWidth = + ref.parentElement.getBoundingClientRect().width; + + // the triangle has the width of the sub-navigation, current nav-item can be wider. + // so the width of the triangle must be adapted to the possibly wider nav-item. + ref.style.setProperty( + '--db-navigation-item-inline-size', + `${parentElementWidth}px` + ); + + ref.addEventListener('mouseenter', state.onMouseEnter); + ref.addEventListener('mouseleave', state.onMouesLeave); + + // state.addMouseListeners(); + + // state.triangleData = { + // itemRect, + // parentElementWidth, + // subNavigationHeight: + // state.subNavigation.getBoundingClientRect().height, + // padding: (parentElementWidth - itemRect.width) / 2 + // }; + }, + + addMouseListeners: () => { + // ref.addEventListener('mouseenter', state.onMouseEnter); + // ref.addEventListener('mouseleave', state.onMouesLeave); + }, + + onMouseEnter: () => { + if (!ref || !state.subNavigation) { + return; + } + + state.tryUpdateSubNavigationOffset(); + const itemRect = ref.getBoundingClientRect(); const parentElementWidth = ref.parentElement.getBoundingClientRect().width; @@ -90,40 +123,32 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { parentElementWidth, subNavigationHeight: state.subNavigation.getBoundingClientRect().height, - padding: (parentElementWidth - itemRect.width) / 2 + padding: (parentElementWidth - itemRect.width) / 2, + outsideVX: state.subNavigation.getAttribute('data-outside-vx'), + outsideVY: state.subNavigation.getAttribute('data-outside-vy') }; }, - addMouseListeners: () => { - ref.addEventListener('mouseenter', () => { - state.tryUpdateSubNavigationOffset(); - - document.removeEventListener('mousemove', state.onMouseMove); - document.addEventListener('mousemove', state.onMouseMove); - }); - - ref.addEventListener('mouseleave', () => { - // state.triangleData = null; - document.removeEventListener('mousemove', state.onMouseMove); - }); + onMouesLeave: () => { + state.triangleData = null; + // document.removeEventListener('mousemove', state.onMouseMove); }, onMouseMove: (event: MouseEvent) => { - if (!state.triangleData || !state.subNavigation) { + if (!ref || !state.triangleData || !state.subNavigation) { return; } + console.log('.'); + const getTriangleTipX = (mouseX: number): number => { - if ( - state.subNavigation.getAttribute('data-outside-vx') === - 'right' - ) { + if (!state.triangleData) return 0; + + if (state.triangleData.outsideVX === 'right') { // vertical flipped triangle needs an inverted x pos return state.triangleData.itemRect.width - mouseX; } - console.log(mouseX, state.triangleData.itemRect.width); - // triangle stops shrinking from 75% x pos return Math.min( mouseX, @@ -132,6 +157,8 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { }; const getTriangleTipY = (mouseY: number): number => { + if (!state.triangleData) return 0; + // padding must be added to the y pos of the tip so that the y pos matches the cursor const mouseYLimited = Math.max( @@ -139,10 +166,7 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { 0 ) + state.triangleData.padding; - if ( - state.subNavigation.getAttribute('data-outside-vy') === - 'bottom' - ) { + if (state.triangleData.outsideVY === 'bottom') { // add offset to tip y pos to match corrected sub-navigation y pos return ( mouseYLimited + @@ -158,6 +182,8 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { const mouseX = event.clientX - state.triangleData.itemRect.left; const mouseY = event.clientY - state.triangleData.itemRect.top; + // console.log(ref, mouseX, mouseY); + const tipX = getTriangleTipX(mouseX); const tipY = getTriangleTipY(mouseY); @@ -171,26 +197,29 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { }, updateSubNavigationState: () => { - if (props.areaPopup !== undefined) { - state.hasAreaPopup = props.areaPopup; - state.hasSubNavigation = state.hasAreaPopup; - return; - } - - if (state.initialized && document && state.subNavigationId) { - const subNavigationSlot = document?.getElementById( - state.subNavigationId - ) as HTMLMenuElement; - - if (subNavigationSlot) { - if (subNavigationSlot.children?.length > 0) { - state.hasAreaPopup = true; - state.subNavigation = subNavigationSlot; - } else { - state.hasSubNavigation = false; - } - } - } + state.hasSubNavigation = false; + // return; + // + // if (props.areaPopup !== undefined) { + // state.hasAreaPopup = props.areaPopup; + // state.hasSubNavigation = state.hasAreaPopup; + // return; + // } + // + // if (state.initialized && document && state.subNavigationId) { + // const subNavigationSlot = document?.getElementById( + // state.subNavigationId + // ) as HTMLMenuElement; + // + // if (subNavigationSlot) { + // if (subNavigationSlot.children?.length > 0) { + // state.hasAreaPopup = true; + // state.subNavigation = subNavigationSlot; + // } else { + // state.hasSubNavigation = false; + // } + // } + // } } }); @@ -199,8 +228,10 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { }); onUnMount(() => { - if (document && state.onMouseMove) { + if (document) { document.removeEventListener('mousemove', state.onMouseMove); + document.removeEventListener('mouseenter', state.onMouseEnter); + document.removeEventListener('mouseleave', state.onMouesLeave); } }); @@ -209,11 +240,18 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { }, [ref, state.hasSubNavigation, state.hasAreaPopup]); onUpdate(() => { - console.log('<<<< state.triangleData', state.triangleData); + console.log('state.test', !!state.triangleData, state.triangleData); if (state.triangleData) { - state.addMouseListeners(); + document.addEventListener('mousemove', state.onMouseMove); + } else { + document.removeEventListener('mousemove', state.onMouseMove); } + + return () => { + console.log('XXXXX'); + document.removeEventListener('mousemove', state.onMouseMove); + }; }, [ref, state.triangleData]); onUpdate(() => { diff --git a/packages/components/src/utils/index.ts b/packages/components/src/utils/index.ts index c397c1f35bd..328b7a61b7d 100644 --- a/packages/components/src/utils/index.ts +++ b/packages/components/src/utils/index.ts @@ -178,6 +178,30 @@ export const handleDataOutside = (el: Element) => { } }; +export class TestClass { + private test: string; + + constructor(test: string) { + this.test = test; + } + + public start() { + document.addEventListener('mousemove', this.callback); + } + + public stop() { + document.removeEventListener('mousemove', this.callback); + } + + public callback() { + console.log('TEST'); + } + + public getCallback() { + return this.callback; + } +} + export default { filterPassingProps, cls, diff --git a/showcases/vue-showcase/src/utils/navigation-items.ts b/showcases/vue-showcase/src/utils/navigation-items.ts index 4a0c66090ab..d7750523830 100644 --- a/showcases/vue-showcase/src/utils/navigation-items.ts +++ b/showcases/vue-showcase/src/utils/navigation-items.ts @@ -111,7 +111,47 @@ export const navigationItems: NavItem[] = [ { path: '/03/checkbox', label: 'Checkbox', - component: Checkbox + subNavigation: getSortedNavigationItems([ + { path: '/03/input', label: 'Input', component: Input }, + { + path: '/03/textarea', + label: 'Textarea', + component: Textarea + }, + { path: '/03/radio', label: 'Radio', component: Radio }, + { + path: '/03/checkbox', + label: 'Checkbox', + subNavigation: getSortedNavigationItems([ + { + path: '/03/input', + label: 'Input', + component: Input + }, + { + path: '/03/textarea', + label: 'Textarea', + component: Textarea + }, + { + path: '/03/radio', + label: 'Radio', + component: Radio + }, + { + path: '/03/checkbox', + label: 'Checkbox', + component: Checkbox + }, + { + path: '/03/select', + label: 'Select', + component: Select + } + ]) + }, + { path: '/03/select', label: 'Select', component: Select } + ]) }, { path: '/03/select', label: 'Select', component: Select } ]) From 63a2dcba157075404f66a32263fc16a0ffdcabfd Mon Sep 17 00:00:00 2001 From: brunoschadeck Date: Mon, 22 Apr 2024 17:42:20 +0200 Subject: [PATCH 04/42] fix: resets menu items within showcases --- packages/components/package.json | 2 +- .../components/scripts/post-build/angular.js | 12 + .../scripts/post-build/components.js | 12 +- .../src/components/navigation-item/model.ts | 18 +- .../navigation-item/navigation-item.lite.tsx | 238 +++--------------- .../navigation-item/navigation-item.scss | 6 +- packages/components/src/utils/index.ts | 43 ++-- packages/components/src/utils/navigation.ts | 123 ++++++++- .../src/utils/navigation-item.tsx | 64 +---- .../src/utils/navigation-items.ts | 42 +--- 10 files changed, 198 insertions(+), 362 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index 31173ef2ca7..83e5f024eb6 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -23,7 +23,7 @@ "build-style:01_sass": "sass src:build --no-source-map --load-path=node_modules/ --load-path=../../node_modules/ --future-deprecation=import", "build-style:02_postcss": "postcss build/**/*.css --replace", "build:mitosis": "mitosis build", - "compile:angular": "mitosis build -c mitosis-angular.config.js && node -e \"require('./scripts/post-build/angular.js')(true)\" && npm run build-components:directives && cpr ../../output/tmp/angular/src ../../output/angular/src -o", + "compile:angular": "mitosis build -c mitosis-angular.config.js && node -e \"require('./scripts/post-build/angular.js')(true)\" && cpr ../../output/tmp/angular/src ../../output/angular/src -o", "compile:react": "mitosis build -c mitosis-react.config.js && node -e \"require('./scripts/post-build/react.js')(true)\" && cpr ../../output/tmp/react/src ../../output/react/src -o", "compile:vue": "mitosis build -c mitosis-vue.config.js && node -e \"require('./scripts/post-build/vue.js')(true)\" && cpr ../../output/tmp/vue/src ../../output/vue/src -o", "copy-assets": "cpr ../foundations/assets build/assets -o", diff --git a/packages/components/scripts/post-build/angular.js b/packages/components/scripts/post-build/angular.js index df282ac9077..3c4b8f4f75d 100644 --- a/packages/components/scripts/post-build/angular.js +++ b/packages/components/scripts/post-build/angular.js @@ -195,6 +195,18 @@ module.exports = (tmp) => { { from: 'attr.disabled', to: 'disabled' + }, + { + from: 'mouseEnter', + to: 'mouseenter' + }, + { + from: 'mouseLeave', + to: 'mouseleave' + }, + { + from: 'mouseMove', + to: 'mousemove' } ]; diff --git a/packages/components/scripts/post-build/components.js b/packages/components/scripts/post-build/components.js index 0e88cce5abc..550bb5e59e2 100644 --- a/packages/components/scripts/post-build/components.js +++ b/packages/components/scripts/post-build/components.js @@ -121,16 +121,8 @@ const getComponents = () => [ overwrites: { angular: [ { - from: 'subNavigation = null;', - to: 'subNavigation: HTMLMenuElement | null = null;' - }, - { - from: 'triangleData = null;', - to: 'triangleData: DBNavigationItemTriangleData | null = null;' - }, - { - from: 'DBNavigationItemProps,', - to: 'DBNavigationItemProps, DBNavigationItemTriangleData,' + from: 'navigationItemSafeTriangle = undefined;', + to: 'navigationItemSafeTriangle: undefined | NavigationItemSafeTriangle = undefined;' } ] }, diff --git a/packages/components/src/components/navigation-item/model.ts b/packages/components/src/components/navigation-item/model.ts index 94f1dde5604..441a5995d36 100644 --- a/packages/components/src/components/navigation-item/model.ts +++ b/packages/components/src/components/navigation-item/model.ts @@ -9,7 +9,7 @@ import { NavigationBackButtonProps, WidthProps } from '../../shared/model'; -import { TestClass } from '../../utils'; +import { NavigationItemSafeTriangle } from '../../utils/navigation'; export interface DBNavigationItemDefaultProps { /** @@ -52,8 +52,8 @@ export interface DBNavigationItemTriangleData { parentElementWidth: number; subNavigationHeight: number; padding: number; - outsideVX: null | string; - outsideVY: null | string; + outsideVX: 'left' | 'right' | undefined; + outsideVY: 'top' | 'bottom' | undefined; } export interface DBNavigationItemDefaultState { @@ -66,18 +66,8 @@ export interface DBNavigationItemDefaultState { * Internal state property to show/hide sub-navigation button */ hasSubNavigation?: boolean; - subNavigation?: HTMLMenuElement; - tryInitSubNavigationHandling: () => void; - addMouseListeners: () => void; - onMouseMove: (event: MouseEvent) => void; - tryUpdateSubNavigationOffset: () => void; updateSubNavigationState: () => void; - // cacheSafeTriangleData: () => void; - triangleData: DBNavigationItemTriangleData | null; - onMouseEnter: () => void; - onMouesLeave: () => void; - outsideVX: null | string; - outsideVY: null | string; + navigationItemSafeTriangle?: NavigationItemSafeTriangle; } export type DBNavigationItemState = DBNavigationItemDefaultState & diff --git a/packages/components/src/components/navigation-item/navigation-item.lite.tsx b/packages/components/src/components/navigation-item/navigation-item.lite.tsx index 71b99a5ae80..837b0e1e31d 100644 --- a/packages/components/src/components/navigation-item/navigation-item.lite.tsx +++ b/packages/components/src/components/navigation-item/navigation-item.lite.tsx @@ -1,6 +1,5 @@ import { onMount, - onUnMount, onUpdate, Show, Slot, @@ -10,7 +9,8 @@ import { } from '@builder.io/mitosis'; import { DBNavigationItemProps, DBNavigationItemState } from './model'; import { DBButton } from '../button'; -import { cls, handleDataOutside, TestClass, uuid } from '../../utils'; +import { cls, uuid } from '../../utils'; +import { NavigationItemSafeTriangle } from '../../utils/navigation'; import { DEFAULT_BACK } from '../../shared/constants'; import { ClickEvent } from '../../shared/model'; @@ -27,11 +27,8 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { hasAreaPopup: false, hasSubNavigation: true, isSubNavigationExpanded: false, - subNavigation: null, subNavigationId: 'sub-navigation-' + uuid(), - triangleData: null, - outsideVX: null, - outsideVY: null, + navigationItemSafeTriangle: undefined, handleClick: (event: ClickEvent) => { if (props.onClick) { @@ -48,178 +45,34 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { state.isSubNavigationExpanded = false; }, - tryUpdateSubNavigationOffset: () => { - if (state.hasSubNavigation && state.subNavigation) { - handleDataOutside(state.subNavigation); - // state.outsideVX = - // state.subNavigation.getAttribute('data-outside-vx'); - // state.outsideVY = - // state.subNavigation.getAttribute('data-outside-vy'); - } - }, - - tryInitSubNavigationHandling: () => { - if ( - !ref || - !ref.parentElement || - !state.hasSubNavigation || - !state.hasAreaPopup || - !ref.parentElement.classList.contains('db-sub-navigation') || - ref.closest('.db-drawer') - ) { - return; - } - - // const itemRect = ref.getBoundingClientRect(); - const parentElementWidth = - ref.parentElement.getBoundingClientRect().width; - - // the triangle has the width of the sub-navigation, current nav-item can be wider. - // so the width of the triangle must be adapted to the possibly wider nav-item. - ref.style.setProperty( - '--db-navigation-item-inline-size', - `${parentElementWidth}px` - ); - - ref.addEventListener('mouseenter', state.onMouseEnter); - ref.addEventListener('mouseleave', state.onMouesLeave); - - // state.addMouseListeners(); - - // state.triangleData = { - // itemRect, - // parentElementWidth, - // subNavigationHeight: - // state.subNavigation.getBoundingClientRect().height, - // padding: (parentElementWidth - itemRect.width) / 2 - // }; - }, - - addMouseListeners: () => { - // ref.addEventListener('mouseenter', state.onMouseEnter); - // ref.addEventListener('mouseleave', state.onMouesLeave); - }, - - onMouseEnter: () => { - if (!ref || !state.subNavigation) { - return; - } - - state.tryUpdateSubNavigationOffset(); - - const itemRect = ref.getBoundingClientRect(); - const parentElementWidth = - ref.parentElement.getBoundingClientRect().width; - - // the triangle has the width of the sub-navigation, current nav-item can be wider. - // so the width of the triangle must be adapted to the possibly wider nav-item. - ref.style.setProperty( - '--db-navigation-item-inline-size', - `${parentElementWidth}px` - ); - - state.triangleData = { - itemRect, - parentElementWidth, - subNavigationHeight: - state.subNavigation.getBoundingClientRect().height, - padding: (parentElementWidth - itemRect.width) / 2, - outsideVX: state.subNavigation.getAttribute('data-outside-vx'), - outsideVY: state.subNavigation.getAttribute('data-outside-vy') - }; - }, - - onMouesLeave: () => { - state.triangleData = null; - // document.removeEventListener('mousemove', state.onMouseMove); - }, - - onMouseMove: (event: MouseEvent) => { - if (!ref || !state.triangleData || !state.subNavigation) { + updateSubNavigationState: () => { + if (props.areaPopup !== undefined) { + state.hasAreaPopup = props.areaPopup; + state.hasSubNavigation = state.hasAreaPopup; return; } - console.log('.'); - - const getTriangleTipX = (mouseX: number): number => { - if (!state.triangleData) return 0; - - if (state.triangleData.outsideVX === 'right') { - // vertical flipped triangle needs an inverted x pos - return state.triangleData.itemRect.width - mouseX; - } - - // triangle stops shrinking from 75% x pos - return Math.min( - mouseX, - state.triangleData.itemRect.width * 0.75 - ); - }; - - const getTriangleTipY = (mouseY: number): number => { - if (!state.triangleData) return 0; - - // padding must be added to the y pos of the tip so that the y pos matches the cursor - const mouseYLimited = - Math.max( - Math.min(mouseY, state.triangleData.itemRect.height), - 0 - ) + state.triangleData.padding; - - if (state.triangleData.outsideVY === 'bottom') { - // add offset to tip y pos to match corrected sub-navigation y pos - return ( - mouseYLimited + - (state.triangleData.subNavigationHeight - - state.triangleData.padding * 2 - - state.triangleData.itemRect.height) - ); + if (state.initialized && document && state.subNavigationId) { + const subNavigationSlot = document?.getElementById( + state.subNavigationId + ) as HTMLMenuElement; + + if (subNavigationSlot) { + if (subNavigationSlot.children?.length > 0) { + state.hasAreaPopup = true; + + if (!state.navigationItemSafeTriangle) { + state.navigationItemSafeTriangle = + new NavigationItemSafeTriangle( + ref, + subNavigationSlot + ); + } + } else { + state.hasSubNavigation = false; + } } - - return mouseYLimited; - }; - - const mouseX = event.clientX - state.triangleData.itemRect.left; - const mouseY = event.clientY - state.triangleData.itemRect.top; - - // console.log(ref, mouseX, mouseY); - - const tipX = getTriangleTipX(mouseX); - const tipY = getTriangleTipY(mouseY); - - const tipUpperPos = `${tipX}px ${tipY + state.triangleData.padding}px`; - const tipLowerPos = `${tipX}px ${tipY - state.triangleData.padding}px`; - - ref.style.setProperty( - '--db-navigation-item-clip-path', - `polygon(${tipUpperPos}, ${tipLowerPos}, 100% 0, 100% 100%)` - ); - }, - - updateSubNavigationState: () => { - state.hasSubNavigation = false; - // return; - // - // if (props.areaPopup !== undefined) { - // state.hasAreaPopup = props.areaPopup; - // state.hasSubNavigation = state.hasAreaPopup; - // return; - // } - // - // if (state.initialized && document && state.subNavigationId) { - // const subNavigationSlot = document?.getElementById( - // state.subNavigationId - // ) as HTMLMenuElement; - // - // if (subNavigationSlot) { - // if (subNavigationSlot.children?.length > 0) { - // state.hasAreaPopup = true; - // state.subNavigation = subNavigationSlot; - // } else { - // state.hasSubNavigation = false; - // } - // } - // } + } } }); @@ -227,33 +80,6 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { state.initialized = true; }); - onUnMount(() => { - if (document) { - document.removeEventListener('mousemove', state.onMouseMove); - document.removeEventListener('mouseenter', state.onMouseEnter); - document.removeEventListener('mouseleave', state.onMouesLeave); - } - }); - - onUpdate(() => { - state.tryInitSubNavigationHandling(); - }, [ref, state.hasSubNavigation, state.hasAreaPopup]); - - onUpdate(() => { - console.log('state.test', !!state.triangleData, state.triangleData); - - if (state.triangleData) { - document.addEventListener('mousemove', state.onMouseMove); - } else { - document.removeEventListener('mousemove', state.onMouseMove); - } - - return () => { - console.log('XXXXX'); - document.removeEventListener('mousemove', state.onMouseMove); - }; - }, [ref, state.triangleData]); - onUpdate(() => { if (props.subNavigationExpanded !== undefined) { state.isSubNavigationExpanded = !!props.subNavigationExpanded; @@ -263,13 +89,21 @@ export default function DBNavigationItem(props: DBNavigationItemProps) { onUpdate(() => { state.updateSubNavigationState(); }, [state.initialized, props.areaPopup]); - // jscpd:ignore-end return (
  • + state.navigationItemSafeTriangle?.onMouseEnter() + } + onMouseLeave={() => + state.navigationItemSafeTriangle?.onMouseLeave() + } + onMouseMove={(event) => + state.navigationItemSafeTriangle?.onMouseMove(event) + } class={cls('db-navigation-item', props.className)} data-width={props.width} data-icon={props.icon} diff --git a/packages/components/src/components/navigation-item/navigation-item.scss b/packages/components/src/components/navigation-item/navigation-item.scss index fe967edf7fa..964cfa9d58d 100644 --- a/packages/components/src/components/navigation-item/navigation-item.scss +++ b/packages/components/src/components/navigation-item/navigation-item.scss @@ -256,11 +256,7 @@ inset-inline-start: 0; block-size: 100%; inline-size: var(--db-navigation-item-inline-size, 100%); - background: color-mix( - in hsl, - transparent 40%, - #{colors.$db-brand-0} - ); + background: transparent; transform: translateX(-100%); clip-path: var( --db-navigation-item-clip-path, diff --git a/packages/components/src/utils/index.ts b/packages/components/src/utils/index.ts index 328b7a61b7d..f79ce3c92ad 100644 --- a/packages/components/src/utils/index.ts +++ b/packages/components/src/utils/index.ts @@ -164,43 +164,32 @@ export const isInView = (el: Element) => { }; }; -export const handleDataOutside = (el: Element) => { +export interface DBDataOutsidePair { + vx?: 'left' | 'right'; + vy?: 'top' | 'bottom'; +} +export const handleDataOutside = (el: Element): DBDataOutsidePair => { const { outTop, outBottom, outLeft, outRight } = isInView(el); + let dataOutsidePair: DBDataOutsidePair = {}; + if (outTop || outBottom) { - el.setAttribute('data-outside-vy', outTop ? 'top' : 'bottom'); + dataOutsidePair = { vy: outTop ? 'top' : 'bottom' }; + el.setAttribute('data-outside-vy', dataOutsidePair.vy!); } else { el.removeAttribute('data-outside-vy'); } if (outLeft || outRight) { - el.setAttribute('data-outside-vx', outRight ? 'right' : 'left'); + dataOutsidePair = { + ...dataOutsidePair, + vx: outRight ? 'right' : 'left' + }; + el.setAttribute('data-outside-vx', dataOutsidePair.vx!); } else { el.removeAttribute('data-outside-vx'); } -}; - -export class TestClass { - private test: string; - - constructor(test: string) { - this.test = test; - } - - public start() { - document.addEventListener('mousemove', this.callback); - } - public stop() { - document.removeEventListener('mousemove', this.callback); - } - - public callback() { - console.log('TEST'); - } - - public getCallback() { - return this.callback; - } -} + return dataOutsidePair; +}; export default { filterPassingProps, diff --git a/packages/components/src/utils/navigation.ts b/packages/components/src/utils/navigation.ts index d10bdb6679c..3a091f30b75 100644 --- a/packages/components/src/utils/navigation.ts +++ b/packages/components/src/utils/navigation.ts @@ -1,3 +1,6 @@ +import { DBNavigationItemTriangleData } from '../components/navigation-item/model'; +import { handleDataOutside } from './index'; + export const isEventTargetNavigationItem = (event: unknown): boolean => { const { target } = event as { target: HTMLElement }; return Boolean( @@ -6,6 +9,124 @@ export const isEventTargetNavigationItem = (event: unknown): boolean => { ); }; +export class NavigationItemSafeTriangle { + private readonly element: HTMLElement; + private readonly subNavigation: Element; + private readonly parentSubNavigation: Element | null; + private triangleData?: DBNavigationItemTriangleData; + private initialized: boolean = false; + constructor(element: HTMLElement, subNavigation: Element) { + this.element = element; + this.subNavigation = subNavigation; + this.parentSubNavigation = this.element.closest('.db-sub-navigation'); + + /* + * only initiate if: + * 1. item is not at root navigation level + * 2. item is not in the mobile navigation / within db-drawer + */ + if (this.parentSubNavigation && !this.element.closest('.db-drawer')) { + this.init(); + } + } + + private init() { + const parentElementWidth = + this.parentSubNavigation?.getBoundingClientRect().width ?? 0; + + // the triangle has the width of the sub-navigation, current nav-item can be wider. + // so the width of the triangle must be adapted to a possibly wider nav-item. + this.element.style.setProperty( + '--db-navigation-item-inline-size', + `${parentElementWidth}px` + ); + + this.initialized = true; + } + + public onMouseEnter() { + if (!this.initialized) { + return; + } + + const dataOutsidePair = handleDataOutside(this.subNavigation); + + const itemRect = this.element.getBoundingClientRect(); + const parentElementWidth = + this.parentSubNavigation?.getBoundingClientRect().width ?? 0; + + this.triangleData = { + itemRect, + parentElementWidth, + subNavigationHeight: + this.subNavigation.getBoundingClientRect().height, + padding: (parentElementWidth - itemRect.width) / 2, + outsideVX: dataOutsidePair.vx, + outsideVY: dataOutsidePair.vy + }; + } + + public onMouseLeave() { + this.triangleData = undefined; + } + + public onMouseMove(event: MouseEvent) { + if (!this.initialized || !this.triangleData) { + return; + } + + const getTriangleTipX = (mouseX: number): number => { + if (!this.triangleData) return 0; + + if (this.triangleData.outsideVX === 'right') { + // vertical flipped triangle needs an inverted x pos + return this.triangleData.itemRect.width - mouseX; + } + + // triangle stops shrinking from 75% x pos + return Math.min(mouseX, this.triangleData.itemRect.width * 0.75); + }; + + const getTriangleTipY = (mouseY: number): number => { + if (!this.triangleData) return 0; + + // padding must be added to the y pos of the tip so that the y pos matches the cursor + const mouseYLimited = + Math.max( + Math.min(mouseY, this.triangleData.itemRect.height), + 0 + ) + this.triangleData.padding; + + if (this.triangleData.outsideVY === 'bottom') { + // add offset to tip y pos to match corrected sub-navigation y pos + return ( + mouseYLimited + + (this.triangleData.subNavigationHeight - + this.triangleData.padding * 2 - + this.triangleData.itemRect.height) + ); + } + + return mouseYLimited; + }; + + const mouseX = event.clientX - this.triangleData.itemRect.left; + const mouseY = event.clientY - this.triangleData.itemRect.top; + + const tipX = getTriangleTipX(mouseX); + const tipY = getTriangleTipY(mouseY); + + const tipUpperPos = `${tipX}px ${tipY + this.triangleData.padding}px`; + const tipLowerPos = `${tipX}px ${tipY - this.triangleData.padding}px`; + + this.element.style.setProperty( + '--db-navigation-item-clip-path', + `polygon(${tipUpperPos}, ${tipLowerPos}, 100% 0, 100% 100%)` + ); + } +} + export default { - isEventTargetNavigationItem + isEventTargetNavigationItem, + NavigationItemSafeTriangle }; diff --git a/showcases/react-showcase/src/utils/navigation-item.tsx b/showcases/react-showcase/src/utils/navigation-item.tsx index 256d9670ab8..7b356f8125a 100644 --- a/showcases/react-showcase/src/utils/navigation-item.tsx +++ b/showcases/react-showcase/src/utils/navigation-item.tsx @@ -116,75 +116,17 @@ export const NAVIGATION_ITEMS: NavigationItem[] = [ path: '03', label: '03 Data-Input', subNavigation: getSortedNavigationItems([ - { - path: 'input', - label: 'Input', - subNavigation: getSortedNavigationItems([ - { - path: 'link', - label: 'Link', - component: - }, - { - path: 'button', - label: 'Button', - component: - } - ]) - }, + { path: 'input', label: 'Input', component: }, { path: 'textarea', label: 'Textarea', - subNavigation: getSortedNavigationItems([ - { - path: 'link', - label: 'Link', - component: - }, - { - path: 'button', - label: 'Button', - component: - } - ]) + component: }, { path: 'radio', label: 'Radio', component: }, { path: 'checkbox', label: 'Checkbox', - subNavigation: getSortedNavigationItems([ - { - path: 'link', - label: 'Link', - component: - }, - { - path: 'button', - label: 'Button', - component: - }, - { - path: 'link', - label: 'Link', - component: - }, - { - path: 'button', - label: 'Button', - subNavigation: getSortedNavigationItems([ - { - path: 'link', - label: 'Link', - component: - }, - { - path: 'button', - label: 'Button', - component: - } - ]) - } - ]) + component: }, { path: 'select', label: 'Select', component: } ]) diff --git a/showcases/vue-showcase/src/utils/navigation-items.ts b/showcases/vue-showcase/src/utils/navigation-items.ts index d7750523830..4a0c66090ab 100644 --- a/showcases/vue-showcase/src/utils/navigation-items.ts +++ b/showcases/vue-showcase/src/utils/navigation-items.ts @@ -111,47 +111,7 @@ export const navigationItems: NavItem[] = [ { path: '/03/checkbox', label: 'Checkbox', - subNavigation: getSortedNavigationItems([ - { path: '/03/input', label: 'Input', component: Input }, - { - path: '/03/textarea', - label: 'Textarea', - component: Textarea - }, - { path: '/03/radio', label: 'Radio', component: Radio }, - { - path: '/03/checkbox', - label: 'Checkbox', - subNavigation: getSortedNavigationItems([ - { - path: '/03/input', - label: 'Input', - component: Input - }, - { - path: '/03/textarea', - label: 'Textarea', - component: Textarea - }, - { - path: '/03/radio', - label: 'Radio', - component: Radio - }, - { - path: '/03/checkbox', - label: 'Checkbox', - component: Checkbox - }, - { - path: '/03/select', - label: 'Select', - component: Select - } - ]) - }, - { path: '/03/select', label: 'Select', component: Select } - ]) + component: Checkbox }, { path: '/03/select', label: 'Select', component: Select } ]) From 7908e739102ff0946c736dd02232e24b84e604c1 Mon Sep 17 00:00:00 2001 From: brunoschadeck Date: Tue, 23 Apr 2024 15:48:31 +0200 Subject: [PATCH 05/42] fix: adds missing type to vue data by post script --- packages/components/scripts/post-build/components.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/components/scripts/post-build/components.js b/packages/components/scripts/post-build/components.js index 550bb5e59e2..2c33950eb2a 100644 --- a/packages/components/scripts/post-build/components.js +++ b/packages/components/scripts/post-build/components.js @@ -124,6 +124,12 @@ const getComponents = () => [ from: 'navigationItemSafeTriangle = undefined;', to: 'navigationItemSafeTriangle: undefined | NavigationItemSafeTriangle = undefined;' } + ], + vue: [ + { + from: 'navigationItemSafeTriangle: undefined', + to: 'navigationItemSafeTriangle: undefined as undefined | NavigationItemSafeTriangle' + } ] }, config: { From 685ae3a0016a1ee2b8334e50435021ad77743a92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 07:40:34 +0200 Subject: [PATCH 06/42] chore(deps-dev): bump @commitlint/cli in the commitlint group (#2572) Bumps the commitlint group with 1 update: [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli). Updates `@commitlint/cli` from 19.2.2 to 19.3.0 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.3.0/@commitlint/cli) --- updated-dependencies: - dependency-name: "@commitlint/cli" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: commitlint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index d8fbe75439b..9316e02d54a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "e2e" ], "devDependencies": { - "@commitlint/cli": "19.2.2", + "@commitlint/cli": "19.3.0", "@commitlint/config-conventional": "19.2.2", "@playwright/test": "1.43.1", "adm-zip": "0.5.12", @@ -4186,12 +4186,12 @@ } }, "node_modules/@commitlint/cli": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.2.2.tgz", - "integrity": "sha512-P8cbOHfg2PQRzfICLSrzUVOCVMqjEZ8Hlth6mtJ4yOEjT47Q5PbIGymgX3rLVylNw+3IAT2Djn9IJ2wHbXFzBg==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.3.0.tgz", + "integrity": "sha512-LgYWOwuDR7BSTQ9OLZ12m7F/qhNY+NpAyPBgo4YNMkACE7lGuUnuQq1yi9hz1KA4+3VqpOYl8H1rY/LYK43v7g==", "dev": true, "dependencies": { - "@commitlint/format": "^19.0.3", + "@commitlint/format": "^19.3.0", "@commitlint/lint": "^19.2.2", "@commitlint/load": "^19.2.0", "@commitlint/read": "^19.2.1", @@ -4259,9 +4259,9 @@ } }, "node_modules/@commitlint/format": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.0.3.tgz", - "integrity": "sha512-QjjyGyoiVWzx1f5xOteKHNLFyhyweVifMgopozSgx1fGNrGV8+wp7k6n1t6StHdJ6maQJ+UUtO2TcEiBFRyR6Q==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.3.0.tgz", + "integrity": "sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==", "dev": true, "dependencies": { "@commitlint/types": "^19.0.3", diff --git a/package.json b/package.json index 9ca55dadf9b..a14201c4b4f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "update:icon-fonts": "npm run update:icon-fonts --workspace=scripts" }, "devDependencies": { - "@commitlint/cli": "19.2.2", + "@commitlint/cli": "19.3.0", "@commitlint/config-conventional": "19.2.2", "@playwright/test": "1.43.1", "adm-zip": "0.5.12", From eb3cbfc46cbd6db09f7b307969a783beefdc4c93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 05:52:04 +0000 Subject: [PATCH 07/42] chore(deps): bump react-router-dom from 6.22.3 to 6.23.0 (#2574) Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.22.3 to 6.23.0. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.23.0/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router-dom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 26 +++++++++++++------------- showcases/react-showcase/package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9316e02d54a..fb2c7fc6c85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8228,9 +8228,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz", + "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==", "engines": { "node": ">=14.0.0" } @@ -25515,11 +25515,11 @@ } }, "node_modules/react-router": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", - "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz", + "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==", "dependencies": { - "@remix-run/router": "1.15.3" + "@remix-run/router": "1.16.0" }, "engines": { "node": ">=14.0.0" @@ -25529,12 +25529,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", - "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz", + "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==", "dependencies": { - "@remix-run/router": "1.15.3", - "react-router": "6.22.3" + "@remix-run/router": "1.16.0", + "react-router": "6.23.0" }, "engines": { "node": ">=14.0.0" @@ -31366,7 +31366,7 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "6.22.3" + "react-router-dom": "6.23.0" }, "devDependencies": { "@types/react": "^18.2.79", diff --git a/showcases/react-showcase/package.json b/showcases/react-showcase/package.json index 17ad060ffc4..75b8e0ab519 100644 --- a/showcases/react-showcase/package.json +++ b/showcases/react-showcase/package.json @@ -16,7 +16,7 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "6.22.3" + "react-router-dom": "6.23.0" }, "devDependencies": { "@types/react": "^18.2.79", From ec3345ebe4ed22d4b1b8f412f57c840c0cef2498 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 05:53:56 +0000 Subject: [PATCH 08/42] chore(deps-dev): bump the builder-io group with 2 updates Bumps the builder-io group with 2 updates: [@builder.io/mitosis](https://github.com/BuilderIO/mitosis) and [@builder.io/mitosis-cli](https://github.com/BuilderIO/mitosis). Updates `@builder.io/mitosis` from 0.2.4 to 0.2.6 - [Release notes](https://github.com/BuilderIO/mitosis/releases) - [Commits](https://github.com/BuilderIO/mitosis/compare/@builder.io/mitosis@0.2.4...@builder.io/mitosis@0.2.6) Updates `@builder.io/mitosis-cli` from 0.2.4 to 0.2.6 - [Release notes](https://github.com/BuilderIO/mitosis/releases) - [Commits](https://github.com/BuilderIO/mitosis/compare/@builder.io/mitosis-cli@0.2.4...@builder.io/mitosis-cli@0.2.6) --- updated-dependencies: - dependency-name: "@builder.io/mitosis" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: builder-io - dependency-name: "@builder.io/mitosis-cli" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: builder-io ... Signed-off-by: dependabot[bot] --- package-lock.json | 18 +++++++++--------- packages/components/package.json | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb2c7fc6c85..73a2bd0e9ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3852,9 +3852,9 @@ } }, "node_modules/@builder.io/mitosis": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@builder.io/mitosis/-/mitosis-0.2.4.tgz", - "integrity": "sha512-itUljsxJ7NBf0p4NvXkFX2So80jjw/8HFqiLLve+vm8t1fxkCAuFxWl7FiMdrhmdsXCiwOX/mHssj00dKJWIdQ==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@builder.io/mitosis/-/mitosis-0.2.6.tgz", + "integrity": "sha512-NLart8jVQ/lq5ena68rlLGLLGDG91Kp5iseXb2evHMhbjeb1fHeHYNggoWRjkmIDHsE+5hwYf3YbXcbONfnydA==", "dev": true, "dependencies": { "@angular/compiler": "^11.2.11", @@ -3885,13 +3885,13 @@ } }, "node_modules/@builder.io/mitosis-cli": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@builder.io/mitosis-cli/-/mitosis-cli-0.2.4.tgz", - "integrity": "sha512-sEhEHSztxBy2P+bYiciwGbx1j75whOcPHBacS+2hsklwBKKU3eNjzgYUGiUvtZIWHpkMcbiYiKiva5E0wmGANw==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@builder.io/mitosis-cli/-/mitosis-cli-0.2.6.tgz", + "integrity": "sha512-qkj59k2u3MEicyRBrdGI2EYx18oLSjWF08fCRNwfdn0bfISdDtoCY2/qiYHqy/xatqrGvAhkO35rlMNTY1lBbQ==", "dev": true, "dependencies": { "@babel/core": "^7.17.8", - "@builder.io/mitosis": "0.2.4", + "@builder.io/mitosis": "0.2.6", "@vue/compiler-sfc": "^3.1.5", "babel-preset-solid": "^1.3.13", "chalk": "^4.1.0", @@ -31011,8 +31011,8 @@ }, "devDependencies": { "@builder.io/eslint-plugin-mitosis": "^0.0.15", - "@builder.io/mitosis": "^0.2.4", - "@builder.io/mitosis-cli": "^0.2.4", + "@builder.io/mitosis": "^0.2.6", + "@builder.io/mitosis-cli": "^0.2.6", "@react-docgen/cli": "^2.0.3", "cpr": "3.0.1", "cssnano": "^6.1.2", diff --git a/packages/components/package.json b/packages/components/package.json index 9f3c58d8473..e49098b2e47 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -47,8 +47,8 @@ }, "devDependencies": { "@builder.io/eslint-plugin-mitosis": "^0.0.15", - "@builder.io/mitosis": "^0.2.4", - "@builder.io/mitosis-cli": "^0.2.4", + "@builder.io/mitosis": "^0.2.6", + "@builder.io/mitosis-cli": "^0.2.6", "@react-docgen/cli": "^2.0.3", "cpr": "3.0.1", "cssnano": "^6.1.2", From abbcbcd624a34559db0f0da3f1dd8221a37c2652 Mon Sep 17 00:00:00 2001 From: Nicolas Merget Date: Wed, 24 Apr 2024 09:06:14 +0200 Subject: [PATCH 09/42] fix: issues with slots --- .../src/components/navigation/navigation.spec.tsx | 2 +- showcases/vue-showcase/src/App.vue | 6 +++--- showcases/vue-showcase/src/NavItemComponent.vue | 2 +- .../src/components/navigation-item/NavigationItem.vue | 2 +- .../vue-showcase/src/components/navigation/Navigation.vue | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/components/src/components/navigation/navigation.spec.tsx b/packages/components/src/components/navigation/navigation.spec.tsx index 9ca8ad6bc5c..4ee3ffb95ba 100644 --- a/packages/components/src/components/navigation/navigation.spec.tsx +++ b/packages/components/src/components/navigation/navigation.spec.tsx @@ -15,7 +15,7 @@ const comp: any = ( Sub1 }> - {/*