import { PropertyValueMap, PropertyValues } from "lit"; import { SubjectWrapper } from "../../models/subject"; import { Signal } from "@lit-labs/preact-signals"; import { decisionColor } from "../../services/colors/colors"; import { GridShape } from "../../helpers/controllers/dynamic-grid-sizes.controller"; import { UrlTransformer } from "../../services/subjectParser/subjectParser"; import { Pixel, Seconds } from "../../models/unitConverters"; import { HeapVariable } from "../../helpers/types/advancedTypes"; import { PageFetcher } from "../../services/gridPageFetcher/gridPageFetcher"; import { SpectrogramOptions } from "../spectrogram/spectrogramOptions"; export type SelectionObserverType = "desktop" | "tablet" | "default"; export type PageOperation = (subject: SubjectWrapper) => T; /** * A map of new subjects to changed decisions. * The map is designed in this way so that you should be able to perform partial * updates because you know exactly what decisions were changed on a subject. */ export type DecisionMadeEvent = Map; export type SubjectChange = { [K in keyof SubjectWrapper]?: SubjectWrapper[K] extends HeapVariable ? Readonly | null : SubjectWrapper[K] | null; }; export interface DecisionMadeEventValue { change: SubjectChange; /** * @deprecated * This property is subject to removal and is a hacky escape hatch that was * used to determine the previous values of the subject properties that were * deleted so that API calls can correctly make DELETE requests on old * out-dated sub-models such as decisions. * * This should be replaced with a more robust solution once the decision-made * spec has been finalized. * https://github.com/ecoacoustics/web-components/issues/448 */ oldSubject: SubjectWrapper; } export interface VerificationGridSettings { isFullscreen: Signal; } export interface VerificationGridInjector { colorService: typeof decisionColor; } export interface MousePosition { x: Pixel; y: Pixel; } /** * @description * An enum that contains all of the possible values the "progress-bar-position" * attribute ("progressBarPosition" property) accepts. * * @example * ```js * const verificationGrid = document.GetElementById("verification-grid"); * verificationGrid.progressBarPosition = ProgressBarPosition.TOP; * ``` */ export declare enum ProgressBarPosition { TOP = "top", BOTTOM = "bottom", HIDDEN = "hidden" } export declare const enum GridState { /** * The datasets subject models are being fetched and there is not enough * subjects to fill the grid. * Note that the verification grid will ONLY enter this state when rendering * is awaiting on the getPage callback to return a sufficient number of * subjects to render the currently viewed page. * * Because the subject models from audio recording prefetching are cached. * It is unlikely that we will enter the DATASET_FETCHING state after the * initial load. The only time we might enter this state after the initial * load is if the datasource (e.g. API) grinds to a halt without crashing and * the user reaches the end of the pre-fetched subjects. * Note that if the getPage callback throws an error while in this state, we * will enter the ERROR state. * * When in this state, a loading indicator is shown in the grid instead of * verification tiles (so be careful of entering/leaving this state too often * as it might cause DOM thrashing). */ DATASET_FETCHING = "dataset-fetching", /** * There are enough subjects to fill the grid, but the verification grid is * still waiting for all of the spectrograms to finish rendering. * * This state can be entered after DATASET_FETCHING completes, or when * modifying the viewHead (e.g. changing page). * We can transition out of this state into the LOADED state once all of the * grid tiles spectrograms have rendered. */ TILES_LOADING = "tiles-loading", /** * All spectrograms and grid tiles have been rendered. * * We can leave the LOADED state by changing the viewHead (e.g. changing page) * causing us to enter the TILES_LOADING state. */ LOADED = "loaded", /** * An error occurred. * An error state can be recovered from if we have enough information to * render either full page or partial page of subjects. * * This state can (currently) only be entered if the getPage callback throws * an error while fetching the currently viewed page of subjects (a getPage * error is thrown while in the DATASET_FETCHING state). * * If the getPage callback throws an error while pre-fetching subjects, the * error is swallowed and re-tried at a later time, meaning that this state * will only be entered if the error occurs if there are no items to show due * to a getPage callback error. */ ERROR = "error", /** * The verification grid has been configured incorrectly and cannot recover. * * This is different from an ERROR state because we cannot recover from an * INVALID_CONFIGURATION without a code, template, or configuration change. * Even if we can render some subjects, if the configuration is invalid, * we will hard fail to this state. */ CONFIGURATION_ERROR = "configuration-error" } declare const VerificationGridComponent_base: import("../../helpers/types/mixins").Component; /** * @description * A verification grid component that can be used to verify audio events * * @example * ```html * * * * * * * * * * ``` * * @dependency oe-verification-grid-tile * @dependency oe-verification-grid-settings * @dependency oe-progress-bar * * @csspart highlight-box - A CSS target for the highlight box so that you can change the color/style. * * @slot - A template element that will be used to create each grid tile * @slot - Decision elements that will be used to create the decision buttons * @slot data-source - An `oe-data-source` element that provides the data * * @event { DecisionMadeEvent } decision-made - Emits information about a batch of decisions that was made * @event grid-loaded - Emits when all the spectrograms have been loaded */ export declare class VerificationGridComponent extends VerificationGridComponent_base { static styles: import("lit").CSSResult; static readonly decisionMadeEventName = "decision-made"; private static readonly loadedEventName; private static readonly defaultGridTileTemplateId; private static readonly defaultSkipButtonId; private static readonly autoPageTimeout; private static readonly slowLoadThreshold; private static readonly defaultGridTileTemplate; protected settings: VerificationGridSettings; spectrogramOptions: Partial; injector: VerificationGridInjector; /** The number of items to display in a single grid */ targetGridSize: number; /** * The selection behavior of the verification grid * @values "desktop" | "tablet" | "default" * @default "default" */ selectionBehavior: SelectionObserverType; emptySubjectText: string; progressBarPosition: ProgressBarPosition; /** A callback function that returns a page of recordings */ getPage?: PageFetcher; /** * A callback function that will be applied to all subject urls * * @default * an identity function that returns the url unchanged */ urlTransformer: UrlTransformer; /** * A duration of time that the verification grid can be in a "loading" * state before it times out and shows an error message. */ set loadingTimeout(value: Seconds); get loadingTimeout(): Seconds; autofocus: boolean; /** selector for oe-verification elements */ private verificationDecisionElements; /** selector for oe-classification elements */ private classificationDecisionElements; /** selector for oe-classification elements */ private tagPromptDecisionElements; /** A selector for all oe-verification and oe-classification elements */ private decisionElements; private skipButtons; private customTileTemplates; private defaultSkipButton?; private gridTiles; private defaultTemplateElement; private bootstrapDialog; private gridContainer; private decisionsContainer; private highlightBox; columns: number; rows: number; private currentSubSelection; private _loadState; private _viewHeadIndex; private _decisionHeadIndex; get gridShape(): GridShape; /** * The index from the `subjects` array indicating up to which point * decisions have been made * It is updated as each page is completed */ get viewHeadIndex(): number; /** * The index from the `subjects` array indicating up to which point * decisions have been made * It is updated as each page is completed */ get decisionHeadIndex(): number; private set decisionHeadIndex(value); /** * All decisions provided by the user, excluding the default skip button. */ private get slottedDecisionComponents(); /** * A count of grid cells available for grid tile components. * Not all grid cells may be currently populated with grid tiles. * * If you want the total number of tiles currently populated/visible on the * screen, use the `pageSize` getter. */ get availableGridCells(): number; /** A count of the number of tiles currently visible on the screen */ get pageSize(): number; /** * Because subject wrappers are highly sensitive to changes (e.g. changing * a subject reference might break downloading), we only expose a readonly * array of subjects. */ get subjects(): ReadonlyArray; get loadState(): GridState; private get currentPageIndices(); private get emptyTileCount(); /** * Returns the current users selection behavior, collapsing the "default" * behavior into either "tablet" or "desktop" depending on the users device * type. */ private get userSelectionBehavior(); /** * When in a single tile view mode, there is some special functionality such * as disabling the sub-selection feature, and not being able to draw a * selection highlight box. */ private get isSingleTileViewMode(); private get hasDatasource(); private get hasFinishedDatasource(); private readonly keydownHandler; private readonly keyupHandler; private readonly blurHandler; private readonly selectionHandler; private readonly decisionHandler; private readonly pointerDownHandler; private readonly pointerUpHandler; private readonly pointerMoveHandler; private readonly scrollHandler; /** * "single decision mode" will automatically advance the selection head if: * 1. There is only one tile selected * 2. All tasks on the selected tile is completed * * A user can enter this mode at any time by selecting just one tile. * They remain in the mode by completing all tasks on the single selected * tile, at which point the selection is advanced. * Once in this mode, there is some special functionality like the first tile * of each new page being automatically selected. */ private singleDecisionMode; private requiredClassificationTags; private requiredDecisions; private showingSelectionShortcuts; private _subjects; private _loadingTimeout; private readonly anyOverlap; private readonly gridController; private readonly datasetLoadingController; private paginationFetcher?; private subjectWriter?; private readonly highlightSelectionAnimation; private readonly highlight; private focusHead; private _rangeSelectionHead; /** * Where range selection will start from. * This pointer moves independently from the focus head. */ private get rangeSelectionHead(); private set rangeSelectionHead(value); private get nextLeftIndex(); private get nextRightIndex(); private get nextUpIndex(); private get nextDownIndex(); private get lastTileIndex(); constructor(); focus(): void; connectedCallback(): void; disconnectedCallback(): void; isViewingHistory(): boolean; resetSpectrogramSettings(): void; isBootstrapDialogOpen(): boolean; flushAllSubjects(): Promise; transitionError(): void; transitionConfigurationError(): void; transitionDatasetFetching(): void; private handleTimeout; firstUpdated(): void; protected willUpdate(change: PropertyValues): void; protected updated(change: PropertyValueMap): Promise; private defaultGridSize; private handleTileInvalidation; /** * handles the data source of the verification grid changing * this will reset the verification task and re-fetch the first page of * subjects from the new data source */ private handleGridSourceInvalidation; private resetForNewDataSource; /** * Virtually pages through the verification grids subjects to find the * decision head. * This is useful for when changing to a partially completed datasource. * * @param minimumIndex * A minimum index to start looking from. This is useful for when reducing the * grid size, and you know that the decision head is ahead of your current * view index. */ private findDecisionHead; private updateRequiredDecisions; private updateRequiredClassificationTags; private updateInjector; private updateDecisionElements; private currentPage; private handleKeyDown; private handleKeyUp; /** * Catches a verification grid tiles play event, and conditionally cancels it * based on the current selection state. * * Reminder: The play shortcut is listened for by each media-controls, so this * handler runs grid size times when the play shortcut is pressed. */ private handleTilePlay; private handleWindowBlur; private handleHelpRequest; private handleBootstrapDialogOpen; private handleBootstrapDialogClose; private handleSlotChange; private validateTemplateValidity; private handleTileOverlap; /** * Every template must both a tag template and task meter to be considered * a valid template otherwise an error will be thrown because the template * does not have enough information for the user to complete any tasks. * * Note that both spectrogram and media controls are optional because the host * application might want to replace the spectrogram without something else to * verify such as an image or video. */ private isTileTemplateValid; private handlePointerMove; private handleScroll; private tileSelectionShortcutsShown; private showSelectionShortcuts; private hideSelectionShortcuts; /** * An event handler for the verification-grid-tile's "oe-selected" event. * This handles alt + number selection, and click selection. */ private handleTileSelection; private toggleTileSelection; private selectTile; private focusTile; private addSubSelectionRange; private subSelectAll; private removeSubSelection; private canSubSelect; private isMobileDevice; private updateSubSelection; /** * A common method that can be used to create consistent selection behavior * across the different selection methods (click, alt, arrow, tab & highlight) */ private processSelection; private resetSelectionHead; private clearSelection; private updateSelectionHead; private moveSelectionHeadToNextUndecided; private selectFirstTile; /** * Fetches (or returns if cached) an array of subjects that could be used to * populate a full page of spectrograms / grid tiles. * Starting from the requested index and ending at the requested index + tile * count. */ private populatePageSubjectsToIndex; /** * Populates the subject buffer up to the requested index + the page size * and returns the subjects that would be rendered for that page. */ private getSubjectPageAtIndex; private setViewHead; private renderHighlightBox; private updateHighlightObservedElements; private resizeHighlightBox; private calculateHighlightIntersection; private hideHighlightBox; private handlePreviousPageClick; private handleNextPageClick; private pageForward; private pageBackward; /** Changes the viewHead to the current page of undecided results */ private resumeVerification; /** * Moves the view and decision head a full page forwards. * This is typically triggered as part of auto-paging. */ private advanceToNextPage; private canNavigatePrevious; private canNavigateNext; private handleDecision; private shouldAutoPage; private setDecisionsDisabled; private updateDecisionWhen; private updateDecisionWhenForSubject; private hasDecisionElements; private areTilesLoaded; private handleTileLoaded; private hasClassificationTask; private hasVerificationTask; private hasNewTagTask; private mixedTaskPromptTemplate; private classificationTaskPromptTemplate; private verificationTaskPromptTemplate; private decisionPromptTemplate; private noItemsTemplate; private loadingTemplate; private datasetFailureTemplate; private configurationFailureTemplate; private unexpectedStateTemplate; private gridLoadedTemplate; private noDecisionsTemplate; private skipDecisionTemplate; private progressBarTemplate; private emptySubjectTemplate; private gridTileTemplate; render(): import("lit").TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { "oe-verification-grid": VerificationGridComponent; } } export {};