Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(grid): support highlight the cell which match the keywords #203

Closed
wants to merge 6 commits into from
Closed
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
1 change: 1 addition & 0 deletions packages/grid/src/constants/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const Colors = {
gray80: '#fafafa',
headSelectedBgColor: '#EAEFFA',
itemActiveBgColor: '#6698ff1a',
itemMatchBgColor: '#ff9f731a',
waring: '#ffcd5d',
success: '#73d897'
};
3 changes: 2 additions & 1 deletion packages/grid/src/core/types/ai-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ export interface AITable {
fields: WritableSignal<AITableFields>;
context?: RendererContext;
selection: WritableSignal<AITableSelection>;
matchedCells: WritableSignal<string[]>; // [`${recordId}-${fieldId}`]
recordsMap: Signal<{ [kay: string]: AITableRecord }>;
fieldsMap: Signal<{ [kay: string]: AITableField }>;
recordsWillHidden: WritableSignal<string[]>;
recordsWillMove: WritableSignal<string[]>
recordsWillMove: WritableSignal<string[]>;
}

export type AIPlugin = (aiTable: AITable) => AITable;
Expand Down
1 change: 1 addition & 0 deletions packages/grid/src/core/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function createAITable(records: WritableSignal<AITableRecords>, fields: W
selectedFields: new Map(),
selectedCells: new Map()
}),
matchedCells: signal([]),
recordsMap: computed(() => {
return records().reduce(
(object, item) => {
Expand Down
5 changes: 5 additions & 0 deletions packages/grid/src/grid-base.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { AI_TABLE_GRID_FIELD_SERVICE_MAP, AITableGridFieldService } from './serv
import { AITableGridSelectionService } from './services/selection.service';
import { AIFieldConfig, AITableFieldMenuItem, AITableContextMenuItem, AITableReferences } from './types';
import { AITableFieldPropertyEditor } from './components';
import { AITableGridMatchCellService } from './services/match-cell.service';

@Component({
selector: 'ai-table-grid-base',
Expand All @@ -59,6 +60,8 @@ export class AITableGridBase implements OnInit {

aiBuildRenderDataFn = input<(aiTable: AITable) => AITableValue>();

aiKeywords = input<string>();

AITableFieldType = AITableFieldType;

AITableSelectOptionStyle = AITableSelectOptionStyle;
Expand Down Expand Up @@ -99,6 +102,7 @@ export class AITableGridBase implements OnInit {
protected aiTableGridFieldService = inject(AITableGridFieldService);
protected aiTableGridEventService = inject(AITableGridEventService);
protected aiTableGridSelectionService = inject(AITableGridSelectionService);
protected aiTableGridMatchCellService = inject(AITableGridMatchCellService);

ngOnInit(): void {
this.initAITable();
Expand All @@ -116,6 +120,7 @@ export class AITableGridBase implements OnInit {
initService() {
this.aiTableGridEventService.initialize(this.aiTable, this.aiFieldConfig()?.fieldPropertyEditor);
this.aiTableGridSelectionService.initialize(this.aiTable);
this.aiTableGridMatchCellService.initialize(this.aiTable);
this.aiTableGridEventService.registerEvents(this.elementRef.nativeElement);
this.aiTableGridFieldService.initAIFieldConfig(this.aiFieldConfig());
AI_TABLE_GRID_FIELD_SERVICE_MAP.set(this.aiTable, this.aiTableGridFieldService);
Expand Down
11 changes: 10 additions & 1 deletion packages/grid/src/grid.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { AITableGridSelectionService } from './services/selection.service';
import { AITableMouseDownType, AITableRendererConfig, ScrollActionOptions } from './types';
import { buildGridLinearRows, getColumnIndicesMap, getDetailByTargetName, handleMouseStyle, isWindows } from './utils';
import { getMousePosition } from './utils/position';
import { AITableGridMatchCellService } from './services/match-cell.service';

@Component({
selector: 'ai-table-grid',
Expand All @@ -53,7 +54,7 @@ import { getMousePosition } from './utils/position';
class: 'ai-table-grid'
},
imports: [AITableRenderer],
providers: [AITableGridEventService, AITableGridFieldService, AITableGridSelectionService]
providers: [AITableGridEventService, AITableGridFieldService, AITableGridSelectionService, AITableGridMatchCellService]
})
export class AITableGrid extends AITableGridBase implements OnInit, OnDestroy {
private viewContainerRef = inject(ViewContainerRef);
Expand Down Expand Up @@ -147,6 +148,14 @@ export class AITableGrid extends AITableGridBase implements OnInit, OnDestroy {
this.toggleHoverCellEditor();
}
});
effect(
() => {
if (this.aiKeywords()) {
this.aiTableGridMatchCellService.findMatchedCells(this.aiKeywords()!, this.aiReferences());
}
},
{ allowSignalWrites: true }
);
}

override ngOnInit(): void {
Expand Down
12 changes: 10 additions & 2 deletions packages/grid/src/renderer/creations/create-cells.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
DEFAULT_FONT_STYLE
} from '../../constants';
import { AITable, AITableQueries, RendererContext } from '../../core';
import { AITableAreaType, AITableCellsDrawerConfig, AITableRender, AITableRowType } from '../../types';
import { AITableCellsDrawerConfig, AITableRender, AITableRowType } from '../../types';
import { getCellHorizontalPosition, transformCellValue } from '../../utils';
import { addRowLayout } from '../drawers/add-row-layout-drawer';
import { cellDrawer } from '../drawers/cell-drawer';
Expand Down Expand Up @@ -85,7 +85,15 @@ export const createCells = (config: AITableCellsDrawerConfig) => {
const isSiblingActiveCell = recordId === activeCell?.recordId && fieldId !== activeCell?.fieldId;
const isActiveCell = recordId === activeCell?.recordId;

if (isCheckedRow || isSelected || isSiblingActiveCell) {
let matchedCellsMap: { [key: string]: boolean } = {};
aiTable.matchedCells().forEach((key) => {
matchedCellsMap[key] = true;
});
const isMatchedCell = matchedCellsMap[`${recordId}-${fieldId}`];

if (isMatchedCell) {
background = colors.itemMatchBgColor;
} else if (isCheckedRow || isSelected || isSiblingActiveCell) {
background = colors.itemActiveBgColor;
} else if (isHoverRow && !isActiveCell) {
background = colors.gray80;
Expand Down
1 change: 1 addition & 0 deletions packages/grid/src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './event.service';
export * from './field.service';
export * from './selection.service';
export * from './match-cell.service';
39 changes: 39 additions & 0 deletions packages/grid/src/services/match-cell.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Injectable } from '@angular/core';
import { AITable, AITableField, AITableQueries } from '../core';
import { AITableReferences } from '../types';
import { transformCellValue } from '../utils';
import { ViewOperationMap } from '@ai-table/state';

@Injectable()
export class AITableGridMatchCellService {
aiTable!: AITable;

initialize(aiTable: AITable) {
this.aiTable = aiTable;
}

findMatchedCells(keywords: string, references: AITableReferences) {
let matchedCells: string[] = [];
this.aiTable.records().forEach((record) => {
this.aiTable.fields().forEach((field) => {
if (this.isCellMatchKeywords(this.aiTable, field, record._id, keywords, references)) {
matchedCells.push(`${record._id}-${field._id}`);
}
});
});
this.aiTable.matchedCells.set([...matchedCells]);
}

private isCellMatchKeywords(aiTable: AITable, field: AITableField, recordId: string, keywords: string, references: AITableReferences) {
const cellValue = AITableQueries.getFieldValue(aiTable, [recordId, field._id]);
const transformValue = transformCellValue(aiTable, field, cellValue);
const fieldMethod = ViewOperationMap[field.type];
let cellFullText: string[] = fieldMethod.cellFullText(transformValue, field, references);

try {
return keywords && cellFullText.length && cellFullText.some((item) => item.toLowerCase().includes(keywords.toLowerCase()));
} catch (error) {
return false;
}
}
}
6 changes: 5 additions & 1 deletion packages/state/src/types/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export enum AITableFilterOperation {
notContain = 'not_contain'
}

export type ViewSettings = AITableFilterConditions & AITableSortOptions;
export type ViewSettings = AITableSearchOptions & AITableFilterConditions & AITableSortOptions;

export interface AITableView {
_id: string;
Expand Down Expand Up @@ -78,4 +78,8 @@ export interface AITableSortOptions {
}[];
}

export interface AITableSearchOptions {
keywords?: string;
}

export type AITableViews = AITableView[];
10 changes: 9 additions & 1 deletion packages/state/src/utils/field/model/field.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AITableField, FieldValue } from '@ai-table/grid';
import { AITableField, AITableReferences, FieldValue } from '@ai-table/grid';
import { AITableFilterCondition, AITableFilterOperation } from '../../../types';
import { isEmpty } from '../../common';
import { isEqual } from 'lodash';
Expand Down Expand Up @@ -72,4 +72,12 @@ export abstract class Field {
// test pinyin sort
return str1 === str2 ? 0 : zhIntlCollator ? zhIntlCollator.compare(str1, str2) : str1.localeCompare(str2, 'zh-CN') > 0 ? 1 : -1;
}

cellFullText(transformValue: any, field: AITableField, references?: AITableReferences): string[] {
let fullText: string[] = [];
if (!isEmpty(transformValue)) {
fullText.push(String(transformValue));
}
return fullText;
}
}
7 changes: 4 additions & 3 deletions packages/state/src/utils/field/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DateField } from './date';
import { NumberField } from './number';
import { RateField } from './rate';
import { LinkField } from './link';
import { MemberField } from './member';

export const ViewOperationMap: Record<AITableFieldType, Field> = {
[AITableFieldType.text]: new TextField(),
Expand All @@ -17,8 +18,8 @@ export const ViewOperationMap: Record<AITableFieldType, Field> = {
[AITableFieldType.number]: new NumberField(),
[AITableFieldType.rate]: new RateField(),
[AITableFieldType.link]: new LinkField(),
[AITableFieldType.member]: new SelectField(),
[AITableFieldType.member]: new MemberField(),
[AITableFieldType.progress]: new NumberField(),
[AITableFieldType.createdBy]: new SelectField(),
[AITableFieldType.updatedBy]: new SelectField()
[AITableFieldType.createdBy]: new MemberField(),
[AITableFieldType.updatedBy]: new MemberField()
};
8 changes: 8 additions & 0 deletions packages/state/src/utils/field/model/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,12 @@ export class LinkField extends Field {
}
return '';
}

override cellFullText(transformValue: LinkFieldValue, field: AITableField): string[] {
let fullText: string[] = [];
if (!isEmpty(transformValue?.text)) {
fullText.push(transformValue.text);
}
return fullText;
}
}
20 changes: 20 additions & 0 deletions packages/state/src/utils/field/model/member.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AITableField, AITableReferences } from '@ai-table/grid';
import { SelectField } from './select';

export class MemberField extends SelectField {
override cellFullText(transformValue: string[], field: AITableField, references?: AITableReferences): string[] {
let fullText: string[] = [];
if (transformValue?.length && references) {
for (let index = 0; index < transformValue.length; index++) {
const userInfo = references?.members[transformValue[index]];
if (!userInfo) {
continue;
}
if (userInfo.display_name) {
fullText.push(userInfo.display_name);
}
}
}
return fullText;
}
}
9 changes: 9 additions & 0 deletions packages/state/src/utils/field/model/progress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { AITableField } from '@ai-table/grid';
import { NumberField } from './number';

export class ProgressField extends NumberField {
override cellFullText(transformValue: number, field: AITableField): string[] {
const fullText = `${transformValue}%`;
return [fullText];
}
}
17 changes: 15 additions & 2 deletions packages/state/src/utils/field/model/select.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isEmpty } from '../../common';
import { AITableFilterCondition, AITableFilterOperation } from '../../../types';
import { Field } from './field';
import { AITableField, AITableSelectOption, SelectFieldValue, SelectSettings } from '@ai-table/grid';
import { AITableField, AITableSelectField, AITableSelectOption, SelectFieldValue, SelectSettings } from '@ai-table/grid';
import { Id } from 'ngx-tethys/types';

export class SelectField extends Field {
Expand Down Expand Up @@ -39,12 +39,25 @@ export class SelectField extends Field {
}

findOptionById(field: AITableField, id: Id): AITableSelectOption | null {
return (field.settings as SelectSettings).options.find(option => option._id === id) || null;
return (field.settings as SelectSettings).options.find((option) => option._id === id) || null;
}

arrayValueToString(cellValues: string[] | null): string | null {
return cellValues && cellValues.length ? cellValues.join(', ') : null;
}

override cellFullText(transformValue: string[], field: AITableField): string[] {
let cellText: string[] = [];
if (transformValue && Array.isArray(transformValue) && transformValue.length) {
transformValue.forEach((optionId) => {
const item = (field as AITableSelectField).settings?.options?.find((option) => option._id === optionId);
if (item?.text) {
cellText.push(item.text);
}
});
}
return cellText;
}
}

function hasIntersect<T extends number | string>(array1: T[], array2: T[]) {
Expand Down
1 change: 1 addition & 0 deletions src/app/component/common/content/content.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
[(aiRecords)]="tableService.records"
[(aiFields)]="tableService.fields"
[aiFieldConfig]="aiFieldConfig()"
[aiKeywords]="tableService.keywords()"
[aiPlugins]="plugins"
[aiReferences]="references()"
(aiAddRecord)="addRecord($event)"
Expand Down
4 changes: 4 additions & 0 deletions src/app/service/table.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export class TableService {
};
});

keywords = computed(() => {
return this.activeView().settings?.keywords;
});

initData(views: AITableView[]) {
this.views = signal(views);
}
Expand Down
Loading