mirror of
https://github.com/zadam/trilium.git
synced 2025-06-05 09:28:45 +02:00
start of mobile layout
This commit is contained in:
parent
f64b343d8d
commit
4f744564f2
@ -1,151 +1,22 @@
|
||||
import treeService from "./services/tree.js";
|
||||
import treeCache from "./services/tree_cache.js";
|
||||
import treeBuilder from "./services/tree_builder.js";
|
||||
import contextMenuWidget from "./services/context_menu.js";
|
||||
import branchService from "./services/branches.js";
|
||||
import utils from "./services/utils.js";
|
||||
import glob from './services/glob.js';
|
||||
import macInit from './services/mac_init.js';
|
||||
import options from "./services/options.js";
|
||||
import noteContentRenderer from "./services/note_content_renderer.js";
|
||||
import appContext from "./services/app_context.js";
|
||||
import noteCreateService from "./services/note_create.js";
|
||||
import glob from "./services/glob.js";
|
||||
import FlexContainer from "./widgets/flex_container.js";
|
||||
import EmptyTypeWidget from "./widgets/type_widgets/empty.js";
|
||||
import TextTypeWidget from "./widgets/type_widgets/text.js";
|
||||
import CodeTypeWidget from "./widgets/type_widgets/code.js";
|
||||
import FileTypeWidget from "./widgets/type_widgets/file.js";
|
||||
import ImageTypeWidget from "./widgets/type_widgets/image.js";
|
||||
import SearchTypeWidget from "./widgets/type_widgets/search.js";
|
||||
import RenderTypeWidget from "./widgets/type_widgets/render.js";
|
||||
import RelationMapTypeWidget from "./widgets/type_widgets/relation_map.js";
|
||||
import ProtectedSessionTypeWidget from "./widgets/type_widgets/protected_session.js";
|
||||
import BookTypeWidget from "./widgets/type_widgets/book.js";
|
||||
import MobileLayout from "./widgets/mobile_layout.js";
|
||||
|
||||
const $leftPane = $("#left-pane");
|
||||
const $tree = $("#tree");
|
||||
const $detail = $("#detail");
|
||||
macInit.init();
|
||||
|
||||
function togglePanes() {
|
||||
if (!$leftPane.is(":visible") || !$detail.is(":visible")) {
|
||||
$detail.toggleClass("d-none");
|
||||
$leftPane.toggleClass("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function showDetailPane() {
|
||||
if (!$detail.is(":visible")) {
|
||||
$detail.removeClass("d-none");
|
||||
$leftPane.addClass("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
$detail.on("click", ".close-detail-button",() => {
|
||||
// no page is opened
|
||||
document.location.hash = '-';
|
||||
|
||||
togglePanes();
|
||||
});
|
||||
|
||||
async function showTree() {
|
||||
const treeData = await treeBuilder.prepareTree();
|
||||
|
||||
$tree.fancytree({
|
||||
autoScroll: true,
|
||||
extensions: ["dnd5", "clones"],
|
||||
source: treeData,
|
||||
scrollParent: $tree,
|
||||
minExpandLevel: 2, // root can't be collapsed
|
||||
click: (event, data) => {
|
||||
if (data.targetType !== 'expander' && data.node.isActive()) {
|
||||
// this is important for single column mobile view, otherwise it's not possible to see again previously displayed note
|
||||
$tree.fancytree('getTree').reactivate(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
activate: async (event, data) => {
|
||||
const node = data.node;
|
||||
|
||||
treeService.clearSelectedNodes();
|
||||
|
||||
showDetailPane();
|
||||
|
||||
const notePath = await treeService.getNotePath(node);
|
||||
|
||||
},
|
||||
expand: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, true),
|
||||
collapse: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, false),
|
||||
init: (event, data) => treeService.treeInitialized(), // don't collapse to short form
|
||||
dnd5: dragAndDropSetup,
|
||||
lazyLoad: function(event, data) {
|
||||
const noteId = data.node.data.noteId;
|
||||
|
||||
data.result = treeCache.getNote(noteId).then(note => treeBuilder.prepareBranch(note));
|
||||
},
|
||||
clones: {
|
||||
highlightActiveClones: true
|
||||
},
|
||||
// this is done to automatically lazy load all expanded search notes after tree load
|
||||
loadChildren: (event, data) => {
|
||||
data.node.visit((subNode) => {
|
||||
// Load all lazy/unloaded child nodes
|
||||
// (which will trigger `loadChildren` recursively)
|
||||
if (subNode.isUndefined() && subNode.isExpanded()) {
|
||||
subNode.load();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
treeService.setTree($.ui.fancytree.getTree("#tree"));
|
||||
}
|
||||
|
||||
$detail.on("click", ".note-menu-button", async e => {
|
||||
// FIXME
|
||||
const node = appContext.getMainNoteTree().getActiveNode();
|
||||
const branch = treeCache.getBranch(node.data.branchId);
|
||||
const note = await treeCache.getNote(node.data.noteId);
|
||||
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
||||
const isNotRoot = note.noteId !== 'root';
|
||||
|
||||
const items = [
|
||||
{ title: "Insert note after", cmd: "insertNoteAfter", uiIcon: "plus",
|
||||
enabled: isNotRoot && parentNote.type !== 'search' },
|
||||
{ title: "Insert child note", cmd: "insertChildNote", uiIcon: "plus",
|
||||
enabled: note.type !== 'search' },
|
||||
{ title: "Delete this note", cmd: "delete", uiIcon: "trash",
|
||||
enabled: isNotRoot && parentNote.type !== 'search' }
|
||||
];
|
||||
|
||||
contextMenuWidget.initContextMenu(e, {
|
||||
getContextMenuItems: () => items,
|
||||
selectContextMenuItem: async (event, cmd) => {
|
||||
if (cmd === "insertNoteAfter") {
|
||||
const parentNoteId = node.data.parentNoteId;
|
||||
const isProtected = await treeService.getParentProtectedStatus(node);
|
||||
|
||||
noteCreateService.createNote(parentNoteId, {
|
||||
isProtected: isProtected,
|
||||
target: 'after',
|
||||
targetBranchId: node.data.branchId
|
||||
});
|
||||
}
|
||||
else if (cmd === "insertChildNote") {
|
||||
noteCreateService.createNote(node.data.noteId);
|
||||
}
|
||||
else if (cmd === "delete") {
|
||||
if (await branchService.deleteNotes([node])) {
|
||||
// move to the tree
|
||||
togglePanes();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error("Unrecognized command " + cmd);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#switch-to-desktop-button").on('click', () => {
|
||||
utils.setCookie('trilium-device', 'desktop');
|
||||
|
||||
utils.reloadApp();
|
||||
});
|
||||
|
||||
$("#log-out-button").on('click', () => {
|
||||
$("#logout-form").trigger('submit');
|
||||
});
|
||||
|
||||
// this is done so that startNotePath is not used
|
||||
if (!document.location.hash) {
|
||||
document.location.hash = '-';
|
||||
}
|
||||
|
||||
showTree();
|
||||
appContext.setLayout(new MobileLayout());
|
||||
appContext.start();
|
151
src/public/javascripts/mobile_old.js
Normal file
151
src/public/javascripts/mobile_old.js
Normal file
@ -0,0 +1,151 @@
|
||||
import treeService from "./services/tree.js";
|
||||
import treeCache from "./services/tree_cache.js";
|
||||
import treeBuilder from "./services/tree_builder.js";
|
||||
import contextMenuWidget from "./services/context_menu.js";
|
||||
import branchService from "./services/branches.js";
|
||||
import utils from "./services/utils.js";
|
||||
import appContext from "./services/app_context.js";
|
||||
import noteCreateService from "./services/note_create.js";
|
||||
import glob from "./services/glob.js";
|
||||
|
||||
const $leftPane = $("#left-pane");
|
||||
const $tree = $("#tree");
|
||||
const $detail = $("#detail");
|
||||
|
||||
function togglePanes() {
|
||||
if (!$leftPane.is(":visible") || !$detail.is(":visible")) {
|
||||
$detail.toggleClass("d-none");
|
||||
$leftPane.toggleClass("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function showDetailPane() {
|
||||
if (!$detail.is(":visible")) {
|
||||
$detail.removeClass("d-none");
|
||||
$leftPane.addClass("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
$detail.on("click", ".close-detail-button",() => {
|
||||
// no page is opened
|
||||
document.location.hash = '-';
|
||||
|
||||
togglePanes();
|
||||
});
|
||||
|
||||
async function showTree() {
|
||||
const treeData = await treeBuilder.prepareTree();
|
||||
|
||||
$tree.fancytree({
|
||||
autoScroll: true,
|
||||
extensions: ["dnd5", "clones"],
|
||||
source: treeData,
|
||||
scrollParent: $tree,
|
||||
minExpandLevel: 2, // root can't be collapsed
|
||||
click: (event, data) => {
|
||||
if (data.targetType !== 'expander' && data.node.isActive()) {
|
||||
// this is important for single column mobile view, otherwise it's not possible to see again previously displayed note
|
||||
$tree.fancytree('getTree').reactivate(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
activate: async (event, data) => {
|
||||
const node = data.node;
|
||||
|
||||
treeService.clearSelectedNodes();
|
||||
|
||||
showDetailPane();
|
||||
|
||||
const notePath = await treeService.getNotePath(node);
|
||||
|
||||
},
|
||||
expand: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, true),
|
||||
collapse: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, false),
|
||||
init: (event, data) => treeService.treeInitialized(), // don't collapse to short form
|
||||
dnd5: dragAndDropSetup,
|
||||
lazyLoad: function(event, data) {
|
||||
const noteId = data.node.data.noteId;
|
||||
|
||||
data.result = treeCache.getNote(noteId).then(note => treeBuilder.prepareBranch(note));
|
||||
},
|
||||
clones: {
|
||||
highlightActiveClones: true
|
||||
},
|
||||
// this is done to automatically lazy load all expanded search notes after tree load
|
||||
loadChildren: (event, data) => {
|
||||
data.node.visit((subNode) => {
|
||||
// Load all lazy/unloaded child nodes
|
||||
// (which will trigger `loadChildren` recursively)
|
||||
if (subNode.isUndefined() && subNode.isExpanded()) {
|
||||
subNode.load();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
treeService.setTree($.ui.fancytree.getTree("#tree"));
|
||||
}
|
||||
|
||||
$detail.on("click", ".note-menu-button", async e => {
|
||||
// FIXME
|
||||
const node = appContext.getMainNoteTree().getActiveNode();
|
||||
const branch = treeCache.getBranch(node.data.branchId);
|
||||
const note = await treeCache.getNote(node.data.noteId);
|
||||
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
||||
const isNotRoot = note.noteId !== 'root';
|
||||
|
||||
const items = [
|
||||
{ title: "Insert note after", cmd: "insertNoteAfter", uiIcon: "plus",
|
||||
enabled: isNotRoot && parentNote.type !== 'search' },
|
||||
{ title: "Insert child note", cmd: "insertChildNote", uiIcon: "plus",
|
||||
enabled: note.type !== 'search' },
|
||||
{ title: "Delete this note", cmd: "delete", uiIcon: "trash",
|
||||
enabled: isNotRoot && parentNote.type !== 'search' }
|
||||
];
|
||||
|
||||
contextMenuWidget.initContextMenu(e, {
|
||||
getContextMenuItems: () => items,
|
||||
selectContextMenuItem: async (event, cmd) => {
|
||||
if (cmd === "insertNoteAfter") {
|
||||
const parentNoteId = node.data.parentNoteId;
|
||||
const isProtected = await treeService.getParentProtectedStatus(node);
|
||||
|
||||
noteCreateService.createNote(parentNoteId, {
|
||||
isProtected: isProtected,
|
||||
target: 'after',
|
||||
targetBranchId: node.data.branchId
|
||||
});
|
||||
}
|
||||
else if (cmd === "insertChildNote") {
|
||||
noteCreateService.createNote(node.data.noteId);
|
||||
}
|
||||
else if (cmd === "delete") {
|
||||
if (await branchService.deleteNotes([node])) {
|
||||
// move to the tree
|
||||
togglePanes();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error("Unrecognized command " + cmd);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#switch-to-desktop-button").on('click', () => {
|
||||
utils.setCookie('trilium-device', 'desktop');
|
||||
|
||||
utils.reloadApp();
|
||||
});
|
||||
|
||||
$("#log-out-button").on('click', () => {
|
||||
$("#logout-form").trigger('submit');
|
||||
});
|
||||
|
||||
// this is done so that startNotePath is not used
|
||||
if (!document.location.hash) {
|
||||
document.location.hash = '-';
|
||||
}
|
||||
|
||||
showTree();
|
@ -12,10 +12,12 @@ export default class Entrypoints extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// hot keys are active also inside inputs and content editables
|
||||
jQuery.hotkeys.options.filterInputAcceptingElements = false;
|
||||
jQuery.hotkeys.options.filterContentEditable = false;
|
||||
jQuery.hotkeys.options.filterTextInputs = false;
|
||||
if (jQuery.hotkeys) {
|
||||
// hot keys are active also inside inputs and content editables
|
||||
jQuery.hotkeys.options.filterInputAcceptingElements = false;
|
||||
jQuery.hotkeys.options.filterContentEditable = false;
|
||||
jQuery.hotkeys.options.filterTextInputs = false;
|
||||
}
|
||||
|
||||
$(document).on('click', "a[data-action='note-revision']", async event => {
|
||||
const linkEl = $(event.target);
|
||||
|
19
src/public/javascripts/widgets/mobile_layout.js
Normal file
19
src/public/javascripts/widgets/mobile_layout.js
Normal file
@ -0,0 +1,19 @@
|
||||
import FlexContainer from "./flex_container.js";
|
||||
import NoteTitleWidget from "./note_title.js";
|
||||
import NoteDetailWidget from "./note_detail.js";
|
||||
import NoteTreeWidget from "./note_tree.js";
|
||||
|
||||
export default class MobileLayout {
|
||||
getRootWidget(appContext) {
|
||||
return new FlexContainer('row')
|
||||
.setParent(appContext)
|
||||
.id('root-widget')
|
||||
.css('height', '100vh')
|
||||
.child(new FlexContainer('column')
|
||||
// .child(/* buttons */)
|
||||
.child(new NoteTreeWidget()))
|
||||
.child(new FlexContainer('column')
|
||||
.child(new NoteTitleWidget())
|
||||
.child(new NoteDetailWidget()));
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
||||
this.$widget.fancytree({
|
||||
autoScroll: true,
|
||||
keyboard: false, // we takover keyboard handling in the hotkeys plugin
|
||||
extensions: ["hotkeys", "dnd5", "clones"],
|
||||
extensions: utils.isMobile() ? ["clones"] : ["hotkeys", "dnd5", "clones"],
|
||||
source: treeData,
|
||||
scrollParent: this.$widget,
|
||||
minExpandLevel: 2, // root can't be collapsed
|
||||
|
@ -7,87 +7,12 @@
|
||||
<title>Trilium Notes</title>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="images/app-icons/ios/apple-touch-icon.png">
|
||||
</head>
|
||||
<body class="mobile">
|
||||
<body class="mobile" style="display: none;">
|
||||
<noscript>Trilium requires JavaScript to be enabled.</noscript>
|
||||
|
||||
<div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div>
|
||||
|
||||
<div class="row" id="container-row" style="display: none;">
|
||||
|
||||
<div id="left-pane" class="d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-4 col-xl-4">
|
||||
<div id="global-buttons">
|
||||
<a id="create-top-level-note-button" title="Create new top level note" class="icon-action bx bx-folder-plus"></a>
|
||||
|
||||
<a id="collapse-tree-button" title="Collapse note tree" class="icon-action bx bx-layer-minus"></a>
|
||||
|
||||
<a id="scroll-to-active-note-button" title="Scroll to active note" class="icon-action bx bx-crosshair"></a>
|
||||
|
||||
<div class="dropdown">
|
||||
<a id="global-actions-button" title="Global actions" class="icon-action bx bx-cog dropdown-toggle" data-toggle="dropdown"></a>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" id="switch-to-desktop-button"><span class="bx bx-laptop"></span> Switch to desktop version</a>
|
||||
<a class="dropdown-item" id="log-out-button"><span class="bx bx-log-out"></span> Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tree"></div>
|
||||
</div>
|
||||
|
||||
<div id="detail" class="d-none d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-8">
|
||||
<div class="note-tab-row">
|
||||
<div class="note-tab-row-content"></div>
|
||||
</div>
|
||||
|
||||
<div id="note-tab-container">
|
||||
<div class="note-tab-content note-tab-content-template">
|
||||
<div class="note-detail-content" style="width: 100%">
|
||||
<div class="note-title-row">
|
||||
<button type="button" class="note-menu-button action-button bx bx-menu"></button>
|
||||
|
||||
<input autocomplete="off" value="" class="form-control note-title" tabindex="1">
|
||||
|
||||
<button type="button" class="close-detail-button action-button d-sm-none d-md-none d-lg-none d-xl-none" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="note-detail-printable-wrapper">
|
||||
<div class="note-detail-text note-detail-printable" tabindex="10000">
|
||||
<div class="note-detail-text-editor"></div>
|
||||
</div>
|
||||
|
||||
<div class="note-detail-code note-detail-printable">
|
||||
<div class="note-detail-code-editor"></div>
|
||||
</div>
|
||||
|
||||
<% include details/search.ejs %>
|
||||
|
||||
<% include details/render.ejs %>
|
||||
|
||||
<% include details/image.ejs %>
|
||||
|
||||
<% include details/relation_map.ejs %>
|
||||
|
||||
<% include details/protected_session_password.ejs %>
|
||||
|
||||
<% include details/book.ejs %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% include dialogs/protected_session_password.ejs %>
|
||||
<% include dialogs/confirm.ejs %>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div>
|
||||
|
||||
<form action="logout" id="logout-form" method="POST" style="display: none;">
|
||||
<input type="hidden" name="_csrf" value="<%= csrfToken %>"/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.baseApiUrl = 'api/';
|
||||
@ -121,10 +46,8 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="libraries/boxicons/css/boxicons.min.css">
|
||||
|
||||
<script type="text/javascript">
|
||||
// we hide container initally because otherwise it is rendered first without CSS and then flickers into
|
||||
// final form which is pretty ugly.
|
||||
$("#container-row").show();
|
||||
<script>
|
||||
$("body").show();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user