Line data Source code
1 : import * as React from 'react';
2 :
3 : import { CLEAR_TABLE_ORDERING, type ComputationAction, type TableComputationState } from '../../compute/computation_state.js';
4 : import { scheduleTask } from '../../compute/computation_tasks.js';
5 : import type { TableOrderingTask } from '../../compute/computation_types.js';
6 : import { TABLE_ORDERING_TASK, TaskStatus } from '../../compute/computation_types.js';
7 : import type { OrderByConstraint } from '../../compute/sql/sqlframe_builder.js';
8 : import type { Dispatch } from '../../utils/variant.js';
9 :
10 1 : export function getColumnSortDirection(field: string, ordering: OrderByConstraint[]): boolean | null {
11 1 : if (ordering.length !== 1 || ordering[0].field !== field) return null;
12 1 : return ordering[0].ascending ?? true;
13 1 : }
14 :
15 1 : export function getNextColumnSortDirection(field: string, ordering: OrderByConstraint[]): boolean | null {
16 1 : const currentDirection = getColumnSortDirection(field, ordering);
17 1 : if (currentDirection == null) return true;
18 1 : return currentDirection ? false : null;
19 1 : }
20 :
21 1 : function areOrderingConstraintsEqual(left: OrderByConstraint[], right: OrderByConstraint[]): boolean {
22 1 : if (left.length !== right.length) return false;
23 1 : for (let i = 0; i < left.length; ++i) {
24 1 : const a = left[i];
25 1 : const b = right[i];
26 1 : if (
27 1 : a.field !== b.field
28 1 : || (a.ascending ?? true) !== (b.ascending ?? true)
29 1 : || (a.nullsFirst ?? false) !== (b.nullsFirst ?? false)
30 : ) {
31 0 : return false;
32 : }
33 1 : }
34 1 : return true;
35 1 : }
36 :
37 1 : export function createOrderingTask(
38 1 : table: TableComputationState,
39 1 : orderingConstraints: OrderByConstraint[],
40 1 : ): TableOrderingTask | null {
41 1 : if (!table.dataFrame || !table.rowNumberColumnName) return null;
42 1 : return {
43 1 : tableId: table.tableId,
44 1 : tableVersion: table.version,
45 1 : inputDataTable: table.dataTable,
46 1 : inputDataTableFieldIndex: table.dataTableFieldsByName,
47 1 : inputDataFrame: table.dataFrame,
48 1 : filterTable: table.filterTable,
49 1 : dataSearchTable: table.dataSearchTable,
50 1 : rowNumberColumnName: table.rowNumberColumnName,
51 1 : orderingConstraints,
52 1 : };
53 1 : }
54 :
55 1 : export function useDataTableOrdering(
56 1 : table: TableComputationState,
57 1 : dispatchComputation: Dispatch<ComputationAction>,
58 1 : ) {
59 1 : const activeOrderingConstraints = React.useMemo<OrderByConstraint[]>(() => {
60 1 : const taskOrdering = table.tasks.orderingTask?.orderingConstraints;
61 1 : return taskOrdering != null && taskOrdering.length > 0 ? taskOrdering : table.dataTableOrdering;
62 1 : }, [table.dataTableOrdering, table.tasks.orderingTask?.orderingConstraints]);
63 :
64 1 : React.useEffect(() => {
65 1 : if (activeOrderingConstraints.length === 0) return;
66 1 : const orderingTask = createOrderingTask(table, activeOrderingConstraints);
67 1 : if (orderingTask == null) return;
68 :
69 1 : const currentTask = table.tasks.orderingTask;
70 1 : const currentOrdering = table.orderingTable;
71 1 : const filterVersion = table.filterTable?.version ?? null;
72 1 : const dataSearchRequestId = table.dataSearchTable?.requestId ?? null;
73 1 : const taskMatchesInput = currentTask?.tableVersion.filterMatches(table.version)
74 1 : && (filterVersion
75 0 : ? (currentTask.filterTable?.version?.filterMatches(filterVersion) ?? false)
76 1 : : currentTask.filterTable === null)
77 1 : && (currentTask.dataSearchTable?.requestId ?? null) === dataSearchRequestId;
78 1 : const hasUpToDateOrdering = currentOrdering != null
79 0 : && currentTask?.progress.status === TaskStatus.TASK_SUCCEEDED
80 0 : && taskMatchesInput
81 0 : && areOrderingConstraintsEqual(currentOrdering.orderingConstraints, activeOrderingConstraints);
82 1 : const hasUpToDateRunningTask = currentTask?.progress.status === TaskStatus.TASK_RUNNING
83 1 : && taskMatchesInput
84 1 : && areOrderingConstraintsEqual(currentTask.orderingConstraints, activeOrderingConstraints);
85 1 : if (hasUpToDateOrdering || hasUpToDateRunningTask) return;
86 1 : void scheduleTask(TABLE_ORDERING_TASK, orderingTask, dispatchComputation);
87 1 : }, [activeOrderingConstraints, table, dispatchComputation]);
88 :
89 1 : const orderByColumn = React.useCallback((fieldId: number) => {
90 1 : const fieldName = table.dataTable.schema.fields[fieldId].name;
91 1 : const nextSortDirection = getNextColumnSortDirection(fieldName, activeOrderingConstraints);
92 1 : if (nextSortDirection == null) {
93 1 : dispatchComputation({ type: CLEAR_TABLE_ORDERING, value: table.tableId });
94 1 : return;
95 1 : }
96 1 : const orderingTask = createOrderingTask(table, [{
97 1 : field: fieldName,
98 1 : ascending: nextSortDirection,
99 1 : nullsFirst: false,
100 1 : }]);
101 1 : if (orderingTask != null) void scheduleTask(TABLE_ORDERING_TASK, orderingTask, dispatchComputation);
102 1 : }, [activeOrderingConstraints, table, dispatchComputation]);
103 :
104 1 : const getSortDirection = React.useCallback((fieldId: number) => {
105 1 : const fieldName = table.dataTable.schema.fields[fieldId].name;
106 1 : return getColumnSortDirection(fieldName, activeOrderingConstraints);
107 1 : }, [activeOrderingConstraints, table.dataTable.schema.fields]);
108 :
109 1 : return { orderByColumn, getSortDirection };
110 1 : }
|