Skip to content

Commit

Permalink
feat(core): add field order & setup web column dnd
Browse files Browse the repository at this point in the history
  • Loading branch information
Chenqin Nee authored and Chenqin Nee committed Dec 19, 2022
1 parent 6dd9496 commit e8c04e2
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 94 deletions.
45 changes: 25 additions & 20 deletions apps/web/components/table-ui/table.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DndContext } from '@dnd-kit/core'
import { Table } from '@egodb/ui'
import { createColumnHelper, getCoreRowModel, useReactTable } from '@tanstack/react-table'
import { useMemo } from 'react'
Expand All @@ -13,6 +14,7 @@ const fieldHelper = createColumnHelper<TData>()
export const EGOTable: React.FC<IProps> = ({ table, records }) => {
const view = table.getOrCreateDefaultView()
const columnVisibility = view.getVisibility()
const columnOrder = table.getFieldsOrder().order

const columns = table.schema.fields
.map((c) =>
Expand All @@ -36,31 +38,34 @@ export const EGOTable: React.FC<IProps> = ({ table, records }) => {
columns,
state: {
columnVisibility,
columnOrder,
},
columnResizeMode: 'onChange',
getCoreRowModel: getCoreRowModel(),
})

return (
<Table
highlightOnHover
withBorder
withColumnBorders
sx={{
backgroundColor: 'white',
width: rt.getCenterTotalSize(),
}}
>
<thead>
{rt.getHeaderGroups().map((headerGroup) => (
<Thead headerGroup={headerGroup} key={headerGroup.id} tableId={table.id.value} />
))}
</thead>
<tbody>
{rt.getRowModel().rows.map((row) => (
<Tr key={row.id} row={row} />
))}
</tbody>
</Table>
<DndContext>
<Table
highlightOnHover
withBorder
withColumnBorders
sx={{
backgroundColor: 'white',
width: rt.getCenterTotalSize(),
}}
>
<thead>
{rt.getHeaderGroups().map((headerGroup) => (
<Thead headerGroup={headerGroup} key={headerGroup.id} tableId={table.id.value} />
))}
</thead>
<tbody>
{rt.getRowModel().rows.map((row) => (
<Tr key={row.id} row={row} />
))}
</tbody>
</Table>
</DndContext>
)
}
37 changes: 32 additions & 5 deletions apps/web/components/table-ui/th.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Text } from '@egodb/ui'
import { Group, Text, useEgoUITheme } from '@egodb/ui'
import { flexRender } from '@tanstack/react-table'
import styled from '@emotion/styled'
import type { THeader } from './interface'
import { trpc } from '../../trpc'
import { useDraggable, useDroppable } from '@dnd-kit/core'

const ResizerLine = styled.div`
display: block;
Expand Down Expand Up @@ -33,6 +34,7 @@ const Resizer = styled.div`

export const Th: React.FC<{ header: THeader; tableId: string }> = ({ header, tableId }) => {
const setFieldWidth = trpc.table.setFieldWidth.useMutation()
const theme = useEgoUITheme()

const onSetFieldWidth = (fieldName: string, width: number) => {
setFieldWidth.mutate({
Expand All @@ -42,11 +44,36 @@ export const Th: React.FC<{ header: THeader; tableId: string }> = ({ header, tab
})
}

const { isOver, setNodeRef: setDroppableRef } = useDroppable({ id: header.id })
const {
attributes,
listeners,
setNodeRef: setDraggableRef,
transform,
} = useDraggable({
id: header.id,
})
const style = transform
? {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
}
: undefined
return (
<th key={header.id} style={{ position: 'relative', width: header.getSize() }} colSpan={header.colSpan}>
<Text fz="sm" color="gray.7" fw={500}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</Text>
<th
ref={setDroppableRef}
key={header.id}
style={{
position: 'relative',
width: header.getSize(),
color: isOver ? theme.colors.gray[2] : theme.colors.gray[7],
}}
colSpan={header.colSpan}
>
<Group position="apart" ref={setDraggableRef} style={style} {...listeners} {...attributes}>
<Text fz="sm" fw={500}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</Text>
</Group>
<Resizer
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
Expand Down
1 change: 0 additions & 1 deletion apps/web/components/table-ui/thead.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import type { THeaderGroup } from './interface'
import { Th } from './th'

Expand Down
1 change: 1 addition & 0 deletions packages/core/specifications/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export * from './table-id.specifaction'
export * from './table-name.specification'
export * from './table-schema.specification'
export * from './table-view-field-option.specification'
export * from './table-view-fields-order.specification'
export * from './table-views.specification'
2 changes: 2 additions & 0 deletions packages/core/specifications/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { WithTableId } from './table-id.specifaction'
import type { WithTableName } from './table-name.specification'
import type { WithTableSchema } from './table-schema.specification'
import type { WithFieldVisibility, WithFieldWidth } from './table-view-field-option.specification'
import type { WithViewFieldsOrder } from './table-view-fields-order.specification'
import type { WithTableViews } from './table-views.specification'

export interface ITableSpecVisitor extends ISpecVisitor {
Expand All @@ -18,6 +19,7 @@ export interface ITableSpecVisitor extends ISpecVisitor {
filterEqual(s: WithFilter): void
newField(s: WithNewField): void

fieldsOrder(s: WithViewFieldsOrder): void
fieldWidthEqual(s: WithFieldWidth): void
fieldVisibility(s: WithFieldVisibility): void
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { CompositeSpecification } from '@egodb/domain'
import type { Result } from 'oxide.ts'
import { Ok } from 'oxide.ts'
import type { ITableSpecVisitor } from '.'
import type { Table } from '../table'
import type { ViewFieldsOrder } from '../view'

export class WithViewFieldsOrder extends CompositeSpecification<Table, ITableSpecVisitor> {
constructor(public readonly viewName: string, public readonly viewFieldsOrder: ViewFieldsOrder) {
super()
}

isSatisfiedBy(t: Table): boolean {
return this.viewFieldsOrder.equals(t.getOrCreateDefaultView(this.viewName).fieldsOrder)
}

mutate(t: Table): Result<Table, string> {
t.getOrCreateDefaultView(this.viewName).fieldsOrder = this.viewFieldsOrder
return Ok(t)
}

accept(v: ITableSpecVisitor): Result<void, string> {
v.fieldsOrder(this)
return Ok(undefined)
}
}
6 changes: 5 additions & 1 deletion packages/core/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type { IEditTableSchema } from './table.schema'
import type { TableId } from './value-objects'
import { TableSchema } from './value-objects'
import type { TableName } from './value-objects/table-name.vo'
import type { IQueryView, ISetFieldVisibilitySchema, ISetFieldWidthSchema } from './view'
import type { IQueryView, ISetFieldVisibilitySchema, ISetFieldWidthSchema, ViewFieldsOrder } from './view'
import { defaultViewDiaplyType, View } from './view'
import { Views } from './view/views'

Expand Down Expand Up @@ -97,6 +97,10 @@ export class Table {
return and(...specs)
}

public getFieldsOrder(viewName?: string): ViewFieldsOrder {
return this.getOrCreateDefaultView(viewName).fieldsOrder ?? this.schema.defaultFieldsOrder
}

public createRecord(input: ICreateRecordInput): Record {
const inputs: ICreateFieldsSchema_internal = pipe(
input.value,
Expand Down
6 changes: 6 additions & 0 deletions packages/core/value-objects/table-schema.vo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createFieldSchema } from '../field'
import { FieldFactory } from '../field/field.factory'
import type { TableCompositeSpecificaiton } from '../specifications/interface'
import { WithNewField } from '../specifications/table-field.specification'
import { ViewFieldsOrder } from '../view/view-fields-order.vo'

function hasDuplicates(elements: ICreateFieldSchema[]): boolean {
const names = elements.map((e) => e.name)
Expand Down Expand Up @@ -46,6 +47,11 @@ export class TableSchema extends ValueObject<Field[]> {
this.props.push(field)
}

public get defaultFieldsOrder(): ViewFieldsOrder {
const order = this.props.map((v) => v.name.value)
return ViewFieldsOrder.fromArray(order)
}

public createField(input: ICreateFieldSchema): TableCompositeSpecificaiton {
// FIXME: check name
const field = FieldFactory.create(input)
Expand Down
1 change: 1 addition & 0 deletions packages/core/view/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './view'
export * from './view-field-options'
export * from './view-fields-order.vo'
export * from './view-name.vo'
export * from './view.schema'
export * from './view.type'
Expand Down
10 changes: 10 additions & 0 deletions packages/core/view/view-fields-order.vo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ViewFieldsOrder } from '.'

describe('ViewFieldsOrder', () => {
test('#move()', () => {
const origin = new ViewFieldsOrder(['1', '2', '3'])
const moved = origin.swap('1', '3')
expect(moved.order).toEqual(['3', '2', '1'])
expect(moved === origin).toBe(false)
})
})
20 changes: 20 additions & 0 deletions packages/core/view/view-fields-order.vo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ValueObject } from '@egodb/domain'

export class ViewFieldsOrder extends ValueObject<string[]> {
public get order() {
return this.props
}

static fromArray(ids: string[]): ViewFieldsOrder {
return new this(ids)
}

public swap(from: string, to: string): ViewFieldsOrder {
const copied = [...this.order]
const fromIndex = copied.findIndex((v) => v === from)
const toIndex = copied.findIndex((v) => v === to)

;[copied[toIndex], copied[fromIndex]] = [copied[fromIndex], copied[toIndex]]
return ViewFieldsOrder.fromArray(copied)
}
}
2 changes: 1 addition & 1 deletion packages/core/view/view.schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from 'zod'
import { fieldNameSchema } from '../field'
import { rootFilter } from '../filter'
import { rootFilter } from '../filter/filter'
import { fieldHiddenSchema, fieldWidthSchema, viewFieldOption } from './view-field-options'
import { viewNameSchema } from './view-name.vo'

Expand Down
11 changes: 10 additions & 1 deletion packages/core/view/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import type { Option } from 'oxide.ts'
import { None } from 'oxide.ts'
import type { IFilterOrGroupList, IRootFilter } from '../filter'
import { RootFilter } from '../filter'
import { WithFieldVisibility, WithFieldWidth } from '../specifications'
import type { TableCompositeSpecificaiton } from '../specifications/interface'
import { WithFieldVisibility, WithFieldWidth } from '../specifications/table-view-field-option.specification'
import type { IViewFieldOption } from './view-field-options'
import { ViewFieldOptions } from './view-field-options'
import type { ViewFieldsOrder } from './view-fields-order.vo'
import { ViewName } from './view-name.vo'
import { createViewInput_internal } from './view.schema'
import type { ICreateViewInput_internal, IView, IViewDisplayType } from './view.type'
Expand Down Expand Up @@ -36,6 +37,14 @@ export class View extends ValueObject<IView> {
return this.props.fieldOptions
}

public get fieldsOrder() {
return this.props.fieldsOrder
}

public set fieldsOrder(v: ViewFieldsOrder | undefined) {
this.props.fieldsOrder = v
}

public getFieldOption(fieldName: string): IViewFieldOption {
return this.fieldOptions.getOption(fieldName)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core/view/view.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { z } from 'zod'
import type { IRootFilter } from '../filter'
import type { RootFilter } from '../filter/root-filter'
import type { IViewFieldOption, ViewFieldOptions } from './view-field-options'
import type { ViewFieldsOrder } from './view-fields-order.vo'
import type { ViewName } from './view-name.vo'
import type { createViewInput_internal, viewDisplayType } from './view.schema'

Expand All @@ -10,6 +11,7 @@ export interface IView {
displayType: IViewDisplayType
filter?: RootFilter
fieldOptions: ViewFieldOptions
fieldsOrder?: ViewFieldsOrder
}

export interface IQueryView {
Expand Down
35 changes: 24 additions & 11 deletions packages/eslint-config/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
module.exports = {
"env": {
"browser": true,
"amd": true,
"node": true,
env: {
browser: true,
amd: true,
node: true,
},
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', "prettier"],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:import/recommended',
'plugin:import/typescript',
],
ignorePatterns: ['node_modules', 'dist'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc'],
settings: {
'import/resolver': {
typescript: true,
node: true,
},
},
rules: {
"no-console": "warn",
"react/jsx-key": "off",
"tsdoc/syntax": "warn",
"@typescript-eslint/consistent-type-imports": "warn",
"@typescript-eslint/prefer-optional-chain": "warn",
'no-console': 'warn',
'react/jsx-key': 'off',
'tsdoc/syntax': 'warn',
'import/no-cycle': 'warn',
'@typescript-eslint/consistent-type-imports': 'warn',
'@typescript-eslint/prefer-optional-chain': 'warn',
},
};
}
2 changes: 2 additions & 0 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.46.1",
"@typescript-eslint/parser": "^5.46.1",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-tsdoc": "^0.2.17",
"typescript": "^4.9.4"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
WithTableName,
WithTableSchema,
WithTableViews,
WithViewFieldsOrder,
} from '@egodb/core'
import type { TableInMemory } from './table'
import { TableInMemoryMapper } from './table-in-memory.mapper'
Expand Down Expand Up @@ -67,4 +68,7 @@ export class TableInMemoryMutationVisitor implements ITableSpecVisitor {
}
}
}
fieldsOrder(s: WithViewFieldsOrder): void {
throw new Error('Method not implemented.')
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
WithFieldWidth,
WithTableId,
WithTableName,
WithViewFieldsOrder,
} from '@egodb/core'
import type { Result } from 'oxide.ts'
import { Err, Ok } from 'oxide.ts'
Expand Down Expand Up @@ -83,4 +84,7 @@ export class TableInMemoryQueryVisitor implements ITableSpecVisitor {
return !!view.fieldOptions?.[s.fieldName]?.hidden === s.hidden
}
}
fieldsOrder(s: WithViewFieldsOrder): void {
throw new Error('Method not implemented.')
}
}
Loading

0 comments on commit e8c04e2

Please sign in to comment.