Merge branch 'dev'
CI/CD / deploy (push) Successful in 2m55s
Details
CI/CD / deploy (push) Successful in 2m55s
Details
This commit is contained in:
commit
17a17103f4
|
|
@ -1,23 +1,18 @@
|
||||||
/**
|
/**
|
||||||
* <folk-tasks-board> — kanban board for workspace task management.
|
* <folk-tasks-board> — kanban board for per-space task management.
|
||||||
*
|
*
|
||||||
* Views: workspace list → board with draggable columns.
|
* Single board per space, loaded directly on page load.
|
||||||
* Supports task creation, status changes, and priority labels.
|
* Supports task creation, status changes, and priority labels.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { boardSchema, type BoardDoc } from "../schemas";
|
import { boardSchema, type BoardDoc } from "../schemas";
|
||||||
import { makeDraggableAll } from "../../../shared/draggable";
|
|
||||||
import type { DocumentId } from "../../../shared/local-first/document";
|
|
||||||
import { TourEngine } from "../../../shared/tour-engine";
|
import { TourEngine } from "../../../shared/tour-engine";
|
||||||
import { ViewHistory } from "../../../shared/view-history.js";
|
|
||||||
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
|
||||||
|
|
||||||
class FolkTasksBoard extends HTMLElement {
|
class FolkTasksBoard extends HTMLElement {
|
||||||
private shadow: ShadowRoot;
|
private shadow: ShadowRoot;
|
||||||
private space = "";
|
private space = "";
|
||||||
private view: "list" | "board" = "list";
|
|
||||||
private workspaceSlug = "";
|
private workspaceSlug = "";
|
||||||
private workspaces: any[] = [];
|
|
||||||
private tasks: any[] = [];
|
private tasks: any[] = [];
|
||||||
private statuses: string[] = ["TODO", "IN_PROGRESS", "REVIEW", "DONE"];
|
private statuses: string[] = ["TODO", "IN_PROGRESS", "REVIEW", "DONE"];
|
||||||
private loading = false;
|
private loading = false;
|
||||||
|
|
@ -32,7 +27,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
private dragSourceStatus: string | null = null;
|
private dragSourceStatus: string | null = null;
|
||||||
private priorities = ["LOW", "MEDIUM", "HIGH", "URGENT"];
|
private priorities = ["LOW", "MEDIUM", "HIGH", "URGENT"];
|
||||||
private _offlineUnsubs: (() => void)[] = [];
|
private _offlineUnsubs: (() => void)[] = [];
|
||||||
private _history = new ViewHistory<"list" | "board">("list");
|
|
||||||
private _backlogTaskId: string | null = null;
|
private _backlogTaskId: string | null = null;
|
||||||
private _stopPresence: (() => void) | null = null;
|
private _stopPresence: (() => void) | null = null;
|
||||||
// Detail panel
|
// Detail panel
|
||||||
|
|
@ -45,8 +39,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
private _showColumnEditor = false;
|
private _showColumnEditor = false;
|
||||||
// Board labels (from server)
|
// Board labels (from server)
|
||||||
private _boardLabels: string[] = [];
|
private _boardLabels: string[] = [];
|
||||||
// Inline workspace creation
|
|
||||||
private _showCreateWs = false;
|
|
||||||
// Inline delete confirmation
|
// Inline delete confirmation
|
||||||
private _confirmDeleteId: string | null = null;
|
private _confirmDeleteId: string | null = null;
|
||||||
// ClickUp integration state
|
// ClickUp integration state
|
||||||
|
|
@ -65,7 +57,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
private _cuImportResult: { boardId: string; taskCount: number } | null = null;
|
private _cuImportResult: { boardId: string; taskCount: number } | null = null;
|
||||||
private _tour!: TourEngine;
|
private _tour!: TourEngine;
|
||||||
private static readonly TOUR_STEPS = [
|
private static readonly TOUR_STEPS = [
|
||||||
{ target: '.workspace-card', title: "Workspaces", message: "Select a workspace to open its kanban board.", advanceOnClick: true },
|
|
||||||
{ target: '#create-task', title: "New Task", message: "Create a new task with title, priority, and description.", advanceOnClick: false },
|
{ target: '#create-task', title: "New Task", message: "Create a new task with title, priority, and description.", advanceOnClick: false },
|
||||||
{ target: '.board', title: "Kanban Board", message: "Drag tasks between columns — TODO, In Progress, Review, Done.", advanceOnClick: false },
|
{ target: '.board', title: "Kanban Board", message: "Drag tasks between columns — TODO, In Progress, Review, Done.", advanceOnClick: false },
|
||||||
{ target: '.badge.clickable', title: "Priority", message: "Click the priority badge on a task to cycle through levels.", advanceOnClick: false },
|
{ target: '.badge.clickable', title: "Priority", message: "Click the priority badge on a task to cycle through levels.", advanceOnClick: false },
|
||||||
|
|
@ -84,13 +75,14 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.space = this.getAttribute("space") || "demo";
|
this.space = this.getAttribute("space") || "demo";
|
||||||
|
this.workspaceSlug = this.space;
|
||||||
// Check for ?backlog= deep-link from email checklist
|
// Check for ?backlog= deep-link from email checklist
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
this._backlogTaskId = params.get("backlog");
|
this._backlogTaskId = params.get("backlog");
|
||||||
if (this.space === "demo") { this.loadDemoData(); }
|
if (this.space === "demo") { this.loadDemoData(); }
|
||||||
else {
|
else {
|
||||||
this.subscribeOffline();
|
this.subscribeOffline();
|
||||||
this.loadWorkspaces();
|
this.loadTasks();
|
||||||
this.loadClickUpStatus();
|
this.loadClickUpStatus();
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
@ -137,19 +129,12 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const docs = await runtime.subscribeModule('tasks', 'boards', boardSchema);
|
const docs = await runtime.subscribeModule('tasks', 'boards', boardSchema);
|
||||||
// Build workspace list from cached boards
|
// Subscribe to changes for the current board
|
||||||
if (docs.size > 0 && this.workspaces.length === 0) {
|
if (docs.size > 0) {
|
||||||
const boards: any[] = [];
|
for (const [docId] of docs) {
|
||||||
for (const [docId, doc] of docs) {
|
|
||||||
const d = doc as BoardDoc;
|
|
||||||
if (!d?.board) continue;
|
|
||||||
boards.push({ slug: d.board.slug, name: d.board.name, icon: null, task_count: Object.keys(d.tasks || {}).length });
|
|
||||||
this._offlineUnsubs.push(runtime.onChange(docId, () => this.refreshFromDocs()));
|
this._offlineUnsubs.push(runtime.onChange(docId, () => this.refreshFromDocs()));
|
||||||
}
|
}
|
||||||
if (boards.length > 0) {
|
this.refreshFromDocs();
|
||||||
this.workspaces = boards;
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch { /* runtime unavailable */ }
|
} catch { /* runtime unavailable */ }
|
||||||
}
|
}
|
||||||
|
|
@ -172,8 +157,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
|
|
||||||
private loadDemoData() {
|
private loadDemoData() {
|
||||||
this.isDemo = true;
|
this.isDemo = true;
|
||||||
this.workspaces = [{ slug: "rspace-dev", name: "rSpace Development", icon: "\u{1F680}", task_count: 11, member_count: 2 }];
|
|
||||||
this.view = "board";
|
|
||||||
this.workspaceSlug = "rspace-dev";
|
this.workspaceSlug = "rspace-dev";
|
||||||
this.tasks = [
|
this.tasks = [
|
||||||
{ id: "d1", title: "Add dark mode toggle to settings page", status: "TODO", priority: "MEDIUM", labels: ["feature"], assignee: "Alice", sort_order: 0 },
|
{ id: "d1", title: "Add dark mode toggle to settings page", status: "TODO", priority: "MEDIUM", labels: ["feature"], assignee: "Alice", sort_order: 0 },
|
||||||
|
|
@ -204,19 +187,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadWorkspaces() {
|
|
||||||
try {
|
|
||||||
const base = this.getApiBase();
|
|
||||||
let res = await fetch(`${base}/api/spaces`, { headers: this.authHeaders() });
|
|
||||||
// If 401 with stale token, retry without auth
|
|
||||||
if (res.status === 401 && localStorage.getItem("encryptid-token")) {
|
|
||||||
res = await fetch(`${base}/api/spaces`);
|
|
||||||
}
|
|
||||||
if (res.ok) this.workspaces = await res.json();
|
|
||||||
} catch { this.workspaces = []; }
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async loadTasks() {
|
private async loadTasks() {
|
||||||
if (!this.workspaceSlug) return;
|
if (!this.workspaceSlug) return;
|
||||||
try {
|
try {
|
||||||
|
|
@ -327,7 +297,7 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
this.error = 'Import failed';
|
this.error = 'Import failed';
|
||||||
this._cuStep = 'list';
|
this._cuStep = 'list';
|
||||||
}
|
}
|
||||||
this.loadWorkspaces();
|
this.loadTasks();
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,33 +413,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createWorkspace(name: string) {
|
|
||||||
if (!name?.trim()) return;
|
|
||||||
if (this.isDemo) {
|
|
||||||
const slug = name.trim().toLowerCase().replace(/\s+/g, "-");
|
|
||||||
this.workspaces.push({ slug, name: name.trim(), icon: "\u{1F4CB}", task_count: 0, member_count: 1 });
|
|
||||||
this._showCreateWs = false;
|
|
||||||
this.render();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const base = this.getApiBase();
|
|
||||||
const res = await fetch(`${base}/api/spaces`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: this.authHeaders({ "Content-Type": "application/json" }),
|
|
||||||
body: JSON.stringify({ name: name.trim() }),
|
|
||||||
});
|
|
||||||
if (res.ok) {
|
|
||||||
this._showCreateWs = false;
|
|
||||||
this.loadWorkspaces();
|
|
||||||
} else {
|
|
||||||
const data = await res.json().catch(() => ({}));
|
|
||||||
this.error = data.error || `Failed to create workspace (${res.status})`;
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
} catch { this.error = "Failed to create workspace"; this.render(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
private async submitCreateTask(title: string, priority: string, description: string, dueDate?: string) {
|
private async submitCreateTask(title: string, priority: string, description: string, dueDate?: string) {
|
||||||
if (!title.trim()) return;
|
if (!title.trim()) return;
|
||||||
if (this.isDemo) {
|
if (this.isDemo) {
|
||||||
|
|
@ -686,20 +629,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
return { sortOrder: ordinal, rebalanceUpdates };
|
return { sortOrder: ordinal, rebalanceUpdates };
|
||||||
}
|
}
|
||||||
|
|
||||||
private openBoard(slug: string) {
|
|
||||||
this.workspaceSlug = slug;
|
|
||||||
this.view = "board";
|
|
||||||
this.loadTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
private goBack() {
|
|
||||||
const prev = this._history.back();
|
|
||||||
if (!prev) return;
|
|
||||||
this.view = prev.view;
|
|
||||||
if (prev.view === "list") this.loadWorkspaces();
|
|
||||||
else this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
private render() {
|
private render() {
|
||||||
this.shadow.innerHTML = `
|
this.shadow.innerHTML = `
|
||||||
<style>
|
<style>
|
||||||
|
|
@ -714,15 +643,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
.rapp-nav__btn { padding: 6px 14px; border-radius: 6px; border: none; background: var(--rs-primary); color: #fff; font-weight: 600; cursor: pointer; font-size: 13px; }
|
.rapp-nav__btn { padding: 6px 14px; border-radius: 6px; border: none; background: var(--rs-primary); color: #fff; font-weight: 600; cursor: pointer; font-size: 13px; }
|
||||||
.rapp-nav__btn:hover { background: var(--rs-primary-hover); }
|
.rapp-nav__btn:hover { background: var(--rs-primary-hover); }
|
||||||
|
|
||||||
.workspace-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
|
|
||||||
.workspace-card {
|
|
||||||
background: var(--rs-bg-surface); border: 1px solid var(--rs-border-strong); border-radius: 10px;
|
|
||||||
padding: 16px; cursor: pointer; transition: border-color 0.2s;
|
|
||||||
}
|
|
||||||
.workspace-card:hover { border-color: var(--rs-border-strong); }
|
|
||||||
.ws-name { font-size: 15px; font-weight: 600; margin-bottom: 4px; }
|
|
||||||
.ws-meta { font-size: 12px; color: var(--rs-text-muted); }
|
|
||||||
|
|
||||||
.board { display: flex; gap: 12px; overflow-x: auto; min-height: 400px; padding-bottom: 12px; }
|
.board { display: flex; gap: 12px; overflow-x: auto; min-height: 400px; padding-bottom: 12px; }
|
||||||
.column {
|
.column {
|
||||||
min-width: 240px; max-width: 280px; flex-shrink: 0;
|
min-width: 240px; max-width: 280px; flex-shrink: 0;
|
||||||
|
|
@ -893,15 +813,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
.col-editor__add input { flex: 1; padding: 6px 8px; border-radius: 6px; border: 1px solid var(--rs-border-strong); background: var(--rs-bg-surface-sunken); color: var(--rs-text-primary); font-size: 12px; outline: none; }
|
.col-editor__add input { flex: 1; padding: 6px 8px; border-radius: 6px; border: 1px solid var(--rs-border-strong); background: var(--rs-bg-surface-sunken); color: var(--rs-text-primary); font-size: 12px; outline: none; }
|
||||||
.col-editor__add button { padding: 6px 14px; border-radius: 6px; border: none; background: var(--rs-primary); color: #fff; cursor: pointer; font-size: 12px; font-weight: 600; }
|
.col-editor__add button { padding: 6px 14px; border-radius: 6px; border: none; background: var(--rs-primary); color: #fff; cursor: pointer; font-size: 12px; font-weight: 600; }
|
||||||
|
|
||||||
/* Inline workspace create form */
|
|
||||||
.ws-create-form { background: var(--rs-bg-surface); border: 1px solid var(--rs-primary); border-radius: 10px; padding: 14px; margin-bottom: 12px; display: flex; gap: 8px; align-items: center; }
|
|
||||||
.ws-create-form input { flex: 1; padding: 8px 12px; border-radius: 6px; border: 1px solid var(--rs-border-strong); background: var(--rs-bg-surface-sunken); color: var(--rs-text-primary); font-size: 14px; outline: none; }
|
|
||||||
.ws-create-form input:focus { border-color: var(--rs-primary-hover); }
|
|
||||||
.ws-create-form button { padding: 8px 16px; border-radius: 6px; border: none; font-size: 13px; cursor: pointer; font-weight: 600; }
|
|
||||||
.ws-create-submit { background: var(--rs-primary); color: #fff; }
|
|
||||||
.ws-create-submit:hover { background: var(--rs-primary-hover); }
|
|
||||||
.ws-create-cancel { background: transparent; color: var(--rs-text-muted); border: 1px solid var(--rs-border-strong) !important; }
|
|
||||||
|
|
||||||
/* Inline delete confirmation */
|
/* Inline delete confirmation */
|
||||||
.confirm-delete { display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: #3b1111; border-radius: 6px; margin-top: 6px; }
|
.confirm-delete { display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: #3b1111; border-radius: 6px; margin-top: 6px; }
|
||||||
.confirm-delete span { font-size: 12px; color: #f87171; }
|
.confirm-delete span { font-size: 12px; color: #f87171; }
|
||||||
|
|
@ -916,7 +827,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.board { flex-direction: column; overflow-x: visible; }
|
.board { flex-direction: column; overflow-x: visible; }
|
||||||
.column { min-width: 100%; max-width: 100%; }
|
.column { min-width: 100%; max-width: 100%; }
|
||||||
.workspace-grid { grid-template-columns: 1fr; }
|
|
||||||
.detail-panel { width: 100vw; }
|
.detail-panel { width: 100vw; }
|
||||||
}
|
}
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
|
|
@ -937,7 +847,7 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
<span>📋 Backlog task: <strong>${this.esc(this._backlogTaskId)}</strong></span>
|
<span>📋 Backlog task: <strong>${this.esc(this._backlogTaskId)}</strong></span>
|
||||||
<button class="backlog-dismiss" data-dismiss-backlog>✕</button>
|
<button class="backlog-dismiss" data-dismiss-backlog>✕</button>
|
||||||
</div>` : ""}
|
</div>` : ""}
|
||||||
${this.view === "list" ? this.renderList() : this.renderBoard()}
|
${this.renderBoard()}
|
||||||
`;
|
`;
|
||||||
this.attachListeners();
|
this.attachListeners();
|
||||||
this._tour.renderOverlay();
|
this._tour.renderOverlay();
|
||||||
|
|
@ -947,36 +857,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
this._tour.start();
|
this._tour.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderList(): string {
|
|
||||||
return `
|
|
||||||
<div class="rapp-nav">
|
|
||||||
<span class="rapp-nav__title">Workspaces</span>
|
|
||||||
${!this.isDemo ? `<button class="cu-connect-btn" id="cu-toggle-panel">${this._cuConnected ? 'CU Connected' : 'Connect ClickUp'}</button>` : ''}
|
|
||||||
<button class="rapp-nav__btn" id="create-ws">+ New Workspace</button>
|
|
||||||
<button class="rapp-nav__btn" id="btn-tour" style="font-size:0.78rem;padding:4px 10px;opacity:0.7">Tour</button>
|
|
||||||
</div>
|
|
||||||
${this._showCreateWs ? `
|
|
||||||
<div class="ws-create-form">
|
|
||||||
<input type="text" id="ws-name-input" placeholder="Workspace name..." autofocus>
|
|
||||||
<button class="ws-create-submit" id="ws-create-submit">Create</button>
|
|
||||||
<button class="ws-create-cancel" id="ws-create-cancel">Cancel</button>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${this.renderClickUpPanel()}
|
|
||||||
${this.workspaces.length > 0 ? `<div class="workspace-grid">
|
|
||||||
${this.workspaces.map(ws => `
|
|
||||||
<div class="workspace-card" data-ws="${ws.slug}">
|
|
||||||
<div class="ws-name">${this.esc(ws.icon || "📋")} ${this.esc(ws.name)}</div>
|
|
||||||
<div class="ws-meta">${ws.task_count || 0} tasks · ${ws.member_count || 0} members</div>
|
|
||||||
</div>
|
|
||||||
`).join("")}
|
|
||||||
</div>` : `<div class="empty">
|
|
||||||
<p style="font-size:16px;margin-bottom:8px">No workspaces yet</p>
|
|
||||||
<p style="font-size:13px">Create a workspace to start managing tasks</p>
|
|
||||||
</div>`}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderCreateForm(): string {
|
private renderCreateForm(): string {
|
||||||
if (!this.showCreateForm) return "";
|
if (!this.showCreateForm) return "";
|
||||||
return `
|
return `
|
||||||
|
|
@ -1006,9 +886,9 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
const hasFilters = !!(this._searchQuery || this._filterPriority || this._filterLabel);
|
const hasFilters = !!(this._searchQuery || this._filterPriority || this._filterLabel);
|
||||||
return `
|
return `
|
||||||
<div class="rapp-nav">
|
<div class="rapp-nav">
|
||||||
${this._history.canGoBack ? '<button class="rapp-nav__back" data-back="list">\u2190 Workspaces</button>' : ''}
|
|
||||||
<span class="rapp-nav__title">${this.esc(this.workspaceSlug)}</span>
|
<span class="rapp-nav__title">${this.esc(this.workspaceSlug)}</span>
|
||||||
${cuSyncInfo}
|
${cuSyncInfo}
|
||||||
|
${!this.isDemo ? `<button class="cu-connect-btn" id="cu-toggle-panel">${this._cuConnected ? 'CU Connected' : 'Connect ClickUp'}</button>` : ''}
|
||||||
<button class="gear-btn" id="toggle-col-editor" title="Manage columns">⚙</button>
|
<button class="gear-btn" id="toggle-col-editor" title="Manage columns">⚙</button>
|
||||||
<div class="view-toggle">
|
<div class="view-toggle">
|
||||||
<button class="view-toggle__btn ${this.boardView === 'board' ? 'active' : ''}" data-set-view="board">Board</button>
|
<button class="view-toggle__btn ${this.boardView === 'board' ? 'active' : ''}" data-set-view="board">Board</button>
|
||||||
|
|
@ -1025,6 +905,7 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
${hasFilters ? '<button class="filter-bar__clear" id="filter-clear">Clear</button>' : ''}
|
${hasFilters ? '<button class="filter-bar__clear" id="filter-clear">Clear</button>' : ''}
|
||||||
<span class="filter-bar__count">${filtered.length} of ${this.tasks.length} tasks</span>
|
<span class="filter-bar__count">${filtered.length} of ${this.tasks.length} tasks</span>
|
||||||
</div>
|
</div>
|
||||||
|
${this.renderClickUpPanel()}
|
||||||
${this._showColumnEditor ? this.renderColumnEditor() : ''}
|
${this._showColumnEditor ? this.renderColumnEditor() : ''}
|
||||||
${this.boardView === "checklist" ? this.renderChecklist() : this.renderKanban()}
|
${this.boardView === "checklist" ? this.renderChecklist() : this.renderKanban()}
|
||||||
${this.detailTaskId ? this.renderDetailPanel() : ''}
|
${this.detailTaskId ? this.renderDetailPanel() : ''}
|
||||||
|
|
@ -1222,26 +1103,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
window.history.replaceState({}, "", url.toString());
|
window.history.replaceState({}, "", url.toString());
|
||||||
this.render();
|
this.render();
|
||||||
});
|
});
|
||||||
this.shadow.querySelector("#btn-tour")?.addEventListener("click", () => this.startTour());
|
|
||||||
this.shadow.getElementById("create-ws")?.addEventListener("click", () => {
|
|
||||||
this._showCreateWs = !this._showCreateWs;
|
|
||||||
this.render();
|
|
||||||
if (this._showCreateWs) setTimeout(() => this.shadow.getElementById("ws-name-input")?.focus(), 0);
|
|
||||||
});
|
|
||||||
this.shadow.getElementById("ws-create-submit")?.addEventListener("click", () => {
|
|
||||||
const input = this.shadow.getElementById("ws-name-input") as HTMLInputElement;
|
|
||||||
if (input?.value?.trim()) this.createWorkspace(input.value.trim());
|
|
||||||
});
|
|
||||||
this.shadow.getElementById("ws-name-input")?.addEventListener("keydown", (e) => {
|
|
||||||
if ((e as KeyboardEvent).key === "Enter") {
|
|
||||||
const input = e.target as HTMLInputElement;
|
|
||||||
if (input.value?.trim()) this.createWorkspace(input.value.trim());
|
|
||||||
}
|
|
||||||
if ((e as KeyboardEvent).key === "Escape") { this._showCreateWs = false; this.render(); }
|
|
||||||
});
|
|
||||||
this.shadow.getElementById("ws-create-cancel")?.addEventListener("click", () => {
|
|
||||||
this._showCreateWs = false; this.render();
|
|
||||||
});
|
|
||||||
this.shadow.getElementById("create-task")?.addEventListener("click", () => {
|
this.shadow.getElementById("create-task")?.addEventListener("click", () => {
|
||||||
this.showCreateForm = !this.showCreateForm;
|
this.showCreateForm = !this.showCreateForm;
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -1303,16 +1164,6 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.shadow.querySelectorAll("[data-ws]").forEach(el => {
|
|
||||||
el.addEventListener("click", () => {
|
|
||||||
this._history.push("list");
|
|
||||||
this._history.push("board", { ws: (el as HTMLElement).dataset.ws });
|
|
||||||
this.openBoard((el as HTMLElement).dataset.ws!);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.shadow.querySelectorAll("[data-back]").forEach(el => {
|
|
||||||
el.addEventListener("click", () => this.goBack());
|
|
||||||
});
|
|
||||||
this.shadow.querySelectorAll("[data-move]").forEach(el => {
|
this.shadow.querySelectorAll("[data-move]").forEach(el => {
|
||||||
el.addEventListener("click", (e) => {
|
el.addEventListener("click", (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -1582,13 +1433,10 @@ class FolkTasksBoard extends HTMLElement {
|
||||||
this._cuEnableSync = (e.target as HTMLInputElement).checked;
|
this._cuEnableSync = (e.target as HTMLInputElement).checked;
|
||||||
});
|
});
|
||||||
this.shadow.getElementById("cu-open-board")?.addEventListener("click", () => {
|
this.shadow.getElementById("cu-open-board")?.addEventListener("click", () => {
|
||||||
const board = (this.shadow.getElementById("cu-open-board") as HTMLElement)?.dataset.cuBoard;
|
this._cuShowPanel = false;
|
||||||
if (board) {
|
this._cuStep = 'token';
|
||||||
this._cuShowPanel = false;
|
this._cuImportResult = null;
|
||||||
this._cuStep = 'token';
|
this.loadTasks();
|
||||||
this._cuImportResult = null;
|
|
||||||
this.openBoard(board);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pointer events drag-and-drop on task cards (works with touch, pen, mouse)
|
// Pointer events drag-and-drop on task cards (works with touch, pen, mouse)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue