Skip to content

Commit 5be2b27

Browse files
committed
Use URL parameters for filter states
This retains the settings during browser navigation and allows sharing links with additional configuration.
1 parent f1fd467 commit 5be2b27

File tree

2 files changed

+153
-24
lines changed

2 files changed

+153
-24
lines changed

util/gh-pages/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,8 @@ <h1>Clippy Lints</h1>
517517
<h2 class="panel-title">
518518
<div class="panel-title-name">
519519
<span>{{lint.id}}</span>
520-
<a href="#{{lint.id}}" class="anchor label label-default" ng-click="open[lint.id] = true; $event.stopPropagation()">&para;</a>
520+
<a href="#{{lint.id}}" class="anchor label label-default"
521+
ng-click="openLint(lint); $event.preventDefault(); $event.stopPropagation()">&para;</a>
521522
<a href="" id="clipboard-{{lint.id}}" class="anchor label label-default" ng-click="copyToClipboard(lint); $event.stopPropagation()">
522523
&#128203;
523524
</a>

util/gh-pages/script.js

Lines changed: 151 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
target.scrollIntoView();
2525
}
2626

27-
function scrollToLintByURL($scope) {
28-
var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
29-
scrollToLint(window.location.hash.slice(1));
27+
function scrollToLintByURL($scope, $location) {
28+
var removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
29+
scrollToLint($location.path().substring(1));
3030
removeListener();
3131
});
3232
}
@@ -106,10 +106,10 @@
106106
}
107107
};
108108
})
109-
.controller("lintList", function ($scope, $http, $timeout) {
109+
.controller("lintList", function ($scope, $http, $location) {
110110
// Level filter
111111
var LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true};
112-
$scope.levels = LEVEL_FILTERS_DEFAULT;
112+
$scope.levels = { ...LEVEL_FILTERS_DEFAULT };
113113
$scope.byLevels = function (lint) {
114114
return $scope.levels[lint.level];
115115
};
@@ -146,6 +146,137 @@
146146
"=": {enabled: false, minorVersion: null },
147147
};
148148

149+
// Map the versionFilters to the query parameters in a way that is easier to work with in a URL
150+
const versionFilterKeyMap = {
151+
"≥": "gte",
152+
"≤": "lte",
153+
"=": "eq"
154+
};
155+
const reverseVersionFilterKeyMap = Object.fromEntries(
156+
Object.entries(versionFilterKeyMap).map(([key, value]) => [value, key])
157+
);
158+
159+
// loadFromURLParameters retrieves filter settings from the URL parameters and assigns them
160+
// to corresponding $scope variables.
161+
function loadFromURLParameters() {
162+
// Extract parameters from URL
163+
const urlParameters = $location.search();
164+
165+
// Define a helper function that assigns URL parameters to a provided scope variable
166+
const handleParameter = (parameter, scopeVariable) => {
167+
if (urlParameters[parameter]) {
168+
const items = urlParameters[parameter].split(',');
169+
for (const key in scopeVariable) {
170+
if (scopeVariable.hasOwnProperty(key)) {
171+
scopeVariable[key] = items.includes(key);
172+
}
173+
}
174+
}
175+
};
176+
177+
handleParameter('levels', $scope.levels);
178+
handleParameter('groups', $scope.groups);
179+
180+
// Handle 'versions' parameter separately because it needs additional processing
181+
if (urlParameters.versions) {
182+
const versionFilters = urlParameters.versions.split(',');
183+
for (const versionFilter of versionFilters) {
184+
const [key, minorVersion] = versionFilter.split(':');
185+
const parsedMinorVersion = parseInt(minorVersion);
186+
187+
// Map the key from the URL parameter to its original form
188+
const originalKey = reverseVersionFilterKeyMap[key];
189+
190+
if (originalKey in $scope.versionFilters && !isNaN(parsedMinorVersion)) {
191+
$scope.versionFilters[originalKey].enabled = true;
192+
$scope.versionFilters[originalKey].minorVersion = parsedMinorVersion;
193+
}
194+
}
195+
}
196+
197+
// Load the search parameter from the URL path
198+
const searchParameter = $location.path().substring(1); // Remove the leading slash
199+
if (searchParameter) {
200+
$scope.search = searchParameter;
201+
$scope.open[searchParameter] = true;
202+
scrollToLintByURL($scope, $location);
203+
}
204+
205+
// If there are any filters in the URL, mark that the filters have been changed
206+
if (urlParameters.levels || urlParameters.groups || urlParameters.versions) {
207+
$scope.filtersChanged = true;
208+
}
209+
}
210+
211+
// updateURLParameter updates the URL parameter with the given key to the given value
212+
function updateURLParameter(filterObj, urlKey, processFilter = filter => filter) {
213+
const parameter = Object.keys(filterObj)
214+
.filter(filter => filterObj[filter])
215+
.map(processFilter)
216+
.filter(Boolean) // Filters out any falsy values, including null
217+
.join(',');
218+
219+
$location.search(urlKey, parameter || null);
220+
}
221+
222+
// updateVersionURLParameter updates the version URL parameter with the given version filters
223+
function updateVersionURLParameter(versionFilters) {
224+
updateURLParameter(
225+
versionFilters,
226+
'versions',
227+
versionFilter => versionFilters[versionFilter].enabled && versionFilters[versionFilter].minorVersion != null
228+
? `${versionFilterKeyMap[versionFilter]}:${versionFilters[versionFilter].minorVersion}`
229+
: null
230+
);
231+
}
232+
233+
// updateAllURLParameters updates all the URL parameters with the current filter settings
234+
function updateAllURLParameters() {
235+
updateURLParameter($scope.levels, 'levels');
236+
updateURLParameter($scope.groups, 'groups');
237+
updateVersionURLParameter($scope.versionFilters);
238+
}
239+
240+
// Add $watches to automatically update URL parameters when the data changes
241+
$scope.$watch('levels', function (newVal, oldVal) {
242+
if (newVal !== oldVal) {
243+
$scope.filtersChanged = true;
244+
updateURLParameter(newVal, 'levels');
245+
}
246+
}, true);
247+
248+
$scope.$watch('groups', function (newVal, oldVal) {
249+
if (newVal !== oldVal) {
250+
$scope.filtersChanged = true;
251+
updateURLParameter(newVal, 'groups');
252+
}
253+
}, true);
254+
255+
$scope.$watch('versionFilters', function (newVal, oldVal) {
256+
if (newVal !== oldVal) {
257+
$scope.filtersChanged = true;
258+
updateVersionURLParameter(newVal);
259+
}
260+
}, true);
261+
262+
$scope.$watch('search', function (newVal, oldVal) {
263+
if (newVal !== oldVal) {
264+
$location.path(newVal);
265+
}
266+
});
267+
268+
// Watch for changes in the URL path and update the search and lint display
269+
$scope.$watch(function () {
270+
return $location.path();
271+
}, function (newPath) {
272+
const searchParameter = newPath.substring(1);
273+
if ($scope.search !== searchParameter) {
274+
$scope.search = searchParameter;
275+
$scope.open[searchParameter] = true;
276+
scrollToLintByURL($scope, $location);
277+
}
278+
});
279+
149280
$scope.selectTheme = function (theme) {
150281
setTheme(theme, true);
151282
}
@@ -272,6 +403,16 @@
272403
return true;
273404
}
274405

406+
// Show details for one lint
407+
$scope.openLint = function (lint) {
408+
$scope.open[lint.id] = true;
409+
$location.path(lint.id);
410+
if ($scope.filtersChanged) {
411+
updateAllURLParameters();
412+
$scope.filtersChanged = false;
413+
}
414+
};
415+
275416
$scope.copyToClipboard = function (lint) {
276417
const clipboard = document.getElementById("clipboard-" + lint.id);
277418
if (clipboard) {
@@ -296,14 +437,13 @@
296437
// Get data
297438
$scope.open = {};
298439
$scope.loading = true;
440+
$scope.filtersChanged = false;
441+
299442
// This will be used to jump into the source code of the version that this documentation is for.
300443
$scope.docVersion = window.location.pathname.split('/')[2] || "master";
301444

302-
if (window.location.hash.length > 1) {
303-
$scope.search = window.location.hash.slice(1);
304-
$scope.open[window.location.hash.slice(1)] = true;
305-
scrollToLintByURL($scope);
306-
}
445+
// Set up the filters from the URL parameters before we start loading the data
446+
loadFromURLParameters();
307447

308448
$http.get('./lints.json')
309449
.success(function (data) {
@@ -315,7 +455,7 @@
315455
selectGroup($scope, selectedGroup.toLowerCase());
316456
}
317457

318-
scrollToLintByURL($scope);
458+
scrollToLintByURL($scope, $location);
319459

320460
setTimeout(function () {
321461
var el = document.getElementById('filter-input');
@@ -326,18 +466,6 @@
326466
$scope.error = data;
327467
$scope.loading = false;
328468
});
329-
330-
window.addEventListener('hashchange', function () {
331-
// trigger re-render
332-
$timeout(function () {
333-
$scope.levels = LEVEL_FILTERS_DEFAULT;
334-
$scope.search = window.location.hash.slice(1);
335-
$scope.open[window.location.hash.slice(1)] = true;
336-
337-
scrollToLintByURL($scope);
338-
});
339-
return true;
340-
}, false);
341469
});
342470
})();
343471

0 commit comments

Comments
 (0)