import './select-notifications.css'; import React from 'dom-chef'; import select from 'select-dom'; import onetime from 'onetime'; import delegate from 'delegate-it'; import * as pageDetect from 'github-url-detection'; import { CheckCircleIcon, CheckIcon, DotFillIcon, DotIcon, GitMergeIcon, GitPullRequestDraftIcon, GitPullRequestIcon, IssueOpenedIcon, XCircleIcon, } from '@primer/octicons-react'; import features from '../feature-manager.js'; import observe from '../helpers/selector-observer.js'; const filters = { 'Pull requests': ':is(.octicon-git-pull-request, .octicon-git-pull-request-closed, .octicon-git-pull-request-draft, .octicon-git-merge)', Issues: ':is(.octicon-issue-opened, .octicon-issue-closed)', Open: ':is(.octicon-issue-opened, .octicon-git-pull-request)', Closed: ':is(.octicon-issue-closed, .octicon-git-pull-request-closed)', Draft: '.octicon-git-pull-request-draft', Merged: '.octicon-git-merge', Read: '.notification-read', Unread: '.notification-unread', }; type Filter = keyof typeof filters; type Category = 'Type' | 'Status' | 'Read'; function resetFilters({target}: React.SyntheticEvent): void { select('form#rgh-select-notifications-form')!.reset(); for (const label of select.all('label', target as Element)) { label.setAttribute('aria-checked', 'false'); } } function getFiltersSelector(formData: FormData, category: Category): string { return formData.getAll(category).map(value => filters[value as Filter]).join(','); } function handleSelection({target}: Event): void { const selectAllCheckbox = select('input[type="checkbox"].js-notifications-mark-all-prompt')!; // Reset the "Select all" checkbox if (selectAllCheckbox.checked) { selectAllCheckbox.click(); } if (select.exists(':checked', target as Element)) { const formData = new FormData(select('form#rgh-select-notifications-form')); const types = getFiltersSelector(formData, 'Type'); const statuses = getFiltersSelector(formData, 'Status'); const readStatus = getFiltersSelector(formData, 'Read'); for (const notification of select.all('.notifications-list-item')) { if ( (types && !select.exists(types, notification)) || (statuses && !select.exists(statuses, notification)) || (readStatus && !notification.matches(readStatus)) ) { // Make excluded notifications unselectable select('.js-notification-bulk-action-check-item', notification)!.removeAttribute('data-check-all-item'); } } // If at least one notification is selectable, trigger the "Select all" checkbox if (select.exists('.js-notification-bulk-action-check-item[data-check-all-item]')) { selectAllCheckbox.click(); } } // Make all notifications selectable again for (const disabledNotificationCheckbox of select.all('.js-notification-bulk-action-check-item:not([data-check-all-item])')) { disabledNotificationCheckbox.setAttribute('data-check-all-item', ''); } } function createDropdownList(category: Category, filters: Filter[]): JSX.Element { const icons: {[Key in Filter]: JSX.Element} = { 'Pull requests': , Issues: , Open: , Closed: , Draft: , Merged: , Read: , Unread: , }; return (
{category}
{filters.map(filter => ( ))}
); } const createDropdown = onetime(() => (
Select by
{createDropdownList('Type', ['Pull requests', 'Issues'])} {createDropdownList('Status', ['Open', 'Closed', 'Merged', 'Draft'])} {createDropdownList('Read', ['Read', 'Unread'])}
)); function closeDropdown(): void { select('.rgh-select-notifications')?.removeAttribute('open'); } function addDropdown(markAllPrompt: Element): void { markAllPrompt.closest('label')!.after(createDropdown()); } function init(signal: AbortSignal): void { observe('.js-notifications-mark-all-prompt', addDropdown, {signal}); // Close the dropdown when one of the toolbar buttons is clicked delegate('.js-notifications-mark-selected-actions > *, .rgh-open-selected-button', 'click', closeDropdown, {signal}); } void features.add(import.meta.url, { shortcuts: { S: 'Open the "Select by" dropdown', }, include: [ pageDetect.isNotifications, ], exclude: [ pageDetect.isBlank, // Empty notification list ], init, });