mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-08-03 13:02:26 +00:00
feat(scheduleRightPanel): improve preview for task dragged onto cal
This commit is contained in:
parent
3c694477c4
commit
e90b2477ca
3 changed files with 54 additions and 85 deletions
|
|
@ -20,14 +20,4 @@
|
|||
[currentTimeRow]="currentTimeRow()"
|
||||
[isTaskDragActive]="isDragging()"
|
||||
></schedule-week>
|
||||
|
||||
@if (isDragging() && dragPreviewTime()) {
|
||||
<div
|
||||
class="drag-time-preview"
|
||||
[style.left.px]="dragPreviewPosition().x"
|
||||
[style.top.px]="dragPreviewPosition().y"
|
||||
>
|
||||
<div class="time-badge">{{ dragPreviewTime() }}</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ import { TaskWithSubTasks } from '../../tasks/task.model';
|
|||
import { TaskSharedActions } from '../../../root-store/meta/task-shared.actions';
|
||||
import { Log } from '../../../core/log';
|
||||
|
||||
const DEFAULT_MIN_DURATION = 15 * 60 * 1000;
|
||||
|
||||
@Component({
|
||||
selector: 'schedule-day-panel',
|
||||
standalone: true,
|
||||
|
|
@ -58,7 +60,6 @@ export class ScheduleDayPanelComponent implements AfterViewInit, OnDestroy {
|
|||
|
||||
// Drag preview properties
|
||||
dragPreviewTime = signal<string | null>(null);
|
||||
dragPreviewPosition = signal<{ x: number; y: number }>({ x: 0, y: 0 });
|
||||
isDragging = signal(false);
|
||||
private lastCalculatedTimestamp: number | null = null;
|
||||
|
||||
|
|
@ -183,11 +184,7 @@ export class ScheduleDayPanelComponent implements AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
onDragEnter(event: CdkDragEnter<{ day: string }>): void {
|
||||
// Only activate preview for allowed drags (tasks etc.).
|
||||
const data: any = (event.item as any)?.data;
|
||||
const title: string =
|
||||
data && typeof data === 'object' && 'title' in data ? data.title : '';
|
||||
this._startScheduleMode(title);
|
||||
this._startScheduleMode();
|
||||
}
|
||||
|
||||
onDragExit(): void {
|
||||
|
|
@ -206,11 +203,9 @@ export class ScheduleDayPanelComponent implements AfterViewInit, OnDestroy {
|
|||
}
|
||||
if (!this.isDragging()) return;
|
||||
|
||||
const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
|
||||
const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
|
||||
|
||||
// Update position for the preview badge (still follow cursor for visibility)
|
||||
this.dragPreviewPosition.set({ x: clientX, y: clientY });
|
||||
|
||||
// Get the actual drag preview position to match what dropPoint will be
|
||||
let previewY = clientY;
|
||||
|
|
@ -229,6 +224,7 @@ export class ScheduleDayPanelComponent implements AfterViewInit, OnDestroy {
|
|||
const date = new Date(timestamp);
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
// TODO internationalization
|
||||
const timeStr = `${hours.toString().padStart(2, '0')}:${minutes
|
||||
.toString()
|
||||
.padStart(2, '0')}`;
|
||||
|
|
@ -287,10 +283,9 @@ export class ScheduleDayPanelComponent implements AfterViewInit, OnDestroy {
|
|||
);
|
||||
// Auto-assign a default duration if none is set
|
||||
if (!task.timeEstimate || task.timeEstimate <= 0) {
|
||||
const FIFTEEN_MIN = 15 * 60 * 1000;
|
||||
this._store.dispatch(
|
||||
TaskSharedActions.updateTask({
|
||||
task: { id: task.id, changes: { timeEstimate: FIFTEEN_MIN } },
|
||||
task: { id: task.id, changes: { timeEstimate: DEFAULT_MIN_DURATION } },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -384,16 +379,18 @@ export class ScheduleDayPanelComponent implements AfterViewInit, OnDestroy {
|
|||
private _updatePreviewTimeBadge(timeStr: string): void {
|
||||
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement | null;
|
||||
if (!preview) return;
|
||||
const timeBadge = preview.querySelector('.time-badge') as HTMLElement | null;
|
||||
const timeBadge = preview.querySelector(
|
||||
'.drag-preview-time-badge',
|
||||
) as HTMLElement | null;
|
||||
if (timeBadge) {
|
||||
timeBadge.textContent = timeStr;
|
||||
}
|
||||
}
|
||||
|
||||
private _startScheduleMode(previewTitle?: string): void {
|
||||
private _startScheduleMode(): void {
|
||||
if (this.isDragging()) return;
|
||||
this.isDragging.set(true);
|
||||
this._applySchedulePreviewStyling(true, previewTitle);
|
||||
this._applySchedulePreviewStyling(true);
|
||||
this._cdr.markForCheck();
|
||||
// Track pointer globally so we can leave schedule mode when not over panel
|
||||
document.addEventListener('mousemove', this._onGlobalPointerMove, {
|
||||
|
|
@ -435,56 +432,49 @@ export class ScheduleDayPanelComponent implements AfterViewInit, OnDestroy {
|
|||
}
|
||||
};
|
||||
|
||||
private _applySchedulePreviewStyling(isEnable: boolean, previewTitle?: string): void {
|
||||
const preview = document.querySelector('.cdk-drag-preview') as HTMLElement | null;
|
||||
if (!preview) {
|
||||
// we're mutating the dom directly here to add some styling to the drag preview, since it is the most efficient way to do it
|
||||
private _applySchedulePreviewStyling(isEnable: boolean): void {
|
||||
const previewEl = document.querySelector('.cdk-drag-preview') as HTMLElement | null;
|
||||
if (!previewEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEnable) {
|
||||
preview.classList.add('as-schedule-event-preview');
|
||||
// Try to match the width of the day column
|
||||
const containerElement = this.scheduleWeekRef?.nativeElement as
|
||||
| HTMLElement
|
||||
| undefined;
|
||||
const day = this.daysToShow()[0];
|
||||
const colEl = containerElement?.querySelector(
|
||||
`.grid-container .col[data-day="${day}"]`,
|
||||
) as HTMLElement | null;
|
||||
if (colEl) {
|
||||
const colRect = colEl.getBoundingClientRect();
|
||||
preview.style.width = `${Math.max(40, colRect.width - 10)}px`;
|
||||
if (previewEl.tagName.toLowerCase() === 'task') {
|
||||
if (isEnable) {
|
||||
previewEl.classList.add('as-schedule-event-preview');
|
||||
// Try to match the width of the day column
|
||||
const containerElement = this.scheduleWeekRef?.nativeElement as
|
||||
| HTMLElement
|
||||
| undefined;
|
||||
const day = this.daysToShow()[0];
|
||||
const colEl = containerElement?.querySelector(
|
||||
`.grid-container .col[data-day="${day}"]`,
|
||||
) as HTMLElement | null;
|
||||
if (colEl) {
|
||||
const colRect = colEl.getBoundingClientRect();
|
||||
previewEl.style.width = `${Math.max(40, colRect.width - 10)}px`;
|
||||
}
|
||||
|
||||
// ensure time badge element exists
|
||||
let timeBadge = previewEl.querySelector(
|
||||
'.drag-preview-time-badge',
|
||||
) as HTMLElement | null;
|
||||
if (!timeBadge) {
|
||||
timeBadge = document.createElement('div');
|
||||
timeBadge.className = 'drag-preview-time-badge';
|
||||
previewEl.appendChild(timeBadge);
|
||||
}
|
||||
if (this.dragPreviewTime()) {
|
||||
timeBadge.textContent = this.dragPreviewTime()!;
|
||||
}
|
||||
} else {
|
||||
previewEl.classList.remove('as-schedule-event-preview');
|
||||
previewEl.style.removeProperty('width');
|
||||
const timeBadge = previewEl.querySelector('.drag-preview-time-badge');
|
||||
if (timeBadge) {
|
||||
timeBadge.remove();
|
||||
}
|
||||
}
|
||||
// Inject basic content to mimic schedule preview: title
|
||||
let titleEl = preview.querySelector('.title') as HTMLElement | null;
|
||||
if (!titleEl) {
|
||||
titleEl = document.createElement('div');
|
||||
titleEl.className = 'title';
|
||||
preview.appendChild(titleEl);
|
||||
}
|
||||
if (previewTitle) {
|
||||
titleEl.textContent = previewTitle;
|
||||
}
|
||||
// ensure time badge element exists
|
||||
let timeBadge = preview.querySelector('.time-badge') as HTMLElement | null;
|
||||
if (!timeBadge) {
|
||||
timeBadge = document.createElement('div');
|
||||
timeBadge.className = 'time-badge';
|
||||
preview.appendChild(timeBadge);
|
||||
}
|
||||
if (this.dragPreviewTime()) {
|
||||
timeBadge.textContent = this.dragPreviewTime()!;
|
||||
}
|
||||
} else {
|
||||
preview.classList.remove('as-schedule-event-preview');
|
||||
preview.style.removeProperty('width');
|
||||
const timeBadge = preview.querySelector('.time-badge');
|
||||
if (timeBadge) {
|
||||
timeBadge.remove();
|
||||
}
|
||||
// remove injected title
|
||||
const titleEl = preview.querySelector('.title');
|
||||
if (titleEl) (titleEl as HTMLElement).remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,32 +198,21 @@ mat-icon.mat-icon[svgicon] {
|
|||
|
||||
// Schedule-style preview should always use accent border while dragging
|
||||
.cdk-drag-preview.as-schedule-event-preview {
|
||||
border: 1px solid var(--c-primary) !important;
|
||||
|
||||
.time-badge {
|
||||
.drag-preview-time-badge {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 15px;
|
||||
height: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
padding: 0 3px;
|
||||
border: 1px solid var(--extra-border-color);
|
||||
padding: 0 3px 2px;
|
||||
border: 2px solid var(--c-primary);
|
||||
border-radius: var(--card-border-radius);
|
||||
color: var(--text-color);
|
||||
background: var(--bg-lighter);
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.cdk-drag-placeholder {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue