Skip to content
Open
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
2,921 changes: 1,599 additions & 1,322 deletions source/.yalc/@flowda-projects/flowda-gateway-trpc-server/index.bundle.d.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"version": "0.0.1",
"type": "commonjs",
"types": "./index.bundle.d.ts",
"yalcSig": "ba6cc3e0ad39c83ff6775f1adec25c84"
"yalcSig": "b11e25c1e1875898aad4a91f54c4b789"
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ba6cc3e0ad39c83ff6775f1adec25c84
b11e25c1e1875898aad4a91f54c4b789
3 changes: 3 additions & 0 deletions source/libs/design/.storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ import '../src/reset.css'
import 'ag-grid-community/styles/ag-grid.css'
import 'ag-grid-community/styles/ag-theme-quartz.css'
import '@elastic/eui/dist/eui_theme_light.css'

import { toJS } from 'mobx'
window.toJS = toJS
12 changes: 11 additions & 1 deletion source/libs/design/src/designModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
GridModelSymbol,
LoginModelSymbol,
NewFormModelSymbol,
postResourceDataInputSchema,
PreviewModelSymbol,
putResourceDataInputSchema,
removeResourceDataInputSchema,
ResourceUISchema,
TaskFormModelSymbol,
ThemeModelSymbol,
Expand Down Expand Up @@ -42,7 +44,15 @@ export class NotImplementedApiService implements ApiService {
}

putResourceData(input: z.infer<typeof putResourceDataInputSchema>): Promise<unknown> {
throw new Error('handlers.getResourceSchema is not implemented')
throw new Error('handlers.putResourceData is not implemented')
}

postResourceData(input: z.infer<typeof postResourceDataInputSchema>): Promise<unknown> {
throw new Error('handlers.postResourceData is not implemented')
}

removeResourceData(input: z.infer<typeof removeResourceDataInputSchema>): Promise<unknown> {
throw new Error('handlers.removeResourceData is not implemented')
}
}

Expand Down
8 changes: 5 additions & 3 deletions source/libs/design/src/grid/grid-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react'
import { GridProps } from './grid'
import { EUI_DARK_COLORS, EUI_LIGHT_COLORS } from '../theme/theme.model'
import { FEuiButtonEmpty } from '../eui'
import { getBtnTextColor } from '../theme/theme-utils'

@observer
export class GridToolbar extends React.Component<GridProps> {
Expand All @@ -14,7 +15,7 @@ export class GridToolbar extends React.Component<GridProps> {
<FEuiButtonEmpty
x-color={this.props.model.theme.colorMode === 'light' ? EUI_LIGHT_COLORS.text : EUI_DARK_COLORS.text}
onClick={() => {
this.props.model.refresh()
void this.props.model.refresh(true)
}}
iconType="refresh"
size="xs"
Expand Down Expand Up @@ -56,11 +57,12 @@ export class GridToolbar extends React.Component<GridProps> {
</Box>
<Box mx={1}>
<FEuiButtonEmpty
x-color={this.props.model.theme.colorMode === 'light' ? EUI_LIGHT_COLORS.text : EUI_DARK_COLORS.text}
onClick={() => {}}
x-color={getBtnTextColor(this.props.model.theme.colorMode, this.props.model.selectedRowPk == null)}
onClick={() => this.props.model.remove()}
iconType="trash"
size="xs"
color="text"
isDisabled={this.props.model.selectedRowPk == null}
>
Delete
</FEuiButtonEmpty>
Expand Down
53 changes: 52 additions & 1 deletion source/libs/design/src/grid/grid-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getReferenceDisplay, shortenDatetime } from './grid-utils'
import 'reflect-metadata'
import { getReferenceDisplay, shortenDatetime, smartMergeFilterModel } from './grid-utils'

describe('grid utils', () => {
it('reference display', () => {
Expand Down Expand Up @@ -60,4 +61,54 @@ describe('grid utils', () => {
const ret2 = shortenDatetime('2023-04-22 11:33:55')
expect(ret2).toMatchInlineSnapshot(`"23-04-22 11:33"`)
})
it('smartMergeFilterModel params.filter cover uri filterModel', () => {
const uri =
'file:///TenantResourceSchema?filterModel[displayName][filterType]=text&filterModel[displayName][type]=contains&filterModel[displayName][filter]=c'
const ret = smartMergeFilterModel(uri, {}, false)
expect(ret).toMatchInlineSnapshot(`{}`)
})

it('smartMergeFilterModel params.filter cover uri filterModel case 2', () => {
const uri =
'file:///TenantResourceSchema?filterModel[displayName][filterType]=text&filterModel[displayName][type]=contains&filterModel[displayName][filter]=c'
const filterModel = {
displayName: {
filterType: 'text',
type: 'contains',
filter: 'ccc',
},
} as const
const ret = smartMergeFilterModel(uri, filterModel, false)
expect(ret).toMatchInlineSnapshot(`
{
"displayName": {
"filter": "ccc",
"filterType": "text",
"type": "contains",
},
}
`)
})

it('smartMergeFilterModel params.filter use uri', () => {
const uri =
'file:///TenantResourceSchema?filterModel[displayName][filterType]=text&filterModel[displayName][type]=contains&filterModel[displayName][filter]=c'
const filterModel = {
displayName: {
filterType: 'text',
type: 'contains',
filter: 'ccc',
},
} as const
const ret = smartMergeFilterModel(uri, filterModel, true)
expect(ret).toMatchInlineSnapshot(`
{
"displayName": {
"filter": "c",
"filterType": "text",
"type": "contains",
},
}
`)
})
})
22 changes: 21 additions & 1 deletion source/libs/design/src/grid/grid-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ReferenceKeySchema } from '@flowda/types'
import { ReferenceKeySchema, agFilterSchema } from '@flowda/types'
import { z } from 'zod'
// https://github.com/iamkun/dayjs/issues/475#issuecomment-460660048
import dayjs from 'dayjs'
import { getUriFilterModel } from '../uri/uri-utils'
import { pickBy } from 'lodash'

export function getReferenceDisplay(refCol: z.infer<typeof ReferenceKeySchema>, val: any) {
return `${refCol.reference_type} ${refCol.display_name}#${val[refCol.primary_key]}`
Expand All @@ -18,3 +20,21 @@ export function shortenDatetime(text: string) {
}
return dayjs(text).format(shortFormat)
}

export function smartMergeFilterModel(
uri: string,
filterModel: z.infer<typeof agFilterSchema>,
isFirstGetRows: boolean,
) {
const uriFilterModel = getUriFilterModel(uri)
// 首次和 onCurrentEditorChanged 直接用 uri
if (isFirstGetRows) return uriFilterModel
// 其他情况下 params.filterModel 合并

const picked = pickBy(uriFilterModel, (value, key) => key in filterModel)

return {
...picked,
...filterModel,
}
}
105 changes: 80 additions & 25 deletions source/libs/design/src/grid/grid.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,39 @@ import {
ThemeModelSymbol,
} from '@flowda/types'
import { z } from 'zod'
import {
createNewFormUri,
getUriSchemaName,
isUriAsKeyLikeEqual,
mergeUriFilterModel,
updateUriFilterModel,
} from '../uri/uri-utils'
import { createNewFormUri, getUriSchemaName, isUriAsKeyLikeEqual, updateUriFilterModel } from '../uri/uri-utils'
import { URI } from '@theia/core'
import axios from 'axios'
import { ThemeModel } from '../theme/theme.model'
import { smartMergeFilterModel } from './grid-utils'
import { makeObservable, observable } from 'mobx'
import { RowSelectedEvent } from 'ag-grid-community/dist/lib/events'

@injectable()
export class GridModel implements ManageableModel {
@observable selectedRowPk: string | number | null = null

columnDefs: z.infer<typeof ColumnUISchema>[] = []
schemaName: string | null = null
schema: ResourceUI | null = null
isNotEmpty = false
gridApi: GridApi | null = null
_gridApi: GridApi | null = null

/**
* 是否是首次请求数据
* 首次请求数据 smartMergeFilterModel 则只返回 uri
* 否则根据 params.filterModel 进行合并
*/
private _isFirstGetRows = false

get gridApi() {
if (this._gridApi == null) throw new Error('gridApi is null')
return this._gridApi
}

get isFirstGetRows() {
return this._isFirstGetRows
}

/**
* 等待 setRef 也就是 widget render 然后才能调用 this.ref.setColDefs
Expand Down Expand Up @@ -69,26 +84,38 @@ export class GridModel implements ManageableModel {
@inject(ApiServiceSymbol) public apiService: ApiService,
@optional() @multiInject(CustomResourceSymbol) private customResources: ICustomResource[],
) {
makeObservable(this)
this.refPromise = new Promise<boolean>(resolve => {
this.refResolve = resolve
})
}

setGridApi(api: GridApi<unknown>) {
this._gridApi = api
}

getUri() {
if (!this._uri) throw new Error('uri is null')
return this._uri.toString(true)
}

getTenant() {
if (!this._uri) throw new Error('uri is null')
return this._uri.authority
}

setUri(uri: string | URI) {
if (typeof uri === 'string') uri = new URI(uri)
this._uri = uri
}

refresh() {
if (this.gridApi == null) throw new Error('gridApi is null')
async refresh(fromToolbar = false) {
if (this.gridApi.isDestroyed()) {
throw new Error(`gridApi isDestroyed: ${this._uri}`)
} else {
if (fromToolbar) {
this.gridApi.showLoadingOverlay()
}
this.gridApi.refreshInfiniteCache()
}
}
Expand Down Expand Up @@ -133,8 +160,10 @@ export class GridModel implements ManageableModel {

async onCurrentEditorChanged() {
const uri = new URI(this.getUri())
const schemaName = `${uri.authority}.${getUriSchemaName(uri)}`
const schemaName = getUriSchemaName(uri)
this._isFirstGetRows = true
await this.getCol(schemaName)
this._isFirstGetRows = false
}

async getCol(schemaName: string) {
Expand All @@ -144,26 +173,27 @@ export class GridModel implements ManageableModel {
}
if (this.columnDefs.length > 0) {
console.warn(`columns is not empty, only refresh data, ${schemaName}`)
this.refresh()
await this.refresh()
} else {
const schemaRes = await this.apiService.getResourceSchema({
tenant: this.getTenant(),
schemaName: this.schemaName,
})
this.schema = schemaRes
this.schemaReadyResolve!(true)
if (schemaRes.columns.length > 0) {
this.columnDefs = schemaRes.columns
if (this.refPromise == null)
throw new Error('refPromise is null, call resetRefPromise in getOrCreateGridModel()')
await this.refPromise
// @ts-expect-error invoke react ref
if (this.ref == null || typeof this.ref['setColDefs'] !== 'function') {
throw new Error('ref is null')
}
// @ts-expect-error invoke react ref
this.ref['setColDefs']()
}
this.schema = schemaRes
}

if (this.refPromise == null) throw new Error('refPromise is null, call resetRefPromise in getOrCreateGridModel()')
await this.refPromise
// @ts-expect-error invoke react ref
if (this.ref == null || typeof this.ref['setColDefs'] !== 'function') {
throw new Error('ref is null')
}
// @ts-expect-error invoke react ref
this.ref['setColDefs']()
}

isOpenTask(colName: string) {
Expand Down Expand Up @@ -201,12 +231,18 @@ export class GridModel implements ManageableModel {
pagination: { total: res.data.length },
}
} else {
params.filterModel = mergeUriFilterModel(this.getUri(), params.filterModel)
if (this.gridApi == null) throw new Error('gridApi is null')
// todo: 这块逻辑需要优化,核心逻辑不变,但是步骤繁琐了 又是合并 又是 set 又是更新 uri 可以简化的
// 因为 test pass 现在这个中间状态也是 work 的,所以有 test refactor 可以随时停下来
// 核心逻辑不变 是 grid filterModel 和 uri 有一个合并策略
// 然后更新到 uri
params.filterModel = smartMergeFilterModel(this.getUri(), params.filterModel, this.isFirstGetRows)
this.gridApi.setFilterModel(params.filterModel)
const uri = updateUriFilterModel(this.getUri(), params.filterModel)
this.setUri(uri)
const dataRet = await this.apiService.getResourceData(params)
const dataRet = await this.apiService.getResourceData({
...params,
tenant: this.getTenant(),
})
const parseRet = getResourceDataOutputInnerSchema.safeParse(dataRet)
if (parseRet.success) {
return parseRet.data
Expand All @@ -220,6 +256,7 @@ export class GridModel implements ManageableModel {

async putData(id: number, updatedValue: unknown) {
await this.apiService.putResourceData({
tenant: this.getTenant(),
schemaName: this.schemaName!,
id: id,
updatedValue: updatedValue,
Expand Down Expand Up @@ -289,4 +326,22 @@ export class GridModel implements ManageableModel {
this.handlers.onClickNew(uri)
}
}

onRowSelected(event: RowSelectedEvent<{ id: string | number }>) {
if (event.node.isSelected()) {
const data = event.node.data
if (data == null) throw new Error(`event.node.data is null, rowIndex: ${event.rowIndex}`)
this.selectedRowPk = data.id
} else {
this.selectedRowPk = null
}
}

remove() {
this.apiService.removeResourceData({
tenant: this.getTenant(),
schemaName: this.schemaName!,
id: this.selectedRowPk,
})
}
}
Loading