mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
feat(tasks): wrap add-task-bar actions onto multiple rows on small screens
On mobile / narrow screens the action bar hid buttons behind a horizontal scroll, making it easy to miss actions like repeat or estimate. Below the `xs` breakpoint the action bar now wraps onto multiple rows so every button stays reachable. Each action button and its optional "clear" button are grouped in an `.action-item` wrapper so a clear button never wraps away from the button it belongs to. Larger screens keep the existing single-row scroll behaviour. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018pcXtnrrfVNFAHgPFEpAF2
This commit is contained in:
parent
245330bd68
commit
855a4d5c29
2 changed files with 161 additions and 135 deletions
|
|
@ -42,164 +42,174 @@
|
|||
</button>
|
||||
|
||||
@if (!isHideDueBtn()) {
|
||||
<button
|
||||
mat-button
|
||||
class="action-btn"
|
||||
data-test="add-task-bar-due-btn"
|
||||
(click)="openScheduleDialog()"
|
||||
[class.has-value]="state().date"
|
||||
>
|
||||
<mat-icon>{{ state().time ? 'schedule' : 'event' }}</mat-icon>
|
||||
<span>{{ dateDisplay() || (T.F.TASK.ADD_TASK_BAR.DUE_BUTTON | translate) }}</span>
|
||||
</button>
|
||||
|
||||
@if (state().date) {
|
||||
<div class="action-item">
|
||||
<button
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
data-test="add-task-bar-clear-due-btn"
|
||||
(click)="clearDateWithSyntax(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_DATE | translate"
|
||||
matTooltipPosition="above"
|
||||
class="action-btn"
|
||||
data-test="add-task-bar-due-btn"
|
||||
(click)="openScheduleDialog()"
|
||||
[class.has-value]="state().date"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
<mat-icon>{{ state().time ? 'schedule' : 'event' }}</mat-icon>
|
||||
<span>{{ dateDisplay() || (T.F.TASK.ADD_TASK_BAR.DUE_BUTTON | translate) }}</span>
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (state().date) {
|
||||
<button
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
data-test="add-task-bar-clear-due-btn"
|
||||
(click)="clearDateWithSyntax(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_DATE | translate"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (state().deadlineDate) {
|
||||
<button
|
||||
mat-button
|
||||
class="action-btn deadline-chip"
|
||||
data-test="add-task-bar-deadline-btn"
|
||||
(click)="openDeadlineDialog()"
|
||||
[class.has-value]="true"
|
||||
>
|
||||
<mat-icon>{{ state().deadlineTime ? 'alarm' : 'flag' }}</mat-icon>
|
||||
<span>{{ deadlineDateDisplay() }}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
data-test="add-task-bar-clear-deadline-btn"
|
||||
(click)="clearDeadlineWithSyntax(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_DEADLINE | translate"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (!isHideTagBtn() && allTags().length) {
|
||||
<button
|
||||
#tagsMenuTrigger
|
||||
mat-button
|
||||
class="action-btn"
|
||||
[class.tags-mode]="hasTagsSelected() || state().newTagTitles.length > 0"
|
||||
[matMenuTriggerFor]="tagsMenu"
|
||||
[class.has-value]="hasTagsSelected() || hasNewTags()"
|
||||
[class.has-new-tags]="hasNewTags()"
|
||||
[class.menu-open]="isTagsMenuOpen()"
|
||||
(click)="onTagsMenuClick()"
|
||||
>
|
||||
<!-- NOTE: we do it this way, since mat-button puts it inside the label otherwise leading to vertical alignment issues -->
|
||||
<mat-icon [class.hide]="hasTagsSelected() || state().newTagTitles.length > 0"
|
||||
>label</mat-icon
|
||||
<div class="action-item">
|
||||
<button
|
||||
mat-button
|
||||
class="action-btn deadline-chip"
|
||||
data-test="add-task-bar-deadline-btn"
|
||||
(click)="openDeadlineDialog()"
|
||||
[class.has-value]="true"
|
||||
>
|
||||
<mat-icon>{{ state().deadlineTime ? 'alarm' : 'flag' }}</mat-icon>
|
||||
<span>{{ deadlineDateDisplay() }}</span>
|
||||
</button>
|
||||
|
||||
@if (!hasTagsSelected() && state().newTagTitles.length === 0) {
|
||||
<ng-container>
|
||||
<span>{{ T.F.TASK.ADD_TASK_BAR.TAGS_BUTTON | translate }}</span>
|
||||
</ng-container>
|
||||
} @else {
|
||||
<div class="tags-display">
|
||||
@for (tag of selectedTags(); track tag.id) {
|
||||
<div class="tag-item">
|
||||
<mat-icon [style.color]="tag.color || tag.theme?.primary">
|
||||
{{ tag.icon || 'label' }}
|
||||
</mat-icon>
|
||||
<span class="tag-title">{{ tag.title }}</span>
|
||||
</div>
|
||||
}
|
||||
@for (newTag of state().newTagTitles; track newTag) {
|
||||
<div class="tag-item new-tag">
|
||||
<mat-icon>new_releases</mat-icon>
|
||||
<span class="tag-title">{{ newTag }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</button>
|
||||
|
||||
@if (hasTagsSelected() || hasNewTags()) {
|
||||
<button
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
(click)="clearTagsWithSyntax(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_TAGS | translate"
|
||||
data-test="add-task-bar-clear-deadline-btn"
|
||||
(click)="clearDeadlineWithSyntax(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_DEADLINE | translate"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!isHideTagBtn() && allTags().length) {
|
||||
<div class="action-item">
|
||||
<button
|
||||
#tagsMenuTrigger
|
||||
mat-button
|
||||
class="action-btn"
|
||||
[class.tags-mode]="hasTagsSelected() || state().newTagTitles.length > 0"
|
||||
[matMenuTriggerFor]="tagsMenu"
|
||||
[class.has-value]="hasTagsSelected() || hasNewTags()"
|
||||
[class.has-new-tags]="hasNewTags()"
|
||||
[class.menu-open]="isTagsMenuOpen()"
|
||||
(click)="onTagsMenuClick()"
|
||||
>
|
||||
<!-- NOTE: we do it this way, since mat-button puts it inside the label otherwise leading to vertical alignment issues -->
|
||||
<mat-icon [class.hide]="hasTagsSelected() || state().newTagTitles.length > 0"
|
||||
>label</mat-icon
|
||||
>
|
||||
|
||||
@if (!hasTagsSelected() && state().newTagTitles.length === 0) {
|
||||
<ng-container>
|
||||
<span>{{ T.F.TASK.ADD_TASK_BAR.TAGS_BUTTON | translate }}</span>
|
||||
</ng-container>
|
||||
} @else {
|
||||
<div class="tags-display">
|
||||
@for (tag of selectedTags(); track tag.id) {
|
||||
<div class="tag-item">
|
||||
<mat-icon [style.color]="tag.color || tag.theme?.primary">
|
||||
{{ tag.icon || 'label' }}
|
||||
</mat-icon>
|
||||
<span class="tag-title">{{ tag.title }}</span>
|
||||
</div>
|
||||
}
|
||||
@for (newTag of state().newTagTitles; track newTag) {
|
||||
<div class="tag-item new-tag">
|
||||
<mat-icon>new_releases</mat-icon>
|
||||
<span class="tag-title">{{ newTag }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</button>
|
||||
|
||||
@if (hasTagsSelected() || hasNewTags()) {
|
||||
<button
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
(click)="clearTagsWithSyntax(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_TAGS | translate"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="action-item">
|
||||
<button
|
||||
#estimateMenuTrigger
|
||||
mat-button
|
||||
class="action-btn estimate-btn"
|
||||
[matMenuTriggerFor]="estimateMenu"
|
||||
[class.has-value]="state().estimate"
|
||||
[class.menu-open]="isEstimateMenuOpen()"
|
||||
(click)="onEstimateMenuClick()"
|
||||
>
|
||||
<mat-icon>hourglass_empty</mat-icon>
|
||||
<span>{{
|
||||
estimateDisplay() || (T.F.TASK.ADD_TASK_BAR.ESTIMATE_BUTTON | translate)
|
||||
}}</span>
|
||||
</button>
|
||||
|
||||
@if (state().estimate) {
|
||||
<button
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
(click)="clearEstimateWithSyntax(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_ESTIMATE | translate"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<button
|
||||
#estimateMenuTrigger
|
||||
mat-button
|
||||
class="action-btn estimate-btn"
|
||||
[matMenuTriggerFor]="estimateMenu"
|
||||
[class.has-value]="state().estimate"
|
||||
[class.menu-open]="isEstimateMenuOpen()"
|
||||
(click)="onEstimateMenuClick()"
|
||||
>
|
||||
<mat-icon>hourglass_empty</mat-icon>
|
||||
<span>{{
|
||||
estimateDisplay() || (T.F.TASK.ADD_TASK_BAR.ESTIMATE_BUTTON | translate)
|
||||
}}</span>
|
||||
</button>
|
||||
|
||||
@if (state().estimate) {
|
||||
<div class="action-item">
|
||||
<button
|
||||
#repeatMenuTrigger
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
(click)="clearEstimateWithSyntax(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_ESTIMATE | translate"
|
||||
matTooltipPosition="above"
|
||||
class="action-btn"
|
||||
data-test="add-task-bar-repeat-btn"
|
||||
[matMenuTriggerFor]="repeatMenu"
|
||||
[class.has-value]="state().repeatQuickSetting"
|
||||
[class.menu-open]="isRepeatMenuOpen()"
|
||||
(click)="onRepeatMenuClick()"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
<mat-icon class="repeat-icon">repeat</mat-icon>
|
||||
<span>{{
|
||||
repeatDisplay() || (T.F.TASK.ADD_TASK_BAR.REPEAT_BUTTON | translate)
|
||||
}}</span>
|
||||
</button>
|
||||
}
|
||||
|
||||
<button
|
||||
#repeatMenuTrigger
|
||||
mat-button
|
||||
class="action-btn"
|
||||
data-test="add-task-bar-repeat-btn"
|
||||
[matMenuTriggerFor]="repeatMenu"
|
||||
[class.has-value]="state().repeatQuickSetting"
|
||||
[class.menu-open]="isRepeatMenuOpen()"
|
||||
(click)="onRepeatMenuClick()"
|
||||
>
|
||||
<mat-icon class="repeat-icon">repeat</mat-icon>
|
||||
<span>{{
|
||||
repeatDisplay() || (T.F.TASK.ADD_TASK_BAR.REPEAT_BUTTON | translate)
|
||||
}}</span>
|
||||
</button>
|
||||
|
||||
@if (state().repeatQuickSetting) {
|
||||
<button
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
data-test="add-task-bar-clear-repeat-btn"
|
||||
(click)="clearRepeatSetting(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_REPEAT | translate"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
}
|
||||
@if (state().repeatQuickSetting) {
|
||||
<button
|
||||
mat-button
|
||||
class="clear-btn"
|
||||
data-test="add-task-bar-clear-repeat-btn"
|
||||
(click)="clearRepeatSetting(); $event.stopPropagation()"
|
||||
[matTooltip]="T.F.TASK.ADD_TASK_BAR.TOOLTIP_CLEAR_REPEAT | translate"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-------------- Menus ------------------->
|
||||
|
|
|
|||
|
|
@ -64,6 +64,22 @@
|
|||
gap: var(--s-quarter);
|
||||
}
|
||||
|
||||
// On smaller screens, wrap the actions onto multiple rows instead of hiding
|
||||
// them behind a horizontal scroll, so every button stays reachable.
|
||||
@include mq(xs, max) {
|
||||
flex-wrap: wrap;
|
||||
overflow-x: visible;
|
||||
gap: var(--s-quarter);
|
||||
}
|
||||
|
||||
// Keeps an action button and its "clear" button together so a paired button
|
||||
// never wraps away from the button it belongs to.
|
||||
.action-item {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
cursor: pointer;
|
||||
opacity: 0.4;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue