Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/board/boardLabels.js
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,9 @@ function initializeBoardLabelControls() {
if (!window.boardRoot) {
return;
}
if (typeof closeAllModals === 'function') {
await closeAllModals({ key: 'Escape' });
}
await ensureBoardLabelsLoaded();
openBoardSettingsModal();
});
Expand Down
42 changes: 27 additions & 15 deletions app/board/boardTabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ function normalizeBoardPath(dir) {
return '';
}

return dir.endsWith('/') ? dir : `${dir}/`;
const normalizedDir = dir.replace(/\\/g, '/').trim();
if (!normalizedDir) {
return '';
}

return normalizedDir.endsWith('/') ? normalizedDir : `${normalizedDir}/`;
}

function getBoardLabelFromPath(boardPath) {
const pathParts = boardPath.split('/').filter(Boolean);
const normalizedPath = normalizeBoardPath(boardPath).replace(/\/+$/, '');
const pathParts = normalizedPath.split('/').filter(Boolean);
return pathParts[pathParts.length - 1] || 'Board';
}

Expand Down Expand Up @@ -95,15 +101,19 @@ function ensureBoardInTabs(boardPath) {
}

function clearRenderedBoard() {
if (typeof setBoardChromeState === 'function') {
setBoardChromeState(false);
}

if (typeof renderBoardEmptyState === 'function') {
renderBoardEmptyState();
return;
}

const boardEl = document.getElementById('board');
if (boardEl) {
boardEl.innerHTML = '';
}

const boardNameEl = document.getElementById('boardName');
if (boardNameEl) {
boardNameEl.textContent = 'No Board Open';
}
}

async function closeBoardTab(boardPath) {
Expand Down Expand Up @@ -151,17 +161,13 @@ async function closeBoardTab(boardPath) {
await renderBoard();
}

function initializeBoardTabsSortable(tabsEl) {
if (!tabsEl || typeof Sortable !== 'function') {
return;
}

if (boardTabsSortable && boardTabsSortable.el !== tabsEl) {
function initializeBoardTabsSortable(tabsEl, canSortTabs = true) {
if (boardTabsSortable && (!tabsEl || boardTabsSortable.el !== tabsEl || !canSortTabs)) {
boardTabsSortable.destroy();
boardTabsSortable = null;
}

if (boardTabsSortable) {
if (!canSortTabs || !tabsEl || typeof Sortable !== 'function' || boardTabsSortable) {
return;
}

Expand Down Expand Up @@ -245,6 +251,12 @@ function renderBoardTabs() {
const openBoards = getStoredOpenBoards();

tabsEl.innerHTML = '';
if (openBoards.length === 0) {
tabsWrapper.classList.add('hidden');
initializeBoardTabsSortable(null, false);
return;
}

tabsWrapper.classList.remove('hidden');
const activeBoard = normalizeBoardPath(window.boardRoot || getStoredActiveBoard());

Expand Down Expand Up @@ -329,5 +341,5 @@ function renderBoardTabs() {
addBoardTab.appendChild(addBoardButton);
tabsEl.appendChild(addBoardTab);

initializeBoardTabsSortable(tabsEl);
initializeBoardTabsSortable(tabsEl, openBoards.length > 1);
}
22 changes: 14 additions & 8 deletions app/board/openBoard.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,28 @@ async function openBoard( dir ) {
const directories = await window.board.listDirectories( boardPath );

if ( directories.length == 0 ) {
await window.board.createList( boardPath + '000-To-do-stock');
await window.board.createList( boardPath + '001-Doing-stock');
await window.board.createList( boardPath + '002-Done-stock');
await window.board.createList( boardPath + '003-On-hold-stock');
await window.board.createList( boardPath + 'XXX-Archive');
await Promise.all([
window.board.createList(boardPath + '000-To-do-stock'),
window.board.createList(boardPath + '001-Doing-stock'),
window.board.createList(boardPath + '002-Done-stock'),
window.board.createList(boardPath + 'XXX-Archive'),
]);

await window.board.createCard( boardPath + '000-To-do-stock/000-hello-stock.md', `👋 Hello

Welcome to Signboard! This card is your first task. Tap on it to view more or edit.
Welcome to Signboard!

Here are some fun things to get you started:

- Create new cards by clicking the + button on any list
- Edit the title or notes on any card by tapping on it
- Edit the title or notes on any card by tapping on them
- Reorder cards in a list or move them between lists by dragging them
- Archive a card by tapping on it and tapping the archive icon
- Archive a card by tapping the archive icon
- Reorder lists by dragging them
- Create new lists by clicking the "+ Add List" button
- Add due dates to cards
- Customize your labels in the Board Settings area
- Customize your light and dark color schemes per board

***Keyboard Shortcuts***

Expand Down
135 changes: 106 additions & 29 deletions app/board/renderBoard.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,94 @@
function setBoardChromeState(hasOpenBoard) {
const body = document.body;
if (body) {
body.classList.toggle('board-empty', !hasOpenBoard);
}

if (hasOpenBoard) {
return;
}

const boardNameEl = document.getElementById('boardName');
if (boardNameEl) {
boardNameEl.textContent = 'Signboard';
}
}

async function handleEmptyBoardCallToActionClick(buttonEl) {
if (!buttonEl || buttonEl.disabled) {
return;
}

buttonEl.disabled = true;
try {
await promptAndOpenBoardFromTabs();
} finally {
buttonEl.disabled = false;
}
}

function createEmptyBoardCallToAction() {
const buttonEl = document.createElement('button');
buttonEl.type = 'button';
buttonEl.id = 'emptyBoardCallToAction';
buttonEl.className = 'empty-board-cta';
buttonEl.setAttribute('aria-label', 'Select a directory to create a board');
buttonEl.innerHTML = `
<span class="empty-board-cta-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
<line x1="12" y1="11" x2="12" y2="17"></line>
<line x1="9" y1="14" x2="15" y2="14"></line>
</svg>
</span>
<span class="empty-board-cta-title">Create your first board</span>
<span class="empty-board-cta-text">Select an empty directory. Signboard will use the directory name as the board name.</span>
`;
buttonEl.addEventListener('click', async () => {
await handleEmptyBoardCallToActionClick(buttonEl);
});
return buttonEl;
}

function renderBoardEmptyState() {
const boardEl = document.getElementById('board');
if (!boardEl) {
return;
}

boardEl.innerHTML = '';
boardEl.appendChild(createEmptyBoardCallToAction());
}

async function renderBoard() {
const boardRoot = window.boardRoot; // set in the drop‑zone handler
await ensureBoardLabelsLoaded();
const boardRoot = window.boardRoot; // set in the drop-zone handler

if (!boardRoot) {
setBoardChromeState(false);
renderBoardTabs();
renderBoardEmptyState();
return;
}

closeCardLabelPopover();
setBoardChromeState(true);

const boardName = document.getElementById('boardName');
boardName.textContent = await window.board.getBoardName( boardRoot );
const boardNameEl = document.getElementById('boardName');
const [boardName, lists] = await Promise.all([
window.board.getBoardName(boardRoot),
window.board.listLists(boardRoot),
ensureBoardLabelsLoaded(),
]);

if (boardNameEl) {
boardNameEl.textContent = boardName;
}
renderBoardTabs();

const lists = await window.board.listLists(boardRoot);
const boardEl = document.getElementById('board');
if (!boardEl) {
return;
}
boardEl.innerHTML = '';

const listsWithCards = await Promise.all(
Expand All @@ -25,41 +99,44 @@ async function renderBoard() {
})
);

for (const { listName, listPath, cards } of listsWithCards) {
const listEl = await createListElement(listName, listPath, cards);
const listElements = await Promise.all(
listsWithCards.map(({ listName, listPath, cards }) => createListElement(listName, listPath, cards))
);

for (const listEl of listElements) {
boardEl.appendChild(listEl);
}

// Enable SortableJS on this column
new Sortable(boardEl, {
group: 'lists',
animation: 150,
onEnd: async (evt) => {

const finalOrder = [...evt.to.querySelectorAll('.list')].map(list =>
if (typeof Sortable === 'function') {
// Enable SortableJS on this column
new Sortable(boardEl, {
group: 'lists',
animation: 150,
onEnd: async (evt) => {
const finalOrder = [...evt.to.querySelectorAll('.list')].map((list) =>
list.getAttribute('data-path')
);
);

let directoryCounter = 0;
for (const directoryPath of finalOrder) {

let directoryNumber = (directoryCounter).toLocaleString('en-US', {
minimumIntegerDigits: 3,
useGrouping: false
let directoryCounter = 0;
for (const directoryPath of finalOrder) {
const directoryNumber = (directoryCounter).toLocaleString('en-US', {
minimumIntegerDigits: 3,
useGrouping: false
});

let newDirectoryName = window.boardRoot + directoryNumber + await window.board.getListDirectoryName(directoryPath).slice(3);
const newDirectoryName = window.boardRoot + directoryNumber + await window.board.getListDirectoryName(directoryPath).slice(3);

await window.board.moveCard(directoryPath, newDirectoryName);

directoryCounter++;

}

await renderBoard();

}
});
feather.replace();
return;
await renderBoard();
}
});
}

if (typeof feather !== 'undefined' && feather && typeof feather.replace === 'function') {
feather.replace();
}
}
35 changes: 28 additions & 7 deletions app/init.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
var turndown = new TurndownService();
const renderMarkdown = (md) => marked.parse(md);

async function init() {
initializeBoardLabelControls();
initializeBoardSearchControls();

const restoredBoard = restoreBoardTabs();
const initializeHeaderControls = () => {
initializeBoardLabelControls();
initializeBoardSearchControls();
};

if (!restoredBoard) {
window.boardRoot = '';
if (typeof setBoardChromeState === 'function') {
setBoardChromeState(false);
}

const emptyBoardCallToAction = document.getElementById('emptyBoardCallToAction');
if (emptyBoardCallToAction) {
emptyBoardCallToAction.addEventListener('click', async () => {
if (typeof handleEmptyBoardCallToActionClick === 'function') {
await handleEmptyBoardCallToActionClick(emptyBoardCallToAction);
return;
}
await promptAndOpenBoardFromTabs();
});
}

window.setTimeout(initializeHeaderControls, 0);
} else {
initializeHeaderControls();
}

if (restoredBoard) {
window.boardRoot = restoredBoard;
Expand Down Expand Up @@ -38,7 +58,8 @@ async function init() {

await closeAllModals(e);
});
document.getElementById('btnAddNewList').addEventListener('click', async () => {
document.getElementById('btnAddNewList').addEventListener('click', async (e) => {
e.stopPropagation();
const listName = document.getElementById('userInputListName');
toggleAddListModal( (window.innerWidth / 2)-200, (window.innerHeight / 2)-100 );
listName.focus();
Expand Down
1 change: 1 addition & 0 deletions app/lists/createListElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ async function createListElement(name, listPath, cardNames) {
addBtn.setAttribute('data-listpath', listPath + '/');
addBtn.setAttribute('class','btnOpenAddCardModal');
addBtn.addEventListener('click', async function (e) {
e.stopPropagation();

toggleAddCardModal( e.x-90, e.y+15 );
const userInput = document.getElementById('userInput');
Expand Down
Loading