1// javascript/chives-sorting.js
4// ChivesSorting manages the sorting of a collection of items.
6// It requires a list of sort options. A sort option is an object with
9// - key: the query parameter key in the URL (for example, 'titleAtoZ')
10// - label: the human-readable description (for example, 'title (A to Z)')
11// - compareFn: a predicate function that determines the order of the items
15// const bookmarkSortOptions = [
18// label: "title (A to Z)",
19// filterFn: (a, b) => a.title > b.title ? 1 : -1,
29 // All my sites use "sortOrder" as the URL parameter for the currently
30 // selected sort order.
31 #paramKey = "sortOrder";
33 constructor(sortOptions, searchParams) {
34 this.#sortOptions = sortOptions;
35 this.#searchParams = searchParams;
38 // Set the default sort key that will be used if the user has not
39 // specified a sort key in the URL query parameters.
40 set defaultSortOrder(sortKey) {
42 this.#defaultSortOrder = sortKey;
45 // getActiveSortOrder returns the currently active sort order.
46 getActiveSortOrder() {
47 let sortOrderKey = this.#searchParams.get(this.#paramKey);
49 if (sortOrderKey === null && typeof this.#defaultSortOrder === 'string') {
50 sortOrderKey = this.#defaultSortOrder;
53 if (sortOrderKey !== null) {
54 const matchingSort = this.#sortOptions.find(s => s.key === sortOrderKey);
55 if (matchingSort) return matchingSort;
58 return this.#sortOptions[0];
61 // Sort the collection of items using the currently selected sort order.
62 apply(items, defaultSortKey) {
63 const activeSortOrder = this.getActiveSortOrder();
65 console.debug(`Active sort order: ${activeSortOrder.key}`);
67 return items.sort(activeSortOrder.compareFn);
70 // Returns the state of the sorting.
73 activeSortOrderKey: this.getActiveSortOrder().key,
74 sortOptions: this.#sortOptions,
75 paramKey: this.#paramKey,
80// ChivesSortOptions provides a <chives-sort-options> component that
81// renders navigation controls for sort order.
83// It uses the state provided by ChivesSorting.
84class ChivesSortOptions extends HTMLElement {
94 set searchParams(params) {
96 this.#searchParams = params;
101 if (!this.#state || !this.#searchParams) {
105 const wrapper = document.createElement("nav");
106 wrapper.classList.add("chives_sort_options");
108 const label = document.createElement("label");
109 label.setAttribute("for", "sortOrder");
110 label.innerHTML = "sort by:"
111 wrapper.appendChild(label);
113 const select = document.createElement("select");
114 select.id = label.getAttribute("for");
115 this.#state.sortOptions.forEach(so => {
116 const option = document.createElement("option");
117 option.value = so.key;
118 option.innerText = so.label;
119 if (so.key === this.#state.activeSortOrderKey) {
120 option.setAttribute("selected", "");
122 option.removeAttribute("selected");
124 select.appendChild(option);
127 // When the user uses the dropdown to select a new sort order,
128 // reload the page with the new sort order applied.
129 select.addEventListener("change", () => {
130 const newSortOrderKey = select.value;
132 console.log(`newSortOrderKey = ${newSortOrderKey}`);
134 if (newSortOrderKey === this.#state.activeSortOrderKey) {
138 const params = new URLSearchParams(this.#searchParams);
139 params.set(this.#state.paramKey, newSortOrderKey);
140 params.delete("page");
141 window.location.search = params.toString();
144 wrapper.appendChild(select);
146 this.replaceChildren(wrapper);
150// Register global styles for the <chives-sorting-options> element.
151if (typeof document !== "undefined" &&
152 !document.getElementById("chives-sorting-styles")) {
153 const style = document.createElement("style");
154 style.id = "chives-sorting-styles";
155 style.textContent = `
156 .chives_sort_options {
162 document.head.appendChild(style);
165// Register the custom HTML tag <chives-sort-options>
166if (!customElements.get("chives-sort-options")) {
167 customElements.define("chives-sort-options", ChivesSortOptions);
170// renderSortingControls renders the sorting controls as
171// a child of `querySelector`.
172function renderSortingControls({ querySelector, sorting, searchParams }) {
173 const elem = document.createElement("chives-sort-options");
174 elem.state = sorting.getState();
175 elem.searchParams = searchParams;
176 document.querySelector(querySelector).appendChild(elem);