-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
2,269 additions
and
250 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"rules": { | ||
"camelcase": ["off", {"properties": "always"}], | ||
"comma-spacing": ["error", {"before": false, "after": true}], | ||
"curly": ["error", "all"], | ||
"dot-notation": ["error", {"allowKeywords": true}], | ||
"eqeqeq": ["error"], | ||
"indent": ["error", 4], | ||
"key-spacing": ["error", {"beforeColon": false, "afterColon": true}], | ||
"linebreak-style": ["error", "unix"], | ||
"new-cap": ["off", {"newIsCap": true, "capIsNew": true}], | ||
"no-alert": ["off"], | ||
"no-eval": ["error"], | ||
"no-extend-native": ["error", {"exceptions": ["Date", "String"]}], | ||
"no-multi-spaces": ["error"], | ||
"no-octal-escape": ["error"], | ||
"no-script-url": ["error"], | ||
"no-shadow": ["error", {"hoist": "functions"}], | ||
"no-underscore-dangle": ["error"], | ||
"no-unused-vars": ["error", {"vars": "local", "args": "none"}], | ||
"no-var": ["error"], | ||
"prefer-const": ["error"], | ||
"quotes": ["off", "single"], | ||
"semi": ["error", "always"], | ||
"space-before-blocks": ["error", "always"], | ||
"space-before-function-paren": ["error", {"anonymous": "never", "named": "never"}], | ||
"space-infix-ops": ["error", {"int32Hint": false}], | ||
"strict": ["error", "global"] | ||
}, | ||
"env": { | ||
"browser": true, | ||
"es6": true | ||
}, | ||
"globals": { | ||
"django": false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
__pycache__/ | ||
dist/ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
256 changes: 128 additions & 128 deletions
256
django_admin_keyboard_shortcuts/static/admin/js/shortcuts.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,145 +1,145 @@ | ||
'use strict'; | ||
{ | ||
let previousKey = undefined; | ||
const shortcutFunctions = new Map([ | ||
["g i", () => { document.location.href = "/admin/"; }], | ||
["g l", () => showDialog("model-list-dialog")] | ||
]); | ||
|
||
function registerDeclarativeShortcuts() { | ||
const elements = document.querySelectorAll('[data-keyboard-shortcut]'); | ||
for (const element of elements) { | ||
shortcutFunctions.set(element.getAttribute('data-keyboard-shortcut'), () => { | ||
element.click(); | ||
}); | ||
let previousKey = undefined; | ||
const shortcutFunctions = new Map([ | ||
["g i", () => { document.location.href = "/admin/"; }], | ||
["g l", () => showDialog("model-list-dialog")] | ||
]); | ||
|
||
function registerDeclarativeShortcuts() { | ||
const elements = document.querySelectorAll('[data-keyboard-shortcut]'); | ||
for (const element of elements) { | ||
shortcutFunctions.set(element.getAttribute('data-keyboard-shortcut'), () => { | ||
element.click(); | ||
}); | ||
} | ||
} | ||
|
||
function isApple() { | ||
return (navigator.platform.indexOf("Mac") === 0 || navigator.platform === "iPhone"); | ||
} | ||
} | ||
|
||
function isApple() { | ||
return (navigator.platform.indexOf("Mac") === 0 || navigator.platform === "iPhone") | ||
} | ||
function removePreviousKey(key) { | ||
if (previousKey === key) { | ||
previousKey = undefined; | ||
} | ||
} | ||
|
||
function removePreviousKey(key) { | ||
if (previousKey === key) { | ||
previousKey = undefined; | ||
function storePreviousKey(key) { | ||
previousKey = key; | ||
setTimeout(function() { | ||
removePreviousKey(key); | ||
}, 5000); | ||
} | ||
} | ||
|
||
function storePreviousKey(key) { | ||
previousKey = key; | ||
setTimeout(function () { | ||
removePreviousKey(key); | ||
}, 5000); | ||
} | ||
|
||
function showDialog(id) { | ||
const dialog = document.getElementById(id); | ||
dialog.showModal(); | ||
} | ||
|
||
function showShortcutsDialog() { | ||
showDialog("shortcuts-dialog"); | ||
} | ||
|
||
function showDialogOnClick() { | ||
const dialogButton = document.getElementById("open-shortcuts"); | ||
dialogButton.addEventListener("click", showShortcutsDialog); | ||
} | ||
|
||
function handleKeyUp(event) { | ||
const shortcut = previousKey ? `${previousKey} ${event.key}` : event.key; | ||
if (shortcutFunctions.has(shortcut)) { | ||
shortcutFunctions.get(shortcut)(); | ||
} else { | ||
storePreviousKey(event.key); | ||
|
||
function showDialog(id) { | ||
const dialog = document.getElementById(id); | ||
dialog.showModal(); | ||
} | ||
|
||
function showShortcutsDialog() { | ||
showDialog("shortcuts-dialog"); | ||
} | ||
} | ||
|
||
function replaceModifiers() { | ||
if (isApple()) { | ||
document.querySelectorAll(".shortcut-keys .alt").forEach(function (modifier) { | ||
modifier.innerHTML = "⌥"; | ||
}); | ||
function showDialogOnClick() { | ||
const dialogButton = document.getElementById("open-shortcuts"); | ||
dialogButton.addEventListener("click", showShortcutsDialog); | ||
} | ||
} | ||
|
||
// This overlaps a lot with the initSidebarQuickFilter function | ||
// defined in django/contrib/admin/static/admin/js/nav_sidebar.js | ||
// If/when merged into core, we could try to reuse some parts | ||
function filterModelList() { | ||
const modelListDialog = document.getElementById('model-list-dialog'); | ||
if (!modelListDialog) { | ||
return; | ||
|
||
function handleKeyUp(event) { | ||
const shortcut = previousKey ? `${previousKey} ${event.key}` : event.key; | ||
if (shortcutFunctions.has(shortcut)) { | ||
shortcutFunctions.get(shortcut)(); | ||
} else { | ||
storePreviousKey(event.key); | ||
} | ||
} | ||
|
||
const appSections = []; | ||
|
||
modelListDialog.querySelectorAll('section').forEach(section => { | ||
const options = []; | ||
section.querySelectorAll('li a').forEach(container => options.push({ | ||
title: container.innerHTML, node: container | ||
})); | ||
|
||
appSections.push({ | ||
node: section, options, | ||
}); | ||
}); | ||
|
||
function checkValue(event) { | ||
let filterValue = event.target.value; | ||
if (filterValue) { | ||
filterValue = filterValue.toLowerCase(); | ||
} | ||
if (event.key === 'Escape') { | ||
filterValue = ''; | ||
event.target.value = ''; // clear input | ||
} | ||
|
||
appSections.forEach(section => { | ||
let matched = false; | ||
// Check if any of the app models match the filter text | ||
section.options.forEach((option) => { | ||
let optionDisplay = ''; | ||
if (option.title.toLowerCase().indexOf(filterValue) === -1) { | ||
optionDisplay = 'none'; | ||
} else { | ||
matched = true; | ||
} | ||
// Set display in parent <li> element | ||
option.node.parentNode.style.display = optionDisplay; | ||
function replaceModifiers() { | ||
if (isApple()) { | ||
document.querySelectorAll(".shortcut-keys .alt").forEach(function(modifier) { | ||
modifier.innerHTML = "⌥"; | ||
}); | ||
} | ||
} | ||
|
||
// This overlaps a lot with the initSidebarQuickFilter function | ||
// defined in django/contrib/admin/static/admin/js/nav_sidebar.js | ||
// If/when merged into core, we could try to reuse some parts | ||
function filterModelList() { | ||
const modelListDialog = document.getElementById('model-list-dialog'); | ||
if (!modelListDialog) { | ||
return; | ||
} | ||
|
||
const appSections = []; | ||
|
||
modelListDialog.querySelectorAll('section').forEach(section => { | ||
const options = []; | ||
section.querySelectorAll('li a').forEach(container => options.push({ | ||
title: container.innerHTML, node: container | ||
})); | ||
|
||
appSections.push({ | ||
node: section, options, | ||
}); | ||
}); | ||
|
||
let sectionDisplay = ''; | ||
// If there's no filter value or no matched models | ||
// for the section, we hide the section entirely | ||
if (!filterValue || !matched) { | ||
sectionDisplay = 'none'; | ||
function checkValue(event) { | ||
let filterValue = event.target.value; | ||
if (filterValue) { | ||
filterValue = filterValue.toLowerCase(); | ||
} | ||
if (event.key === 'Escape') { | ||
filterValue = ''; | ||
event.target.value = ''; // clear input | ||
} | ||
|
||
appSections.forEach(section => { | ||
let matched = false; | ||
// Check if any of the app models match the filter text | ||
section.options.forEach((option) => { | ||
let optionDisplay = ''; | ||
if (option.title.toLowerCase().indexOf(filterValue) === -1) { | ||
optionDisplay = 'none'; | ||
} else { | ||
matched = true; | ||
} | ||
// Set display in parent <li> element | ||
option.node.parentNode.style.display = optionDisplay; | ||
}); | ||
|
||
let sectionDisplay = ''; | ||
// If there's no filter value or no matched models | ||
// for the section, we hide the section entirely | ||
if (!filterValue || !matched) { | ||
sectionDisplay = 'none'; | ||
} | ||
// Set display for the app section | ||
section.node.style.display = sectionDisplay; | ||
}); | ||
} | ||
// Set display for the app section | ||
section.node.style.display = sectionDisplay; | ||
}); | ||
|
||
const nav = document.getElementById('model-list-dialog-search'); | ||
nav.addEventListener('change', checkValue, false); | ||
nav.addEventListener('input', checkValue, false); | ||
nav.addEventListener('keyup', checkValue, false); | ||
|
||
// We don't want to show anything on the list until the user starts typing | ||
checkValue({target: {value: ''}, key: ''}); | ||
} | ||
|
||
const nav = document.getElementById('model-list-dialog-search'); | ||
nav.addEventListener('change', checkValue, false); | ||
nav.addEventListener('input', checkValue, false); | ||
nav.addEventListener('keyup', checkValue, false); | ||
|
||
// We don't want to show anything on the list until the user starts typing | ||
checkValue({target: {value: ''}, key: ''}) | ||
} | ||
|
||
|
||
if (document.readyState === "loading") { | ||
document.addEventListener("DOMContentLoaded", showDialogOnClick); | ||
document.addEventListener("DOMContentLoaded", replaceModifiers); | ||
document.addEventListener("DOMContentLoaded", filterModelList); | ||
document.addEventListener("DOMContentLoaded", registerDeclarativeShortcuts); | ||
} else { | ||
showDialogOnClick(); | ||
replaceModifiers(); | ||
filterModelList(); | ||
registerDeclarativeShortcuts(); | ||
} | ||
document.addEventListener("keyup", handleKeyUp); | ||
|
||
if (document.readyState === "loading") { | ||
document.addEventListener("DOMContentLoaded", showDialogOnClick); | ||
document.addEventListener("DOMContentLoaded", replaceModifiers); | ||
document.addEventListener("DOMContentLoaded", filterModelList); | ||
document.addEventListener("DOMContentLoaded", registerDeclarativeShortcuts); | ||
} else { | ||
showDialogOnClick(); | ||
replaceModifiers(); | ||
filterModelList(); | ||
registerDeclarativeShortcuts(); | ||
} | ||
document.addEventListener("keyup", handleKeyUp); | ||
} |
Oops, something went wrong.