- @for (
- item of [
- { title: "Explore the Docs", link: "https://angular.dev" },
- {
- title: "Learn with Tutorials",
- link: "https://angular.dev/tutorials",
- },
- { title: "CLI Docs", link: "https://angular.dev/tools/cli" },
- {
- title: "Angular Language Service",
- link: "https://angular.dev/tools/language-service",
- },
- {
- title: "Angular DevTools",
- link: "https://angular.dev/tools/devtools",
- },
- ];
- track item.title
- ) {
-
- {{ item.title }}
-
-
- }
+@if (!isPortrait()) {
+
+
+
+
+
+
+
-
+} @else {
+
+
-
-
-
-
-
-
-
-
-
-
-
+}
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index e69de29bb..946ee6b10 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -0,0 +1,42 @@
+.layout-desktop {
+ display: flex;
+ height: 100vh;
+
+ .content {
+ flex: 1;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ border: 6px solid #24384a;
+ border-radius: 12px;
+ box-sizing: content-box;
+
+ .content-wrapper {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ }
+ }
+}
+
+.layout-tablet {
+ display: flex;
+ height: 100vh;
+
+ .content {
+ flex: 1;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+
+ .content-wrapper {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ }
+ }
+}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index a3e0702c5..e529d68ac 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,14 +1,29 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { SidenavComponent } from '@osf/sidenav/sidenav.component';
import { RouterOutlet } from '@angular/router';
+import { HeaderComponent } from '@osf/header/header.component';
+import { MainContentComponent } from '@osf/main-content/main-content.component';
+import { FooterComponent } from '@osf/footer/footer.component';
+import { TopnavComponent } from '@osf/topnav/topnav.component';
+import { IS_PORTRAIT } from '@shared/utils/breakpoints.tokens';
+import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'osf-root',
- imports: [RouterOutlet],
+ imports: [
+ SidenavComponent,
+ RouterOutlet,
+ HeaderComponent,
+ MainContentComponent,
+ FooterComponent,
+ TopnavComponent,
+ ],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
- title = 'osf';
+ private isPortrait$ = inject(IS_PORTRAIT);
+ isPortrait = toSignal(this.isPortrait$);
}
diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html
new file mode 100644
index 000000000..28c0d7d79
--- /dev/null
+++ b/src/app/footer/footer.component.html
@@ -0,0 +1 @@
+
footer works!
diff --git a/src/app/footer/footer.component.scss b/src/app/footer/footer.component.scss
new file mode 100644
index 000000000..64a90091f
--- /dev/null
+++ b/src/app/footer/footer.component.scss
@@ -0,0 +1,4 @@
+:host {
+ height: 50px;
+ background: aqua;
+}
diff --git a/src/app/footer/footer.component.spec.ts b/src/app/footer/footer.component.spec.ts
new file mode 100644
index 000000000..0070205d8
--- /dev/null
+++ b/src/app/footer/footer.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FooterComponent } from './footer.component';
+
+describe('FooterComponent', () => {
+ let component: FooterComponent;
+ let fixture: ComponentFixture
;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [FooterComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(FooterComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/footer/footer.component.ts b/src/app/footer/footer.component.ts
new file mode 100644
index 000000000..711bcacdf
--- /dev/null
+++ b/src/app/footer/footer.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ standalone: true,
+ selector: 'osf-footer',
+ imports: [],
+ templateUrl: './footer.component.html',
+ styleUrl: './footer.component.scss',
+})
+export class FooterComponent {}
diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html
new file mode 100644
index 000000000..ddbaa8fc6
--- /dev/null
+++ b/src/app/header/header.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss
new file mode 100644
index 000000000..49b29c8fa
--- /dev/null
+++ b/src/app/header/header.component.scss
@@ -0,0 +1,7 @@
+:host {
+ position: absolute;
+ height: 50px;
+ top: 0;
+ right: 0;
+ background: transparent;
+}
diff --git a/src/app/header/header.component.spec.ts b/src/app/header/header.component.spec.ts
new file mode 100644
index 000000000..cf5779579
--- /dev/null
+++ b/src/app/header/header.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeaderComponent } from './header.component';
+
+describe('HeaderComponent', () => {
+ let component: HeaderComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [HeaderComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(HeaderComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts
new file mode 100644
index 000000000..011749b13
--- /dev/null
+++ b/src/app/header/header.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ standalone: true,
+ selector: 'osf-header',
+ imports: [],
+ templateUrl: './header.component.html',
+ styleUrl: './header.component.scss',
+})
+export class HeaderComponent {}
diff --git a/src/app/main-content/main-content.component.html b/src/app/main-content/main-content.component.html
new file mode 100644
index 000000000..aec502cfa
--- /dev/null
+++ b/src/app/main-content/main-content.component.html
@@ -0,0 +1,3 @@
+
+ main-content works!
+
diff --git a/src/app/main-content/main-content.component.scss b/src/app/main-content/main-content.component.scss
new file mode 100644
index 000000000..be3b91cdb
--- /dev/null
+++ b/src/app/main-content/main-content.component.scss
@@ -0,0 +1,12 @@
+:host {
+ min-height: calc(100% - 100px);
+ padding-top: 50px;
+ background: antiquewhite;
+
+ .content-body {
+ flex: 1;
+ }
+ .main-container {
+ height: 100px;
+ }
+}
diff --git a/src/app/main-content/main-content.component.spec.ts b/src/app/main-content/main-content.component.spec.ts
new file mode 100644
index 000000000..face57c42
--- /dev/null
+++ b/src/app/main-content/main-content.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MainContentComponent } from './main-content.component';
+
+describe('MainContentComponent', () => {
+ let component: MainContentComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MainContentComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(MainContentComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/main-content/main-content.component.ts b/src/app/main-content/main-content.component.ts
new file mode 100644
index 000000000..b8dbf3e05
--- /dev/null
+++ b/src/app/main-content/main-content.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ standalone: true,
+ selector: 'osf-main-content',
+ imports: [],
+ templateUrl: './main-content.component.html',
+ styleUrl: './main-content.component.scss',
+})
+export class MainContentComponent {}
diff --git a/src/app/shared/utils/breakpoints.tokens.ts b/src/app/shared/utils/breakpoints.tokens.ts
new file mode 100644
index 000000000..b39c89d5c
--- /dev/null
+++ b/src/app/shared/utils/breakpoints.tokens.ts
@@ -0,0 +1,24 @@
+import { inject, InjectionToken } from '@angular/core';
+import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
+import { Observable, map } from 'rxjs';
+
+function createBreakpointToken(
+ query: string,
+): InjectionToken> {
+ return new InjectionToken>(`Breakpoint ${query}`, {
+ providedIn: 'root',
+ factory: () => {
+ const breakpointObserver = inject(BreakpointObserver);
+ return breakpointObserver
+ .observe([query])
+ .pipe(map((result) => result.matches));
+ },
+ });
+}
+
+export const IS_XSMALL = createBreakpointToken(Breakpoints.XSmall);
+export const IS_SMALL = createBreakpointToken(Breakpoints.Small);
+export const IS_MEDIUM = createBreakpointToken(Breakpoints.Medium);
+export const IS_LARGE = createBreakpointToken(Breakpoints.Large);
+export const IS_XLARGE = createBreakpointToken(Breakpoints.XLarge);
+export const IS_PORTRAIT = createBreakpointToken('(orientation: portrait)');
diff --git a/src/app/sidenav/sidenav.component.html b/src/app/sidenav/sidenav.component.html
new file mode 100644
index 000000000..3442f3ab8
--- /dev/null
+++ b/src/app/sidenav/sidenav.component.html
@@ -0,0 +1,6 @@
+OSF
+
+ - Home
+ - Search OSF
+ - Support
+
diff --git a/src/app/sidenav/sidenav.component.scss b/src/app/sidenav/sidenav.component.scss
new file mode 100644
index 000000000..bfd821be9
--- /dev/null
+++ b/src/app/sidenav/sidenav.component.scss
@@ -0,0 +1,7 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ width: 250px;
+ height: 100%;
+ color: white;
+}
diff --git a/src/app/sidenav/sidenav.component.spec.ts b/src/app/sidenav/sidenav.component.spec.ts
new file mode 100644
index 000000000..bcd47dfda
--- /dev/null
+++ b/src/app/sidenav/sidenav.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SidenavComponent } from './sidenav.component';
+
+describe('SidenavDComponent', () => {
+ let component: SidenavComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [SidenavComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(SidenavComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/sidenav/sidenav.component.ts b/src/app/sidenav/sidenav.component.ts
new file mode 100644
index 000000000..2a69cd824
--- /dev/null
+++ b/src/app/sidenav/sidenav.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ standalone: true,
+ selector: 'osf-sidenav',
+ imports: [],
+ templateUrl: './sidenav.component.html',
+ styleUrl: './sidenav.component.scss',
+})
+export class SidenavComponent {}
diff --git a/src/app/topnav/topnav.component.html b/src/app/topnav/topnav.component.html
new file mode 100644
index 000000000..bda3aad39
--- /dev/null
+++ b/src/app/topnav/topnav.component.html
@@ -0,0 +1 @@
+OSF
diff --git a/src/app/topnav/topnav.component.scss b/src/app/topnav/topnav.component.scss
new file mode 100644
index 000000000..f88421c37
--- /dev/null
+++ b/src/app/topnav/topnav.component.scss
@@ -0,0 +1,5 @@
+:host {
+ background: #24384a;
+ height: 96px;
+ color: white;
+}
diff --git a/src/app/topnav/topnav.component.spec.ts b/src/app/topnav/topnav.component.spec.ts
new file mode 100644
index 000000000..4b7235b54
--- /dev/null
+++ b/src/app/topnav/topnav.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TopnavComponent } from './topnav.component';
+
+describe('TopnavComponent', () => {
+ let component: TopnavComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TopnavComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(TopnavComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/topnav/topnav.component.ts b/src/app/topnav/topnav.component.ts
new file mode 100644
index 000000000..04e47034d
--- /dev/null
+++ b/src/app/topnav/topnav.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ standalone: true,
+ selector: 'osf-topnav',
+ imports: [],
+ templateUrl: './topnav.component.html',
+ styleUrl: './topnav.component.scss',
+})
+export class TopnavComponent {}
diff --git a/src/index.html b/src/index.html
index e3d3fb501..7bb289d81 100644
--- a/src/index.html
+++ b/src/index.html
@@ -8,6 +8,6 @@
-
+