Skip to content

Commit

Permalink
Add option to force horizontal or vertical layout mode (#2)
Browse files Browse the repository at this point in the history
Closes #2
  • Loading branch information
benji300 committed Feb 26, 2021
1 parent 6e4c06e commit abd7d05
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 53 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- User option to force horizontal or vertical layout mode ([#2](https://github.com/benji300/joplin-note-tabs/issues/2))
- Ability to scroll tabs and breadcrums horizontally without holding `shift` key ([#34](https://github.com/benji300/joplin-note-tabs/issues/34))

### Changed
Expand Down
77 changes: 46 additions & 31 deletions src/panel.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import joplin from 'api';
import { NoteTabs } from './noteTabs';
import { Settings } from './settings';
import { Settings, LayoutMode } from './settings';

export class Panel {
private _panel: any;
Expand All @@ -12,6 +12,14 @@ export class Panel {
this._settings = settings;
}

private get tabs() {
return this._tabs;
}

private get sets() {
return this._settings;
}

private async toggleTodoState(noteId: string, checked: any) {
try {
const note: any = await joplin.data.get(['notes', noteId], { fields: ['id', 'is_todo', 'todo_completed'] });
Expand Down Expand Up @@ -45,10 +53,10 @@ export class Panel {

// create HTML for each tab
private async getNoteTabsHtml(selectedNote: any): Promise<string> {
const showCompletedTodos: boolean = await this._settings.showCompletedTodos;
const showCompletedTodos: boolean = await this.sets.showCompletedTodos;
const noteTabsHtml: any = [];

for (const noteTab of this._tabs.tabs) {
for (const noteTab of this.tabs.tabs) {
let note: any = null;

// get real note from database, if no longer exists remove tab and continue with next one
Expand All @@ -57,7 +65,7 @@ export class Panel {
// console.log(`add note: ${JSON.stringify(note)}`);
} catch (error) {
// console.log(`delete note: ${noteTab.id}`);
await this._tabs.delete(noteTab.id);
await this.tabs.delete(noteTab.id);
continue;
}

Expand All @@ -66,24 +74,24 @@ export class Panel {
if ((!showCompletedTodos) && note.todo_completed) continue;

// prepare tab style attributes
const bg: string = (selectedNote && note.id == selectedNote.id) ? this._settings.actBackground : this._settings.background;
const fg: string = (selectedNote && note.id == selectedNote.id) ? this._settings.actForeground : this._settings.foreground;
const bg: string = (selectedNote && note.id == selectedNote.id) ? this.sets.actBackground : this.sets.background;
const fg: string = (selectedNote && note.id == selectedNote.id) ? this.sets.actForeground : this.sets.foreground;
const newTab: string = (NoteTabs.isTemporary(noteTab)) ? ' new' : '';
const icon: string = (NoteTabs.isPinned(noteTab)) ? 'fa-times' : 'fa-thumbtack';
const iconTitle: string = (NoteTabs.isPinned(noteTab)) ? 'Unpin' : 'Pin';
const textDecoration: string = (note.is_todo && note.todo_completed) ? 'line-through' : '';

// prepare checkbox for todo
let checkboxHtml: string = '';
if (this._settings.showTodoCheckboxes && note.is_todo) {
if (this.sets.showTodoCheckboxes && note.is_todo) {
checkboxHtml = `<input id="check" type="checkbox" ${(note.todo_completed) ? "checked" : ''}>`;
}

noteTabsHtml.push(`
<div id="tab" data-id="${note.id}" data-bg="${bg}" draggable="${this._settings.enableDragAndDrop}" class="${newTab}" role="tab" title="${note.title}"
onclick="tabClick(event);" ondblclick="pinNote(event);" onmouseover="setBackground(event,'${this._settings.hoverBackground}');" onmouseout="resetBackground(this);"
ondragstart="dragStart(event);" ondragend="dragEnd(event);" ondragover="dragOver(event, '${this._settings.hoverBackground}');" ondragleave="dragLeave(event);" ondrop="drop(event);"
style="height:${this._settings.tabHeight}px;min-width:${this._settings.minTabWidth}px;max-width:${this._settings.maxTabWidth}px;border-color:${this._settings.dividerColor};background:${bg};">
<div id="tab" data-id="${note.id}" data-bg="${bg}" draggable="${this.sets.enableDragAndDrop}" class="${newTab}" role="tab" title="${note.title}"
onclick="tabClick(event);" ondblclick="pinNote(event);" onmouseover="setBackground(event,'${this.sets.hoverBackground}');" onmouseout="resetBackground(this);"
ondragstart="dragStart(event);" ondragend="dragEnd(event);" ondragover="dragOver(event, '${this.sets.hoverBackground}');" ondragleave="dragLeave(event);" ondrop="drop(event);"
style="height:${this.sets.tabHeight}px;min-width:${this.sets.minTabWidth}px;max-width:${this.sets.maxTabWidth}px;border-color:${this.sets.dividerColor};background:${bg};">
<span class="tab-inner">
${checkboxHtml}
<span class="tab-title" style="color:${fg};text-decoration: ${textDecoration};">
Expand All @@ -103,11 +111,11 @@ export class Panel {
private getControlsHtml(): string {
let controlsHtml: string = '';

if (!this._settings.enableDragAndDrop) {
if (!this.sets.enableDragAndDrop) {
controlsHtml = `
<div id="controls" style="height:${this._settings.tabHeight}px;">
<a href="#" class="fas fa-chevron-left" title="Move active tab left" style="color:${this._settings.foreground};" onclick="message('tabsMoveLeft');"></a>
<a href="#" class="fas fa-chevron-right" title="Move active tab right" style="color:${this._settings.foreground};" onclick="message('tabsMoveRight');"></a>
<div id="controls" style="height:${this.sets.tabHeight}px;">
<a href="#" class="fas fa-chevron-left" title="Move active tab left" style="color:${this.sets.foreground};" onclick="message('tabsMoveLeft');"></a>
<a href="#" class="fas fa-chevron-right" title="Move active tab right" style="color:${this.sets.foreground};" onclick="message('tabsMoveRight');"></a>
</div>
`;
}
Expand All @@ -119,13 +127,13 @@ export class Panel {
private async getBreadcrumbsHtml(selectedNote: any): Promise<string> {
let breadcrumbsHtml: string = '';

if (this._settings.showBreadcrumbs && selectedNote) {
if (this.sets.showBreadcrumbs && selectedNote) {
let navigationHtml: string = '';
if (this._settings.showNavigationButtons) {
if (this.sets.showNavigationButtons) {
navigationHtml = `
<div class="navigation-icons" style="border-color:${this._settings.dividerColor};">
<a href="#" class="fas fa-chevron-left" title="Back" style="color:${this._settings.foreground};" onclick="message('tabsBack');"></a>
<a href="#" class="fas fa-chevron-right" title="Forward" style="color:${this._settings.foreground};" onclick="message('tabsForward');"></a>
<div class="navigation-icons" style="border-color:${this.sets.dividerColor};">
<a href="#" class="fas fa-chevron-left" title="Back" style="color:${this.sets.foreground};" onclick="message('tabsBack');"></a>
<a href="#" class="fas fa-chevron-right" title="Forward" style="color:${this.sets.foreground};" onclick="message('tabsForward');"></a>
</div>
`;
}
Expand All @@ -140,21 +148,21 @@ export class Panel {

parentsHtml.push(`
<div class="breadcrumb" data-id="${parent.id}" onClick="openFolder(event);"
style="min-width:${this._settings.breadcrumbsMinWidth}px;max-width:${this._settings.breadcrumbsMaxWidth}px;">
style="min-width:${this.sets.breadcrumbsMinWidth}px;max-width:${this.sets.breadcrumbsMaxWidth}px;">
<span class="breadcrumb-inner">
<a href="#" class="breadcrumb-title" style="color:${this._settings.foreground};" title="${parent.title}">${parent.title}</a>
<span class="fas fa-chevron-right" style="color:${this._settings.foreground};"></span>
<a href="#" class="breadcrumb-title" style="color:${this.sets.foreground};" title="${parent.title}">${parent.title}</a>
<span class="fas fa-chevron-right" style="color:${this.sets.foreground};"></span>
</span>
</div>
`);
}

// setup breadcrumbs container html
breadcrumbsHtml = `
<div id="breadcrumbs-container" style="background:${this._settings.breadcrumbsBackground};">
<div id="breadcrumbs-container" style="background:${this.sets.breadcrumbsBackground};">
${navigationHtml}
<div class="breadcrumbs-icon">
<span class="fas fa-book" style="color:${this._settings.foreground};"></span>
<span class="fas fa-book" style="color:${this.sets.foreground};"></span>
</div>
${parentsHtml.join(`\n`)}
</div>
Expand All @@ -170,7 +178,15 @@ export class Panel {
this._panel = await joplin.views.panels.create('note.tabs.panel');
await joplin.views.panels.addScript(this._panel, './assets/fontawesome/css/all.min.css');
await joplin.views.panels.addScript(this._panel, './webview.css');
if (this.sets.isLayoutMode(LayoutMode.Auto)) {
await joplin.views.panels.addScript(this._panel, './webview_auto.css');
}
if (this.sets.isLayoutMode(LayoutMode.Vertical)) {
await joplin.views.panels.addScript(this._panel, './webview_vertical.css');
}
await joplin.views.panels.addScript(this._panel, './webview.js');

// message handler
await joplin.views.panels.onMessage(this._panel, async (message: any) => {
if (message.name === 'tabsOpenFolder') {
await joplin.commands.execute('openFolder', message.id);
Expand Down Expand Up @@ -202,8 +218,7 @@ export class Panel {
await joplin.commands.execute('historyForward');
}
if (message.name === 'tabsDrag') {
// TODO move to index.ts as internal command
await this._tabs.moveWithId(message.sourceId, message.targetId);
await this.tabs.moveWithId(message.sourceId, message.targetId);
await this.updateWebview();
}
if (message.name === 'tabsDragNotes') {
Expand All @@ -213,7 +228,7 @@ export class Panel {

// set init message
await joplin.views.panels.setHtml(this._panel, `
<div id="container" style="background:${this._settings.background};font-family:'${this._settings.fontFamily}',sans-serif;font-size:${this._settings.fontSize};">
<div id="container" style="background:${this.sets.background};font-family:'${this.sets.fontFamily}',sans-serif;font-size:${this.sets.fontSize};">
<div id="tabs-container">
<p style="padding-left:8px;">Loading panel...</p>
</div>
Expand All @@ -232,9 +247,9 @@ export class Panel {

// add entries to container and push to panel
await joplin.views.panels.setHtml(this._panel, `
<div id="container" style="background:${this._settings.background};font-family:'${this._settings.fontFamily}',sans-serif;font-size:${this._settings.fontSize};">
<div id="tabs-container" role="tablist" draggable="${this._settings.enableDragAndDrop}"
ondragover="dragOver(event, '${this._settings.hoverBackground}');" ondragleave="dragLeave(event);" ondrop="drop(event);" ondragend="dragEnd(event);">
<div id="container" style="background:${this.sets.background};font-family:'${this.sets.fontFamily}',sans-serif;font-size:${this.sets.fontSize};">
<div id="tabs-container" role="tablist" draggable="${this.sets.enableDragAndDrop}"
ondragover="dragOver(event, '${this.sets.hoverBackground}');" ondragleave="dragLeave(event);" ondrop="drop(event);" ondragend="dragEnd(event);">
${noteTabsHtml}
${controlsHtml}
</div>
Expand Down
45 changes: 41 additions & 4 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ enum SettingDefaults {
DividerColor = 'var(--joplin-divider-color)'
}

export enum LayoutMode {
Auto,
Horizontal,
Vertical
}

/**
* Definitions of plugin settings.
*/
Expand All @@ -31,12 +37,13 @@ export class Settings {
private _showNavigationButtons: boolean = false;
private _pinEditedNotes: boolean = false;
private _unpinCompletedTodos: boolean = false;
private _layoutMode: number = LayoutMode.Auto;
// advanced settings
private _tabHeight: number = 35;
private _minTabWidth: number = 50;
private _maxTabWidth: number = 150;
private _breadcrumbsMinWidth: number = 10;
private _breadcrumbsMaxWidth: number = 100;
// advanced settings
private _fontFamily: string = SettingDefaults.Default;
private _fontSize: string = SettingDefaults.Default;
private _background: string = SettingDefaults.Default;
Expand Down Expand Up @@ -82,6 +89,10 @@ export class Settings {
return this._unpinCompletedTodos;
}

get layoutMode(): LayoutMode {
return this._layoutMode;
}

get tabHeight(): number {
return this._tabHeight;
}
Expand Down Expand Up @@ -191,7 +202,7 @@ export class Settings {
section: 'note.tabs.settings',
public: true,
label: 'Show breadcrumbs below tabs',
description: 'Display full breadcrumbs for selected note below tabs. Only available in horizontal layout.'
description: 'Display full breadcrumbs for selected note below tabs. Only available in horizontal layout mode.'
});
await joplin.settings.registerSetting('showNavigationButtons', {
value: this._showNavigationButtons,
Expand All @@ -217,11 +228,27 @@ export class Settings {
label: 'Automatically unpin completed to-dos',
description: 'Unpin notes automatically as soon as the to-do status changes to completed. Removes the tab completely unless it is the selected note.'
});
await joplin.settings.registerSetting('layoutMode', {
value: LayoutMode.Auto,
type: SettingItemType.Int,
section: 'note.tabs.settings',
isEnum: true,
public: true,
label: 'Force tabs layout',
description: 'Force tabs horizontal or vertical layout. If Auto, the layout switches automatically at a width of about 400px. Requires restart to be applied.',
options: {
'0': 'Auto',
'1': 'Horizontal',
'2': 'Vertical'
},
});
// advanced settings
await joplin.settings.registerSetting('tabHeight', {
value: this._tabHeight,
type: SettingItemType.Int,
section: 'note.tabs.settings',
public: true,
advanced: true,
label: 'Note Tabs height (px)',
description: 'Height of the tabs. Row height in vertical layout.'
});
Expand All @@ -230,6 +257,7 @@ export class Settings {
type: SettingItemType.Int,
section: 'note.tabs.settings',
public: true,
advanced: true,
label: 'Minimum Tab width (px)',
description: 'Minimum width of one tab in pixel.'
});
Expand All @@ -238,6 +266,7 @@ export class Settings {
type: SettingItemType.Int,
section: 'note.tabs.settings',
public: true,
advanced: true,
label: 'Maximum Tab width (px)',
description: 'Maximum width of one tab in pixel.'
});
Expand All @@ -246,6 +275,7 @@ export class Settings {
type: SettingItemType.Int,
section: 'note.tabs.settings',
public: true,
advanced: true,
label: 'Minimum breadcrumb width (px)',
description: 'Minimum width of one breadcrumb in pixel.'
});
Expand All @@ -254,11 +284,10 @@ export class Settings {
type: SettingItemType.Int,
section: 'note.tabs.settings',
public: true,
advanced: true,
label: 'Maximum breadcrumb width (px)',
description: 'Maximum width of one breadcrumb in pixel.'
});

// advanced settings
await joplin.settings.registerSetting('fontFamily', {
value: this._fontFamily,
type: SettingItemType.String,
Expand Down Expand Up @@ -373,6 +402,7 @@ export class Settings {
this._maxTabWidth = await this.getOrDefault(event, this._maxTabWidth, 'maxTabWidth');
this._breadcrumbsMinWidth = await this.getOrDefault(event, this._breadcrumbsMinWidth, 'breadcrumbsMinWidth');
this._breadcrumbsMaxWidth = await this.getOrDefault(event, this._breadcrumbsMaxWidth, 'breadcrumbsMaxWidth');
this._layoutMode = await this.getOrDefault(event, this._layoutMode, 'layoutMode');
this._fontFamily = await this.getOrDefault(event, this._fontFamily, 'fontFamily', SettingDefaults.FontFamily);
this._fontSize = await this.getOrDefault(event, this._fontSize, 'fontSize', SettingDefaults.FontSize);
this._background = await this.getOrDefault(event, this._background, 'mainBackground', SettingDefaults.Background);
Expand All @@ -398,4 +428,11 @@ export class Settings {
this._noteTabs = [];
await this.storeTabs();
}

/**
* Check whether or not the handled mode matches with the setting.
*/
isLayoutMode(mode: LayoutMode): boolean {
return (this._layoutMode === mode);
}
}
19 changes: 1 addition & 18 deletions src/webview.css
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ span {
-ms-user-select: none;
}

/* SCROLLBARS */
::-webkit-scrollbar {
height: 4px;
width: 7px;
Expand All @@ -181,21 +182,3 @@ span {
background: rgba(0, 0, 0, 0.2);
border-radius: 5px;
}

/* VERCTICAL LAYOUT OVERWRITES */
@media screen and (max-width: 400px) {
#tabs-container {
display: block;
width: 100%;
}

#tab {
border-right-width: 0;
border-bottom-width: 1px;
max-width: 100% !important;
}

#breadcrumbs-container {
display: none !important;
}
}
17 changes: 17 additions & 0 deletions src/webview_auto.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* AUTOMATIC VERTICAL LAYOUT */
@media screen and (max-width: 400px) {
#tabs-container {
display: block;
height: 100%;
}

#tab {
border-right-width: 0;
border-bottom-width: 1px;
max-width: 100% !important;
}

#breadcrumbs-container {
display: none !important;
}
}
15 changes: 15 additions & 0 deletions src/webview_vertical.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* FORCE VERTICAL LAYOUT */
#tabs-container {
display: block;
height: 100%;
}

#tab {
border-right-width: 0;
border-bottom-width: 1px;
max-width: 100% !important;
}

#breadcrumbs-container {
display: none !important;
}

0 comments on commit abd7d05

Please sign in to comment.