mirror of
https://github.com/zadam/trilium.git
synced 2025-11-05 04:45:47 +01:00
chore(react/collections/board): render empty columns
This commit is contained in:
@@ -8,6 +8,7 @@ import GeoView from "./geomap";
|
|||||||
import ViewModeStorage from "../view_widgets/view_mode_storage";
|
import ViewModeStorage from "../view_widgets/view_mode_storage";
|
||||||
import CalendarView from "./calendar";
|
import CalendarView from "./calendar";
|
||||||
import TableView from "./table";
|
import TableView from "./table";
|
||||||
|
import BoardView from "./board";
|
||||||
|
|
||||||
interface NoteListProps<T extends object> {
|
interface NoteListProps<T extends object> {
|
||||||
note?: FNote | null;
|
note?: FNote | null;
|
||||||
@@ -88,6 +89,8 @@ function getComponentByViewType(viewType: ViewTypeOptions, props: ViewModeProps<
|
|||||||
return <CalendarView {...props} />
|
return <CalendarView {...props} />
|
||||||
case "table":
|
case "table":
|
||||||
return <TableView {...props} />
|
return <TableView {...props} />
|
||||||
|
case "board":
|
||||||
|
return <BoardView {...props} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import FBranch from "../../../entities/fbranch";
|
import FBranch from "../../../entities/fbranch";
|
||||||
import FNote from "../../../entities/fnote";
|
import FNote from "../../../entities/fnote";
|
||||||
import { BoardData } from "./config";
|
import { BoardViewData } from "./index";
|
||||||
|
|
||||||
export type ColumnMap = Map<string, {
|
export type ColumnMap = Map<string, {
|
||||||
branch: FBranch;
|
branch: FBranch;
|
||||||
note: FNote;
|
note: FNote;
|
||||||
}[]>;
|
}[]>;
|
||||||
|
|
||||||
export async function getBoardData(parentNote: FNote, groupByColumn: string, persistedData: BoardData) {
|
export async function getBoardData(parentNote: FNote, groupByColumn: string, persistedData: BoardViewData) {
|
||||||
const byColumn: ColumnMap = new Map();
|
const byColumn: ColumnMap = new Map();
|
||||||
|
|
||||||
// First, scan all notes to find what columns actually exist
|
// First, scan all notes to find what columns actually exist
|
||||||
@@ -43,7 +43,7 @@ export async function getBoardData(parentNote: FNote, groupByColumn: string, per
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return updated persisted data only if there were changes
|
// Return updated persisted data only if there were changes
|
||||||
let newPersistedData: BoardData | undefined;
|
let newPersistedData: BoardViewData | undefined;
|
||||||
const hasChanges = newColumnValues.length > 0 ||
|
const hasChanges = newColumnValues.length > 0 ||
|
||||||
existingPersistedColumns.length !== deduplicatedColumns.length ||
|
existingPersistedColumns.length !== deduplicatedColumns.length ||
|
||||||
!existingPersistedColumns.every((col, idx) => deduplicatedColumns[idx]?.value === col.value);
|
!existingPersistedColumns.every((col, idx) => deduplicatedColumns[idx]?.value === col.value);
|
||||||
264
apps/client/src/widgets/collections/board/index.css
Normal file
264
apps/client/src/widgets/collections/board/index.css
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
.board-view {
|
||||||
|
overflow-x: auto;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 1em;
|
||||||
|
padding-bottom: 0;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column {
|
||||||
|
width: 250px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
transition: border-color 0.2s ease;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column.drag-over {
|
||||||
|
border-color: var(--main-text-color);
|
||||||
|
background-color: var(--hover-item-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3 {
|
||||||
|
font-size: 1em;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
padding: 0.5em 0.5em 0.5em 0.5em;
|
||||||
|
border-bottom: 1px solid var(--main-border-color);
|
||||||
|
cursor: grab;
|
||||||
|
position: relative;
|
||||||
|
transition: background-color 0.2s ease, border-radius 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3.editing {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3:hover {
|
||||||
|
background-color: var(--hover-item-background-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3.editing {
|
||||||
|
background-color: var(--main-background-color);
|
||||||
|
border: 1px solid var(--main-text-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column.column-dragging {
|
||||||
|
opacity: 0.6;
|
||||||
|
transform: scale(0.98);
|
||||||
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3 input {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
color: inherit;
|
||||||
|
width: 100%;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3 .edit-icon {
|
||||||
|
opacity: 0;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
color: var(--muted-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3:hover .edit-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-column h3.editing .edit-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note {
|
||||||
|
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.25);
|
||||||
|
margin: 0.65em 0;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: move;
|
||||||
|
position: relative;
|
||||||
|
background-color: var(--main-background-color);
|
||||||
|
border: 1px solid var(--main-border-color);
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.15s ease;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note.fade-in {
|
||||||
|
animation: fadeIn 0.15s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note.fade-out {
|
||||||
|
animation: fadeOut 0.15s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(-10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeOut {
|
||||||
|
from { opacity: 1; transform: translateY(0); }
|
||||||
|
to { opacity: 0; transform: translateY(-10px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note.card-updated {
|
||||||
|
animation: cardUpdate 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cardUpdate {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.02); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note.dragging {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: rotate(5deg);
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note.editing {
|
||||||
|
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
||||||
|
border-color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note.editing input {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-view-container .board-note .icon {
|
||||||
|
margin-right: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-drop-indicator {
|
||||||
|
height: 3px;
|
||||||
|
background-color: var(--main-text-color);
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-drop-indicator.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-drop-indicator {
|
||||||
|
width: 4px;
|
||||||
|
background-color: var(--main-text-color);
|
||||||
|
border-radius: 2px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-drop-indicator.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-new-item {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: var(--muted-text-color);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-new-item:hover {
|
||||||
|
border-color: var(--main-text-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
|
background-color: var(--hover-item-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-new-item .icon {
|
||||||
|
margin-right: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-add-column {
|
||||||
|
width: 180px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--muted-text-color);
|
||||||
|
font-size: 0.9em;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-add-column:hover {
|
||||||
|
border-color: var(--main-text-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
|
background-color: var(--hover-item-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-add-column .icon {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-drag-preview {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10000;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: rotate(5deg);
|
||||||
|
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
||||||
|
background-color: var(--main-background-color);
|
||||||
|
border: 1px solid var(--main-border-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0.5em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
max-width: 200px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
59
apps/client/src/widgets/collections/board/index.tsx
Normal file
59
apps/client/src/widgets/collections/board/index.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { ViewModeProps } from "../interface";
|
||||||
|
import "./index.css";
|
||||||
|
import { ColumnMap, getBoardData } from "./data";
|
||||||
|
import { useNoteLabel } from "../../react/hooks";
|
||||||
|
|
||||||
|
export interface BoardViewData {
|
||||||
|
columns?: BoardColumnData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BoardColumnData {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BoardView({ note: parentNote, noteIds, viewConfig, saveConfig }: ViewModeProps<BoardViewData>) {
|
||||||
|
const [ statusAttribute ] = useNoteLabel(parentNote, "board:groupBy");
|
||||||
|
const [ byColumn, setByColumn ] = useState<ColumnMap>();
|
||||||
|
const [ columns, setColumns ] = useState<string[]>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getBoardData(parentNote, statusAttribute ?? "status", viewConfig ?? {}).then(({ byColumn, newPersistedData }) => {
|
||||||
|
setByColumn(byColumn);
|
||||||
|
|
||||||
|
if (newPersistedData) {
|
||||||
|
viewConfig = { ...newPersistedData };
|
||||||
|
saveConfig(newPersistedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the order from persistedData.columns, then add any new columns found
|
||||||
|
const orderedColumns = viewConfig?.columns?.map(col => col.value) || [];
|
||||||
|
const allColumns = Array.from(byColumn.keys());
|
||||||
|
const newColumns = allColumns.filter(col => !orderedColumns.includes(col));
|
||||||
|
setColumns([...orderedColumns, ...newColumns]);
|
||||||
|
});
|
||||||
|
}, [ parentNote ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="board-view">
|
||||||
|
<div className="board-view-container">
|
||||||
|
{columns?.map(column => (
|
||||||
|
<Column column={column} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Column({ column }: { column: string }) {
|
||||||
|
return (
|
||||||
|
<div className="board-column">
|
||||||
|
<h3>
|
||||||
|
<span>{column}</span>
|
||||||
|
<span
|
||||||
|
className="edit-icon icon bx bx-edit-alt"
|
||||||
|
title="Click to edit column title" />
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -135,12 +135,9 @@ export default class BoardApi {
|
|||||||
|
|
||||||
async refresh(parentNote: FNote) {
|
async refresh(parentNote: FNote) {
|
||||||
// Refresh the API data by re-fetching from the parent note
|
// Refresh the API data by re-fetching from the parent note
|
||||||
const statusAttribute = parentNote.getLabelValue("board:groupBy") ?? "status";
|
|
||||||
this._statusAttribute = statusAttribute;
|
|
||||||
|
|
||||||
// Use the current in-memory persisted data instead of restoring from storage
|
// Use the current in-memory persisted data instead of restoring from storage
|
||||||
// This ensures we don't lose recent updates like column renames
|
// This ensures we don't lose recent updates like column renames
|
||||||
const { byColumn, newPersistedData } = await getBoardData(parentNote, statusAttribute, this.persistedData);
|
|
||||||
|
|
||||||
// Update internal state
|
// Update internal state
|
||||||
this.byColumn = byColumn;
|
this.byColumn = byColumn;
|
||||||
@@ -161,18 +158,6 @@ export default class BoardApi {
|
|||||||
const statusAttribute = parentNote.getLabelValue("board:groupBy") ?? "status";
|
const statusAttribute = parentNote.getLabelValue("board:groupBy") ?? "status";
|
||||||
|
|
||||||
let persistedData = await viewStorage.restore() ?? {};
|
let persistedData = await viewStorage.restore() ?? {};
|
||||||
const { byColumn, newPersistedData } = await getBoardData(parentNote, statusAttribute, persistedData);
|
|
||||||
|
|
||||||
// Use the order from persistedData.columns, then add any new columns found
|
|
||||||
const orderedColumns = persistedData.columns?.map(col => col.value) || [];
|
|
||||||
const allColumns = Array.from(byColumn.keys());
|
|
||||||
const newColumns = allColumns.filter(col => !orderedColumns.includes(col));
|
|
||||||
const columns = [...orderedColumns, ...newColumns];
|
|
||||||
|
|
||||||
if (newPersistedData) {
|
|
||||||
persistedData = newPersistedData;
|
|
||||||
viewStorage.store(persistedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BoardApi(columns, parentNote.noteId, viewStorage, byColumn, persistedData, statusAttribute);
|
return new BoardApi(columns, parentNote.noteId, viewStorage, byColumn, persistedData, statusAttribute);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
export interface BoardColumnData {
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BoardData {
|
|
||||||
columns?: BoardColumnData[];
|
|
||||||
}
|
|
||||||
@@ -329,24 +329,6 @@ export class DifferentialBoardRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createColumn(column: string, columnItems: { note: any; branch: any }[]): JQuery<HTMLElement> {
|
private createColumn(column: string, columnItems: { note: any; branch: any }[]): JQuery<HTMLElement> {
|
||||||
const $columnEl = $("<div>")
|
|
||||||
.addClass("board-column")
|
|
||||||
.attr("data-column", column);
|
|
||||||
|
|
||||||
// Create header
|
|
||||||
const $titleEl = $("<h3>").attr("data-column-value", column);
|
|
||||||
|
|
||||||
// Create title text
|
|
||||||
const $titleText = $("<span>").text(column);
|
|
||||||
|
|
||||||
// Create edit icon
|
|
||||||
const $editIcon = $("<span>")
|
|
||||||
.addClass("edit-icon icon bx bx-edit-alt")
|
|
||||||
.attr("title", "Click to edit column title");
|
|
||||||
|
|
||||||
$titleEl.append($titleText, $editIcon);
|
|
||||||
$columnEl.append($titleEl);
|
|
||||||
|
|
||||||
// Setup column dragging
|
// Setup column dragging
|
||||||
this.dragHandler.setupColumnDrag($columnEl, column);
|
this.dragHandler.setupColumnDrag($columnEl, column);
|
||||||
|
|
||||||
|
|||||||
@@ -9,279 +9,6 @@ import BoardApi from "./api";
|
|||||||
import { BoardDragHandler, DragContext } from "./drag_handler";
|
import { BoardDragHandler, DragContext } from "./drag_handler";
|
||||||
import { DifferentialBoardRenderer } from "./differential_renderer";
|
import { DifferentialBoardRenderer } from "./differential_renderer";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
|
||||||
<div class="board-view">
|
|
||||||
<style>
|
|
||||||
.board-view {
|
|
||||||
overflow-x: auto;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
gap: 1em;
|
|
||||||
padding: 1em;
|
|
||||||
padding-bottom: 0;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column {
|
|
||||||
width: 250px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 0.5em;
|
|
||||||
background-color: var(--accented-background-color);
|
|
||||||
transition: border-color 0.2s ease;
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column.drag-over {
|
|
||||||
border-color: var(--main-text-color);
|
|
||||||
background-color: var(--hover-item-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3 {
|
|
||||||
font-size: 1em;
|
|
||||||
margin-bottom: 0.75em;
|
|
||||||
padding: 0.5em 0.5em 0.5em 0.5em;
|
|
||||||
border-bottom: 1px solid var(--main-border-color);
|
|
||||||
cursor: grab;
|
|
||||||
position: relative;
|
|
||||||
transition: background-color 0.2s ease, border-radius 0.2s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3:active {
|
|
||||||
cursor: grabbing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3.editing {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3:hover {
|
|
||||||
background-color: var(--hover-item-background-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3.editing {
|
|
||||||
background-color: var(--main-background-color);
|
|
||||||
border: 1px solid var(--main-text-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column.column-dragging {
|
|
||||||
opacity: 0.6;
|
|
||||||
transform: scale(0.98);
|
|
||||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3 input {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
color: inherit;
|
|
||||||
width: 100%;
|
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3 .edit-icon {
|
|
||||||
opacity: 0;
|
|
||||||
margin-left: 0.5em;
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
color: var(--muted-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3:hover .edit-icon {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-column h3.editing .edit-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note {
|
|
||||||
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.25);
|
|
||||||
margin: 0.65em 0;
|
|
||||||
padding: 0.5em;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: move;
|
|
||||||
position: relative;
|
|
||||||
background-color: var(--main-background-color);
|
|
||||||
border: 1px solid var(--main-border-color);
|
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.15s ease;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note.fade-in {
|
|
||||||
animation: fadeIn 0.15s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note.fade-out {
|
|
||||||
animation: fadeOut 0.15s ease-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from { opacity: 0; transform: translateY(-10px); }
|
|
||||||
to { opacity: 1; transform: translateY(0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeOut {
|
|
||||||
from { opacity: 1; transform: translateY(0); }
|
|
||||||
to { opacity: 0; transform: translateY(-10px); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note.card-updated {
|
|
||||||
animation: cardUpdate 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cardUpdate {
|
|
||||||
0% { transform: scale(1); }
|
|
||||||
50% { transform: scale(1.02); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); }
|
|
||||||
100% { transform: scale(1); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note.dragging {
|
|
||||||
opacity: 0.8;
|
|
||||||
transform: rotate(5deg);
|
|
||||||
z-index: 1000;
|
|
||||||
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note.editing {
|
|
||||||
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
|
||||||
border-color: var(--main-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note.editing input {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
color: inherit;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-view-container .board-note .icon {
|
|
||||||
margin-right: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-drop-indicator {
|
|
||||||
height: 3px;
|
|
||||||
background-color: var(--main-text-color);
|
|
||||||
border-radius: 2px;
|
|
||||||
margin: 0.25em 0;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-drop-indicator.show {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column-drop-indicator {
|
|
||||||
width: 4px;
|
|
||||||
background-color: var(--main-text-color);
|
|
||||||
border-radius: 2px;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1000;
|
|
||||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column-drop-indicator.show {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-new-item {
|
|
||||||
margin-top: 0.5em;
|
|
||||||
padding: 0.5em;
|
|
||||||
border-radius: 5px;
|
|
||||||
color: var(--muted-text-color);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-new-item:hover {
|
|
||||||
border-color: var(--main-text-color);
|
|
||||||
color: var(--main-text-color);
|
|
||||||
background-color: var(--hover-item-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-new-item .icon {
|
|
||||||
margin-right: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-add-column {
|
|
||||||
width: 180px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
height: 60px;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 0.5em;
|
|
||||||
background-color: var(--accented-background-color);
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--muted-text-color);
|
|
||||||
font-size: 0.9em;
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-add-column:hover {
|
|
||||||
border-color: var(--main-text-color);
|
|
||||||
color: var(--main-text-color);
|
|
||||||
background-color: var(--hover-item-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-add-column .icon {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-drag-preview {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 10000;
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.8;
|
|
||||||
transform: rotate(5deg);
|
|
||||||
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
|
||||||
background-color: var(--main-background-color);
|
|
||||||
border: 1px solid var(--main-border-color);
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 0.5em;
|
|
||||||
font-size: 0.9em;
|
|
||||||
max-width: 200px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="board-view-container"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default class BoardView extends ViewMode<BoardData> {
|
export default class BoardView extends ViewMode<BoardData> {
|
||||||
|
|
||||||
private $root: JQuery<HTMLElement>;
|
private $root: JQuery<HTMLElement>;
|
||||||
|
|||||||
Reference in New Issue
Block a user