Skip to content
Snippets Groups Projects
Unverified Commit ad16c5bd authored by ArtOfCode's avatar ArtOfCode Committed by GitHub
Browse files

Merge pull request #127 from codidact/luap42/keyboard-shortcuts

Include keyboard script by default
parents df45ffdf f6bd8cca
Branches
Tags
No related merge requests found
$(() => {
_CodidactKeyboard = {
state: 'home',
selectedItem: null,
user_id: parseInt($('.header--item.is-complex.is-visible-on-mobile[href^="/users/"').attr('href').split("/").pop()),
is_mod: !!$('.header--item[href="/mod/flags"]').length,
categories: function () {
category_elements = $("a.category-header--tab");
return_obj = {};
category_elements.each(function () {
return_obj[this.innerText] = this.getAttribute('href');
});
return return_obj;
},
dialog: function (msg) {
_CodidactKeyboard.dialogClose();
d = document.createElement("div")
d.classList.add("__keyboard_help");
d.innerText = msg;
document.body.appendChild(d);
},
dialogClose: function () {
$(".__keyboard_help").remove();
_CodidactKeyboard.state = 'home';
},
updateSelected: function () {
$(".__keyboard_selected").removeClass('__keyboard_selected');
if (_CodidactKeyboard.selectedItem) {
_CodidactKeyboard.selectedItem.classList.add('__keyboard_selected');
_CodidactKeyboard.selectedItem.scrollIntoView({ behavior: 'smooth' });
_CodidactKeyboard.selectedItem.focus();
_CodidactKeyboard.selectedItemData = {
type: _CodidactKeyboard.selectedItem.getAttribute("data-ckb-item-type"),
post_id: _CodidactKeyboard.selectedItem.getAttribute("data-ckb-post-id")
};
}
}
}
// Use html, so that all prior attempts to access keyup event have priority
$("html").on("keyup", function (e) {
if (e.target != document.body) return;
if (e.key == "Escape") {
_CodidactKeyboard.dialogClose();
} else if (_CodidactKeyboard.state == 'home') {
homeMenu(e);
} else if (_CodidactKeyboard.state == 'goto') {
gotoMenu(e);
} else if (_CodidactKeyboard.state == 'goto/category') {
categoryMenu(e);
} else if (_CodidactKeyboard.state == 'goto/category-tags') {
categoryTagsMenu(e);
} else if (_CodidactKeyboard.state == 'tools') {
toolsMenu(e);
} else if (_CodidactKeyboard.state == 'tools/vote') {
voteMenu(e);
}
});
function homeMenu(e) {
if (e.key == "?") {
_CodidactKeyboard.dialog('Codidact Keyboard Shortcuts\n' +
'===========================\n' +
'? Open this help\n' +
'n New post\n' +
' (in current category)\n' +
's Search for something\n' +
'g Go to a page...\n\n' +
'a Go to answer field\n\n' +
'Selection shortcuts:\n\n' +
'j Move one item down\n' +
'k Move one item up\n' +
't Use a tool (on selection)\n\n' +
'(Selection shortcuts will select\n' +
'first post, if none selected)'
);
} else if (e.key == 'n') {
new_post_link = $('a.category-header--nav-item.is-button').attr('href');
if (new_post_link)
window.location.href = new_post_link;
} else if (e.key == 'g') {
_CodidactKeyboard.dialog('Go to ...\n' +
'=========\n' +
'm Main page\n' +
'u User list\n' +
'h Help\n' +
'p Your profile page\n' +
'c Category ...\n' +
't Tags of category ...' +
(_CodidactKeyboard.is_mod ? '\nf Flags (mod only)' : '')
);
_CodidactKeyboard.state = 'goto';
} else if (e.key == 'k') {
if (_CodidactKeyboard.selectedItem == null) _CodidactKeyboard.selectedItem = $("[data-ckb-list-item]:first-of-type")[0];
else {
_CodidactKeyboard.selectedItem = $(_CodidactKeyboard.selectedItem).nextAll('[data-ckb-list-item]')[0] || _CodidactKeyboard.selectedItem;
}
_CodidactKeyboard.updateSelected();
} else if (e.key == 'j') {
if (_CodidactKeyboard.selectedItem == null) _CodidactKeyboard.selectedItem = $("[data-ckb-list-item]:first-of-type")[0];
else {
_CodidactKeyboard.selectedItem = $(_CodidactKeyboard.selectedItem).prevAll('[data-ckb-list-item]')[0] || _CodidactKeyboard.selectedItem;
}
_CodidactKeyboard.updateSelected();
} else if (e.key == 't') {
if (_CodidactKeyboard.selectedItem == null) _CodidactKeyboard.selectedItem = $("[data-ckb-list-item]:first-of-type")[0];
_CodidactKeyboard.updateSelected();
if (_CodidactKeyboard.selectedItemData.type == "post") {
_CodidactKeyboard.dialog('Use tool ...\n' +
'============\n' +
'f Flag\n' +
'e Edit\n' +
'c Comment\n' +
'l Get permalink\n' +
'h View history\n' +
'v Vote ...' +
(_CodidactKeyboard.is_mod ? '\nt Use tools' : '')
);
_CodidactKeyboard.state = 'tools';
}
} else if (e.key == 'a') {
cl = $('#answer_body_markdown');
cl[0].scrollIntoView({ behavior: "smooth" });
cl.focus();
_CodidactKeyboard.dialogClose();
} else if (e.key == 'Enter') {
if (_CodidactKeyboard.selectedItemData.type == "link") {
link = $(_CodidactKeyboard.selectedItem).find("[data-ckb-item-link]").attr("href");
window.location.href = link;
}
}
}
function gotoMenu(e) {
if (e.key == 'm') {
window.location.href = '/';
} else if (e.key == 'u') {
window.location.href = '/users';
} else if (e.key == 'h') {
window.location.href = '/help';
} else if (e.key == 'p') {
window.location.href = '/users/' + _CodidactKeyboard.user_id;
} else if (e.key == 'f') {
window.location.href = '/mod/flags';
} else if (e.key == 'f') {
window.location.href = '/mod/flags';
} else if (e.key == "t") {
data = _CodidactKeyboard.categories();
data = Object.entries(data);
string_response = "";
for (var i = 0; i < data.length; i++) {
entry = data[i];
string_response += (i + 1) + " " + entry[0] + "\n"
}
_CodidactKeyboard.dialog('Go to tags of category ...\n' +
'==================\n' +
string_response.trim()
);
_CodidactKeyboard.state = 'goto/category-tags';
window.location.href = tlink;
} else if (e.key == 'c') {
data = _CodidactKeyboard.categories();
data = Object.entries(data);
string_response = "";
for (var i = 0; i < data.length; i++) {
entry = data[i];
string_response += (i + 1) + " " + entry[0] + "\n"
}
_CodidactKeyboard.dialog('Go to category ...\n' +
'==================\n' +
"c Category List\n" +
string_response.trim()
);
_CodidactKeyboard.state = 'goto/category';
}
}
function categoryMenu(e) {
if (e.key == "c") {
window.location.href = "/categories";
} else {
number = parseInt(e.key);
if (number != NaN) {
data = _CodidactKeyboard.categories();
data = Object.entries(data);
category = data[number - 1];
window.location.href = category[1];
}
}
}
function categoryTagsMenu(e) {
number = parseInt(e.key);
if (number != NaN) {
data = _CodidactKeyboard.categories();
data = Object.entries(data);
category = data[number - 1];
window.location.href = category[1] + "/tags";
}
}
function toolsMenu(e) {
if (e.key == 'e') {
window.location.href = $(_CodidactKeyboard.selectedItem).find('.tools--item i.fa.fa-pencil-alt').parent().attr("href");
} else if (e.key == 'h') {
window.location.href = $(_CodidactKeyboard.selectedItem).find('.tools--item i.fa.fa-history').parent().attr("href");
} else if (e.key == 'l') {
window.location.href = $(_CodidactKeyboard.selectedItem).find('.tools--item i.fa.fa-link').parent().attr("href");
} else if (e.key == 'c') {
cl = $(_CodidactKeyboard.selectedItem).find('.js-add-comment');
cl.nextAll("form").css("display", "block");
cl.nextAll("form")[0].scrollIntoView({ behavior: "smooth" });
cl.nextAll("form").find(".js-comment-content").focus();
_CodidactKeyboard.dialogClose();
} else if (e.key == 'f') {
cl = $(_CodidactKeyboard.selectedItem).find('.post--action-dialog.js-flag-box');
cl.addClass("is-active");
cl[0].scrollIntoView({ behavior: "smooth" });
cl.find(".js-flag-comment").focus();
_CodidactKeyboard.dialogClose();
} else if (e.key == 'v') {
_CodidactKeyboard.dialog('Vote ...\n' +
'========\n' +
'u Up\n' +
'd Down\n' +
'c Close'
);
_CodidactKeyboard.state = 'tools/vote';
} else if (e.key == 't') {
cl = $(_CodidactKeyboard.selectedItem).find('a.tools--item i.fa.fa-wrench').parent();
cl = $(cl.attr("data-modal"));
cl.toggleClass("is-active");
cl.focus();
_CodidactKeyboard.dialogClose();
}
}
function voteMenu(e) {
if (e.key == 'u') {
cl = $(_CodidactKeyboard.selectedItem).find('.vote-button[data-vote-type="1"]');
cl.click();
_CodidactKeyboard.dialogClose();
} else if (e.key == 'd') {
cl = $(_CodidactKeyboard.selectedItem).find('.vote-button[data-vote-type="-1"]');
cl.click();
_CodidactKeyboard.dialogClose();
} else if (e.key == 'c') {
cl = $(_CodidactKeyboard.selectedItem).find('.post--action-dialog.js-close-box');
cl.addClass("is-active");
cl[0].scrollIntoView({ behavior: "smooth" });
cl.focus();
_CodidactKeyboard.dialogClose();
}
}
});
\ No newline at end of file
.__keyboard_help {
padding: 1rem;
font-family: monospace;
white-space: pre-wrap;
width: 350px;
background-color: rgba(0,0,0,0.8);
color: white;
border-radius: 0.25rem;
position: fixed;
left: 1rem;
bottom: 1rem;
}
.__keyboard_selected {
outline: 0.25rem solid red;
}
\ No newline at end of file
......@@ -7,93 +7,93 @@
<div class="grid">
<% if current_user.is_global_admin %>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-globe has-color-red-700"></i> <i class="fas fa-cogs"></i>
<%= link_to 'Global Site Settings', global_settings_path %>
<%= link_to 'Global Site Settings', global_settings_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-globe has-color-red-700"></i> <i class="fas fa-tags"></i>
<%= link_to 'Global Tag Sets', global_tag_sets_path %>
<%= link_to 'Global Tag Sets', global_tag_sets_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-globe has-color-red-700"></i> <i class="fas fa-envelope"></i>
<%= link_to 'Email Moderators', moderator_email_path %>
<%= link_to 'Email Moderators', moderator_email_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<% end %>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-cogs"></i>
<%= link_to 'Site Settings', site_settings_path %>
<%= link_to 'Site Settings', site_settings_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-tags"></i>
<%= link_to 'Tag Sets', tag_sets_path %>
<%= link_to 'Tag Sets', tag_sets_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-sliders-h"></i>
<%= link_to 'Privileges', admin_privileges_path %>
<%= link_to 'Privileges', admin_privileges_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-exclamation-triangle"></i>
<%= link_to 'Error Reports', admin_error_reports_path %>
<%= link_to 'Error Reports', admin_error_reports_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-hand-paper"></i>
<%= link_to 'Close Reasons', close_reasons_path %>
<%= link_to 'Close Reasons', close_reasons_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-balance-scale"></i>
<%= link_to 'Licenses', licenses_path %>
<%= link_to 'Licenses', licenses_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
<div class="grid--cell is-4-lg is-6-md is-12-sm">
<div class="grid--cell is-4-lg is-6-md is-12-sm" data-ckb-list-item data-ckb-item-type="link">
<div class="widget">
<div class="widget--body">
<i class="fas fa-user-secret"></i>
<%= link_to 'Audit Log', audit_log_path %>
<%= link_to 'Audit Log', audit_log_path, 'data-ckb-item-link' => '' %>
</div>
</div>
</div>
......
<% active_user = post.last_activity_by || post.user %>
<% show_type_tag ||= false %>
<% show_category_tag ||= false %>
<div class="item-list--item <%= post.deleted ? 'deleted-content' : '' %>">
<div class="item-list--item <%= post.deleted ? 'deleted-content' : '' %>" data-ckb-list-item data-ckb-item-type="link">
<div class="item-list--number-value">
<div class="meter is-question-score">
<div class="meter--bar is-<%= (post.score * 100).to_i %>%"><%= (post.score * 100).to_i %>%</div>
......@@ -18,7 +18,7 @@
<% if show_category_tag %>
<span class="badge is-tag is-filled"><%= defined?(@category) ? @category.name : post.category.name %></span>
<% end %>
<%= link_to post.title, share_article_path(post) %>
<%= link_to post.title, share_article_path(post), 'data-ckb-item-link' => '' %>
</div>
<p class="has-color-tertiary-600 has-float-right post-list--meta">
last activity <%= time_ago_in_words(post.last_activity) %> ago by <%= link_to active_user.username, user_path(active_user) %>
......
......@@ -2,7 +2,7 @@
<% is_top_level = post.parent_id.nil? %>
<% has_tags = is_top_level && !post.tag_ids.empty? %>
<div class="post <%= post.meta? ? 'is-meta' : '' %> <%= is_top_level ? '' : 'has-border-bottom-style-solid has-border-bottom-width-1 has-border-color-tertiary-100' %>" data-post-id="<%= post.id %>" id="<%= (is_question ? 'question-' : 'answer-') + post.id.to_s %>">
<div class="post <%= post.meta? ? 'is-meta' : '' %> <%= is_top_level ? '' : 'has-border-bottom-style-solid has-border-bottom-width-1 has-border-color-tertiary-100' %>" data-post-id="<%= post.id %>" id="<%= (is_question ? 'question-' : 'answer-') + post.id.to_s %>" data-ckb-list-item data-ckb-item-type="post" data-ckb-post-id="<%= post.id %>">
<% if is_top_level %>
<h1 class="post--title has-border-top-width-4 has-border-top-style-solid has-border-color-<%= post.meta? ? 'tertiary' : 'primary' %>-400 has-padding-2">
<%= post.title %>
......
......@@ -3,7 +3,7 @@
<% active_user = post.last_activity_by || post.user %>
<% show_type_tag ||= false %>
<% show_category_tag ||= false %>
<div class="item-list--item <%= is_meta ? 'post__meta' : '' %> <%= post.deleted ? 'deleted-content' : '' %>">
<div class="item-list--item <%= is_meta ? 'post__meta' : '' %> <%= post.deleted ? 'deleted-content' : '' %>" data-ckb-list-item data-ckb-item-type="link">
<div class="item-list--number-value">
<div class="meter is-question-score">
<div class="meter--bar is-<%= (post.score * 100).to_i %>%"><%= (post.score * 100).to_i %>%</div>
......@@ -17,7 +17,7 @@
</div>
<div class="item-list--text-value is-primary">
<div class="post-list--title">
<%= link_to generic_share_link(post) do %>
<%= link_to generic_share_link(post), 'data-ckb-item-link' => '' do %>
<% if show_category_tag %>
<span class="badge is-tag is-filled"><%= defined?(@category) ? @category.name : post.category.name %></span>
<% end %>
......
<div class="grid--cell tag-cell <%= params[:hierarchical].present? ? 'is-12-sm is-6' : 'is-4-lg is-6-md is-12-sm' %>">
<div class="grid--cell tag-cell <%= params[:hierarchical].present? ? 'is-12-sm is-6' : 'is-4-lg is-6-md is-12-sm' %>" data-ckb-list-item data-ckb-item-type="link">
<% if tag.respond_to?(:path) && tag.path.present? %>
<span class="has-font-size-caption">
<% tag.path.split(' > ')[0..-2].each do |tag| %>
......@@ -6,7 +6,7 @@
<% end %>
</span>
<% end %>
<%= link_to tag.name, tag_path(id: category.id, tag_id: tag.id), class: classes %>
<%= link_to tag.name, tag_path(id: category.id, tag_id: tag.id), class: classes, 'data-ckb-item-link' => '' %>
<span class="has-color-tertiary-900">&times;&nbsp;<%= tag.post_count %></span>
<% if tag.excerpt.present? %>
<p class="has-font-size-caption has-color-tertiary-900">
......
<div class="user-list--user">
<div class="user-list--user" data-ckb-list-item data-ckb-item-type="link">
<div class="flex-row-always">
<div><img src="<%= avatar_url(user) %>" alt="user avatar" height="32" width="32" /></div>
<div class="user--meta">
<% user_posts = defined?(@post_counts) ? @post_counts[user.id] : user.posts.count %>
<span class="username"><%= link_to user_path(user), class: "is-not-underlined" do %>
<span class="username"><%= link_to user_path(user), class: "is-not-underlined", 'data-ckb-item-link' => '' do %>
<%= user.username %>
<% if user.is_admin && SiteSetting['AdminBadgeCharacter'] %>
<span class="badge is-user-role"><%= SiteSetting['AdminBadgeCharacter'] %></span>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment