From 5f4509f8125a4b41828c0b4021b68c425977f3b1 Mon Sep 17 00:00:00 2001 From: Mathis Poncet Date: Wed, 26 Feb 2025 14:50:16 +0100 Subject: [PATCH] WIP first version works --- src/components.d.ts | 10 + .../com-nav-category/com-nav-category.scss | 182 ++++++------ .../com-nav-category/com-nav-category.tsx | 14 +- .../com-nav-submenu/com-nav-submenu.scss | 280 +++++++++--------- .../com-nav-submenu/com-nav-submenu.tsx | 26 +- src/components/com-nav/com-nav-utils.ts | 13 +- src/components/com-nav/com-nav.scss | 167 ++++++++--- src/components/com-nav/com-nav.tsx | 39 ++- 8 files changed, 461 insertions(+), 270 deletions(-) diff --git a/src/components.d.ts b/src/components.d.ts index a4359aa3..5b59dcfd 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -432,6 +432,10 @@ export namespace Components { * Name of the application to be displayed in the menu bar */ "appName": string; + /** + * Breakpoint at which the component switches from desktop to mobile mode. If not set, the breakpoint is set to default value of 575px. + */ + "breakpoint"?: number; "setAriaAttribute": (attr: AriaAttributeName, value: string | null | undefined) => Promise; } /** @@ -471,6 +475,7 @@ export namespace Components { */ "open": () => Promise; "setAriaAttribute": (attr: AriaAttributeName, value: string | null | undefined) => Promise; + "setCustomBreakpoint": (customBreakpoint: number) => Promise; } /** * The com-nav-item is a subcomponent of `wcs-com-nav`. It represents a list-item wrapper around a link. @@ -516,6 +521,7 @@ export namespace Components { "panelDescription": string; "panelTitle": string; "setAriaAttribute": (attr: AriaAttributeName, value: string | null | undefined) => Promise; + "setCustomBreakpoint": (customBreakpoint: number) => Promise; } /** * Counter component, meant to be used for small range of values (e.g : 0 - 5).
@@ -4630,6 +4636,10 @@ declare namespace LocalJSX { * Name of the application to be displayed in the menu bar */ "appName"?: string; + /** + * Breakpoint at which the component switches from desktop to mobile mode. If not set, the breakpoint is set to default value of 575px. + */ + "breakpoint"?: number; } /** * The com-nav-category is a subcomponent of `wcs-com-nav`. It represents a category nested inside a `wcs-com-nav-submenu`. diff --git a/src/components/com-nav-category/com-nav-category.scss b/src/components/com-nav-category/com-nav-category.scss index fb652b67..9359af86 100644 --- a/src/components/com-nav-category/com-nav-category.scss +++ b/src/components/com-nav-category/com-nav-category.scss @@ -2,6 +2,94 @@ @import '../com-nav/com-nav-focus.scss'; @import '../../style/breakpoints.scss'; +@mixin wcs-com-nav-category-desktop { + padding-bottom: unset; + + .item-container:not([data-open]) { + display: none; + } + + button { + cursor: pointer; + } + + .label-container { + display: flex; + align-items: center; + gap: var(--wcs-com-nav-category-label-desktop-gap); + + font-weight: var(--wcs-com-nav-category-label-desktop-font-weight); + cursor: pointer; + user-select: none; + padding-left: unset; + color: var(--wcs-com-nav-category-label-desktop-color); + + .arrow-icon { + display: inline-block; + font-family: icons; + font-size: 0.6rem; + line-height: 1; + box-sizing: border-box; + } + + .arrow-container { + display: unset; + } + } + + .label-container:focus-visible { + @include focus-outline(var(--wcs-com-nav-category-label-desktop-focus-outline-color), 5px, 0.1rem); + } + + @supports not selector(.label-container:focus-visible) { + .label-container:focus-within { + @include focus-outline(var(--wcs-com-nav-category-label-desktop-focus-outline-color), 5px, 0.1rem); + } + } + + button.label-container[aria-expanded="true"]:after { + position: absolute; + left: calc(calc(calc(100% / 2) + calc(var(--wcs-com-content-max-width) / 8)) - calc(1.125rem / 2)); + z-index: 2; + display: block; + width: 1.125rem; + height: 1.125rem; + content: ""; + background-color: var(--wcs-com-nav-category-desktop-menu-background-indicator-color); + transform: rotate(45deg); + } + + .item-container[data-open] { + position: absolute; + border-left: solid var(--wcs-com-nav-category-desktop-menu-border-left-width) var(--wcs-com-nav-category-desktop-menu-border-left-color); + left: calc(calc(100% / 2) + calc(var(--wcs-com-content-max-width) / 8)); + top: 0; + right: 0; + padding: var(--wcs-com-nav-category-desktop-menu-padding-top) var(--wcs-com-nav-category-desktop-menu-padding-right) var(--wcs-com-nav-category-desktop-menu-padding-bottom) var(--wcs-com-nav-category-desktop-menu-padding-left); + background-blend-mode: multiply; + background-color: var(--wcs-com-nav-category-desktop-menu-background-color); + box-sizing: border-box; + height: calc(100%); + + margin-left: unset; + margin-top: unset; + + display: flex; + flex-direction: column; + gap: var(--wcs-com-nav-category-desktop-menu-gap); + align-items: start; + + ::slotted(wcs-com-nav-item) { + color: var(--wcs-com-nav-category-item-desktop-color); + font-weight: var(--wcs-com-nav-category-item-desktop-font-weight); + } + + ::slotted(wcs-com-nav-item:focus-within) { + @include com-nav-submenu-item-desktop-focus-outline(var(--wcs-com-nav-category-item-desktop-color)); + } + } +} + :host { --wcs-com-nav-category-label-mobile-font-weight: var(--wcs-semantic-font-weight-medium); --wcs-com-nav-category-label-mobile-color: var(--wcs-semantic-color-text-primary); @@ -74,92 +162,12 @@ } } -@include for-tablet-portrait-up { - :host { - padding-bottom: unset; - - .item-container:not([data-open]) { - display: none; - } - - button { - cursor: pointer; - } - - .label-container { - display: flex; - align-items: center; - gap: var(--wcs-com-nav-category-label-desktop-gap); - - font-weight: var(--wcs-com-nav-category-label-desktop-font-weight); - cursor: pointer; - user-select: none; - padding-left: unset; - color: var(--wcs-com-nav-category-label-desktop-color); - - .arrow-icon { - display: inline-block; - font-family: icons; - font-size: 0.6rem; - line-height: 1; - box-sizing: border-box; - } - - .arrow-container { - display: unset; - } - } - - .label-container:focus-visible { - @include focus-outline(var(--wcs-com-nav-category-label-desktop-focus-outline-color), 5px, 0.1rem); - } - - @supports not selector(.label-container:focus-visible) { - .label-container:focus-within { - @include focus-outline(var(--wcs-com-nav-category-label-desktop-focus-outline-color), 5px, 0.1rem); - } - } - - button.label-container[aria-expanded="true"]:after { - position: absolute; - left: calc(calc(calc(100% / 2) + calc(var(--wcs-com-content-max-width) / 8)) - calc(1.125rem / 2)); - z-index: 2; - display: block; - width: 1.125rem; - height: 1.125rem; - content: ""; - background-color: var(--wcs-com-nav-category-desktop-menu-background-indicator-color); - transform: rotate(45deg); - } - - .item-container[data-open] { - position: absolute; - border-left: solid var(--wcs-com-nav-category-desktop-menu-border-left-width) var(--wcs-com-nav-category-desktop-menu-border-left-color); - left: calc(calc(100% / 2) + calc(var(--wcs-com-content-max-width) / 8)); - top: 0; - right: 0; - padding: var(--wcs-com-nav-category-desktop-menu-padding-top) var(--wcs-com-nav-category-desktop-menu-padding-right) var(--wcs-com-nav-category-desktop-menu-padding-bottom) var(--wcs-com-nav-category-desktop-menu-padding-left); - background-blend-mode: multiply; - background-color: var(--wcs-com-nav-category-desktop-menu-background-color); - box-sizing: border-box; - height: calc(100%); - - margin-left: unset; - margin-top: unset; - - display: flex; - flex-direction: column; - gap: var(--wcs-com-nav-category-desktop-menu-gap); - align-items: start; - - ::slotted(wcs-com-nav-item) { - color: var(--wcs-com-nav-category-item-desktop-color); - font-weight: var(--wcs-com-nav-category-item-desktop-font-weight); - } - - ::slotted(wcs-com-nav-item:focus-within) { - @include com-nav-submenu-item-desktop-focus-outline(var(--wcs-com-nav-category-item-desktop-color)); - } - } +:host(:not(.has-custom-breakpoint)) { + @include for-tablet-portrait-up { + @include wcs-com-nav-category-desktop; } } + +:host(.has-custom-breakpoint.desktop) { + @include wcs-com-nav-category-desktop; +} \ No newline at end of file diff --git a/src/components/com-nav-category/com-nav-category.tsx b/src/components/com-nav-category/com-nav-category.tsx index e7359894..2f2d2326 100644 --- a/src/components/com-nav-category/com-nav-category.tsx +++ b/src/components/com-nav-category/com-nav-category.tsx @@ -67,6 +67,8 @@ export class ComNavCategory implements ComponentInterface, MutableAriaAttribute */ @State() public currentActiveSizing: ComNavSize = 'desktop'; + @State() customBreakpoint?: number; + @Listen('click', {target: 'window'}) onWindowClickEvent(_: MouseEvent) { if (this.categoryOpen) this.categoryOpen = false; @@ -109,6 +111,14 @@ export class ComNavCategory implements ComponentInterface, MutableAriaAttribute setOrRemoveAttribute(this.nativeButton, attr, value); } + @Method() + public async setCustomBreakpoint(customBreakpoint: number) { + this.resizeObserver?.disconnect(); + this.resizeObserver = comNavDidLoadWithResizeObserver(this, customBreakpoint); + this.resizeObserver.observe(document.body); + this.customBreakpoint = customBreakpoint; + } + /** * Close the category */ @@ -151,8 +161,10 @@ export class ComNavCategory implements ComponentInterface, MutableAriaAttribute } render(): any { + const hasCustomBreakpoint = this.customBreakpoint ? 'has-custom-breakpoint' : ''; + return ( - this.onClick(evt)}> + this.onClick(evt)} class={`${hasCustomBreakpoint} ${this.currentActiveSizing}`}> { this.currentActiveSizing === 'mobile' ?