mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[CL-718] nav updates (#15226)
* add basic new nav item styling * update alt-3 bg color * add x padding to item * remove copy left in error * style app switcher to match nav items * adding new button hover colors * add new logo lockups * use new logos in web vault * fix color and svg fills * use set height for nav items * optimize SVGs * move if logic * use rem for icon size * move shield logo * use shield svg for collapsed icon * remove unused eslint disable directive * run prettier * remove variables * update logo hover styles * use more standard flow control syntax * update admin console logo svg * add new hover variables * use class instead of fill * use variable for logo hover * remove unnecessary container * use hover variable for nav switcher * use correct variables for fill colors * update hover state to use variable left in error * give icon width to preserve text alignment * remove tree story as functionality no longer supported * remove nested styles helper * remove obsolete afterContentInit * remove tree example from layout story * remove tree example from secondary layout story * remove tree example from kitchen sink story * Fix interaction test * remove remaining references to tree variant
This commit is contained in:
@@ -10,7 +10,8 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
||||
import { IconModule, Icon } from "../icon";
|
||||
import { BitwardenLogo, BitwardenShield } from "../icon/icons";
|
||||
import { BitwardenLogo } from "../icon/icons";
|
||||
import { BitwardenShield } from "../icon/logos";
|
||||
import { SharedModule } from "../shared";
|
||||
import { TypographyModule } from "../typography";
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { svgIcon } from "../icon";
|
||||
|
||||
export const BitwardenShield = svgIcon`
|
||||
<svg viewBox="0 0 120 132" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="tw-fill-marketing-logo" d="M82.2944 69.1899V37.2898H60V93.9624C63.948 91.869 67.4812 89.5927 70.5998 87.1338C78.3962 81.0196 82.2944 75.0383 82.2944 69.1899ZM91.8491 30.9097V69.1899C91.8491 72.0477 91.2934 74.8805 90.182 77.6883C89.0706 80.4962 87.6938 82.9884 86.0516 85.1649C84.4094 87.3415 82.452 89.4598 80.1794 91.5201C77.9068 93.5803 75.8084 95.2916 73.8842 96.654C71.96 98.0164 69.9528 99.304 67.8627 100.517C65.7726 101.73 64.288 102.552 63.4088 102.984C62.5297 103.416 61.8247 103.748 61.2939 103.981C60.8958 104.18 60.4645 104.28 60 104.28C59.5355 104.28 59.1042 104.18 58.7061 103.981C58.1753 103.748 57.4703 103.416 56.5911 102.984C55.712 102.552 54.2273 101.73 52.1372 100.517C50.0471 99.304 48.04 98.0164 46.1158 96.654C44.1916 95.2916 42.0932 93.5803 39.8206 91.5201C37.548 89.4598 35.5906 87.3415 33.9484 85.1649C32.3062 82.9884 30.9294 80.4962 29.818 77.6883C28.7066 74.8805 28.1509 72.0477 28.1509 69.1899V30.9097C28.1509 30.0458 28.4661 29.2981 29.0964 28.6668C29.7267 28.0354 30.4732 27.7197 31.3358 27.7197H88.6642C89.5268 27.7197 90.2732 28.0354 90.9036 28.6668C91.5339 29.2981 91.8491 30.0458 91.8491 30.9097Z" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from "./bitwarden-logo.icon";
|
||||
export * from "./bitwarden-shield.icon";
|
||||
export * from "./extension-bitwarden-logo.icon";
|
||||
export * from "./lock.icon";
|
||||
export * from "./generator";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./icon.module";
|
||||
export * from "./icon";
|
||||
export * as Icons from "./icons";
|
||||
export { AdminConsoleLogo, PasswordManagerLogo, SecretsManagerLogo } from "./logos";
|
||||
|
||||
File diff suppressed because one or more lines are too long
4
libs/components/src/icon/logos/bitwarden/index.ts
Normal file
4
libs/components/src/icon/logos/bitwarden/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as AdminConsoleLogo } from "./admin-console";
|
||||
export { default as BitwardenShield } from "./shield";
|
||||
export { default as PasswordManagerLogo } from "./password-manager";
|
||||
export { default as SecretsManagerLogo } from "./secrets-manager";
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
libs/components/src/icon/logos/bitwarden/shield.ts
Normal file
7
libs/components/src/icon/logos/bitwarden/shield.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { svgIcon } from "../../icon";
|
||||
|
||||
const BitwardenShield = svgIcon`
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 28 33"><path d="M26.696.403A1.274 1.274 0 0 0 25.764 0H1.83C1.467 0 1.16.137.898.403a1.294 1.294 0 0 0-.398.944v16.164c0 1.203.235 2.405.697 3.587.462 1.188 1.038 2.24 1.728 3.155.682.922 1.5 1.815 2.453 2.68a28.077 28.077 0 0 0 2.63 2.167 32.181 32.181 0 0 0 2.518 1.628c.875.511 1.493.857 1.863 1.045.37.18.661.324.882.417.163.087.348.13.54.13.192 0 .377-.043.54-.13.221-.1.52-.237.882-.417.37-.18.989-.534 1.863-1.045a34.4 34.4 0 0 0 2.517-1.628c.804-.576 1.679-1.296 2.631-2.168a20.206 20.206 0 0 0 2.454-2.68 13.599 13.599 0 0 0 1.72-3.154c.463-1.189.697-2.384.697-3.587V1.347a1.406 1.406 0 0 0-.42-.944ZM23.61 17.662c0 5.849-9.813 10.89-9.813 10.89V3.458h9.813v14.205Z" class="tw-fill-marketing-logo"/></svg>
|
||||
`;
|
||||
|
||||
export default BitwardenShield;
|
||||
1
libs/components/src/icon/logos/index.ts
Normal file
1
libs/components/src/icon/logos/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./bitwarden";
|
||||
@@ -55,207 +55,15 @@ export const WithContent: Story = {
|
||||
template: /* HTML */ `
|
||||
<bit-layout>
|
||||
<bit-side-nav>
|
||||
<bit-nav-item text="Item A" route="#" icon="bwi-lock"></bit-nav-item>
|
||||
<bit-nav-group text="Tree A" icon="bwi-family" [open]="true">
|
||||
<bit-nav-group
|
||||
text="Level 1 - with children (empty)"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 1 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
text="Level 1 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-group
|
||||
text="Level 2 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item
|
||||
text="Level 3 - no children, no icon"
|
||||
route="#"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
text="Level 3 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item
|
||||
text="Level 4 - no children, no icon"
|
||||
route="#"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group
|
||||
text="Level 2 - with children (empty)"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
></bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 2 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 1 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group text="Hello World (Anchor)" [route]="['a']" icon="bwi-filter">
|
||||
<bit-nav-item text="Child A" route="a" icon="bwi-filter"></bit-nav-item>
|
||||
<bit-nav-item text="Child B" route="b"></bit-nav-item>
|
||||
<bit-nav-item text="Child C" route="c" icon="bwi-filter"></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group text="Tree B" icon="bwi-collection-shared" [open]="true">
|
||||
<bit-nav-group
|
||||
text="Level 1 - with children (empty)"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 1 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
text="Level 1 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-group
|
||||
text="Level 2 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item
|
||||
text="Level 3 - no children, no icon"
|
||||
route="#"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
text="Level 3 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item
|
||||
text="Level 4 - no children, no icon"
|
||||
route="#"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group
|
||||
text="Level 2 - with children (empty)"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
></bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 2 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 1 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group text="Tree C" icon="bwi-key" [open]="true">
|
||||
<bit-nav-group
|
||||
text="Level 1 - with children (empty)"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 1 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
text="Level 1 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-group
|
||||
text="Level 2 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item
|
||||
text="Level 3 - no children, no icon"
|
||||
route="#"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
text="Level 3 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item
|
||||
text="Level 4 - no children, no icon"
|
||||
route="#"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group
|
||||
text="Level 2 - with children (empty)"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
></bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 2 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 1 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group text="Lorem Ipsum (Button)" icon="bwi-filter">
|
||||
<bit-nav-item text="Child A" icon="bwi-filter"></bit-nav-item>
|
||||
<bit-nav-item text="Child B"></bit-nav-item>
|
||||
<bit-nav-item text="Child C" icon="bwi-filter"></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-side-nav>
|
||||
<bit-callout title="Foobar"> Hello world! </bit-callout>
|
||||
@@ -277,77 +85,15 @@ export const Secondary: Story = {
|
||||
template: /* HTML */ `
|
||||
<bit-layout>
|
||||
<bit-side-nav variant="secondary">
|
||||
<bit-nav-item text="Item A" icon="bwi-collection-shared"></bit-nav-item>
|
||||
<bit-nav-item text="Item B" icon="bwi-collection-shared"></bit-nav-item>
|
||||
<bit-nav-divider></bit-nav-divider>
|
||||
<bit-nav-item text="Item C" icon="bwi-collection-shared"></bit-nav-item>
|
||||
<bit-nav-item text="Item D" icon="bwi-collection-shared"></bit-nav-item>
|
||||
<bit-nav-group text="Tree example" icon="bwi-collection-shared" [open]="true">
|
||||
<bit-nav-group
|
||||
text="Level 1 - with children (empty)"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 1 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
text="Level 1 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-group
|
||||
text="Level 2 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item
|
||||
text="Level 3 - no children, no icon"
|
||||
route="#"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group
|
||||
text="Level 3 - with children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item
|
||||
text="Level 4 - no children, no icon"
|
||||
route="#"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group
|
||||
text="Level 2 - with children (empty)"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
></bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 2 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-item
|
||||
text="Level 1 - no children"
|
||||
route="#"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
></bit-nav-item>
|
||||
<bit-nav-group text="Hello World (Anchor)" [route]="['a']" icon="bwi-filter">
|
||||
<bit-nav-item text="Child A" route="a" icon="bwi-filter"></bit-nav-item>
|
||||
<bit-nav-item text="Child B" route="b"></bit-nav-item>
|
||||
<bit-nav-item text="Child C" route="c" icon="bwi-filter"></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group text="Lorem Ipsum (Button)" icon="bwi-filter">
|
||||
<bit-nav-item text="Child A" icon="bwi-filter"></bit-nav-item>
|
||||
<bit-nav-item text="Child B"></bit-nav-item>
|
||||
<bit-nav-item text="Child C" icon="bwi-filter"></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-side-nav>
|
||||
<bit-callout title="Foobar"> Hello world! </bit-callout>
|
||||
|
||||
@@ -55,16 +55,6 @@ export abstract class NavBaseComponent {
|
||||
matrixParams: "ignored",
|
||||
};
|
||||
|
||||
/**
|
||||
* If this item is used within a tree, set `variant` to `"tree"`
|
||||
*/
|
||||
@Input() variant: "default" | "tree" = "default";
|
||||
|
||||
/**
|
||||
* Depth level when nested inside of a `'tree'` variant
|
||||
*/
|
||||
@Input() treeDepth = 0;
|
||||
|
||||
/**
|
||||
* If `true`, do not change styles when nav item is active.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
@if (sideNavService.open$ | async) {
|
||||
<div class="tw-h-px tw-w-full tw-bg-secondary-300"></div>
|
||||
<div class="tw-h-px tw-w-full tw-my-2 tw-bg-secondary-300"></div>
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
[route]="route"
|
||||
[relativeTo]="relativeTo"
|
||||
[routerLinkActiveOptions]="routerLinkActiveOptions"
|
||||
[variant]="variant"
|
||||
[treeDepth]="treeDepth"
|
||||
(mainContentClicked)="handleMainContentClicked()"
|
||||
[ariaLabel]="ariaLabel"
|
||||
[hideActiveStyles]="parentHideActiveStyles"
|
||||
@@ -16,9 +14,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="tw-ms-auto"
|
||||
[bitIconButton]="
|
||||
open ? 'bwi-angle-up' : variant === 'tree' ? 'bwi-angle-right' : 'bwi-angle-down'
|
||||
"
|
||||
[bitIconButton]="open ? 'bwi-angle-up' : 'bwi-angle-down'"
|
||||
[buttonType]="'light'"
|
||||
(click)="toggle($event)"
|
||||
size="small"
|
||||
@@ -29,17 +25,9 @@
|
||||
[attr.aria-label]="['toggleCollapse' | i18n, text].join(' ')"
|
||||
></button>
|
||||
</ng-template>
|
||||
<!-- Show toggle to the left for trees otherwise to the right -->
|
||||
@if (variant === "tree") {
|
||||
<ng-container slot="start">
|
||||
<ng-container *ngTemplateOutlet="button"></ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
<ng-container slot="end">
|
||||
<ng-content select="[slot=end]"></ng-content>
|
||||
@if (variant !== "tree") {
|
||||
<ng-container *ngTemplateOutlet="button"></ng-container>
|
||||
}
|
||||
<ng-container *ngTemplateOutlet="button"></ng-container>
|
||||
</ng-container>
|
||||
</bit-nav-item>
|
||||
<!-- [attr.aria-controls] of the above button expects a unique ID on the controlled element -->
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import {
|
||||
AfterContentInit,
|
||||
booleanAttribute,
|
||||
Component,
|
||||
ContentChildren,
|
||||
@@ -29,7 +28,7 @@ import { SideNavService } from "./side-nav.service";
|
||||
],
|
||||
imports: [CommonModule, NavItemComponent, IconButtonModule, I18nPipe],
|
||||
})
|
||||
export class NavGroupComponent extends NavBaseComponent implements AfterContentInit {
|
||||
export class NavGroupComponent extends NavBaseComponent {
|
||||
@ContentChildren(NavBaseComponent, {
|
||||
descendants: true,
|
||||
})
|
||||
@@ -80,18 +79,6 @@ export class NavGroupComponent extends NavBaseComponent implements AfterContentI
|
||||
this.setOpen(!this.open);
|
||||
}
|
||||
|
||||
/**
|
||||
* - For any nested NavGroupComponents or NavItemComponents, increment the `treeDepth` by 1.
|
||||
*/
|
||||
private initNestedStyles() {
|
||||
if (this.variant !== "tree") {
|
||||
return;
|
||||
}
|
||||
[...this.nestedNavComponents].forEach((navGroupOrItem) => {
|
||||
navGroupOrItem.treeDepth += 1;
|
||||
});
|
||||
}
|
||||
|
||||
protected handleMainContentClicked() {
|
||||
if (!this.sideNavService.open) {
|
||||
if (!this.route) {
|
||||
@@ -103,8 +90,4 @@ export class NavGroupComponent extends NavBaseComponent implements AfterContentI
|
||||
}
|
||||
this.mainContentClicked.emit();
|
||||
}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
this.initNestedStyles();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,31 +111,6 @@ export const HideEmptyGroups: StoryObj<NavGroupComponent & { renderChildren: boo
|
||||
}),
|
||||
};
|
||||
|
||||
export const Tree: StoryObj<NavGroupComponent> = {
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: /*html*/ `
|
||||
<bit-side-nav>
|
||||
<bit-nav-group text="Tree example" icon="bwi-collection-shared" [open]="true">
|
||||
<bit-nav-group text="Level 1 - with children (empty)" route="t1" icon="bwi-collection-shared" variant="tree"></bit-nav-group>
|
||||
<bit-nav-item text="Level 1 - no children" route="t2" icon="bwi-collection-shared" variant="tree"></bit-nav-item>
|
||||
<bit-nav-group text="Level 1 - with children" route="t3" icon="bwi-collection-shared" variant="tree" [open]="true">
|
||||
<bit-nav-group text="Level 2 - with children" route="t4" icon="bwi-collection-shared" variant="tree" [open]="true">
|
||||
<bit-nav-item text="Level 3 - no children, no icon" route="t5" variant="tree"></bit-nav-item>
|
||||
<bit-nav-group text="Level 3 - with children" route="t6" icon="bwi-collection-shared" variant="tree" [open]="true">
|
||||
<bit-nav-item text="Level 4 - no children, no icon" route="t7" variant="tree"></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group text="Level 2 - with children (empty)" route="t8" icon="bwi-collection-shared" variant="tree" [open]="true"></bit-nav-group>
|
||||
<bit-nav-item text="Level 2 - no children" route="t9" icon="bwi-collection-shared" variant="tree"></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-item text="Level 1 - no children" route="t10" icon="bwi-collection-shared" variant="tree"></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-side-nav>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const Secondary: StoryObj<NavGroupComponent> = {
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
|
||||
@@ -1,113 +1,77 @@
|
||||
<ng-container
|
||||
*ngIf="{
|
||||
open: sideNavService.open$ | async,
|
||||
} as data"
|
||||
>
|
||||
<div
|
||||
*ngIf="data.open || icon"
|
||||
class="tw-relative"
|
||||
[ngClass]="[
|
||||
showActiveStyles
|
||||
? 'tw-bg-background-alt4'
|
||||
: 'tw-bg-background-alt3 hover:tw-bg-primary-300/60',
|
||||
fvwStyles$ | async,
|
||||
]"
|
||||
>
|
||||
<div class="tw-ps-2 tw-pe-2">
|
||||
@let open = sideNavService.open$ | async;
|
||||
@if (open || icon) {
|
||||
<div
|
||||
[ngStyle]="{
|
||||
'padding-left': data.open ? (variant === 'tree' ? 2.5 : 1) + treeDepth * 1.5 + 'rem' : '0',
|
||||
}"
|
||||
class="tw-relative tw-flex"
|
||||
class="tw-relative tw-rounded-md tw-h-10"
|
||||
[ngClass]="[
|
||||
showActiveStyles
|
||||
? 'tw-bg-background-alt4'
|
||||
: 'tw-bg-background-alt3 hover:tw-bg-hover-contrast',
|
||||
fvwStyles$ | async,
|
||||
]"
|
||||
>
|
||||
<div [ngClass]="[variant === 'tree' ? 'tw-py-1' : 'tw-py-2']">
|
||||
<div
|
||||
#slotStart
|
||||
class="[&>*:focus-visible::before]:!tw-ring-text-alt2 [&>*:hover]:!tw-border-text-alt2 [&>*]:!tw-text-alt2"
|
||||
>
|
||||
<ng-content select="[slot=start]"></ng-content>
|
||||
</div>
|
||||
<!-- Default content for #slotStart (for consistent sizing) -->
|
||||
<div
|
||||
*ngIf="slotStart.childElementCount === 0"
|
||||
[ngClass]="{
|
||||
'tw-w-0': variant !== 'tree',
|
||||
}"
|
||||
>
|
||||
<div class="tw-relative tw-flex tw-items-center tw-h-full">
|
||||
<ng-container *ngIf="route; then isAnchor; else isButton"></ng-container>
|
||||
|
||||
<!-- Main content of `NavItem` -->
|
||||
<ng-template #anchorAndButtonContent>
|
||||
<div
|
||||
[title]="text"
|
||||
class="tw-truncate tw-gap-2 tw-items-center tw-font-bold"
|
||||
[ngClass]="{ 'tw-text-center': !open, 'tw-flex': open }"
|
||||
>
|
||||
<i
|
||||
class="!tw-m-0 tw-w-4 bwi bwi-fw tw-text-alt2 {{ icon }}"
|
||||
[attr.aria-hidden]="open"
|
||||
[attr.aria-label]="text"
|
||||
></i>
|
||||
@if (open) {
|
||||
<span>{{ text }}</span>
|
||||
}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<!-- Show if a value was passed to `this.to` -->
|
||||
<ng-template #isAnchor>
|
||||
<!-- The `data-fvw` attribute passes focus to `this.focusVisibleWithin$` -->
|
||||
<!-- The following `class` field should match the `#isButton` class field below -->
|
||||
<a
|
||||
class="tw-w-full tw-px-3 tw-block tw-truncate tw-border-none tw-bg-transparent tw-text-start !tw-text-alt2 hover:tw-text-alt2 hover:tw-no-underline focus:tw-outline-none"
|
||||
data-fvw
|
||||
[routerLink]="route"
|
||||
[relativeTo]="relativeTo"
|
||||
[attr.aria-label]="ariaLabel || text"
|
||||
routerLinkActive
|
||||
[routerLinkActiveOptions]="routerLinkActiveOptions"
|
||||
[ariaCurrentWhenActive]="'page'"
|
||||
(isActiveChange)="setIsActive($event)"
|
||||
(click)="mainContentClicked.emit()"
|
||||
>
|
||||
<ng-container *ngTemplateOutlet="anchorAndButtonContent"></ng-container>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<!-- Show if `this.to` is falsy -->
|
||||
<ng-template #isButton>
|
||||
<!-- Class field should match `#isAnchor` class field above -->
|
||||
<button
|
||||
type="button"
|
||||
class="tw-invisible"
|
||||
[bitIconButton]="'bwi-angle-down'"
|
||||
size="small"
|
||||
aria-hidden="true"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="route; then isAnchor; else isButton"></ng-container>
|
||||
|
||||
<!-- Main content of `NavItem` -->
|
||||
<ng-template #anchorAndButtonContent>
|
||||
<div
|
||||
[title]="text"
|
||||
class="tw-truncate"
|
||||
[ngClass]="[
|
||||
variant === 'tree' ? 'tw-py-1' : 'tw-py-2',
|
||||
data.open ? 'tw-pe-4' : 'tw-text-center',
|
||||
]"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw tw-text-alt2 tw-mx-1 {{ icon }}"
|
||||
[attr.aria-hidden]="data.open"
|
||||
[attr.aria-label]="text"
|
||||
></i
|
||||
><span
|
||||
*ngIf="data.open"
|
||||
[ngClass]="showActiveStyles ? 'tw-font-bold' : 'tw-font-semibold'"
|
||||
>{{ text }}</span
|
||||
class="tw-w-full tw-px-3 tw-truncate tw-border-none tw-bg-transparent tw-text-start !tw-text-alt2 hover:tw-text-alt2 hover:tw-no-underline focus:tw-outline-none"
|
||||
data-fvw
|
||||
(click)="mainContentClicked.emit()"
|
||||
>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-container *ngTemplateOutlet="anchorAndButtonContent"></ng-container>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<!-- Show if a value was passed to `this.to` -->
|
||||
<ng-template #isAnchor>
|
||||
<!-- The `data-fvw` attribute passes focus to `this.focusVisibleWithin$` -->
|
||||
<!-- The following `class` field should match the `#isButton` class field below -->
|
||||
<a
|
||||
class="tw-w-full tw-truncate tw-border-none tw-bg-transparent tw-p-0 tw-text-start !tw-text-alt2 hover:tw-text-alt2 hover:tw-no-underline focus:tw-outline-none"
|
||||
data-fvw
|
||||
[routerLink]="route"
|
||||
[relativeTo]="relativeTo"
|
||||
[attr.aria-label]="ariaLabel || text"
|
||||
routerLinkActive
|
||||
[routerLinkActiveOptions]="routerLinkActiveOptions"
|
||||
[ariaCurrentWhenActive]="'page'"
|
||||
(isActiveChange)="setIsActive($event)"
|
||||
(click)="mainContentClicked.emit()"
|
||||
>
|
||||
<ng-container *ngTemplateOutlet="anchorAndButtonContent"></ng-container>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<!-- Show if `this.to` is falsy -->
|
||||
<ng-template #isButton>
|
||||
<!-- Class field should match `#isAnchor` class field above -->
|
||||
<button
|
||||
type="button"
|
||||
class="tw-w-full tw-truncate tw-border-none tw-bg-transparent tw-p-0 tw-text-start !tw-text-alt2 hover:tw-text-alt2 hover:tw-no-underline focus:tw-outline-none"
|
||||
data-fvw
|
||||
(click)="mainContentClicked.emit()"
|
||||
>
|
||||
<ng-container *ngTemplateOutlet="anchorAndButtonContent"></ng-container>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div
|
||||
*ngIf="data.open"
|
||||
class="tw-flex -tw-ms-3 tw-pe-4 tw-gap-1 [&>*:focus-visible::before]:!tw-ring-text-alt2 [&>*:hover]:!tw-border-text-alt2 [&>*]:tw-text-alt2 empty:tw-hidden"
|
||||
[ngClass]="[variant === 'tree' ? 'tw-py-1' : 'tw-py-2']"
|
||||
>
|
||||
<ng-content select="[slot=end]"></ng-content>
|
||||
@if (open) {
|
||||
<div
|
||||
class="tw-flex tw-items-center tw-pe-1.5 tw-gap-1 [&>*:focus-visible::before]:!tw-ring-text-alt2 [&>*:hover]:!tw-border-text-alt2 [&>*]:tw-text-alt2 empty:tw-hidden"
|
||||
>
|
||||
<ng-content select="[slot=end]"></ng-content>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
@if (sideNavService.open) {
|
||||
<div class="tw-sticky tw-top-0 tw-z-50">
|
||||
<a
|
||||
[routerLink]="route"
|
||||
class="tw-px-5 tw-pb-5 tw-pt-7 tw-block tw-bg-background-alt3 tw-outline-none focus-visible:tw-ring focus-visible:tw-ring-inset focus-visible:tw-ring-text-alt2"
|
||||
[attr.aria-label]="label"
|
||||
[title]="label"
|
||||
routerLinkActive
|
||||
[ariaCurrentWhenActive]="'page'"
|
||||
>
|
||||
<bit-icon [icon]="openIcon"></bit-icon>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
@if (!sideNavService.open) {
|
||||
<bit-nav-item
|
||||
class="tw-block tw-pt-7"
|
||||
[hideActiveStyles]="true"
|
||||
[route]="route"
|
||||
[icon]="closedIcon"
|
||||
[text]="label"
|
||||
></bit-nav-item>
|
||||
}
|
||||
<div
|
||||
[ngClass]="{
|
||||
'tw-sticky tw-top-0 tw-z-50 tw-pb-2': sideNavService.open,
|
||||
'tw-pb-5': !sideNavService.open,
|
||||
}"
|
||||
class="tw-px-2 tw-pt-5"
|
||||
>
|
||||
<a
|
||||
[routerLink]="route"
|
||||
class="tw-p-3 tw-block tw-rounded-md tw-bg-background-alt3 tw-outline-none focus-visible:tw-ring focus-visible:tw-ring-inset focus-visible:tw-ring-text-alt2 hover:tw-bg-hover-contrast [&_path]:tw-fill-text-alt2"
|
||||
[ngClass]="{ '[&_svg]:tw-w-[1.687rem]': !sideNavService.open }"
|
||||
[attr.aria-label]="label"
|
||||
[title]="label"
|
||||
routerLinkActive
|
||||
[ariaCurrentWhenActive]="'page'"
|
||||
>
|
||||
<bit-icon [icon]="sideNavService.open ? openIcon : closedIcon"></bit-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { RouterLinkActive, RouterLink } from "@angular/router";
|
||||
|
||||
import { Icon } from "../icon";
|
||||
import { BitIconComponent } from "../icon/icon.component";
|
||||
import { BitwardenShield } from "../icon/logos";
|
||||
|
||||
import { NavItemComponent } from "./nav-item.component";
|
||||
import { SideNavService } from "./side-nav.service";
|
||||
|
||||
@Component({
|
||||
selector: "bit-nav-logo",
|
||||
templateUrl: "./nav-logo.component.html",
|
||||
imports: [RouterLinkActive, RouterLink, BitIconComponent, NavItemComponent],
|
||||
imports: [CommonModule, RouterLinkActive, RouterLink, BitIconComponent],
|
||||
})
|
||||
export class NavLogoComponent {
|
||||
/** Icon that is displayed when the side nav is closed */
|
||||
@Input() closedIcon = "bwi-shield";
|
||||
@Input() closedIcon = BitwardenShield;
|
||||
|
||||
/** Icon that is displayed when the side nav is open */
|
||||
@Input({ required: true }) openIcon: Icon;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
'--color-text-alt2': 'var(--color-text-main)',
|
||||
'--color-background-alt3': 'var(--color-secondary-100)',
|
||||
'--color-background-alt4': 'var(--color-secondary-300)',
|
||||
'--color-hover-contrast': 'var(--color-hover-default)',
|
||||
}
|
||||
"
|
||||
[cdkTrapFocus]="data.isOverlay"
|
||||
|
||||
@@ -81,16 +81,18 @@ export const Default: Story = {
|
||||
template: /* HTML */ `<bit-layout>
|
||||
<bit-side-nav>
|
||||
<bit-nav-group text="Password Managers" icon="bwi-collection-shared" [open]="true">
|
||||
<bit-nav-group
|
||||
text="Favorites"
|
||||
icon="bwi-collection-shared"
|
||||
variant="tree"
|
||||
[open]="true"
|
||||
>
|
||||
<bit-nav-item text="Bitwarden" route="bitwarden"></bit-nav-item>
|
||||
<bit-nav-divider></bit-nav-divider>
|
||||
</bit-nav-group>
|
||||
<bit-nav-item text="Virtual Scroll" route="virtual-scroll"></bit-nav-item>
|
||||
<bit-nav-item text="Child A" route="a" icon="bwi-filter"></bit-nav-item>
|
||||
<bit-nav-item text="Child B" route="b"></bit-nav-item>
|
||||
<bit-nav-item
|
||||
text="Virtual Scroll"
|
||||
route="virtual-scroll"
|
||||
icon="bwi-filter"
|
||||
></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
<bit-nav-group text="Favorites" icon="bwi-filter">
|
||||
<bit-nav-item text="Favorites Child A" icon="bwi-filter"></bit-nav-item>
|
||||
<bit-nav-item text="Favorites Child B"></bit-nav-item>
|
||||
<bit-nav-item text="Favorites Child C" icon="bwi-filter"></bit-nav-item>
|
||||
</bit-nav-group>
|
||||
</bit-side-nav>
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
--color-background: 255 255 255;
|
||||
--color-background-alt: 243 246 249;
|
||||
--color-background-alt2: 23 92 219;
|
||||
--color-background-alt3: 26 65 172;
|
||||
--color-background-alt3: 22 55 146;
|
||||
--color-background-alt4: 2 15 102;
|
||||
|
||||
--color-primary-100: 219 229 246;
|
||||
@@ -56,6 +56,9 @@
|
||||
--color-text-alt2: 255 255 255;
|
||||
--color-text-code: 192 17 118;
|
||||
|
||||
--color-hover-default: rgb(26 65 172 / 0.1);
|
||||
--color-hover-contrast: rgb(219 229 246 / 0.15);
|
||||
|
||||
--color-marketing-logo: 23 93 220;
|
||||
|
||||
--tw-ring-offset-color: #ffffff;
|
||||
@@ -124,6 +127,9 @@
|
||||
--color-text-alt2: 255 255 255;
|
||||
--color-text-code: 255 143 208;
|
||||
|
||||
--color-hover-default: rgb(170 195 239 / 0.1);
|
||||
--color-hover-contrast: rgb(26 39 78 / 0.15);
|
||||
|
||||
--color-marketing-logo: 255 255 255;
|
||||
|
||||
--tw-ring-offset-color: #1f242e;
|
||||
|
||||
@@ -83,6 +83,10 @@ module.exports = {
|
||||
alt3: rgba("--color-background-alt3"),
|
||||
alt4: rgba("--color-background-alt4"),
|
||||
},
|
||||
hover: {
|
||||
default: "var(--color-hover-default)",
|
||||
contrast: "var(--color-hover-contrast)",
|
||||
},
|
||||
"marketing-logo": rgba("--color-marketing-logo"),
|
||||
illustration: {
|
||||
outline: rgba("--color-illustration-outline"),
|
||||
|
||||
Reference in New Issue
Block a user