Fix broken JS
This commit is contained in:
parent
d7ad64b4e0
commit
28b326aedf
14 changed files with 63 additions and 218 deletions
|
@ -1,7 +1,4 @@
|
|||
import define from "../../utils/define.js";
|
||||
import {globalBus} from "../../utils/events.js";
|
||||
import addResult from "./add-result.js";
|
||||
import emptyResult from "./empty-result.js";
|
||||
|
||||
|
||||
export default define('add-button', class extends HTMLButtonElement {
|
||||
|
|
|
@ -19,7 +19,7 @@ export default define('delete-button', class extends HTMLButtonElement {
|
|||
const result = this.closest('.result');
|
||||
const parent = result.parentNode;
|
||||
|
||||
const index = Array.prototype.indexOf.call(parent.children, result);
|
||||
const index = Array.prototype.indexOf.call(parent.getElementsByClassName('result'), result);
|
||||
console.log("Delete index", index);
|
||||
|
||||
const beginCuratingEvent = new CustomEvent('curate-delete-result', {
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import define from '../../utils/define.js';
|
||||
|
||||
const template = () => /*html*/`
|
||||
<p>We could not find anything for your search...</p>
|
||||
`;
|
||||
|
||||
export default define('empty-result', class extends HTMLLIElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.classList.add('empty-result');
|
||||
this.__setup();
|
||||
}
|
||||
|
||||
__setup() {
|
||||
this.innerHTML = template();
|
||||
}
|
||||
}, { extends: 'li' });
|
|
@ -21,7 +21,7 @@ export default define('validate-button', class extends HTMLButtonElement {
|
|||
const result = this.closest('.result');
|
||||
const parent = result.parentNode;
|
||||
|
||||
const index = Array.prototype.indexOf.call(parent.children, result);
|
||||
const index = Array.prototype.indexOf.call(parent.getElementsByClassName('result'), result);
|
||||
console.log("Validate index", index);
|
||||
|
||||
const curationValidateEvent = new CustomEvent('curate-validate-result', {
|
||||
|
|
|
@ -10,19 +10,12 @@ class ResultsHandler {
|
|||
|
||||
__setup() {
|
||||
this.__events();
|
||||
this.__initializeResults();
|
||||
}
|
||||
|
||||
__events() {
|
||||
document.body.addEventListener('htmx:load', e => {
|
||||
this.results = document.querySelector('.results');
|
||||
|
||||
// Allow the user to re-order search results
|
||||
$(".results").sortable({
|
||||
"activate": this.__sortableActivate.bind(this),
|
||||
"deactivate": this.__sortableDeactivate.bind(this),
|
||||
});
|
||||
|
||||
this.curating = false;
|
||||
this.__initializeResults();
|
||||
});
|
||||
|
||||
// Focus first element when coming from the search bar
|
||||
|
@ -113,6 +106,18 @@ class ResultsHandler {
|
|||
});
|
||||
}
|
||||
|
||||
__initializeResults() {
|
||||
this.results = document.querySelector('.results');
|
||||
|
||||
// Allow the user to re-order search results
|
||||
$(".results").sortable({
|
||||
"activate": this.__sortableActivate.bind(this),
|
||||
"deactivate": this.__sortableDeactivate.bind(this),
|
||||
});
|
||||
|
||||
this.curating = false;
|
||||
}
|
||||
|
||||
__sortableActivate(event, ui) {
|
||||
console.log("Sortable activate", ui);
|
||||
this.__beginCurating();
|
||||
|
|
|
@ -1,180 +0,0 @@
|
|||
import define from '../../utils/define.js';
|
||||
import config from '../../../config.js';
|
||||
import { globalBus } from '../../utils/events.js';
|
||||
import debounce from '../../utils/debounce.js'
|
||||
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion)').matches;
|
||||
|
||||
const template = () => /*html*/`
|
||||
<form class="search-bar">
|
||||
<i class="ph-magnifying-glass-bold"></i>
|
||||
<input
|
||||
type='search'
|
||||
class='search-bar-input'
|
||||
placeholder='Search on mwmbl...'
|
||||
title='Use "CTRL+K" or "/" to focus.'
|
||||
autocomplete='off'
|
||||
>
|
||||
</form>
|
||||
`;
|
||||
|
||||
export default define('search-bar', class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.searchInput = null;
|
||||
this.searchForm = null;
|
||||
this.abortController = new AbortController();
|
||||
this.__setup();
|
||||
}
|
||||
|
||||
__setup() {
|
||||
this.innerHTML = template();
|
||||
this.searchInput = this.querySelector('input');
|
||||
this.searchForm = this.querySelector('form');
|
||||
this.__events();
|
||||
}
|
||||
|
||||
__dispatchSearch({ results = null, error = null }) {
|
||||
const searchEvent = new CustomEvent('search', {
|
||||
detail: {
|
||||
results,
|
||||
error,
|
||||
},
|
||||
});
|
||||
globalBus.dispatch(searchEvent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the overall layout of the page.
|
||||
*
|
||||
* `home` centers the search bar on the page.
|
||||
* `compact` raises it to the top and makes room for displaying results.
|
||||
*
|
||||
* @param {'compact' | 'home'} mode
|
||||
* @return {void}
|
||||
*/
|
||||
__setDisplayMode(mode) {
|
||||
switch (mode) {
|
||||
case 'compact': {
|
||||
document.body.style.paddingTop = '25px';
|
||||
document.querySelector('.search-menu').classList.add('compact');
|
||||
break;
|
||||
}
|
||||
case 'home': {
|
||||
document.body.style.paddingTop = '30vh';
|
||||
document.querySelector('.search-menu').classList.remove('compact');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async __executeSearch() {
|
||||
this.abortController.abort();
|
||||
this.abortController = new AbortController();
|
||||
// Get response from API
|
||||
const response = await fetch(`${config.publicApiURL}search?s=${encodeURIComponent(this.searchInput.value)}`, {
|
||||
signal: this.abortController.signal
|
||||
});
|
||||
// Getting results from API
|
||||
const search = await (response).json();
|
||||
return search;
|
||||
}
|
||||
|
||||
__handleSearch = async () => {
|
||||
// Update page title
|
||||
document.title = `MWMBL - ${this.searchInput.value || "Search"}`;
|
||||
|
||||
// Update query params
|
||||
const queryParams = new URLSearchParams(document.location.search);
|
||||
// Sets query param if search value is not empty
|
||||
if (this.searchInput.value) queryParams.set(config.searchQueryParam, this.searchInput.value);
|
||||
else queryParams.delete(config.searchQueryParam);
|
||||
// New URL with query params
|
||||
const newURL =
|
||||
document.location.protocol
|
||||
+ "//"
|
||||
+ document.location.host
|
||||
+ document.location.pathname
|
||||
+ (this.searchInput.value ? '?' : '')
|
||||
+ queryParams.toString();
|
||||
// Replace history state
|
||||
window.history.replaceState({ path: newURL }, '', newURL);
|
||||
|
||||
if (this.searchInput.value) {
|
||||
this.__setDisplayMode('compact')
|
||||
|
||||
try {
|
||||
const search = await this.__executeSearch()
|
||||
// This is a guess at an explanation
|
||||
// Check the searcInput.value before setting the results to prevent
|
||||
// race condition where the user has cleared the search input after
|
||||
// submitting an original search but before the search results have
|
||||
// come back from the API
|
||||
this.__dispatchSearch({ results: this.searchInput.value ? search : null });
|
||||
}
|
||||
catch(error) {
|
||||
this.__dispatchSearch({ error })
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.__setDisplayMode('home')
|
||||
this.__dispatchSearch({ results: null });
|
||||
}
|
||||
}
|
||||
|
||||
__events() {
|
||||
/**
|
||||
* Always add the submit event, it makes things feel faster if
|
||||
* someone does not prefer reduced motion and reflexively hits
|
||||
* return once they've finished typing.
|
||||
*/
|
||||
this.searchForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.__handleSearch(e);
|
||||
});
|
||||
|
||||
/**
|
||||
* Only add the "real time" search behavior when the client does
|
||||
* not prefer reduced motion; this prevents the page from changing
|
||||
* while the user is still typing their query.
|
||||
*/
|
||||
if (!prefersReducedMotion) {
|
||||
this.searchInput.addEventListener('input', debounce(this.__handleSearch, 500))
|
||||
}
|
||||
|
||||
// Focus search bar when pressing `ctrl + k` or `/`
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if ((e.key === 'k' && e.ctrlKey) || e.key === '/' || e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.searchInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Focus first result when pressing down arrow
|
||||
this.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowDown' && this.searchInput.value) {
|
||||
e.preventDefault();
|
||||
const focusResultEvent = new CustomEvent('focus-result');
|
||||
globalBus.dispatch(focusResultEvent);
|
||||
}
|
||||
});
|
||||
|
||||
globalBus.on('focus-search', (e) => {
|
||||
this.searchInput.focus();
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
// Focus search input when component is connected
|
||||
this.searchInput.focus();
|
||||
|
||||
const searchQuery = new URLSearchParams(document.location.search).get(config.searchQueryParam);
|
||||
this.searchInput.value = searchQuery;
|
||||
/**
|
||||
* Trigger search handling to coordinate the value pulled from the query string
|
||||
* across the rest of the UI and to actually retrieve the results if the search
|
||||
* value is now non-empty.
|
||||
*/
|
||||
this.__handleSearch();
|
||||
}
|
||||
});
|
|
@ -5,6 +5,7 @@
|
|||
* Please do not pollute this file if you can make
|
||||
* util or component files instead.
|
||||
*/
|
||||
import 'vite/modulepreload-polyfill';
|
||||
|
||||
// Waiting for top-level await to be better supported.
|
||||
(async () => {
|
||||
|
@ -17,5 +18,10 @@
|
|||
import("./components/organisms/results.js");
|
||||
import("./components/organisms/footer.js");
|
||||
import("./components/organisms/save.js");
|
||||
import("./components/molecules/add-button.js");
|
||||
import("./components/molecules/add-result.js");
|
||||
import("./components/molecules/delete-button.js");
|
||||
import("./components/molecules/result.js");
|
||||
import("./components/molecules/validate-button.js");
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -7,12 +7,14 @@ export default {
|
|||
publicDir: '../assets',
|
||||
build: {
|
||||
outDir: '../dist',
|
||||
manifest: true,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
index: resolve(__dirname, 'src/index.js'),
|
||||
stats: resolve(__dirname, 'src/stats/index.html'),
|
||||
},
|
||||
},
|
||||
minify: false,
|
||||
},
|
||||
plugins: [
|
||||
legacy({
|
||||
|
|
|
@ -30,7 +30,8 @@ INSTALLED_APPS = [
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'mwmbl',
|
||||
"django_htmx",
|
||||
'django_htmx',
|
||||
'django_vite',
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
|
@ -106,6 +107,9 @@ USE_TZ = True
|
|||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
DJANGO_VITE_DEV_MODE = False
|
||||
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||
|
||||
|
|
|
@ -13,7 +13,11 @@ DATABASES = {
|
|||
}
|
||||
|
||||
|
||||
STATICFILES_DIRS = [str(Path(__file__).parent.parent / "front-end" / "dist")]
|
||||
STATIC_ROOT = ""
|
||||
DJANGO_VITE_ASSETS_PATH = Path(__file__).parent.parent / "front-end" / "dist"
|
||||
DJANGO_VITE_MANIFEST_PATH = DJANGO_VITE_ASSETS_PATH / "manifest.json"
|
||||
|
||||
STATICFILES_DIRS = [str(DJANGO_VITE_ASSETS_PATH)]
|
||||
|
||||
|
||||
DEBUG = True
|
||||
|
|
|
@ -9,8 +9,9 @@ SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
|
|||
|
||||
|
||||
STATIC_ROOT = "/app/static/"
|
||||
STATICFILES_DIRS = ["/front-end-build/"]
|
||||
|
||||
DJANGO_VITE_ASSETS_PATH = "/front-end-build/"
|
||||
STATICFILES_DIRS = [DJANGO_VITE_ASSETS_PATH]
|
||||
|
||||
DATABASES = {'default': dj_database_url.config(default=os.environ["DATABASE_URL"])}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{% load django_vite %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
|
@ -45,6 +46,9 @@
|
|||
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
|
||||
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
|
||||
|
||||
{% vite_hmr_client %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -53,9 +57,7 @@
|
|||
<mwmbl-app></mwmbl-app>
|
||||
|
||||
<!-- Javasript entrypoint -->
|
||||
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
|
||||
<script src="/static/src/index.js" type="module"></script>
|
||||
|
||||
{#<script src="/static/src/index.js" type="module"></script>#}
|
||||
<header class="search-menu">
|
||||
<!-- <ul>
|
||||
<li is="${save}"></li>
|
||||
|
@ -89,6 +91,9 @@
|
|||
</main>
|
||||
<div is="mwmbl-add-result"></div>
|
||||
<footer is="mwmbl-footer"></footer>
|
||||
{% vite_asset 'index.js' %}
|
||||
{% vite_legacy_polyfills %}
|
||||
{% vite_legacy_asset 'index-legacy.js' %}
|
||||
</body>
|
||||
|
||||
</html>
|
19
poetry.lock
generated
19
poetry.lock
generated
|
@ -592,6 +592,23 @@ dev = ["pre-commit"]
|
|||
doc = ["markdown-include", "mkdocs", "mkdocs-material", "mkdocstrings"]
|
||||
test = ["black", "django-stubs", "flake8", "isort", "mypy (==0.931)", "psycopg2-binary", "pytest", "pytest-asyncio", "pytest-cov", "pytest-django"]
|
||||
|
||||
[[package]]
|
||||
name = "django-vite"
|
||||
version = "2.1.3"
|
||||
description = "Integration of ViteJS in a Django project."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "django-vite-2.1.3.tar.gz", hash = "sha256:c59b3bbd85501bc1faf63c500df66542abed2951cfa10dfbf8be8ecf229f7652"},
|
||||
{file = "django_vite-2.1.3-py3-none-any.whl", hash = "sha256:97984ac495910b7b71039228ccddff52d132231fa6612d3d31c6c228c95b0217"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=1.11"
|
||||
|
||||
[package.extras]
|
||||
dev = ["black", "flake8"]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.1.3"
|
||||
|
@ -2544,4 +2561,4 @@ indexer = ["Levenshtein", "beautifulsoup4", "idna", "langdetect", "lxml", "pyarr
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<3.11"
|
||||
content-hash = "88ebe68ab5d0445ed67c07723156e622ebe10dbc50381b4982741f1404c0e8ce"
|
||||
content-hash = "4e4233221e9f3bd317c0693584612898b7b736f45983b7f3f5bad4d43e567353"
|
||||
|
|
|
@ -40,6 +40,7 @@ redis = {extras = ["hiredis"], version = "^5.0.1"}
|
|||
django-allauth = "^0.57.0"
|
||||
dj-database-url = "^2.1.0"
|
||||
django-htmx = "^1.17.0"
|
||||
django-vite = "^2.1.3"
|
||||
|
||||
[tool.poetry.extras]
|
||||
indexer = [
|
||||
|
|
Loading…
Reference in a new issue