diff --git a/apps/web/src/app/modules/board/board/board.component.html b/apps/web/src/app/modules/board/board/board.component.html
index e1db1fd4..d524422d 100644
--- a/apps/web/src/app/modules/board/board/board.component.html
+++ b/apps/web/src/app/modules/board/board/board.component.html
@@ -53,7 +53,8 @@
}
@defer {
-
+
+
@if (!isDemo) {
}
diff --git a/apps/web/src/app/modules/board/board/board.component.ts b/apps/web/src/app/modules/board/board/board.component.ts
index 4d9a644d..736634ff 100644
--- a/apps/web/src/app/modules/board/board/board.component.ts
+++ b/apps/web/src/app/modules/board/board/board.component.ts
@@ -97,6 +97,7 @@ import { PopupPortalComponent } from '@tapiz/ui/popup/popup-portal.component';
import { NotesVisibilityComponent } from '../components/notes-visibility/notes-visibility.component';
import { NoteHeightCalculatorComponent } from '@tapiz/nodes/note';
import { BoardDragDirective } from './directives/board-drag.directive';
+import { BoardHeaderOptionsComponent } from '../components/board-header-options/board-header-options.component';
@Component({
selector: 'tapiz-board',
@@ -132,6 +133,7 @@ import { BoardDragDirective } from './directives/board-drag.directive';
PopupPortalComponent,
NotesVisibilityComponent,
NoteHeightCalculatorComponent,
+ BoardHeaderOptionsComponent,
],
hostDirectives: [
CopyPasteDirective,
diff --git a/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.html b/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.html
new file mode 100644
index 00000000..599542b4
--- /dev/null
+++ b/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.html
@@ -0,0 +1,55 @@
+@if (boardMode() === 0) {
+ @if (isAdmin() && !isDemo) {
+
+ }
+
+
+}
+
+@if (allowSwitchMode()) {
+
+ @if (boardMode() === 0) {
+
+ } @else {
+
+ }
+
+}
+
+@if (boardMode() === 0 && !isDemo && showUsers() && users().length > 0) {
+
+}
diff --git a/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.scss b/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.scss
new file mode 100644
index 00000000..80b0fdf7
--- /dev/null
+++ b/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.scss
@@ -0,0 +1,165 @@
+:host {
+ --toolbar-button-height: 3rem;
+ --toolbar-button-width: 3rem;
+
+ align-items: center;
+ cursor: default;
+ display: flex;
+ justify-content: space-between;
+ right: 1rem;
+ position: fixed;
+ top: 1rem;
+ transition: background 0.5s ease;
+ background: var(--blur-bg);
+ border-radius: var(--radius-6);
+ gap: var(--spacing-1);
+ margin-inline-start: auto;
+ flex-shrink: 0;
+ padding: var(--size-2);
+ height: 65px;
+ backdrop-filter: blur(90px);
+}
+
+.change-mode-button-holder {
+ display: flex;
+}
+
+.change-mode-button {
+ align-items: center;
+ display: flex;
+ font-family:
+ "Inter",
+ -apple-system,
+ system-ui,
+ sans-serif;
+ justify-content: center;
+ block-size: 50px;
+ padding: var(--spacing-3) var(--spacing-4);
+ color: var(--grey-90);
+ background: var(--grey-30);
+ border-radius: var(--radius-6);
+ font-size: var(--font-size-1);
+
+ mat-icon {
+ --icon-size: 16px;
+ inline-size: var(--icon-size);
+ block-size: var(--icon-size);
+ font-size: var(--icon-size);
+ margin-block-start: 1px;
+ }
+
+ &:hover {
+ background: var(--grey-40);
+ }
+}
+
+.change-edit mat-icon {
+ margin-inline-end: 2px;
+}
+
+.change-close mat-icon {
+ margin-inline-start: 4px;
+}
+
+.textarea {
+ color: var(--white);
+ background: transparent;
+ border: none;
+ height: 100%;
+ outline: none;
+ overflow: hidden;
+ resize: none;
+}
+
+.button-action {
+ display: flex;
+ align-items: center;
+ color: var(--primary-color-button-bg);
+ border-radius: var(--radius-round);
+ padding: var(--spacing-2);
+ justify-content: center;
+ height: var(--toolbar-button-height);
+ width: var(--toolbar-button-width);
+ .mat-icon {
+ color: var(--primary-color-button-bg);
+ fill: var(--primary-color-button-bg);
+ }
+ &:hover {
+ transition: background 0.2s ease;
+ background: var(--grey-30);
+ }
+}
+
+.avatar {
+ border-radius: var(--radius-round);
+ width: 48px;
+ height: 48px;
+ img {
+ display: block;
+ }
+}
+
+.user-count {
+ align-items: center;
+ background: var(--grey-30);
+ border-radius: 30px;
+ box-sizing: border-box;
+ color: #2c2c2c;
+ display: inline-flex;
+ font-family:
+ "Work Sans",
+ -apple-system,
+ system-ui,
+ sans-serif;
+ font-size: var(--font-size-1);
+ font-weight: 400;
+ height: 44px;
+ width: 44px;
+ justify-content: center;
+ line-height: 1.2;
+ margin-left: 8px;
+ position: relative;
+
+ &:hover {
+ background: var(--grey-40);
+
+ .user-hover {
+ background: var(--grey-90);
+ border-radius: 20px;
+ color: var(--white);
+ display: flex;
+ align-items: center;
+ font-size: 16px;
+ font-weight: var(--font-weight-medium);
+ line-height: 1.2;
+ height: 44px;
+ width: 200%;
+ padding: 0 16px;
+ opacity: 1;
+
+ &::after {
+ content: "";
+ position: absolute;
+ top: 50%; /* Start at vertical center */
+ left: calc(100% - 0.18rem); /* Align to the right edge */
+ transform: translateY(-50%); /* Center vertically */
+
+ /* Create the triangle using borders */
+ width: 0;
+ height: 0;
+ border-left: 10px solid #1f255a; /* Color matches the bubble background */
+ border-top: 6px solid transparent;
+ border-bottom: 6px solid transparent;
+ }
+ }
+ }
+
+ .user-hover {
+ height: 0;
+ width: 0;
+ opacity: 0;
+ position: absolute;
+ right: calc(100% + 0.5rem);
+ top: 0;
+ }
+}
diff --git a/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.ts b/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.ts
new file mode 100644
index 00000000..718d68e7
--- /dev/null
+++ b/apps/web/src/app/modules/board/components/board-header-options/board-header-options.component.ts
@@ -0,0 +1,112 @@
+import {
+ Component,
+ ChangeDetectionStrategy,
+ inject,
+ input,
+} from '@angular/core';
+import { Store } from '@ngrx/store';
+import { PageActions } from '../../actions/page.actions';
+import { selectIsAdmin, selectUserId } from '../../selectors/page.selectors';
+import { ExportService } from '../../services/export.service';
+import { MatIconModule } from '@angular/material/icon';
+import { MatDialog, MatDialogModule } from '@angular/material/dialog';
+import { ShareBoardComponent } from '../share-board/share-board.component';
+import { pageFeature } from '../../reducers/page.reducer';
+import { BoardSettingsComponent } from '../board-settings/board-settings.component';
+import { toSignal } from '@angular/core/rxjs-interop';
+import { map } from 'rxjs';
+import { computed } from '@angular/core';
+import { ConfigService } from '../../../../services/config.service';
+import { MatButtonModule } from '@angular/material/button';
+import { BoardFacade } from '../../../../services/board-facade.service';
+
+@Component({
+ selector: 'tapiz-board-header-options',
+ templateUrl: './board-header-options.component.html',
+ styleUrls: ['./board-header-options.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ imports: [MatIconModule, MatDialogModule, MatButtonModule],
+})
+export class BoardHeaderOptionsComponent {
+ #exportService = inject(ExportService);
+ #store = inject(Store);
+ #dialog = inject(MatDialog);
+ #boardFacade = inject(BoardFacade);
+ #boardUsers = this.#store.selectSignal(pageFeature.selectBoardUsers);
+ #configService = inject(ConfigService);
+ #users = toSignal(
+ this.#boardFacade
+ .getUsers()
+ .pipe(map((users) => users.map((user) => user.content))),
+ { initialValue: [] },
+ );
+ userId = this.#store.selectSignal(selectUserId);
+ users = computed(() => {
+ const boardUsers = this.#boardUsers();
+
+ return this.#users()
+ .filter((user) => user.id !== this.userId())
+ .map((user) => {
+ const boardUser = boardUsers.find(
+ (boardUser) => boardUser.id === user.id,
+ );
+
+ return {
+ ...user,
+ picture: boardUser?.picture,
+ };
+ });
+ });
+ #settings = toSignal(this.#boardFacade.getSettings(), { initialValue: null });
+
+ showUsers = computed(() => {
+ return !this.#settings()?.content.anonymousMode;
+ });
+
+ allowSwitchMode = input(true);
+
+ boardMode = this.#store.selectSignal(pageFeature.selectBoardMode);
+ isAdmin = this.#store.selectSignal(selectIsAdmin);
+ boardId = this.#store.selectSignal(pageFeature.selectBoardId);
+
+ get isDemo() {
+ return !!this.#configService.config.DEMO;
+ }
+
+ changeBoardMode(boardMode: number) {
+ this.#store.dispatch(
+ PageActions.changeBoardMode({
+ boardMode,
+ }),
+ );
+ }
+
+ export() {
+ this.#exportService.getExportFile().then(
+ (url) => {
+ const fileLink = document.createElement('a');
+
+ fileLink.href = url;
+ fileLink.download = `${this.boardId()}.json`;
+ fileLink.click();
+ },
+ () => {
+ console.error('export error');
+ },
+ );
+ }
+
+ share() {
+ this.#dialog.open(ShareBoardComponent, {
+ width: '600px',
+ autoFocus: 'dialog',
+ });
+ }
+
+ settings() {
+ this.#dialog.open(BoardSettingsComponent, {
+ width: '600px',
+ autoFocus: 'dialog',
+ });
+ }
+}
diff --git a/apps/web/src/app/modules/board/components/header/header.component.html b/apps/web/src/app/modules/board/components/header/header.component.html
index ff88c0aa..28f1473b 100644
--- a/apps/web/src/app/modules/board/components/header/header.component.html
+++ b/apps/web/src/app/modules/board/components/header/header.component.html
@@ -47,60 +47,3 @@
} @else {
Demo
}
-
diff --git a/apps/web/src/app/modules/board/components/header/header.component.scss b/apps/web/src/app/modules/board/components/header/header.component.scss
index 9b37a56e..d20494d5 100644
--- a/apps/web/src/app/modules/board/components/header/header.component.scss
+++ b/apps/web/src/app/modules/board/components/header/header.component.scss
@@ -1,17 +1,12 @@
:host {
- --toolbar-button-height: 3rem;
- --toolbar-button-width: 3rem;
-
align-items: center;
cursor: default;
display: flex;
justify-content: space-between;
- left: 0;
- padding: 1rem;
+ left: 1rem;
position: fixed;
- top: 0;
+ top: 1rem;
transition: background 0.5s ease;
- width: 100%;
}
.logo {
@@ -68,60 +63,6 @@
padding: 0;
}
-.change-mode-button-holder {
- display: flex;
-}
-
-.change-mode-button {
- align-items: center;
- display: flex;
- font-family:
- "Inter",
- -apple-system,
- system-ui,
- sans-serif;
- justify-content: center;
- block-size: 50px;
- padding: var(--spacing-3) var(--spacing-4);
- color: var(--grey-90);
- background: var(--grey-30);
- border-radius: var(--radius-6);
- font-size: var(--font-size-1);
-
- mat-icon {
- --icon-size: 16px;
- inline-size: var(--icon-size);
- block-size: var(--icon-size);
- font-size: var(--icon-size);
- margin-block-start: 1px;
- }
-
- &:hover {
- background: var(--grey-40);
- }
-}
-
-.change-edit mat-icon {
- margin-inline-end: 2px;
-}
-
-.change-close mat-icon {
- margin-inline-start: 4px;
-}
-
-.header-end {
- background: var(--blur-bg);
- border-radius: var(--radius-6);
- display: flex;
- align-items: center;
- gap: var(--spacing-1);
- margin-inline-start: auto;
- flex-shrink: 0;
- position: relative;
- padding: var(--size-2);
- height: 65px;
- backdrop-filter: blur(90px);
-}
.textarea {
color: var(--white);
@@ -132,96 +73,3 @@
overflow: hidden;
resize: none;
}
-
-.button-action {
- display: flex;
- align-items: center;
- color: var(--primary-color-button-bg);
- border-radius: var(--radius-round);
- padding: var(--spacing-2);
- justify-content: center;
- height: var(--toolbar-button-height);
- width: var(--toolbar-button-width);
- .mat-icon {
- color: var(--primary-color-button-bg);
- fill: var(--primary-color-button-bg);
- }
- &:hover {
- transition: background 0.2s ease;
- background: var(--grey-30);
- }
-}
-
-.avatar {
- border-radius: var(--radius-round);
- width: 48px;
- height: 48px;
- img {
- display: block;
- }
-}
-
-.user-count {
- align-items: center;
- background: var(--grey-30);
- border-radius: 30px;
- box-sizing: border-box;
- color: #2c2c2c;
- display: inline-flex;
- font-family:
- "Work Sans",
- -apple-system,
- system-ui,
- sans-serif;
- font-size: var(--font-size-1);
- font-weight: 400;
- height: 44px;
- width: 44px;
- justify-content: center;
- line-height: 1.2;
- margin-left: 8px;
- position: relative;
-
- &:hover {
- background: var(--grey-40);
-
- .user-hover {
- background: var(--grey-90);
- border-radius: 20px;
- color: var(--white);
- display: flex;
- align-items: center;
- font-size: 16px;
- font-weight: var(--font-weight-medium);
- line-height: 1.2;
- height: 44px;
- width: 200%;
- padding: 0 16px;
- opacity: 1;
-
- &::after {
- content: "";
- position: absolute;
- top: 50%; /* Start at vertical center */
- left: calc(100% - 0.18rem); /* Align to the right edge */
- transform: translateY(-50%); /* Center vertically */
-
- /* Create the triangle using borders */
- width: 0;
- height: 0;
- border-left: 10px solid #1f255a; /* Color matches the bubble background */
- border-top: 6px solid transparent;
- border-bottom: 6px solid transparent;
- }
- }
- }
-
- .user-hover {
- height: 0;
- width: 0;
- opacity: 0;
- position: absolute;
- right: calc(100% + 0.5rem);
- top: 0;
- }
-}
diff --git a/apps/web/src/app/modules/board/components/header/header.component.ts b/apps/web/src/app/modules/board/components/header/header.component.ts
index c8ad5efe..a1f9f8a3 100644
--- a/apps/web/src/app/modules/board/components/header/header.component.ts
+++ b/apps/web/src/app/modules/board/components/header/header.component.ts
@@ -5,34 +5,22 @@ import {
signal,
inject,
viewChild,
- input,
} from '@angular/core';
import { Store } from '@ngrx/store';
import { BoardActions } from '../../actions/board.actions';
import { PageActions } from '../../actions/page.actions';
-import { selectIsAdmin, selectUserId } from '../../selectors/page.selectors';
-import { ExportService } from '../../services/export.service';
+import { selectIsAdmin } from '../../selectors/page.selectors';
import { ClickOutside } from 'ngxtension/click-outside';
import { AutoFocusDirective } from '../../directives/autofocus.directive';
import { RouterLink } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
-import { MatDialog, MatDialogModule } from '@angular/material/dialog';
-import { ShareBoardComponent } from '../share-board/share-board.component';
import { pageFeature } from '../../reducers/page.reducer';
-import { BoardSettingsComponent } from '../board-settings/board-settings.component';
import { HotkeysService } from '@tapiz/cdk/services/hostkeys.service';
-import {
- takeUntilDestroyed,
- toObservable,
- toSignal,
-} from '@angular/core/rxjs-interop';
-import { map, switchMap } from 'rxjs';
-import { computed } from '@angular/core';
+import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
+import { switchMap } from 'rxjs';
import { ConfigService } from '../../../../services/config.service';
import { NgOptimizedImage } from '@angular/common';
-import { appFeature } from '../../../../+state/app.reducer';
import { MatButtonModule } from '@angular/material/button';
-import { BoardFacade } from '../../../../services/board-facade.service';
@Component({
selector: 'tapiz-header',
@@ -44,61 +32,23 @@ import { BoardFacade } from '../../../../services/board-facade.service';
AutoFocusDirective,
ClickOutside,
MatIconModule,
- MatDialogModule,
MatButtonModule,
NgOptimizedImage,
],
providers: [HotkeysService],
})
export class HeaderComponent {
- #exportService = inject(ExportService);
#store = inject(Store);
- #dialog = inject(MatDialog);
#hotkeysService = inject(HotkeysService);
- #boardFacade = inject(BoardFacade);
- #boardUsers = this.#store.selectSignal(pageFeature.selectBoardUsers);
#configService = inject(ConfigService);
- #users = toSignal(
- this.#boardFacade
- .getUsers()
- .pipe(map((users) => users.map((user) => user.content))),
- { initialValue: [] },
- );
- userId = this.#store.selectSignal(selectUserId);
- users = computed(() => {
- const boardUsers = this.#boardUsers();
-
- return this.#users()
- .filter((user) => user.id !== this.userId())
- .map((user) => {
- const boardUser = boardUsers.find(
- (boardUser) => boardUser.id === user.id,
- );
-
- return {
- ...user,
- picture: boardUser?.picture,
- };
- });
- });
- #settings = toSignal(this.#boardFacade.getSettings(), { initialValue: null });
-
- showUsers = computed(() => {
- return !this.#settings()?.content.anonymousMode;
- });
-
textarea = viewChild>('textarea');
- allowSwitchMode = input(true);
-
edit = signal(false);
- boardMode = this.#store.selectSignal(pageFeature.selectBoardMode);
name = this.#store.selectSignal(pageFeature.selectName);
isAdmin = this.#store.selectSignal(selectIsAdmin);
boardId = this.#store.selectSignal(pageFeature.selectBoardId);
teamName = this.#store.selectSignal(pageFeature.selectTeamName);
teamId = this.#store.selectSignal(pageFeature.selectTeamId);
- user = this.#store.selectSignal(appFeature.selectUser);
get isDemo() {
return !!this.#configService.config.DEMO;
@@ -128,21 +78,6 @@ export class HeaderComponent {
);
}
- export() {
- this.#exportService.getExportFile().then(
- (url) => {
- const fileLink = document.createElement('a');
-
- fileLink.href = url;
- fileLink.download = `${this.boardId()}.json`;
- fileLink.click();
- },
- () => {
- console.error('export error');
- },
- );
- }
-
editName() {
this.edit.set(true);
}
@@ -173,18 +108,4 @@ export class HeaderComponent {
this.#store.dispatch(BoardActions.setBoardName({ name }));
}
}
-
- share() {
- this.#dialog.open(ShareBoardComponent, {
- width: '600px',
- autoFocus: 'dialog',
- });
- }
-
- settings() {
- this.#dialog.open(BoardSettingsComponent, {
- width: '600px',
- autoFocus: 'dialog',
- });
- }
}