diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index acd1abbf2..ce02d4bbb 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index ebf3e0a39..e21571e0c 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/src/buttons/index.ts b/netbox/project-static/src/buttons/index.ts index 6a9001cd1..e677ff599 100644 --- a/netbox/project-static/src/buttons/index.ts +++ b/netbox/project-static/src/buttons/index.ts @@ -3,6 +3,7 @@ import { initDepthToggle } from './depthToggle'; import { initMoveButtons } from './moveOptions'; import { initReslug } from './reslug'; import { initSelectAll } from './selectAll'; +import { initSelectMultiple } from './selectMultiple'; export function initButtons(): void { for (const func of [ @@ -10,6 +11,7 @@ export function initButtons(): void { initConnectionToggle, initReslug, initSelectAll, + initSelectMultiple, initMoveButtons, ]) { func(); diff --git a/netbox/project-static/src/buttons/selectMultiple.ts b/netbox/project-static/src/buttons/selectMultiple.ts new file mode 100644 index 000000000..d8bad3105 --- /dev/null +++ b/netbox/project-static/src/buttons/selectMultiple.ts @@ -0,0 +1,105 @@ +import { getElements } from '../util'; +import { StateManager } from 'src/state'; +import { previousPkCheckState } from '../stores'; + +type PreviousPkCheckState = { element: Nullable }; + +/** + * If there is a text selection, removes it. + */ +function removeTextSelection(): void { + window.getSelection()?.removeAllRanges(); +} + +/** + * Sets the state object passed in to the eventTargetElement object passed in. + * + * @param eventTargetElement HTML Input Element, retrieved from getting the target of the + * event passed in from handlePkCheck() + * @param state PreviousPkCheckState object. + */ +function updatePreviousPkCheckState( + eventTargetElement: HTMLInputElement, + state: StateManager, +): void { + state.set('element', eventTargetElement); +} + +/** + * For all checkboxes between eventTargetElement and previousStateElement in elementList, toggle + * "checked" value to eventTargetElement.checked + * + * @param eventTargetElement HTML Input Element, retrieved from getting the target of the + * event passed in from handlePkCheck() + * @param state PreviousPkCheckState object. + */ +function toggleCheckboxRange( + eventTargetElement: HTMLInputElement, + previousStateElement: HTMLInputElement, + elementList: Generator, +): void { + let changePkCheckboxState = false; + for (const element of elementList) { + const typedElement = element as HTMLInputElement; + //Change loop's current checkbox state to eventTargetElement checkbox state + if (changePkCheckboxState === true) { + typedElement.checked = eventTargetElement.checked; + } + //The previously clicked checkbox was above the shift clicked checkbox + if (element === previousStateElement) { + if (changePkCheckboxState === true) { + changePkCheckboxState = false; + return; + } + changePkCheckboxState = true; + typedElement.checked = eventTargetElement.checked; + } + //The previously clicked checkbox was below the shift clicked checkbox + if (element === eventTargetElement) { + if (changePkCheckboxState === true) { + changePkCheckboxState = false; + return; + } + changePkCheckboxState = true; + } + } +} + +/** + * IF the shift key is pressed and there is state is not null, toggleCheckboxRange between the + * event target element and the state element. + * + * @param event Mouse event. + * @param state PreviousPkCheckState object. + */ +function handlePkCheck(event: MouseEvent, state: StateManager): void { + const eventTargetElement = event.target as HTMLInputElement; + const previousStateElement = state.get('element'); + updatePreviousPkCheckState(eventTargetElement, state); + //Stop if user is not holding shift key + if (!event.shiftKey) { + return; + } + removeTextSelection(); + //If no previous state, store event target element as previous state and return + if (previousStateElement === null) { + return updatePreviousPkCheckState(eventTargetElement, state); + } + const checkboxList = getElements('input[type="checkbox"][name="pk"]'); + toggleCheckboxRange(eventTargetElement, previousStateElement, checkboxList); +} + +/** + * Initialize table select all elements. + */ +export function initSelectMultiple(): void { + const checkboxElements = getElements('input[type="checkbox"][name="pk"]'); + for (const element of checkboxElements) { + element.addEventListener('click', event => { + removeTextSelection(); + //Stop propogation to avoid event firing multiple times + event.stopPropagation(); + handlePkCheck(event, previousPkCheckState); + }); + } +} diff --git a/netbox/project-static/src/stores/index.ts b/netbox/project-static/src/stores/index.ts index 42d4aa0b5..d4644e619 100644 --- a/netbox/project-static/src/stores/index.ts +++ b/netbox/project-static/src/stores/index.ts @@ -1,2 +1,3 @@ export * from './objectDepth'; export * from './rackImages'; +export * from './previousPkCheck'; diff --git a/netbox/project-static/src/stores/previousPkCheck.ts b/netbox/project-static/src/stores/previousPkCheck.ts new file mode 100644 index 000000000..19b244ec7 --- /dev/null +++ b/netbox/project-static/src/stores/previousPkCheck.ts @@ -0,0 +1,6 @@ +import { createState } from '../state'; + +export const previousPkCheckState = createState<{ element: Nullable }>( + { element: null }, + { persist: false }, +);