diff --git a/client/css/tags.styl b/client/css/tags.styl new file mode 100644 index 0000000..b72a8e9 --- /dev/null +++ b/client/css/tags.styl @@ -0,0 +1,27 @@ +@import colors + +.tag-list + table + width: 100% + text-align: left + .usages + text-align: center + ul + list-style-type: none + margin: 0 + padding: 0 + display: inline + li + padding: 0 + display: inline + &:not(:last-child):after + content: ', ' +.tag-list-header + text-align: left + form + width: auto + input[name=search-text] + max-width: 15em + .append + font-size: 0.95em + color: $inactive-link-color diff --git a/client/html/tag_list_header.hbs b/client/html/tag_list_header.hbs new file mode 100644 index 0000000..05d51e2 --- /dev/null +++ b/client/html/tag_list_header.hbs @@ -0,0 +1,15 @@ +
diff --git a/client/html/tag_list_page.hbs b/client/html/tag_list_page.hbs new file mode 100644 index 0000000..e5d7210 --- /dev/null +++ b/client/html/tag_list_page.hbs @@ -0,0 +1,50 @@ + diff --git a/client/js/controllers/tags_controller.js b/client/js/controllers/tags_controller.js index 6058847..e799214 100644 --- a/client/js/controllers/tags_controller.js +++ b/client/js/controllers/tags_controller.js @@ -1,15 +1,43 @@ 'use strict'; const page = require('page'); +const api = require('../api.js'); +const misc = require('../util/misc.js'); const topNavController = require('../controllers/top_nav_controller.js'); +const pageController = require('../controllers/page_controller.js'); +const TagListHeaderView = require('../views/tag_list_header_view.js'); +const TagListPageView = require('../views/tag_list_page_view.js'); class TagsController { - registerRoutes() { - page('/tags', (ctx, next) => { this.listTagsRoute(); }); + constructor() { + this.tagListHeaderView = new TagListHeaderView(); + this.tagListPageView = new TagListPageView(); } - listTagsRoute() { + registerRoutes() { + page( + '/tags/:query?', + (ctx, next) => { misc.parseSearchQueryRoute(ctx, next); }, + (ctx, next) => { this.listTagsRoute(ctx, next); }); + } + + listTagsRoute(ctx, next) { topNavController.activate('tags'); + + pageController.run({ + state: ctx.state, + requestPage: page => { + return api.get( + '/tags/?query={text}&page={page}&pageSize=50'.format({ + text: ctx.searchQuery.text, + page: page})); + }, + clientUrl: '/tags/' + misc.formatSearchQuery({ + text: ctx.searchQuery.text, page: '{page}'}), + searchQuery: ctx.searchQuery, + headerRenderer: this.tagListHeaderView, + pageRenderer: this.tagListPageView, + }); } } diff --git a/client/js/views/tag_list_header_view.js b/client/js/views/tag_list_header_view.js new file mode 100644 index 0000000..1fa661a --- /dev/null +++ b/client/js/views/tag_list_header_view.js @@ -0,0 +1,35 @@ +'use strict'; + +const page = require('page'); +const keyboard = require('../util/keyboard.js'); +const misc = require('../util/misc.js'); +const views = require('../util/views.js'); + +class TagListHeaderView { + constructor() { + this.template = views.getTemplate('tag-list-header'); + } + + render(ctx) { + const target = ctx.target; + const source = this.template(ctx); + + const form = source.querySelector('form'); + + keyboard.bind('q', () => { + form.querySelector('input').focus(); + }); + + form.addEventListener('submit', e => { + e.preventDefault(); + const searchTextInput = form.querySelector('[name=search-text]'); + const text = searchTextInput.value; + searchTextInput.blur(); + page('/tags/' + misc.formatSearchQuery({text: text})); + }); + + views.showView(target, source); + } +} + +module.exports = TagListHeaderView; diff --git a/client/js/views/tag_list_page_view.js b/client/js/views/tag_list_page_view.js new file mode 100644 index 0000000..e7142fb --- /dev/null +++ b/client/js/views/tag_list_page_view.js @@ -0,0 +1,17 @@ +'use strict'; + +const views = require('../util/views.js'); + +class TagListPageView { + constructor() { + this.template = views.getTemplate('tag-list-page'); + } + + render(ctx) { + const target = ctx.target; + const source = this.template(ctx); + views.showView(target, source); + } +} + +module.exports = TagListPageView;