Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 156 additions & 7 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",

"plugins": ["eslint", "oxc", "react", "typescript", "vitest"],
"plugins": ["eslint", "oxc", "react", "typescript", "unicorn", "vitest"],

"options": {
"denyWarnings": true,
Expand All @@ -20,7 +20,7 @@
// https://oxc.rs/docs/guide/usage/linter/rules.html
/*
// copy all the rules from the rules table for easy pasting
const groups = new Set(['eslint', 'oxc', 'react', 'typescript', 'vitest']);
const groups = new Set(['eslint', 'oxc', 'react', 'typescript', 'unicorn', 'vitest']);
copy(
Iterator.from(document.querySelectorAll('tbody tr'))
.map((row) => [
Expand Down Expand Up @@ -128,7 +128,7 @@ copy(
"eslint/no-misleading-character-class": "warn",
"eslint/no-multi-assign": "off",
"eslint/no-multi-str": "warn",
"eslint/no-negated-condition": "off",
"eslint/no-negated-condition": "warn",
"eslint/no-nested-ternary": "off",
"eslint/no-new": "warn",
"eslint/no-new-func": "warn",
Expand Down Expand Up @@ -490,14 +490,155 @@ copy(
"typescript/triple-slash-reference": ["warn", { "lib": "never", "types": "never" }],
"typescript/unbound-method": "off",
"typescript/unified-signatures": "off",
"typescript/use-unknown-in-catch-callback-variable": "warn"
"typescript/use-unknown-in-catch-callback-variable": "warn",
"unicorn/catch-error-name": "warn",
"unicorn/consistent-assert": "warn",
"unicorn/consistent-date-clone": "warn",
"unicorn/consistent-empty-array-spread": "warn",
"unicorn/consistent-existence-index-check": "warn",
"unicorn/consistent-function-scoping": "warn",
"unicorn/consistent-template-literal-escape": "warn",
"unicorn/custom-error-definition": "warn",
"unicorn/empty-brace-spaces": "off",
"unicorn/error-message": "warn",
"unicorn/escape-case": "warn",
"unicorn/explicit-length-check": "warn",
"unicorn/explicit-timer-delay": "warn",
"unicorn/filename-case": ["warn", { "cases": { "camelCase": true, "pascalCase": true } }],
"unicorn/import-style": "off",
"unicorn/max-nested-calls": "off",
"unicorn/new-for-builtins": "warn",
"unicorn/no-abusive-eslint-disable": "warn",
"unicorn/no-accessor-recursion": "warn",
"unicorn/no-anonymous-default-export": "warn",
"unicorn/no-array-callback-reference": "off",
"unicorn/no-array-fill-with-reference-type": "warn",
"unicorn/no-array-for-each": "warn",
"unicorn/no-array-method-this-argument": "warn",
"unicorn/no-array-reduce": "warn",
"unicorn/no-array-reverse": "warn",
"unicorn/no-array-sort": "warn",
"unicorn/no-await-expression-member": "warn",
"unicorn/no-await-in-promise-methods": "warn",
"unicorn/no-confusing-array-with": "off",
"unicorn/no-console-spaces": "warn",
"unicorn/no-document-cookie": "warn",
"unicorn/no-empty-file": "warn",
"unicorn/no-hex-escape": "warn",
"unicorn/no-immediate-mutation": "warn",
"unicorn/no-instanceof-array": "off",
"unicorn/no-instanceof-builtins": "warn",
"unicorn/no-invalid-fetch-options": "warn",
"unicorn/no-invalid-remove-event-listener": "warn",
"unicorn/no-length-as-slice-end": "warn",
"unicorn/no-lonely-if": "warn",
"unicorn/no-magic-array-flat-depth": "off",
"unicorn/no-negated-condition": "off",
"unicorn/no-negation-in-equality-check": "warn",
"unicorn/no-nested-ternary": "off",
"unicorn/no-new-array": "warn",
"unicorn/no-new-buffer": "warn",
"unicorn/no-null": "off",
"unicorn/no-object-as-default-parameter": "warn",
"unicorn/no-process-exit": "warn",
"unicorn/no-single-promise-in-promise-methods": "warn",
"unicorn/no-static-only-class": "warn",
"unicorn/no-thenable": "warn",
"unicorn/no-this-assignment": "warn",
"unicorn/no-typeof-undefined": "warn",
"unicorn/no-unnecessary-array-flat-depth": "warn",
"unicorn/no-unnecessary-array-splice-count": "warn",
"unicorn/no-unnecessary-await": "warn",
"unicorn/no-unnecessary-slice-end": "warn",
"unicorn/no-unreadable-array-destructuring": "warn",
"unicorn/no-unreadable-iife": "warn",
"unicorn/no-useless-collection-argument": "warn",
"unicorn/no-useless-error-capture-stack-trace": "warn",
"unicorn/no-useless-fallback-in-spread": "warn",
"unicorn/no-useless-iterator-to-array": "warn",
"unicorn/no-useless-length-check": "warn",
"unicorn/no-useless-promise-resolve-reject": "warn",
"unicorn/no-useless-spread": "warn",
"unicorn/no-useless-switch-case": "warn",
"unicorn/no-useless-undefined": "off",
"unicorn/no-zero-fractions": "warn",
"unicorn/number-literal-case": "off",
"unicorn/numeric-separators-style": "warn",
"unicorn/prefer-add-event-listener": "warn",
"unicorn/prefer-array-find": "warn",
"unicorn/prefer-array-flat": "warn",
"unicorn/prefer-array-flat-map": "warn",
"unicorn/prefer-array-index-of": "warn",
"unicorn/prefer-array-some": "warn",
"unicorn/prefer-at": "off",
"unicorn/prefer-bigint-literals": "warn",
"unicorn/prefer-blob-reading-methods": "warn",
"unicorn/prefer-class-fields": "warn",
"unicorn/prefer-classlist-toggle": "warn",
"unicorn/prefer-code-point": "warn",
"unicorn/prefer-date-now": "warn",
"unicorn/prefer-default-parameters": "warn",
"unicorn/prefer-dom-node-append": "warn",
"unicorn/prefer-dom-node-dataset": "warn",
"unicorn/prefer-dom-node-remove": "warn",
"unicorn/prefer-dom-node-text-content": "warn",
"unicorn/prefer-event-target": "warn",
"unicorn/prefer-export-from": "warn",
"unicorn/prefer-global-this": "warn",
"unicorn/prefer-import-meta-properties": "warn",
"unicorn/prefer-includes": "off",
"unicorn/prefer-keyboard-event-key": "off",
"unicorn/prefer-logical-operator-over-ternary": "warn",
"unicorn/prefer-math-min-max": "warn",
"unicorn/prefer-math-trunc": "warn",
"unicorn/prefer-modern-dom-apis": "warn",
"unicorn/prefer-modern-math-apis": "warn",
"unicorn/prefer-module": "warn",
"unicorn/prefer-native-coercion-functions": "warn",
"unicorn/prefer-negative-index": "warn",
"unicorn/prefer-node-protocol": "warn",
"unicorn/prefer-number-coercion": "off",
"unicorn/prefer-number-properties": ["warn", { "checkInfinity": true }],
"unicorn/prefer-object-from-entries": "warn",
"unicorn/prefer-optional-catch-binding": "warn",
"unicorn/prefer-prototype-methods": "warn",
"unicorn/prefer-query-selector": "off",
"unicorn/prefer-reflect-apply": "warn",
"unicorn/prefer-regexp-test": "warn",
"unicorn/prefer-response-static-json": "warn",
"unicorn/prefer-set-has": "warn",
"unicorn/prefer-set-size": "warn",
"unicorn/prefer-single-call": "warn",
"unicorn/prefer-spread": "warn",
"unicorn/prefer-string-raw": "warn",
"unicorn/prefer-string-replace-all": "warn",
"unicorn/prefer-string-slice": "warn",
"unicorn/prefer-string-starts-ends-with": "off",
"unicorn/prefer-string-trim-start-end": "warn",
"unicorn/prefer-structured-clone": "warn",
"unicorn/prefer-ternary": "off",
"unicorn/prefer-top-level-await": "warn",
"unicorn/prefer-type-error": "warn",
"unicorn/relative-url-style": "warn",
"unicorn/require-array-join-separator": "warn",
"unicorn/require-module-attributes": "warn",
"unicorn/require-module-specifiers": "warn",
"unicorn/require-number-to-fixed-digits-argument": "warn",
"unicorn/require-post-message-target-origin": "warn",
"unicorn/switch-case-braces": ["warn", "avoid"],
"unicorn/switch-case-break-position": "warn",
"unicorn/text-encoding-identifier-case": "warn",
"unicorn/throw-new-error": "warn"
},

"overrides": [
{
"files": ["test/**/*"],

"rules": {
"typescript/no-floating-promises": "warn",
"unicorn/consistent-function-scoping": "off",
"unicorn/prefer-ternary": "off",
"vitest/consistent-each-for": "warn",
"vitest/consistent-test-filename": "off",
"vitest/consistent-test-it": "warn",
Expand All @@ -508,7 +649,7 @@ copy(
"vitest/max-nested-describe": "off",
"vitest/no-alias-methods": "warn",
"vitest/no-commented-out-tests": "warn",
"vitest/no-conditional-expect": "warn",
"vitest/no-conditional-expect": "off",
"vitest/no-conditional-in-test": "off",
"vitest/no-conditional-tests": "warn",
"vitest/no-disabled-tests": "off",
Expand Down Expand Up @@ -582,8 +723,7 @@ copy(
"vitest/valid-expect": "warn",
"vitest/valid-expect-in-promise": "warn",
"vitest/valid-title": "warn",
"vitest/warn-todo": "warn",
"typescript/no-floating-promises": "warn"
"vitest/warn-todo": "warn"
}
},

Expand All @@ -593,6 +733,15 @@ copy(
"rules": {
"eslint/no-console": "off"
}
},

{
"files": ["**/*.d.ts"],

"rules": {
"typescript/consistent-indexed-object-style": "off",
"unicorn/require-module-specifiers": "off"
}
}
]
}
2 changes: 1 addition & 1 deletion src/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
);
const [isColumnResizing, setIsColumnResizing] = useState(false);
const [isDragging, setIsDragging] = useState(false);
const [draggedOverRowIdx, setDraggedOverRowIdx] = useState<number | undefined>(undefined);
const [draggedOverRowIdx, setDraggedOverRowIdx] = useState<number | undefined>();
const [previousRowIdx, setPreviousRowIdx] = useState(-1);

const isColumnWidthsControlled =
Expand Down
8 changes: 4 additions & 4 deletions src/EditCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ export default function EditCell<R, SR>({
}
}

window.addEventListener('mousedown', onWindowCaptureMouseDown, { capture: true });
window.addEventListener('mousedown', onWindowMouseDown);
globalThis.addEventListener('mousedown', onWindowCaptureMouseDown, { capture: true });
globalThis.addEventListener('mousedown', onWindowMouseDown);

return () => {
window.removeEventListener('mousedown', onWindowCaptureMouseDown, { capture: true });
window.removeEventListener('mousedown', onWindowMouseDown);
globalThis.removeEventListener('mousedown', onWindowCaptureMouseDown, { capture: true });
globalThis.removeEventListener('mousedown', onWindowMouseDown);
cancelTask();
};
}, [commitOnOutsideClick]);
Expand Down
12 changes: 6 additions & 6 deletions src/HeaderCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,6 @@ export default function HeaderCell<R, SR>({
setDraggedColumnKey(undefined);
}

function onDragOver(event: React.DragEvent<HTMLDivElement>) {
// prevent default to allow drop
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}

function onDrop(event: React.DragEvent<HTMLDivElement>) {
setIsOver(false);
// prevent the browser from redirecting in some cases
Expand Down Expand Up @@ -381,6 +375,12 @@ function ResizeHandle<R, SR>({
);
}

function onDragOver(event: React.DragEvent<HTMLDivElement>) {
// prevent default to allow drop
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}

// only accept pertinent drag events:
// - ignore drag events going from the container to an element inside the container
// - ignore drag events going from an element inside the container to the container
Expand Down
39 changes: 21 additions & 18 deletions src/TreeDataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ export interface TreeDataGridProps<R, SR = unknown, K extends Key = Key> extends
groupIdGetter?: Maybe<(groupKey: string, parentId?: string) => string>;
}

type GroupByDictionary<TRow> = Record<
string,
{
readonly childRows: readonly TRow[];
readonly childGroups: readonly TRow[] | Readonly<GroupByDictionary<TRow>>;
readonly startRowIndex: number;
}
>;
interface GroupBy<TRow> {
readonly childRows: readonly TRow[];
readonly childGroups: readonly TRow[] | ReadonlyGroupByMap<TRow>;
readonly startRowIndex: number;
}

type GroupByMap<TRow> = Map<string, GroupBy<TRow>>;
type ReadonlyGroupByMap<TRow> = ReadonlyMap<string, GroupBy<TRow>>;

export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
columns: rawColumns,
Expand Down Expand Up @@ -119,16 +119,16 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
rows: readonly R[],
[groupByKey, ...remainingGroupByKeys]: readonly string[],
startRowIndex: number
): [Readonly<GroupByDictionary<R>>, number] => {
): [ReadonlyGroupByMap<R>, number] => {
let groupRowsCount = 0;
const groups: GroupByDictionary<R> = {};
const groups: GroupByMap<R> = new Map();
for (const [key, childRows] of Object.entries(rowGrouper(rows, groupByKey))) {
// Recursively group each parent group
const [childGroups, childRowsCount] =
remainingGroupByKeys.length === 0
? [childRows, childRows.length]
: groupRows(childRows, remainingGroupByKeys, startRowIndex + groupRowsCount + 1); // 1 for parent row
groups[key] = { childRows, childGroups, startRowIndex: startRowIndex + groupRowsCount };
groups.set(key, { childRows, childGroups, startRowIndex: startRowIndex + groupRowsCount });
groupRowsCount += childRowsCount + 1; // 1 for parent row
}

Expand All @@ -148,18 +148,21 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
const flattenedRows: (R | GroupRow<R>)[] = [];

const expandGroup = (
rows: GroupByDictionary<R> | readonly R[],
rows: ReadonlyGroupByMap<R> | readonly R[],
parentId: string | undefined,
level: number
): void => {
if (isReadonlyArray(rows)) {
flattenedRows.push(...rows);
return;
}
Object.keys(rows).forEach((groupKey, posInSet, keys) => {

let posInSet = 0;

for (const [groupKey, row] of rows) {
const id = groupIdGetter(groupKey, parentId);
const isExpanded = expandedGroupIds.has(id);
const { childRows, childGroups, startRowIndex } = rows[groupKey];
const { childRows, childGroups, startRowIndex } = row;

const groupRow: GroupRow<R> = {
id,
Expand All @@ -168,17 +171,17 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
isExpanded,
childRows,
level,
posInSet,
posInSet: posInSet++,
startRowIndex,
setSize: keys.length
setSize: rows.size
};
flattenedRows.push(groupRow);
allGroupRows.add(groupRow);

if (isExpanded) {
expandGroup(childGroups, id, level + 1);
}
});
}
};

expandGroup(groupedRows, undefined, 0);
Expand Down Expand Up @@ -449,7 +452,7 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
}

function defaultGroupIdGetter(groupKey: string, parentId: string | undefined) {
return parentId !== undefined ? `${parentId}__${groupKey}` : groupKey;
return parentId === undefined ? groupKey : `${parentId}__${groupKey}`;
}

function isReadonlyArray(arr: unknown): arr is readonly unknown[] {
Expand Down
1 change: 0 additions & 1 deletion src/globals.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
declare module 'react' {
interface CSSProperties {
// oxlint-disable-next-line typescript/consistent-indexed-object-style
[key: `--${string}`]: string | number | undefined;
}
}
Expand Down
Loading
Loading