diff --git a/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app-routes.tsx b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app-routes.tsx new file mode 100644 index 000000000..e65531d40 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app-routes.tsx @@ -0,0 +1,5 @@ +import Home from './home/home'; + +export const routes = [ + { path: '/', element: , text: 'Home', icon: 'home' } +]; diff --git a/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.css b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.css new file mode 100644 index 000000000..039fafee2 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.css @@ -0,0 +1,97 @@ +.app { + display: flex; + flex-flow: column nowrap; + height: 100%; + overflow: hidden; +} + +.app__navbar { + display: flex; + align-items: center; + flex: 0 0 auto; + height: 56px; + padding: 0 16px; + background: #239ef0; + box-shadow: 0 2px 4px rgba(0, 0, 0, .24); + box-sizing: border-box; +} + +.app__menu-button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + padding: 0; + color: #000; + border: 0; + background: transparent; + cursor: pointer; + font-size: 24px; +} + +.app__title { + margin: 0 0 0 16px; + color: #000; + font-size: 1.25rem; + font-weight: 600; + line-height: 1; +} + +.app__body { + display: flex; + flex: 1 1 auto; + min-height: 0; +} + +.app__drawer { + flex: 0 0 auto; + height: 100%; + --menu-full-width: 280px; +} + +igc-nav-drawer-item[active]::part(base) { + background: #e0f2ff; + color: #0075d2; +} + +igc-nav-drawer-item[active] igc-icon { + color: #0075d2; +} + +.app--mini .app__drawer { + --menu-full-width: 68px; +} + +igc-nav-drawer.app__drawer::part(base) { + transition: width 0.3s ease-out; + overflow: hidden; +} + +.app--mini igc-nav-drawer-item::part(base) { + justify-content: center; + width: 40px; + min-height: 40px; + padding: 0; + margin: 4px auto; + border-radius: 8px; +} + +.app--mini igc-nav-drawer-item::part(content) { + display: none; +} + +.app__content { + flex: 1 1 auto; + min-width: 0; + overflow: auto; + display: flex; + justify-content: center; + align-items: flex-start; +} + +@media (max-width: 1024px) { + .app__menu-button { + display: none; + } +} diff --git a/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.test.tsx b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.test.tsx new file mode 100644 index 000000000..5ff3083c9 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.test.tsx @@ -0,0 +1,20 @@ +import { beforeAll, expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import App from './app'; +import 'element-internals-polyfill'; +import { setupTestMocks } from '../setupTests'; + +beforeAll(() => { + setupTestMocks(); +}); + +test('renders without crashing', () => { + const wrapper = render( + + + + ); + + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.tsx b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.tsx new file mode 100644 index 000000000..f92274eb2 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/app.tsx @@ -0,0 +1,78 @@ +import { useState, useEffect } from 'react'; +import { Outlet, useNavigate, useLocation } from 'react-router-dom'; +import { + IgrNavDrawer, + IgrNavDrawerItem, + IgrIcon, + registerIcon, +} from 'igniteui-react'; +import { routes } from './app-routes'; +import 'igniteui-webcomponents/themes/light/bootstrap.css'; +import './app.css'; + +const materialIcons = [ + ['home', 'action/svg/production/ic_home_24px.svg'], + ['menu', 'navigation/svg/production/ic_menu_24px.svg'], + ['apps', 'navigation/svg/production/ic_apps_24px.svg'], + ['code', 'action/svg/production/ic_code_24px.svg'], + ['build', 'action/svg/production/ic_build_24px.svg'], + ['palette', 'image/svg/production/ic_palette_24px.svg'], +] as const; + +materialIcons.forEach(([name, path]) => + registerIcon(name, `https://unpkg.com/material-design-icons@3.0.1/${path}`, 'material') +); + +const navRoutes = routes.filter((r) => r.text); + +export default function App() { + const [drawerOpen, setDrawerOpen] = useState(true); + const navigate = useNavigate(); + const location = useLocation(); + + useEffect(() => { + const mq = window.matchMedia('(min-width: 1025px)'); + const update = () => setDrawerOpen(mq.matches); + update(); + mq.addEventListener('change', update); + return () => mq.removeEventListener('change', update); + }, []); + + return ( +
+
+ +

$(name)

+
+
+ + {navRoutes.map((route) => ( + navigate(route.path)} + > + + {route.text} + + ))} + +
+ +
+
+
+ ); +} diff --git a/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/home/home.tsx b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/home/home.tsx new file mode 100644 index 000000000..cc9946e08 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/home/home.tsx @@ -0,0 +1,69 @@ +import logo from "/logo.svg"; +import { IgrIcon } from "igniteui-react"; +import styles from "./style.module.css"; + +export default function App() { + return ( +
+
+

Welcome to Ignite UI for React!

+

+ A routed application shell with a responsive side navigation drawer and curated Ignite UI resources. +

+
+ + Ignite UI for React + +
+ + + + Component Demos + Browse React components and examples + + + + + + React Repository + Explore Ignite UI for React on GitHub + + + + + + Ignite UI CLI + Review the CLI source and project tooling + + + + + + Figma UI Kit + Open Infragistics design resources + + +
+
+ ); +} diff --git a/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/home/style.module.css b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/home/style.module.css new file mode 100644 index 000000000..ca8b97595 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/files/src/app/home/style.module.css @@ -0,0 +1,105 @@ +:local(.home) { + width: 100%; + max-width: 920px; + margin: auto; + padding: 48px 24px; + box-sizing: border-box; + font-family: "Titillium Web", "Segoe UI", Arial, sans-serif; +} + +:local(.intro) { + max-width: 720px; + margin: 0 auto 24px; + text-align: center; +} + +:local(.header) { + margin: 0 0 12px; + color: #09f; + font-size: 3rem; + font-weight: 600; + line-height: 1.2; +} + +:local(.description) { + margin: 0; + color: #000; + font-size: 1.125rem; + line-height: 1.5; +} + +:local(.logo) { + display: block; + width: 500px; + height: 350px; + margin: 0 auto 28px; + max-width: 100%; + object-fit: contain; +} + +:local(.resources) { + display: grid; + grid-template-columns: repeat(2, 300px); + justify-content: center; + margin: 0 auto; + gap: 4px 64px; +} + +:local(.resource) { + display: flex; + align-items: center; + min-height: 64px; + padding: 6px 0; + color: rgba(0, 0, 0, .74); + text-align: left; + text-decoration: none; +} + +:local(.resource):hover strong { + text-decoration: underline; +} + +:local(.resourceIcon) { + flex: 0 0 28px; + margin-right: 16px; + color: #09f; + font-size: 28px; +} + +:local(.resource) strong, +:local(.resource) small { + display: block; +} + +:local(.resource) strong { + margin-bottom: 2px; + color: #731963; + font-size: 1rem; +} + +:local(.resource) small { + color: #000; + font-size: 0.875rem; + line-height: 1.4; +} + +@media (max-width: 768px) { + :local(.home) { + padding: 32px 16px; + } + + :local(.header) { + font-size: 2.25rem; + } + + :local(.logo) { + width: 100%; + height: 240px; + } + + :local(.resources) { + grid-template-columns: minmax(0, 300px); + justify-content: center; + gap: 4px; + } +} diff --git a/packages/cli/templates/react/igr-ts/projects/side-nav-mini/index.ts b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/index.ts new file mode 100644 index 000000000..8e7b62f4e --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/side-nav-mini/index.ts @@ -0,0 +1,19 @@ +import { ProjectTemplate } from "@igniteui/cli-core"; +import * as path from "path"; +import { BaseWithHomeIgrTsProject } from "../_base_with_home"; + +export class SideNavMiniIgrTsProject extends BaseWithHomeIgrTsProject implements ProjectTemplate { + public id: string = "side-nav-mini"; + public name = "Side navigation + collapsible mini nav"; + public description = "Side navigation with a collapsible mini (icons-only) variant and responsive breakpoints"; + public dependencies: string[] = []; + public framework: string = "react"; + public projectType: string = "igr-ts"; + public hasExtraConfiguration: boolean = false; + public isHidden: boolean = false; + + public get templatePaths(): string[] { + return [...super.templatePaths, path.join(__dirname, "files")]; + } +} +export default new SideNavMiniIgrTsProject(); diff --git a/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/app.css b/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/app.css index 9ebd834c8..458fca9a9 100644 --- a/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/app.css +++ b/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/app.css @@ -51,9 +51,6 @@ flex: 0 0 auto; height: 100%; --ig-nav-drawer-size: 280px; - --ig-navdrawer-item-active-background: #e0f2ff; - --ig-navdrawer-item-active-text-color: #0075d2; - --ig-navdrawer-item-active-icon-color: #0075d2; } igc-nav-drawer-item[active]::part(base) { diff --git a/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/files/src/app/app-routing.ts b/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/files/src/app/app-routing.ts new file mode 100644 index 000000000..bd84d8975 --- /dev/null +++ b/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/files/src/app/app-routing.ts @@ -0,0 +1,13 @@ +import { type Route } from '@vaadin/router'; +import './home/home.js'; +import './not-found/not-found.js'; + +export interface AppRoute extends Route { + icon?: string; +} + +export const routes: AppRoute[] = [ + { path: '/', component: 'app-home', name: 'Home', icon: 'home' }, + // The fallback route should always be after other alternatives. + { path: '(.*)', component: 'app-not-found' }, +]; diff --git a/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/files/src/app/app.ts b/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/files/src/app/app.ts new file mode 100644 index 000000000..a97318025 --- /dev/null +++ b/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/files/src/app/app.ts @@ -0,0 +1,230 @@ +import { html, css, LitElement } from 'lit'; +import { customElement, state } from 'lit/decorators.js'; +import { + defineComponents, + IgcIconComponent, + IgcNavDrawerComponent, + IgcNavDrawerItemComponent, + registerIcon, +} from 'igniteui-webcomponents'; +import { Router } from '@vaadin/router'; +import { routes, type AppRoute } from './app-routing.js'; + +defineComponents( + IgcIconComponent, + IgcNavDrawerComponent, + IgcNavDrawerItemComponent, +); + +const materialIcons = [ + ['home', 'action/svg/production/ic_home_24px.svg'], + ['menu', 'navigation/svg/production/ic_menu_24px.svg'], + ['apps', 'navigation/svg/production/ic_apps_24px.svg'], + ['code', 'action/svg/production/ic_code_24px.svg'], + ['build', 'action/svg/production/ic_build_24px.svg'], + ['palette', 'image/svg/production/ic_palette_24px.svg'], +] as const; + +materialIcons.forEach(([name, path]) => + registerIcon(name, `https://unpkg.com/material-design-icons@3.0.1/${path}`, 'material') +); + +@customElement('app-root') +export default class App extends LitElement { + @state() + private drawerOpen = true; + + @state() + private currentPath = window.location.pathname; + + private mediaQuery?: MediaQueryList; + + static styles = css` + :host { + display: flex; + height: 100%; + } + + .app { + display: flex; + flex-flow: column nowrap; + width: 100%; + height: 100%; + overflow: hidden; + } + + .app__navbar { + display: flex; + align-items: center; + flex: 0 0 auto; + height: 56px; + padding: 0 16px; + background: #239ef0; + box-shadow: 0 2px 4px rgba(0, 0, 0, .24); + box-sizing: border-box; + } + + .app__menu-button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + padding: 0; + color: #000; + border: 0; + background: transparent; + cursor: pointer; + } + + .app__menu-button igc-icon { + font-size: 24px; + } + + .app__title { + margin: 0 0 0 16px; + color: #000; + font-size: 1.25rem; + font-weight: 600; + line-height: 1; + } + + .app__body { + display: flex; + flex: 1 1 auto; + min-height: 0; + } + + .app__drawer { + flex: 0 0 auto; + height: 100%; + --menu-full-width: 280px; + } + + .app--mini .app__drawer { + --menu-full-width: 68px; + } + + igc-nav-drawer.app__drawer::part(base) { + transition: width 0.3s ease-out; + overflow: hidden; + } + + .app--mini igc-nav-drawer-item::part(base) { + justify-content: center; + width: 40px; + min-height: 40px; + padding: 0; + margin: 4px auto; + border-radius: 8px; + } + + .app--mini igc-nav-drawer-item::part(content) { + display: none; + } + + igc-nav-drawer-item[active]::part(base) { + background: #e0f2ff; + color: #0075d2; + } + + igc-nav-drawer-item[active] igc-icon { + color: #0075d2; + } + + router-outlet { + flex: 1 1 auto; + display: flex; + align-items: stretch; + justify-content: center; + min-width: 0; + overflow: auto; + } + + @media (max-width: 1024px) { + .app__menu-button { + display: none; + } + } + `; + + render() { + const visibleRoutes = (routes as AppRoute[]).filter((route) => route.name); + + return html` +
+
+ +

$(name)

+
+
+ + ${visibleRoutes.map((route) => html` + this.navigate(route.path)} + > + + ${route.name} + + `)} + + +
+
+ `; + } + + connectedCallback() { + super.connectedCallback(); + this.mediaQuery = window.matchMedia('(min-width: 1025px)'); + this.updateDrawerState(); + this.mediaQuery.addEventListener('change', this.updateDrawerState); + window.addEventListener('popstate', this.updateCurrentPath); + } + + disconnectedCallback() { + this.mediaQuery?.removeEventListener('change', this.updateDrawerState); + window.removeEventListener('popstate', this.updateCurrentPath); + super.disconnectedCallback(); + } + + firstUpdated() { + const outlet = this.shadowRoot?.querySelector('router-outlet'); + const router = new Router(outlet); + router.setRoutes(routes); + } + + private toggleDrawer = () => { + this.drawerOpen = !this.drawerOpen; + }; + + private navigate(path: string) { + this.currentPath = path; + Router.go(path); + } + + private updateDrawerState = () => { + this.drawerOpen = Boolean(this.mediaQuery?.matches); + }; + + private updateCurrentPath = () => { + this.currentPath = window.location.pathname; + }; +} diff --git a/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/index.ts b/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/index.ts new file mode 100644 index 000000000..6092d6526 --- /dev/null +++ b/packages/cli/templates/webcomponents/igc-ts/projects/side-nav-mini/index.ts @@ -0,0 +1,18 @@ +import { ProjectTemplate } from "@igniteui/cli-core"; +import * as path from "path"; +import { SideNavProject } from "../side-nav"; + +export class SideNavMiniProject extends SideNavProject implements ProjectTemplate { + public id: string = "side-nav-mini"; + public name = "Side navigation + collapsible mini nav"; + public description = "Side navigation with a collapsible mini (icons-only) variant and responsive breakpoints"; + public framework: string = "webcomponents"; + public projectType: string = "igc-ts"; + public dependencies: string[] = []; + public hasExtraConfiguration: boolean = false; + + public get templatePaths() { + return [...super.templatePaths, path.join(__dirname, "files")]; + } +} +export default new SideNavMiniProject(); diff --git a/packages/cli/templates/webcomponents/igc-ts/projects/side-nav/files/src/app/app.ts b/packages/cli/templates/webcomponents/igc-ts/projects/side-nav/files/src/app/app.ts index 29a542cf0..52aeb7529 100644 --- a/packages/cli/templates/webcomponents/igc-ts/projects/side-nav/files/src/app/app.ts +++ b/packages/cli/templates/webcomponents/igc-ts/projects/side-nav/files/src/app/app.ts @@ -108,9 +108,6 @@ export default class App extends LitElement { flex: 0 0 auto; height: 100%; --ig-nav-drawer-size: 280px; - --ig-navdrawer-item-active-background: #e0f2ff; - --ig-navdrawer-item-active-text-color: #0075d2; - --ig-navdrawer-item-active-icon-color: #0075d2; } igc-nav-drawer-item[active]::part(base) { diff --git a/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.config.ts b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.config.ts new file mode 100644 index 000000000..3cd4ad001 --- /dev/null +++ b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.config.ts @@ -0,0 +1,29 @@ +import { ApplicationConfig, importProvidersFrom, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core'; +import { BrowserModule, HammerModule } from '@angular/platform-browser'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { provideRouter } from '@angular/router'; +import { + IgxNavigationDrawerModule, + IgxNavbarModule, + IgxIconModule, + IgxRippleModule +} from '<%=igxPackage%>'; + +import { routes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideBrowserGlobalErrorListeners(), + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), + importProvidersFrom( + BrowserModule, + HammerModule, + IgxNavigationDrawerModule, + IgxNavbarModule, + IgxIconModule, + IgxRippleModule + ), + provideAnimations() + ] +}; diff --git a/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.html b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.html new file mode 100644 index 000000000..2a4c499ee --- /dev/null +++ b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.html @@ -0,0 +1,29 @@ + + + + + +
+ + + @for (link of topNavLinks; track link) { + + {{link.icon}} + {{link.name}} + + } + + + @for (link of topNavLinks; track link) { + + {{link.icon}} + + } + + +
+ +
+
diff --git a/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.routes.ts b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.routes.ts new file mode 100644 index 000000000..d86b75332 --- /dev/null +++ b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.routes.ts @@ -0,0 +1,11 @@ +import { Routes } from '@angular/router'; +import { Home } from './home/home'; +import { NotFound } from './error-routing/not-found/not-found'; +import { UncaughtError } from './error-routing/error/uncaught-error'; + +export const routes: Routes = [ + { path: '', redirectTo: '/home', pathMatch: 'full'}, + { path: 'home', component: Home, data: { text: 'Home', icon: 'home' }}, + { path: 'error', component: UncaughtError }, + { path: '**', component: NotFound } // must always be last +]; diff --git a/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.scss b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.scss new file mode 100644 index 000000000..9a8437b9b --- /dev/null +++ b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.scss @@ -0,0 +1,55 @@ +// Root element fills the viewport and stacks navbar above content +app-root { + height: 100%; + display: flex; + flex-direction: column; +} + +// Navbar spans full width; height is determined by igx-navbar itself +.app-navbar { + flex-shrink: 0; + width: 100%; +} + +// Row container holds the nav drawer and the page content side by side +.row-layout { + display: flex; + flex-direction: row; + flex: 1 1 auto; + min-height: 0; // allow flex children to shrink below their content height +} + +// Page content fills remaining horizontal space and scrolls independently +.view-container { + flex: 1 1 auto; + min-width: 0; + overflow: auto; + display: flex; + flex-flow: row nowrap; + justify-content: center; + align-items: flex-start; + + > *:not(router-outlet) { + padding: 0 24px; + width: 100%; + } + + p { + margin: 15px; + } +} + +// On small screens: hide the burger button — the nav is always in mini mode +@media only screen and (max-width: 1024px) { + .nav-toggle-btn { + display: none; + } + + .view-container { + justify-content: flex-start; + + > *:not(router-outlet) { + padding: 0 15px; + } + } +} diff --git a/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.spec.ts b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.spec.ts new file mode 100644 index 000000000..a93a46d34 --- /dev/null +++ b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.spec.ts @@ -0,0 +1,20 @@ +import { TestBed } from '@angular/core/testing'; +import { RouterModule } from '@angular/router'; +import { App } from './app'; + +describe('App', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + RouterModule.forRoot([]), + App + ] + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); +}); diff --git a/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.ts b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.ts new file mode 100644 index 000000000..82c79010d --- /dev/null +++ b/packages/igx-templates/igx-ts/projects/side-nav-mini/files/src/app/app.ts @@ -0,0 +1,83 @@ +import { Component, HostListener, OnInit, AfterViewInit, viewChild, ViewEncapsulation, inject } from '@angular/core'; +import { NavigationStart, Router, RouterLinkActive, RouterLink, RouterOutlet } from '@angular/router'; +import { + IgxNavigationDrawerComponent, + IgxNavDrawerTemplateDirective, + IgxNavDrawerMiniTemplateDirective, + IgxNavDrawerItemDirective, + IgxRippleDirective, + IgxNavbarComponent, + IgxNavbarActionDirective, + IgxIconComponent, + IgxIconButtonDirective, +} from 'igniteui-angular'; +import { filter } from 'rxjs/operators'; +import { routes } from './app.routes'; + +const MINI_BREAKPOINT = 1024; + +@Component({ + selector: 'app-root', + templateUrl: './app.html', + styleUrl: './app.scss', + encapsulation: ViewEncapsulation.None, + imports: [ + IgxNavigationDrawerComponent, + IgxNavDrawerTemplateDirective, + IgxNavDrawerMiniTemplateDirective, + IgxNavDrawerItemDirective, + IgxRippleDirective, + RouterLinkActive, + RouterLink, + IgxNavbarComponent, + IgxNavbarActionDirective, + IgxIconComponent, + IgxIconButtonDirective, + RouterOutlet + ] +}) +export class App implements OnInit, AfterViewInit { + // Start open on wide screens, collapsed (mini) on narrow screens + public readonly initiallyOpen = window.innerWidth > MINI_BREAKPOINT; + public topNavLinks: Array<{ path: string; name: string; icon: string }> = []; + + public navdrawer = viewChild.required(IgxNavigationDrawerComponent); + + private router = inject(Router); + + constructor() { + for (const route of routes) { + if (route.path && route.data && route.path.indexOf('*') === -1) { + this.topNavLinks.push({ + name: route.data['text'], + icon: route.data['icon'] ?? 'radio_button_unchecked', + path: '/' + route.path + }); + } + } + } + + public ngOnInit(): void { + this.router.events.pipe( + filter((x): x is NavigationStart => x instanceof NavigationStart) + ).subscribe(() => this.updateDrawerState()); + } + + public ngAfterViewInit(): void { + this.updateDrawerState(); + } + + /** Burger menu toggle — only active on large screens */ + public toggleNav(): void { + this.navdrawer().toggle(); + } + + @HostListener('window:resize') + public updateDrawerState(): void { + const isWide = window.innerWidth > MINI_BREAKPOINT; + if (!isWide && this.navdrawer().isOpen) { + // Collapse to mini on small screens + this.navdrawer().close(); + } + } +} diff --git a/packages/igx-templates/igx-ts/projects/side-nav-mini/index.ts b/packages/igx-templates/igx-ts/projects/side-nav-mini/index.ts new file mode 100644 index 000000000..992810c78 --- /dev/null +++ b/packages/igx-templates/igx-ts/projects/side-nav-mini/index.ts @@ -0,0 +1,19 @@ +import { ProjectTemplate } from "@igniteui/cli-core"; +import * as path from "path"; +import { SideNavProject } from "../side-nav"; + +export class SideNavMiniProject extends SideNavProject implements ProjectTemplate { + public id: string = "side-nav-mini"; + public name = "Side navigation + collapsible mini nav"; + public description = "Side navigation with a collapsible mini (icons-only) variant and responsive breakpoints"; + public dependencies: string[] = []; + public framework: string = "angular"; + public projectType: string = "igx-ts"; + public hasExtraConfiguration = false; + + public get templatePaths() { + return [...super.templatePaths, path.join(__dirname, "files")]; + } +} + +export default new SideNavMiniProject();