client/paging: avoid redrawing header navigation
This commit is contained in:
parent
e83e1b06a1
commit
cf1d15354d
16 changed files with 263 additions and 164 deletions
|
@ -127,6 +127,11 @@
|
|||
.start-tagging,
|
||||
.stop-tagging
|
||||
display: none
|
||||
.masstag-hint
|
||||
display: none
|
||||
&.active
|
||||
.open-masstag
|
||||
display: none
|
||||
|
||||
.safety
|
||||
margin-right: 0.25em
|
||||
|
|
|
@ -12,11 +12,8 @@
|
|||
%><% if (ctx.canMassTag) { %><%
|
||||
%><wbr/><%
|
||||
%><span class='masstag'><%
|
||||
%><% if (ctx.parameters.tag) { %><%
|
||||
%><span class='append masstag-hint'>Tagging with:</span><%
|
||||
%><% } else { %><%
|
||||
%><a href class='mousetrap button append open-masstag'>Mass tag</a><%
|
||||
%><% } %><%
|
||||
%><span class='append masstag-hint'>Tagging with:</span><%
|
||||
%><a href class='mousetrap button append open-masstag'>Mass tag</a><%
|
||||
%><wbr/><%
|
||||
%><%= ctx.makeTextInput({name: 'masstag', value: ctx.parameters.tag}) %><%
|
||||
%><input class='mousetrap start-tagging' type='submit' value='Start tagging'/><%
|
||||
|
|
|
@ -22,7 +22,8 @@ class CommentsController {
|
|||
topNavigation.activate('comments');
|
||||
topNavigation.setTitle('Listing comments');
|
||||
|
||||
this._pageController = new PageController({
|
||||
this._pageController = new PageController();
|
||||
this._pageController.run({
|
||||
parameters: ctx.parameters,
|
||||
getClientUrlForPage: page => {
|
||||
const parameters = Object.assign(
|
||||
|
|
|
@ -6,19 +6,25 @@ const ManualPageView = require('../views/manual_page_view.js');
|
|||
|
||||
class PageController {
|
||||
constructor(ctx) {
|
||||
if (settings.get().endlessScroll) {
|
||||
this._view = new EndlessPageView();
|
||||
} else {
|
||||
this._view = new ManualPageView();
|
||||
}
|
||||
}
|
||||
|
||||
get view() {
|
||||
return this._view;
|
||||
}
|
||||
|
||||
run(ctx) {
|
||||
const extendedContext = {
|
||||
getClientUrlForPage: ctx.getClientUrlForPage,
|
||||
parameters: ctx.parameters,
|
||||
};
|
||||
|
||||
ctx.headerContext = Object.assign({}, extendedContext);
|
||||
ctx.pageContext = Object.assign({}, extendedContext);
|
||||
|
||||
if (settings.get().endlessScroll) {
|
||||
this._view = new EndlessPageView(ctx);
|
||||
} else {
|
||||
this._view = new ManualPageView(ctx);
|
||||
}
|
||||
this._view.run(ctx);
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
|
|
|
@ -26,37 +26,18 @@ class PostListController {
|
|||
topNavigation.setTitle('Listing posts');
|
||||
|
||||
this._ctx = ctx;
|
||||
this._pageController = new PageController({
|
||||
this._pageController = new PageController();
|
||||
|
||||
this._headerView = new PostsHeaderView({
|
||||
hostNode: this._pageController.view.pageHeaderHolderNode,
|
||||
parameters: ctx.parameters,
|
||||
getClientUrlForPage: page => {
|
||||
const parameters = Object.assign(
|
||||
{}, ctx.parameters, {page: page});
|
||||
return '/posts/' + misc.formatUrlParameters(parameters);
|
||||
},
|
||||
requestPage: page => {
|
||||
return PostList.search(
|
||||
this._decorateSearchQuery(ctx.parameters.query),
|
||||
page, settings.get().postsPerPage, fields);
|
||||
},
|
||||
headerRenderer: headerCtx => {
|
||||
Object.assign(headerCtx, {
|
||||
canMassTag: api.hasPrivilege('tags:masstag'),
|
||||
massTagTags: this._massTagTags,
|
||||
});
|
||||
return new PostsHeaderView(headerCtx);
|
||||
},
|
||||
pageRenderer: pageCtx => {
|
||||
Object.assign(pageCtx, {
|
||||
canViewPosts: api.hasPrivilege('posts:view'),
|
||||
canMassTag: api.hasPrivilege('tags:masstag'),
|
||||
massTagTags: this._massTagTags,
|
||||
});
|
||||
const view = new PostsPageView(pageCtx);
|
||||
view.addEventListener('tag', e => this._evtTag(e));
|
||||
view.addEventListener('untag', e => this._evtUntag(e));
|
||||
return view;
|
||||
},
|
||||
canMassTag: api.hasPrivilege('tags:masstag'),
|
||||
massTagTags: this._massTagTags,
|
||||
});
|
||||
this._headerView.addEventListener(
|
||||
'navigate', e => this._evtNavigate(e));
|
||||
|
||||
this._syncPageController();
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
|
@ -67,6 +48,15 @@ class PostListController {
|
|||
return (this._ctx.parameters.tag || '').split(/\s+/).filter(s => s);
|
||||
}
|
||||
|
||||
_evtNavigate(e) {
|
||||
history.pushState(
|
||||
null,
|
||||
window.title,
|
||||
'/posts/' + misc.formatUrlParameters(e.detail.parameters));
|
||||
Object.assign(this._ctx.parameters, e.detail.parameters);
|
||||
this._syncPageController();
|
||||
}
|
||||
|
||||
_evtTag(e) {
|
||||
for (let tag of this._massTagTags) {
|
||||
e.detail.post.addTag(tag);
|
||||
|
@ -100,6 +90,32 @@ class PostListController {
|
|||
}
|
||||
return text.trim();
|
||||
}
|
||||
|
||||
_syncPageController() {
|
||||
this._pageController.run({
|
||||
parameters: this._ctx.parameters,
|
||||
getClientUrlForPage: page => {
|
||||
return '/posts/' + misc.formatUrlParameters(
|
||||
Object.assign({}, this._ctx.parameters, {page: page}));
|
||||
},
|
||||
requestPage: page => {
|
||||
return PostList.search(
|
||||
this._decorateSearchQuery(this._ctx.parameters.query),
|
||||
page, settings.get().postsPerPage, fields);
|
||||
},
|
||||
pageRenderer: pageCtx => {
|
||||
Object.assign(pageCtx, {
|
||||
canViewPosts: api.hasPrivilege('posts:view'),
|
||||
canMassTag: api.hasPrivilege('tags:masstag'),
|
||||
massTagTags: this._massTagTags,
|
||||
});
|
||||
const view = new PostsPageView(pageCtx);
|
||||
view.addEventListener('tag', e => this._evtTag(e));
|
||||
view.addEventListener('untag', e => this._evtUntag(e));
|
||||
return view;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router => {
|
||||
|
|
|
@ -19,7 +19,8 @@ class SnapshotsController {
|
|||
topNavigation.activate('');
|
||||
topNavigation.setTitle('History');
|
||||
|
||||
this._pageController = new PageController({
|
||||
this._pageController = new PageController();
|
||||
this._pageController.run({
|
||||
parameters: ctx.parameters,
|
||||
getClientUrlForPage: page => {
|
||||
const parameters = Object.assign(
|
||||
|
|
|
@ -23,27 +23,18 @@ class TagListController {
|
|||
topNavigation.activate('tags');
|
||||
topNavigation.setTitle('Listing tags');
|
||||
|
||||
this._pageController = new PageController({
|
||||
this._ctx = ctx;
|
||||
this._pageController = new PageController();
|
||||
|
||||
this._headerView = new TagsHeaderView({
|
||||
hostNode: this._pageController.view.pageHeaderHolderNode,
|
||||
parameters: ctx.parameters,
|
||||
getClientUrlForPage: page => {
|
||||
const parameters = Object.assign(
|
||||
{}, ctx.parameters, {page: page});
|
||||
return '/tags/' + misc.formatUrlParameters(parameters);
|
||||
},
|
||||
requestPage: page => {
|
||||
return TagList.search(ctx.parameters.query, page, 50, fields);
|
||||
},
|
||||
headerRenderer: headerCtx => {
|
||||
Object.assign(headerCtx, {
|
||||
canEditTagCategories:
|
||||
api.hasPrivilege('tagCategories:edit'),
|
||||
});
|
||||
return new TagsHeaderView(headerCtx);
|
||||
},
|
||||
pageRenderer: pageCtx => {
|
||||
return new TagsPageView(pageCtx);
|
||||
},
|
||||
canEditTagCategories: api.hasPrivilege('tagCategories:edit'),
|
||||
});
|
||||
this._headerView.addEventListener(
|
||||
'navigate', e => this._evtNavigate(e));
|
||||
|
||||
this._syncPageController();
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
|
@ -53,6 +44,33 @@ class TagListController {
|
|||
showError(message) {
|
||||
this._pageController.showError(message);
|
||||
}
|
||||
|
||||
_evtNavigate(e) {
|
||||
history.pushState(
|
||||
null,
|
||||
window.title,
|
||||
'/tags/' + misc.formatUrlParameters(e.detail.parameters));
|
||||
Object.assign(this._ctx.parameters, e.detail.parameters);
|
||||
this._syncPageController();
|
||||
}
|
||||
|
||||
_syncPageController() {
|
||||
this._pageController.run({
|
||||
parameters: this._ctx.parameters,
|
||||
getClientUrlForPage: page => {
|
||||
const parameters = Object.assign(
|
||||
{}, this._ctx.parameters, {page: page});
|
||||
return '/tags/' + misc.formatUrlParameters(parameters);
|
||||
},
|
||||
requestPage: page => {
|
||||
return TagList.search(
|
||||
this._ctx.parameters.query, page, 50, fields);
|
||||
},
|
||||
pageRenderer: pageCtx => {
|
||||
return new TagsPageView(pageCtx);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router => {
|
||||
|
|
|
@ -20,18 +20,42 @@ class UserListController {
|
|||
topNavigation.activate('users');
|
||||
topNavigation.setTitle('Listing users');
|
||||
|
||||
this._pageController = new PageController({
|
||||
this._ctx = ctx;
|
||||
this._pageController = new PageController();
|
||||
|
||||
this._headerView = new UsersHeaderView({
|
||||
hostNode: this._pageController.view.pageHeaderHolderNode,
|
||||
parameters: ctx.parameters,
|
||||
});
|
||||
this._headerView.addEventListener(
|
||||
'navigate', e => this._evtNavigate(e));
|
||||
|
||||
this._syncPageController();
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
this._pageController.showSuccess(message);
|
||||
}
|
||||
|
||||
_evtNavigate(e) {
|
||||
history.pushState(
|
||||
null,
|
||||
window.title,
|
||||
'/users/' + misc.formatUrlParameters(e.detail.parameters));
|
||||
Object.assign(this._ctx.parameters, e.detail.parameters);
|
||||
this._syncPageController();
|
||||
}
|
||||
|
||||
_syncPageController() {
|
||||
this._pageController.run({
|
||||
parameters: this._ctx.parameters,
|
||||
getClientUrlForPage: page => {
|
||||
const parameters = Object.assign(
|
||||
{}, ctx.parameters, {page: page});
|
||||
{}, this._ctx.parameters, {page: page});
|
||||
return '/users/' + misc.formatUrlParameters(parameters);
|
||||
},
|
||||
requestPage: page => {
|
||||
return UserList.search(ctx.parameters.query, page);
|
||||
},
|
||||
headerRenderer: headerCtx => {
|
||||
return new UsersHeaderView(headerCtx);
|
||||
return UserList.search(this._ctx.parameters.query, page);
|
||||
},
|
||||
pageRenderer: pageCtx => {
|
||||
Object.assign(pageCtx, {
|
||||
|
@ -41,10 +65,6 @@ class UserListController {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
this._pageController.showSuccess(message);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router => {
|
||||
|
|
|
@ -137,8 +137,8 @@ class Router {
|
|||
}
|
||||
|
||||
show(path, state, push) {
|
||||
const oldPath = this.ctx ? this.ctx.path : ctx.path;
|
||||
const ctx = new Context(path, state);
|
||||
const oldPath = this.ctx ? this.ctx.path : ctx.path;
|
||||
this.dispatch(ctx, () => {
|
||||
if (ctx.path !== oldPath && push !== false) {
|
||||
ctx.pushState();
|
||||
|
|
17
client/js/util/search.js
Normal file
17
client/js/util/search.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
const misc = require('./misc.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const views = require('./views.js');
|
||||
|
||||
function searchInputNodeFocusHelper(inputNode) {
|
||||
keyboard.bind('q', () => {
|
||||
inputNode.focus();
|
||||
inputNode.setSelectionRange(
|
||||
inputNode.value.length, inputNode.value.length);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = misc.arrayToObject([
|
||||
searchInputNodeFocusHelper,
|
||||
], func => func.name);
|
|
@ -248,6 +248,25 @@ function makeVoidElement(name, attributes) {
|
|||
return `<${_serializeElement(name, attributes)}/>`;
|
||||
}
|
||||
|
||||
function emptyContent(target) {
|
||||
while (target.lastChild) {
|
||||
target.removeChild(target.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
function replaceContent(target, source) {
|
||||
emptyContent(target);
|
||||
if (source instanceof NodeList) {
|
||||
for (let child of [...source]) {
|
||||
target.appendChild(child);
|
||||
}
|
||||
} else if (source instanceof Node) {
|
||||
target.appendChild(source);
|
||||
} else if (source !== null) {
|
||||
throw `Invalid view source: ${source}`;
|
||||
}
|
||||
}
|
||||
|
||||
function showMessage(target, message, className) {
|
||||
if (!message) {
|
||||
message = 'Unknown message';
|
||||
|
@ -283,9 +302,7 @@ function showInfo(target, message) {
|
|||
function clearMessages(target) {
|
||||
const messagesHolder = target.querySelector('.messages');
|
||||
/* TODO: animate that */
|
||||
while (messagesHolder.lastChild) {
|
||||
messagesHolder.removeChild(messagesHolder.lastChild);
|
||||
}
|
||||
emptyContent(messagesHolder);
|
||||
}
|
||||
|
||||
function htmlToDom(html) {
|
||||
|
@ -394,25 +411,10 @@ function enableForm(form) {
|
|||
}
|
||||
}
|
||||
|
||||
function replaceContent(target, source) {
|
||||
while (target.lastChild) {
|
||||
target.removeChild(target.lastChild);
|
||||
}
|
||||
if (source instanceof NodeList) {
|
||||
for (let child of [...source]) {
|
||||
target.appendChild(child);
|
||||
}
|
||||
} else if (source instanceof Node) {
|
||||
target.appendChild(source);
|
||||
} else if (source !== null) {
|
||||
throw `Invalid view source: ${source}`;
|
||||
}
|
||||
}
|
||||
|
||||
function syncScrollPosition() {
|
||||
window.requestAnimationFrame(
|
||||
() => {
|
||||
if (history.state.hasOwnProperty('scrollX')) {
|
||||
if (history.state && history.state.hasOwnProperty('scrollX')) {
|
||||
window.scrollTo(history.state.scrollX, history.state.scrollY);
|
||||
} else {
|
||||
window.scrollTo(0, 0);
|
||||
|
@ -488,6 +490,7 @@ document.addEventListener('click', e => {
|
|||
module.exports = misc.arrayToObject([
|
||||
htmlToDom,
|
||||
getTemplate,
|
||||
emptyContent,
|
||||
replaceContent,
|
||||
enableForm,
|
||||
disableForm,
|
||||
|
|
|
@ -9,27 +9,22 @@ const pageTemplate = views.getTemplate('endless-pager-page');
|
|||
class EndlessPageView {
|
||||
constructor(ctx) {
|
||||
this._hostNode = document.getElementById('content-holder');
|
||||
views.replaceContent(this._hostNode, holderTemplate());
|
||||
}
|
||||
|
||||
run(ctx) {
|
||||
this._active = true;
|
||||
this._working = 0;
|
||||
this._init = false;
|
||||
|
||||
views.emptyContent(this._pagesHolderNode);
|
||||
|
||||
this.threshold = window.innerHeight / 3;
|
||||
this.minPageShown = null;
|
||||
this.maxPageShown = null;
|
||||
this.totalPages = null;
|
||||
this.currentPage = null;
|
||||
|
||||
const sourceNode = holderTemplate();
|
||||
const pageHeaderHolderNode
|
||||
= sourceNode.querySelector('.page-header-holder');
|
||||
this._pagesHolderNode = sourceNode.querySelector('.pages-holder');
|
||||
views.replaceContent(this._hostNode, sourceNode);
|
||||
|
||||
ctx.headerContext.hostNode = pageHeaderHolderNode;
|
||||
if (ctx.headerRenderer) {
|
||||
ctx.headerRenderer(ctx.headerContext);
|
||||
}
|
||||
|
||||
this._loadPage(ctx, ctx.parameters.page, true).then(pageNode => {
|
||||
if (ctx.parameters.page !== 1) {
|
||||
pageNode.scrollIntoView();
|
||||
|
@ -40,6 +35,14 @@ class EndlessPageView {
|
|||
views.monitorNodeRemoval(this._pagesHolderNode, () => this._destroy());
|
||||
}
|
||||
|
||||
get pageHeaderHolderNode() {
|
||||
return this._hostNode.querySelector('.page-header-holder');
|
||||
}
|
||||
|
||||
get _pagesHolderNode() {
|
||||
return this._hostNode.querySelector('.pages-holder');
|
||||
}
|
||||
|
||||
_destroy() {
|
||||
this._active = false;
|
||||
}
|
||||
|
|
|
@ -56,25 +56,17 @@ function _getPages(currentPage, pageNumbers, ctx) {
|
|||
class ManualPageView {
|
||||
constructor(ctx) {
|
||||
this._hostNode = document.getElementById('content-holder');
|
||||
views.replaceContent(this._hostNode, holderTemplate());
|
||||
}
|
||||
|
||||
const sourceNode = holderTemplate();
|
||||
const pageContentHolderNode
|
||||
= sourceNode.querySelector('.page-content-holder');
|
||||
const pageHeaderHolderNode
|
||||
= sourceNode.querySelector('.page-header-holder');
|
||||
const pageNavNode = sourceNode.querySelector('.page-nav');
|
||||
run(ctx) {
|
||||
const currentPage = ctx.parameters.page;
|
||||
|
||||
ctx.headerContext.hostNode = pageHeaderHolderNode;
|
||||
if (ctx.headerRenderer) {
|
||||
ctx.headerRenderer(ctx.headerContext);
|
||||
}
|
||||
|
||||
views.replaceContent(this._hostNode, sourceNode);
|
||||
this.clearMessages();
|
||||
views.emptyContent(this._pageNavNode);
|
||||
|
||||
ctx.requestPage(currentPage).then(response => {
|
||||
Object.assign(ctx.pageContext, response);
|
||||
ctx.pageContext.hostNode = pageContentHolderNode;
|
||||
ctx.pageContext.hostNode = this._pageContentHolderNode;
|
||||
ctx.pageRenderer(ctx.pageContext);
|
||||
|
||||
const totalPages = Math.ceil(response.total / response.pageSize);
|
||||
|
@ -94,7 +86,7 @@ class ManualPageView {
|
|||
|
||||
if (response.total) {
|
||||
views.replaceContent(
|
||||
pageNavNode,
|
||||
this._pageNavNode,
|
||||
navTemplate({
|
||||
prevLink: ctx.getClientUrlForPage(currentPage - 1),
|
||||
nextLink: ctx.getClientUrlForPage(currentPage + 1),
|
||||
|
@ -114,6 +106,22 @@ class ManualPageView {
|
|||
});
|
||||
}
|
||||
|
||||
get pageHeaderHolderNode() {
|
||||
return this._hostNode.querySelector('.page-header-holder');
|
||||
}
|
||||
|
||||
get _pageContentHolderNode() {
|
||||
return this._hostNode.querySelector('.page-content-holder');
|
||||
}
|
||||
|
||||
get _pageNavNode() {
|
||||
return this._hostNode.querySelector('.page-nav');
|
||||
}
|
||||
|
||||
clearMessages() {
|
||||
views.clearMessages(this._hostNode);
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
views.showSuccess(this._hostNode, message);
|
||||
}
|
||||
|
|
|
@ -1,41 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
const router = require('../router.js');
|
||||
const events = require('../events.js');
|
||||
const settings = require('../models/settings.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const search = require('../util/search.js');
|
||||
const views = require('../util/views.js');
|
||||
const TagAutoCompleteControl =
|
||||
require('../controls/tag_auto_complete_control.js');
|
||||
|
||||
const template = views.getTemplate('posts-header');
|
||||
|
||||
class PostsHeaderView {
|
||||
class PostsHeaderView extends events.EventTarget {
|
||||
constructor(ctx) {
|
||||
super();
|
||||
|
||||
ctx.settings = settings.get();
|
||||
this._ctx = ctx;
|
||||
this._hostNode = ctx.hostNode;
|
||||
views.replaceContent(this._hostNode, template(ctx));
|
||||
|
||||
if (this._queryInputNode) {
|
||||
new TagAutoCompleteControl(this._queryInputNode, {addSpace: true});
|
||||
}
|
||||
this._queryAutoCompleteControl = new TagAutoCompleteControl(
|
||||
this._queryInputNode, {addSpace: true});
|
||||
if (this._massTagInputNode) {
|
||||
new TagAutoCompleteControl(
|
||||
this._masstagAutoCompleteControl = new TagAutoCompleteControl(
|
||||
this._massTagInputNode, {addSpace: false});
|
||||
}
|
||||
|
||||
keyboard.bind('q', () => {
|
||||
this._formNode.querySelector('input:first-of-type').focus();
|
||||
});
|
||||
|
||||
keyboard.bind('p', () => {
|
||||
const firstPostNode =
|
||||
document.body.querySelector('.post-list li:first-child a');
|
||||
if (firstPostNode) {
|
||||
firstPostNode.focus();
|
||||
}
|
||||
});
|
||||
keyboard.bind('p', () => this._focusFirstPostNode());
|
||||
search.searchInputNodeFocusHelper(this._queryInputNode);
|
||||
|
||||
for (let safetyButtonNode of this._safetyButtonNodes) {
|
||||
safetyButtonNode.addEventListener(
|
||||
|
@ -51,8 +44,6 @@ class PostsHeaderView {
|
|||
}
|
||||
this._stopMassTagLinkNode.addEventListener(
|
||||
'click', e => this._evtStopTaggingClick(e));
|
||||
// this._massTagFormNode.addEventListener(
|
||||
// 'submit', e => this._evtMassTagFormSubmit(e));
|
||||
this._toggleMassTagVisibility(!!ctx.parameters.tag);
|
||||
}
|
||||
}
|
||||
|
@ -93,10 +84,11 @@ class PostsHeaderView {
|
|||
|
||||
_evtStopTaggingClick(e) {
|
||||
e.preventDefault();
|
||||
router.show('/posts/' + misc.formatUrlParameters({
|
||||
this._toggleMassTagVisibility(false);
|
||||
this.dispatchEvent(new CustomEvent('navigate', {detail: {parameters: {
|
||||
query: this._ctx.parameters.query,
|
||||
page: this._ctx.parameters.page,
|
||||
}));
|
||||
}}}));
|
||||
}
|
||||
|
||||
_evtSafetyButtonClick(e, url) {
|
||||
|
@ -107,20 +99,35 @@ class PostsHeaderView {
|
|||
browsingSettings.listPosts[safety] =
|
||||
!browsingSettings.listPosts[safety];
|
||||
settings.save(browsingSettings, true);
|
||||
router.show(router.url);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(
|
||||
'navigate', {detail: {parameters: this._ctx.parameters}}));
|
||||
}
|
||||
|
||||
_evtFormSubmit(e) {
|
||||
e.preventDefault();
|
||||
let params = {
|
||||
this._queryAutoCompleteControl.hide();
|
||||
if (this._masstagAutoCompleteControl) {
|
||||
this._masstagAutoCompleteControl.hide();
|
||||
}
|
||||
let parameters = {
|
||||
query: this._queryInputNode.value,
|
||||
page: this._ctx.parameters.page,
|
||||
};
|
||||
if (this._massTagInputNode) {
|
||||
params.tag = this._massTagInputNode.value;
|
||||
parameters.tag = this._massTagInputNode.value;
|
||||
this._massTagInputNode.blur();
|
||||
}
|
||||
router.show('/posts/' + misc.formatUrlParameters(params));
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('navigate', {detail: {parameters: parameters}}));
|
||||
}
|
||||
|
||||
_focusFirstPostNode() {
|
||||
const firstPostNode =
|
||||
document.body.querySelector('.post-list li:first-child a');
|
||||
if (firstPostNode) {
|
||||
firstPostNode.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
'use strict';
|
||||
|
||||
const router = require('../router.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const events = require('../events.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const search = require('../util/search.js');
|
||||
const views = require('../util/views.js');
|
||||
const TagAutoCompleteControl =
|
||||
require('../controls/tag_auto_complete_control.js');
|
||||
|
||||
const template = views.getTemplate('tags-header');
|
||||
|
||||
class TagsHeaderView {
|
||||
class TagsHeaderView extends events.EventTarget {
|
||||
constructor(ctx) {
|
||||
super();
|
||||
|
||||
this._hostNode = ctx.hostNode;
|
||||
views.replaceContent(this._hostNode, template(ctx));
|
||||
|
||||
|
@ -18,9 +20,7 @@ class TagsHeaderView {
|
|||
new TagAutoCompleteControl(this._queryInputNode);
|
||||
}
|
||||
|
||||
keyboard.bind('q', () => {
|
||||
form.querySelector('input').focus();
|
||||
});
|
||||
search.searchInputNodeFocusHelper(this._queryInputNode);
|
||||
|
||||
this._formNode.addEventListener('submit', e => this._evtSubmit(e));
|
||||
}
|
||||
|
@ -36,10 +36,9 @@ class TagsHeaderView {
|
|||
_evtSubmit(e) {
|
||||
e.preventDefault();
|
||||
this._queryInputNode.blur();
|
||||
router.show(
|
||||
'/tags/' + misc.formatUrlParameters({
|
||||
query: this._queryInputNode.value,
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('navigate', {detail: {parameters: {
|
||||
query: this._queryInputNode.value,
|
||||
}}}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
const router = require('../router.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const events = require('../events.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const search = require('../util/search.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
const template = views.getTemplate('users-header');
|
||||
|
||||
class UsersHeaderView {
|
||||
class UsersHeaderView extends events.EventTarget {
|
||||
constructor(ctx) {
|
||||
super();
|
||||
|
||||
this._hostNode = ctx.hostNode;
|
||||
views.replaceContent(this._hostNode, template(ctx));
|
||||
|
||||
keyboard.bind('q', () => {
|
||||
this._formNode.querySelector('input').focus();
|
||||
});
|
||||
search.searchInputNodeFocusHelper(this._queryInputNode);
|
||||
|
||||
this._formNode.addEventListener('submit', e => this._evtSubmit(e));
|
||||
}
|
||||
|
@ -29,11 +29,9 @@ class UsersHeaderView {
|
|||
|
||||
_evtSubmit(e) {
|
||||
e.preventDefault();
|
||||
this._queryInputNode.blur();
|
||||
router.show(
|
||||
'/users/' + misc.formatUrlParameters({
|
||||
query: this._queryInputNode.value,
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('navigate', {detail: {parameters: {
|
||||
query: this._queryInputNode.value,
|
||||
}}}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue