+
+
}
+ />
+
}
onClick={handleClick}
- >
- {t('grid.field.newColumn')}
-
+ />
- >
+
);
-}
\ No newline at end of file
+};
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/GridNewRow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/GridNewRow.tsx
index 971fe15f98..cf9dd8788b 100644
--- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/GridNewRow.tsx
+++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/GridNewRow.tsx
@@ -1,5 +1,6 @@
import { useCallback } from 'react';
import { t } from 'i18next';
+import { Button } from '@mui/material';
import { ReactComponent as AddSvg } from '$app/assets/add.svg';
import * as service from '$app/components/database/database_bd_svc';
import { useDatabase, useViewId } from '../../database.hooks';
@@ -16,14 +17,16 @@ export const GridNewRow = () => {
}, [viewId, lastRowId]);
return (
-
-
-
- {t('grid.row.newRow')}
-
+
+
);
-};
\ No newline at end of file
+};
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/GridRow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/GridRow.tsx
index 1d7d9fda52..a1749451d3 100644
--- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/GridRow.tsx
+++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/GridRow.tsx
@@ -1,38 +1,36 @@
-import { VirtualItem } from '@tanstack/react-virtual';
+import { Virtualizer } from '@tanstack/react-virtual';
import { FC } from 'react';
import { RenderRow, RenderRowType } from './constants';
import { GridCellRow } from './GridCellRow';
import { GridFieldRow } from './GridFieldRow';
import { GridNewRow } from './GridNewRow';
+import { GridCalculateRow } from './GridCalculateRow';
-export const GridRow: FC<{
+export interface GridRowProps {
row: RenderRow;
- columnVirtualItems: VirtualItem[];
- before: number;
- after: number;
-}> = ({ row, columnVirtualItems, before, after }) => {
+ virtualizer: Virtualizer
;
+}
+
+export const GridRow: FC = ({
+ row,
+ virtualizer,
+}) => {
switch (row.type) {
case RenderRowType.Row:
return (
);
case RenderRowType.Fields:
- return (
-
- );
+ return ;
case RenderRowType.NewRow:
return ;
+ case RenderRowType.Calculate:
+ return ;
default:
return null;
}
-};
\ No newline at end of file
+};
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/constants.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/constants.ts
index 286d51ae10..e8ae30c1dd 100644
--- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/constants.ts
+++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRow/constants.ts
@@ -4,6 +4,7 @@ export enum RenderRowType {
Fields = 'fields',
Row = 'row',
NewRow = 'new-row',
+ Calculate = 'calculate',
}
export interface FieldRenderRow {
@@ -19,4 +20,8 @@ export interface NewRenderRow {
type: RenderRowType.NewRow;
}
-export type RenderRow = FieldRenderRow | CellRenderRow | NewRenderRow;
\ No newline at end of file
+export interface CalculateRenderRow {
+ type: RenderRowType.Calculate;
+}
+
+export type RenderRow = FieldRenderRow | CellRenderRow | NewRenderRow | CalculateRenderRow;
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.tsx
index b8f94065a6..587e2ec8ea 100644
--- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.tsx
+++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.tsx
@@ -1,26 +1,32 @@
-import { Virtualizer, useVirtualizer } from '@tanstack/react-virtual';
+import { useVirtualizer } from '@tanstack/react-virtual';
import { FC, useContext, useMemo, useRef } from 'react';
import { VerticalScrollElementRefContext } from '../../database.context';
import { useDatabase } from '../../database.hooks';
+import { VirtualizedList } from '../../_shared';
import { GridRow, RenderRow, RenderRowType } from '../GridRow';
-import { VirtualizedRows } from './VirtualizedRows';
-const calculateBeforeAfter = (columnVirtualizer: Virtualizer) => {
- const columnVirtualItems = columnVirtualizer.getVirtualItems();
+const getRenderRowKey = (row: RenderRow) => {
+ if (row.type === RenderRowType.Row) {
+ return `row:${row.data.id}`;
+ }
- return columnVirtualItems.length > 0
- ? [
- columnVirtualItems[0].start,
- columnVirtualizer.getTotalSize() - columnVirtualItems[columnVirtualItems.length - 1].end,
- ]
- : [0, 0];
+ return row.type;
+};
+
+const getRenderRowHeight = (row: RenderRow) => {
+ const defaultRowHeight = 37;
+
+ if (row.type === RenderRowType.Row) {
+ return row.data.height ?? defaultRowHeight;
+ }
+
+ return defaultRowHeight;
};
export const GridTable: FC = () => {
const verticalScrollElementRef = useContext(VerticalScrollElementRefContext);
- const { rows, fields } = useDatabase();
-
const horizontalScrollElementRef = useRef(null);
+ const { rows, fields } = useDatabase();
const renderRows = useMemo(() => {
return [
@@ -34,12 +40,22 @@ export const GridTable: FC = () => {
{
type: RenderRowType.NewRow,
},
+ {
+ type: RenderRowType.Calculate,
+ },
];
}, [rows]);
- const defaultColumnWidth = 221;
+ const rowVirtualizer = useVirtualizer({
+ count: renderRows.length,
+ overscan: 10,
+ getItemKey: i => getRenderRowKey(renderRows[i]),
+ getScrollElement: () => verticalScrollElementRef.current,
+ estimateSize: i => getRenderRowHeight(renderRows[i]),
+ });
- const columnVirtualizer = useVirtualizer({
+ const defaultColumnWidth = 221;
+ const columnVirtualizer = useVirtualizer({
horizontal: true,
count: fields.length,
overscan: 5,
@@ -48,28 +64,20 @@ export const GridTable: FC = () => {
estimateSize: (i) => fields[i].width ?? defaultColumnWidth,
});
- const columnVirtualItems = columnVirtualizer.getVirtualItems();
- const [before, after] = calculateBeforeAfter(columnVirtualizer);
-
return (
-
- (
-
- )}
- />
-
+
(
+
+ )}
+ />
);
-};
\ No newline at end of file
+};
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/VirtualizedRows.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/VirtualizedRows.tsx
deleted file mode 100644
index decbe3d893..0000000000
--- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/VirtualizedRows.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { useVirtualizer } from '@tanstack/react-virtual';
-import { FC, RefObject } from 'react';
-import { RenderRow, RenderRowType } from '../GridRow';
-
-export interface VirtualizedRowsProps {
- rows: RenderRow[];
- scrollElementRef: RefObject;
- defaultHeight?: number;
- renderRow: (row: RenderRow, index: number) => React.ReactNode;
-}
-
-const getRenderRowKey = (row: RenderRow) => {
- switch (row.type) {
- case RenderRowType.Row:
- return `row:${row.data.id}`;
- case RenderRowType.Fields:
- return 'fields';
- case RenderRowType.NewRow:
- return 'new-row';
- default:
- return '';
- }
-};
-
-const getRenderRowHeight = (row: RenderRow) => {
- switch (row.type) {
- case RenderRowType.Row:
- return row.data.height ?? 41;
- case RenderRowType.Fields:
- return 41;
- case RenderRowType.NewRow:
- return 36;
- default:
- return 0;
- }
-};
-
-export const VirtualizedRows: FC = ({
- rows,
- scrollElementRef,
- renderRow,
-}) => {
- const virtualizer = useVirtualizer({
- count: rows.length,
- overscan: 5,
- getItemKey: i => getRenderRowKey(rows[i]),
- getScrollElement: () => scrollElementRef.current,
- estimateSize: i => getRenderRowHeight(rows[i]),
- });
-
- const virtualItems = virtualizer.getVirtualItems();
-
- return (
-
- {virtualItems.map((virtualRow) => {
- return (
-
- {renderRow(rows[virtualRow.index], virtualRow.index)}
-
- );
- })}
-
- );
-};
\ No newline at end of file
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts
index 46e146003f..fafeee00d4 100644
--- a/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts
+++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts
@@ -1,3 +1,3 @@
export * from './database.context';
export * from './database.hooks';
-export * from './grid';
\ No newline at end of file
+export * from './Database';
diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts
index c53911c52f..de291b980d 100644
--- a/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts
+++ b/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts
@@ -14,10 +14,14 @@ export function debounce(fn: (...args: any[]) => void, delay: number) {
return debounceFn;
}
-export function throttle(fn: (...args: any[]) => void, delay: number, immediate = true) {
+export function throttle void = (...args: any[]) => void>(
+ fn: T,
+ delay: number,
+ immediate = true,
+): T {
let timeout: NodeJS.Timeout | null = null;
- return (...args: any[]) => {
+ const run = (...args: Parameters) => {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
@@ -26,6 +30,8 @@ export function throttle(fn: (...args: any[]) => void, delay: number, immediate
immediate && fn.apply(undefined, args);
}
};
+
+ return run as T;
}
export function get(obj: any, path: string[], defaultValue?: any): T {
@@ -126,3 +132,59 @@ export function chunkArray(array: T[], chunkSize: number) {
return chunks;
}
+
+/**
+ * Creates an interval that repeatedly calls the given function with a specified delay.
+ *
+ * @param {Function} fn - The function to be called repeatedly.
+ * @param {number} [delay] - The delay between function calls in milliseconds.
+ * @param {Object} [options] - Additional options for the interval.
+ * @param {boolean} [options.immediate] - Whether to immediately call the function when the interval is created. Default is true.
+ *
+ * @return {Function} - The function that runs the interval.
+ * @return {Function.cancel} - A method to cancel the interval.
+ *
+ * @example
+ * const log = interval((message) => console.log(message), 1000);
+ *
+ * log('foo'); // prints 'foo' every second.
+ *
+ * log('bar'); // change to prints 'bar' every second.
+ *
+ * log.cancel(); // stops the interval.
+ */
+export function interval any = (...args: any[]) => any>(
+ fn: T,
+ delay?: number,
+ options?: { immediate?: boolean },
+): T & { cancel: () => void } {
+ const { immediate = true } = options || {};
+ let intervalId: NodeJS.Timer | null = null;
+ let parameters: any[] = [];
+
+ function run(...args: Parameters) {
+ parameters = args;
+
+ if (intervalId !== null) {
+ return;
+ }
+
+ immediate && fn.apply(undefined, parameters);
+ intervalId = setInterval(() => {
+ fn.apply(undefined, parameters);
+ }, delay);
+ }
+
+ function cancel() {
+ if (intervalId === null) {
+ return;
+ }
+
+ clearInterval(intervalId);
+ intervalId = null;
+ parameters = [];
+ }
+
+ run.cancel = cancel;
+ return run as T & { cancel: () => void };
+}
diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx
index 2da3cc00a5..61f86d22f0 100644
--- a/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx
+++ b/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx
@@ -1,38 +1,5 @@
-import { useRef } from 'react';
-import { useSnapshot } from 'valtio';
-import { DatabaseLayoutPB } from '@/services/backend';
-import {
- VerticalScrollElementRefContext,
- DatabaseContext,
- Grid,
- useViewId,
- useConnectDatabase,
-} from '../components/database';
+import { Database } from '../components/database';
export const DatabasePage = () => {
- const viewId = useViewId();
- const scrollElementRef = useRef(null);
- const database = useConnectDatabase(viewId);
- const snapshot = useSnapshot(database);
-
- return (
-
-
-
-
Grid
-
- 👋 Welcome to AppFlowy
-
-
-
-
- {snapshot.layoutType === DatabaseLayoutPB.Grid ? : null}
-
-
-
-
- );
+ return ;
};
diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json
index eb3a740dc7..6b1cd97c22 100644
--- a/frontend/resources/translations/en.json
+++ b/frontend/resources/translations/en.json
@@ -351,6 +351,9 @@
"grid": {
"deleteView": "Are you sure you want to delete this view?",
"createView": "New",
+ "title": {
+ "placeholder": "Untitled"
+ },
"settings": {
"filter": "Filter",
"sort": "Sort",
@@ -457,7 +460,9 @@
"copyProperty": "Copied property to clipboard",
"count": "Count",
"newRow": "New row",
- "action": "Action"
+ "action": "Action",
+ "add": "Click add to below",
+ "drag": "Drag to move"
},
"selectOption": {
"create": "Create",