better
This commit is contained in:
parent
003f38ea60
commit
f87480e79d
7 changed files with 124 additions and 36 deletions
|
|
@ -5,7 +5,8 @@
|
||||||
"packageManager": "npm",
|
"packageManager": "npm",
|
||||||
"schematicCollections": [
|
"schematicCollections": [
|
||||||
"angular-eslint"
|
"angular-eslint"
|
||||||
]
|
],
|
||||||
|
"analytics": false
|
||||||
},
|
},
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ interface EditedValue {
|
||||||
[alwaysDropShadow]="true"
|
[alwaysDropShadow]="true"
|
||||||
[onlyShadowBorder]="true"
|
[onlyShadowBorder]="true"
|
||||||
placeholder="Tag this item…"
|
placeholder="Tag this item…"
|
||||||
|
emptyHint="No tags yet. Type one below to create it."
|
||||||
(select)="updateTag(b.id, $event)"
|
(select)="updateTag(b.id, $event)"
|
||||||
(add)="updateTag(b.id, $event)"
|
(add)="updateTag(b.id, $event)"
|
||||||
/>
|
/>
|
||||||
|
|
@ -125,6 +126,7 @@ interface EditedValue {
|
||||||
[alwaysDropShadow]="true"
|
[alwaysDropShadow]="true"
|
||||||
[onlyShadowBorder]="true"
|
[onlyShadowBorder]="true"
|
||||||
placeholder="Set a category…"
|
placeholder="Set a category…"
|
||||||
|
emptyHint="No tags yet. Type one below to create it."
|
||||||
(select)="updateNewTag($event)"
|
(select)="updateNewTag($event)"
|
||||||
(add)="updateNewTag($event)"
|
(add)="updateNewTag($event)"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@
|
||||||
|
|
||||||
transition: box-shadow $short-animation-time;
|
transition: box-shadow $short-animation-time;
|
||||||
|
|
||||||
max-width: 800px;
|
max-width: 100%;
|
||||||
|
gap: var(--medium-padding);
|
||||||
|
|
||||||
&.cdk-drop-list-dragging {
|
&.cdk-drop-list-dragging {
|
||||||
*:not(.cdk-drag-placeholder) {
|
*:not(.cdk-drag-placeholder) {
|
||||||
|
|
@ -55,38 +56,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
|
width: 100%;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
box-sizing: content-box;
|
box-sizing: border-box;
|
||||||
flex: 0 0 auto;
|
flex: 1 1 0;
|
||||||
|
min-width: 0;
|
||||||
min-height: 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;
|
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).
|
// 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) {
|
@media (max-width: $mobile-width) {
|
||||||
--mobile-tower-width: calc(66vw - var(--small-padding));
|
--mobile-tower-width: calc(66vw - var(--small-padding));
|
||||||
--mobile-tower-side-padding: max(
|
--mobile-tower-side-padding: max(
|
||||||
|
|
@ -109,17 +89,12 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override the @for width-calc rules above.
|
|
||||||
& > * {
|
& > * {
|
||||||
width: var(--mobile-tower-width) !important;
|
width: var(--mobile-tower-width) !important;
|
||||||
max-width: var(--mobile-tower-width) !important;
|
max-width: var(--mobile-tower-width) !important;
|
||||||
min-width: var(--mobile-tower-width) !important;
|
min-width: var(--mobile-tower-width) !important;
|
||||||
scroll-snap-align: center;
|
scroll-snap-align: center;
|
||||||
flex-shrink: 0;
|
flex: 0 0 var(--mobile-tower-width);
|
||||||
}
|
|
||||||
|
|
||||||
& > *:not(:nth-last-child(1)) {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,31 @@
|
||||||
[page]="selectedPage()"
|
[page]="selectedPage()"
|
||||||
(close)="showSettings.set(false)"
|
(close)="showSettings.set(false)"
|
||||||
(updatePage)="onUpdatePage($event)"
|
(updatePage)="onUpdatePage($event)"
|
||||||
(deletePage)="onRemovePage()"
|
(deletePage)="onRequestRemovePage()"
|
||||||
(switchAccount)="onSwitchAccount($event)"
|
(switchAccount)="onSwitchAccount($event)"
|
||||||
/>
|
/>
|
||||||
</lt-modal>
|
</lt-modal>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (confirmDeletePageId()) {
|
||||||
|
<lt-modal (close)="cancelRemovePage()">
|
||||||
|
<div class="confirm-delete">
|
||||||
|
<div class="header">
|
||||||
|
<button class="exit" type="button" (click)="cancelRemovePage()" aria-label="Cancel"></button>
|
||||||
|
<h2>Delete page</h2>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Delete <strong>{{ confirmDeletePageName() || 'this page' }}</strong> and all of its towers and blocks?
|
||||||
|
This can't be undone.
|
||||||
|
</p>
|
||||||
|
<div class="confirm-buttons">
|
||||||
|
<button type="button" (click)="cancelRemovePage()">Cancel</button>
|
||||||
|
<button type="button" class="danger" (click)="confirmRemovePage()">Delete page</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</lt-modal>
|
||||||
|
}
|
||||||
|
|
||||||
@if (showWelcome()) {
|
@if (showWelcome()) {
|
||||||
<lt-modal (close)="showWelcome.set(false)">
|
<lt-modal (close)="showWelcome.set(false)">
|
||||||
<lt-welcome
|
<lt-welcome
|
||||||
|
|
|
||||||
|
|
@ -44,4 +44,65 @@
|
||||||
font-size: var(--medium-font-size);
|
font-size: var(--medium-font-size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.confirm-delete {
|
||||||
|
@include card();
|
||||||
|
width: 66vw;
|
||||||
|
max-width: 500px;
|
||||||
|
@media (max-width: $mobile-width) {
|
||||||
|
width: 88vw;
|
||||||
|
max-width: 88vw;
|
||||||
|
padding: var(--medium-padding);
|
||||||
|
}
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: var(--large-padding);
|
||||||
|
position: relative;
|
||||||
|
box-shadow: $shadow;
|
||||||
|
@include inner-spacing(var(--large-padding));
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
@include center-child();
|
||||||
|
|
||||||
|
.exit {
|
||||||
|
position: absolute;
|
||||||
|
right: var(--large-padding);
|
||||||
|
@include exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--large-padding);
|
||||||
|
|
||||||
|
button {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.danger {
|
||||||
|
color: #b53f3f;
|
||||||
|
border-bottom-color: #b53f3f55;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
background-color: #b53f3f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $mobile-width) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--small-padding);
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ export class PagesComponent {
|
||||||
readonly showSettings = signal(false);
|
readonly showSettings = signal(false);
|
||||||
readonly dragHappening = signal(false);
|
readonly dragHappening = signal(false);
|
||||||
readonly showWelcome = signal(false);
|
readonly showWelcome = signal(false);
|
||||||
|
readonly confirmDeletePageId = signal<string | null>(null);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
|
|
@ -63,6 +64,12 @@ export class PagesComponent {
|
||||||
|
|
||||||
readonly selectedPageName = computed(() => this.selectedPage()?.name ?? null);
|
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(() => {
|
readonly selectedPageIndex = computed(() => {
|
||||||
const page = this.selectedPage();
|
const page = this.selectedPage();
|
||||||
if (!page) return -1;
|
if (!page) return -1;
|
||||||
|
|
@ -94,12 +101,23 @@ export class PagesComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemovePage(): void {
|
onRequestRemovePage(): void {
|
||||||
const page = this.selectedPage();
|
const page = this.selectedPage();
|
||||||
if (!page) return;
|
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.selectedPageId.set(null);
|
||||||
this.showSettings.set(false);
|
this.showSettings.set(false);
|
||||||
|
this.confirmDeletePageId.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelRemovePage(): void {
|
||||||
|
this.confirmDeletePageId.set(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSwitchAccount(token: string): void {
|
onSwitchAccount(token: string): void {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ import {
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-container">
|
<div class="bottom-container">
|
||||||
<div class="bottom" [class.open]="open()">
|
<div class="bottom" [class.open]="open()">
|
||||||
|
@if (resolvedItems().length === 0 && emptyHint()) {
|
||||||
|
<p class="empty-hint">{{ emptyHint() }}</p>
|
||||||
|
}
|
||||||
@for (item of resolvedItems(); track item) {
|
@for (item of resolvedItems(); track item) {
|
||||||
@if (editing()) {
|
@if (editing()) {
|
||||||
<input
|
<input
|
||||||
|
|
@ -201,6 +204,14 @@ import {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-hint {
|
||||||
|
@include medium-text();
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
color: rgba($text-color, 0.72);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
input[type='text'] {
|
input[type='text'] {
|
||||||
@include sub-title-text();
|
@include sub-title-text();
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -373,6 +384,7 @@ export class SelectAddComponent implements OnChanges {
|
||||||
readonly selected = input<string | null>(null);
|
readonly selected = input<string | null>(null);
|
||||||
readonly editable = input<boolean>(false);
|
readonly editable = input<boolean>(false);
|
||||||
readonly placeholder = input<string>('Select…');
|
readonly placeholder = input<string>('Select…');
|
||||||
|
readonly emptyHint = input<string>('');
|
||||||
readonly alwaysDropShadow = input<boolean>(false);
|
readonly alwaysDropShadow = input<boolean>(false);
|
||||||
readonly onlyShadowBorder = input<boolean>(false);
|
readonly onlyShadowBorder = input<boolean>(false);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue