diff --git a/frontend/angular.json b/frontend/angular.json index 4a1b666..e4ba9a9 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -5,7 +5,8 @@ "packageManager": "npm", "schematicCollections": [ "angular-eslint" - ] + ], + "analytics": false }, "newProjectRoot": "projects", "projects": { diff --git a/frontend/src/app/components/modal/block-edit.component.ts b/frontend/src/app/components/modal/block-edit.component.ts index 604e6f5..63296ae 100644 --- a/frontend/src/app/components/modal/block-edit.component.ts +++ b/frontend/src/app/components/modal/block-edit.component.ts @@ -74,6 +74,7 @@ interface EditedValue { [alwaysDropShadow]="true" [onlyShadowBorder]="true" placeholder="Tag this item…" + emptyHint="No tags yet. Type one below to create it." (select)="updateTag(b.id, $event)" (add)="updateTag(b.id, $event)" /> @@ -125,6 +126,7 @@ interface EditedValue { [alwaysDropShadow]="true" [onlyShadowBorder]="true" placeholder="Set a category…" + emptyHint="No tags yet. Type one below to create it." (select)="updateNewTag($event)" (add)="updateNewTag($event)" /> diff --git a/frontend/src/app/components/page/page.component.scss b/frontend/src/app/components/page/page.component.scss index 4ae3ad3..cf5689d 100644 --- a/frontend/src/app/components/page/page.component.scss +++ b/frontend/src/app/components/page/page.component.scss @@ -28,7 +28,8 @@ transition: box-shadow $short-animation-time; - max-width: 800px; + max-width: 100%; + gap: var(--medium-padding); &.cdk-drop-list-dragging { *:not(.cdk-drag-placeholder) { @@ -55,38 +56,17 @@ } & > * { + width: 100%; max-width: 200px; - box-sizing: content-box; - flex: 0 0 auto; + box-sizing: border-box; + flex: 1 1 0; + min-width: 0; min-height: 0; - - &:not(:nth-last-child(1)) { - margin-right: var(--medium-padding); - @media (max-width: $mobile-width) { - // Was --small-padding (7.5px) — too tight to read as a real gap - // between white tower cards on the cream-pink background. - margin-right: var(--medium-padding); - } - } } position: relative; - @for $i from 1 to 12 { - & > *:first-child:nth-last-child(#{$i}), - & > *:first-child:nth-last-child(#{$i}) ~ * { - width: calc((100% - (#{$i} - 1) * var(--medium-padding)) / #{$i}); - - @media (max-width: $mobile-width) { - width: calc((100% - (#{$i} - 1) * var(--small-padding)) / #{$i}); - } - } - } - // Mobile: fixed-width towers with horizontal scroll (1.5-column rhythm). - // This block is declared AFTER the @for loop so it wins the specificity race - // without needing !important on every property — only the width triples need it - // to beat the per-child-count selectors generated above. @media (max-width: $mobile-width) { --mobile-tower-width: calc(66vw - var(--small-padding)); --mobile-tower-side-padding: max( @@ -109,17 +89,12 @@ display: none; } - // Override the @for width-calc rules above. & > * { width: var(--mobile-tower-width) !important; max-width: var(--mobile-tower-width) !important; min-width: var(--mobile-tower-width) !important; scroll-snap-align: center; - flex-shrink: 0; - } - - & > *:not(:nth-last-child(1)) { - margin-right: 0; + flex: 0 0 var(--mobile-tower-width); } } } diff --git a/frontend/src/app/components/pages/pages.component.html b/frontend/src/app/components/pages/pages.component.html index 44dbccf..28fd921 100644 --- a/frontend/src/app/components/pages/pages.component.html +++ b/frontend/src/app/components/pages/pages.component.html @@ -27,12 +27,31 @@ [page]="selectedPage()" (close)="showSettings.set(false)" (updatePage)="onUpdatePage($event)" - (deletePage)="onRemovePage()" + (deletePage)="onRequestRemovePage()" (switchAccount)="onSwitchAccount($event)" /> } +@if (confirmDeletePageId()) { + +
+
+ +

Delete page

+
+

+ Delete {{ confirmDeletePageName() || 'this page' }} and all of its towers and blocks? + This can't be undone. +

+
+ + +
+
+
+} + @if (showWelcome()) { (null); constructor() { effect(() => { @@ -63,6 +64,12 @@ export class PagesComponent { readonly selectedPageName = computed(() => this.selectedPage()?.name ?? null); + readonly confirmDeletePageName = computed(() => { + const id = this.confirmDeletePageId(); + if (!id) return ''; + return this.store.pages().find((p) => p.id === id)?.name ?? ''; + }); + readonly selectedPageIndex = computed(() => { const page = this.selectedPage(); if (!page) return -1; @@ -94,12 +101,23 @@ export class PagesComponent { } } - onRemovePage(): void { + onRequestRemovePage(): void { const page = this.selectedPage(); if (!page) return; - this.store.deletePage(page.id); + this.confirmDeletePageId.set(page.id); + } + + confirmRemovePage(): void { + const pageId = this.confirmDeletePageId(); + if (!pageId) return; + this.store.deletePage(pageId); this.selectedPageId.set(null); this.showSettings.set(false); + this.confirmDeletePageId.set(null); + } + + cancelRemovePage(): void { + this.confirmDeletePageId.set(null); } onSwitchAccount(token: string): void { diff --git a/frontend/src/app/components/shared/select-add/select-add.component.ts b/frontend/src/app/components/shared/select-add/select-add.component.ts index 399763d..f1cb3fb 100644 --- a/frontend/src/app/components/shared/select-add/select-add.component.ts +++ b/frontend/src/app/components/shared/select-add/select-add.component.ts @@ -28,6 +28,9 @@ import {
+ @if (resolvedItems().length === 0 && emptyHint()) { +

{{ emptyHint() }}

+ } @for (item of resolvedItems(); track item) { @if (editing()) { (null); readonly editable = input(false); readonly placeholder = input('Select…'); + readonly emptyHint = input(''); readonly alwaysDropShadow = input(false); readonly onlyShadowBorder = input(false);