Skip to content

Commit

Permalink
Fixes #15 - Adds ESLint
Browse files Browse the repository at this point in the history
  • Loading branch information
erosselli committed Jun 10, 2024
1 parent 2a6479b commit 36a09cd
Show file tree
Hide file tree
Showing 9 changed files with 2,269 additions and 250 deletions.
37 changes: 37 additions & 0 deletions .eslintrc
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
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
__pycache__/
dist/
node_modules/
17 changes: 17 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,23 @@ above the `django.contrib.admin` app so that the template overrides work.

Now you can hack away.

Linting
-------

This project uses ESLint as its JS linter and uses the same configuration as the
Django repository. To run it, you'll first need to install dependencies

.. code-block:: console
npm install
and then run

.. code-block:: console
npm run lint
Credits
-------

Expand Down
256 changes: 128 additions & 128 deletions django_admin_keyboard_shortcuts/static/admin/js/shortcuts.js
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);
}
Loading

0 comments on commit 36a09cd

Please sign in to comment.