mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-20 15:57:06 +08:00
feat(editor): add created-time and created-by property for database block (#12156)
close: BS-3431 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added "Created By" property type and cell renderer, displaying creator's avatar and name in database blocks. - Introduced "Created Time" property type and cell renderer, showing formatted creation timestamps. - **Improvements** - Enhanced table and Kanban views with improved column and row movement, hiding, and statistics capabilities. - Streamlined property and row management with unified object handling and reactive signals for better performance and reliability. - Improved avatar display logic to handle removed or unnamed users gracefully. - Refactored property and row APIs to consolidate access patterns and support reactive updates. - Updated icon retrieval and reactive value access for improved UI responsiveness in database and Kanban cells. - Consolidated property and cell access methods to use "get or create" patterns ensuring consistent data availability. - Added locking mechanism to stabilize computed signals during locked states. - Modularized table and Kanban column and row abstractions for better encapsulation and maintainability. - **Bug Fixes** - Corrected row and column deletion, movement, and selection behaviors across table and Kanban views. - Fixed issues with property and row referencing, ensuring consistent handling of identifiers and objects. - Removed debugging logs and fixed method calls to align with updated APIs. - **Style** - Updated and simplified table column header styles for a cleaner appearance. - **Chores** - Added `@emotion/css` dependency for styling support. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -134,7 +134,7 @@ export class DataViewPropertiesSettingView extends SignalWatcher(
|
||||
accessor view!: SingleView;
|
||||
|
||||
items$ = computed(() => {
|
||||
return this.view.propertiesWithoutFilter$.value;
|
||||
return this.view.propertiesRaw$.value.map(property => property.id);
|
||||
});
|
||||
|
||||
renderProperty = (property: Property) => {
|
||||
@@ -171,8 +171,7 @@ export class DataViewPropertiesSettingView extends SignalWatcher(
|
||||
const activeIndex = properties.findIndex(id => id === activeId);
|
||||
const overIndex = properties.findIndex(id => id === over.id);
|
||||
|
||||
this.view.propertyMove(
|
||||
activeId,
|
||||
this.view.propertyGetOrCreate(activeId).move(
|
||||
activeIndex > overIndex
|
||||
? {
|
||||
before: true,
|
||||
@@ -198,9 +197,7 @@ export class DataViewPropertiesSettingView extends SignalWatcher(
|
||||
});
|
||||
|
||||
private itemsGroup() {
|
||||
return this.view.propertiesWithoutFilter$.value.map(id =>
|
||||
this.view.propertyGet(id)
|
||||
);
|
||||
return this.view.propertiesRaw$.value;
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
@@ -246,14 +243,12 @@ export const popPropertiesSetting = (
|
||||
text: 'Properties',
|
||||
onBack: props.onBack,
|
||||
postfix: () => {
|
||||
const items = props.view.propertiesWithoutFilter$.value.map(id =>
|
||||
props.view.propertyGet(id)
|
||||
);
|
||||
const isAllShowed = items.every(v => !v.hide$.value);
|
||||
const items = props.view.propertiesRaw$.value;
|
||||
const isAllShowed = items.every(property => !property.hide$.value);
|
||||
const clickChangeAll = () => {
|
||||
props.view.propertiesWithoutFilter$.value.forEach(id => {
|
||||
if (props.view.propertyCanHide(id)) {
|
||||
props.view.propertyHideSet(id, isAllShowed);
|
||||
items.forEach(property => {
|
||||
if (property.hideCanSet) {
|
||||
property.hideSet(isAllShowed);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -58,9 +58,7 @@ export const typeConfig = (property: Property) => {
|
||||
return menu.action({
|
||||
isSelected: config.type === property.type$.value,
|
||||
name: config.config.name,
|
||||
prefix: renderUniLit(
|
||||
property.view.propertyIconGet(config.type)
|
||||
),
|
||||
prefix: renderUniLit(config.renderer.icon),
|
||||
select: () => {
|
||||
if (property.type$.value === config.type) {
|
||||
return;
|
||||
|
||||
@@ -120,7 +120,7 @@ export class RecordDetail extends SignalWatcher(
|
||||
items: this.view.propertyMetas$.value.map(meta => {
|
||||
return menu.action({
|
||||
name: meta.config.name,
|
||||
prefix: renderUniLit(this.view.propertyIconGet(meta.type)),
|
||||
prefix: renderUniLit(meta.renderer.icon),
|
||||
select: () => {
|
||||
this.view.propertyAdd('end', meta.type);
|
||||
},
|
||||
@@ -136,9 +136,7 @@ export class RecordDetail extends SignalWatcher(
|
||||
accessor view!: SingleView;
|
||||
|
||||
properties$ = computed(() => {
|
||||
return this.view.detailProperties$.value.map(id =>
|
||||
this.view.propertyGet(id)
|
||||
);
|
||||
return this.view.detailProperties$.value;
|
||||
});
|
||||
|
||||
selection = new DetailSelection(this);
|
||||
@@ -184,16 +182,20 @@ export class RecordDetail extends SignalWatcher(
|
||||
this.dataset.widgetId = 'affine-detail-widget';
|
||||
}
|
||||
|
||||
row$ = computed(() => {
|
||||
return this.view.rowGetOrCreate(this.rowId);
|
||||
});
|
||||
|
||||
hasNext() {
|
||||
return this.view.rowNextGet(this.rowId) != null;
|
||||
return this.row$.value.next$.value != null;
|
||||
}
|
||||
|
||||
hasPrev() {
|
||||
return this.view.rowPrevGet(this.rowId) != null;
|
||||
return this.row$.value.prev$.value != null;
|
||||
}
|
||||
|
||||
nextRow() {
|
||||
const rowId = this.view.rowNextGet(this.rowId);
|
||||
const rowId = this.row$.value.next$.value?.rowId;
|
||||
if (rowId == null) {
|
||||
return;
|
||||
}
|
||||
@@ -202,7 +204,7 @@ export class RecordDetail extends SignalWatcher(
|
||||
}
|
||||
|
||||
prevRow() {
|
||||
const rowId = this.view.rowPrevGet(this.rowId);
|
||||
const rowId = this.row$.value.prev$.value?.rowId;
|
||||
if (rowId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -140,15 +140,16 @@ export class RecordField extends SignalWatcher(
|
||||
${MoveLeftIcon()}
|
||||
</div>`,
|
||||
hide: () =>
|
||||
properties.findIndex(v => v === this.column.id) === 0,
|
||||
properties.findIndex(
|
||||
property => property.id === this.column.id
|
||||
) === 0,
|
||||
select: () => {
|
||||
const index = properties.findIndex(v => v === this.column.id);
|
||||
const targetId = properties[index - 1];
|
||||
if (!targetId) {
|
||||
const prev = this.column.prev$.value;
|
||||
if (!prev) {
|
||||
return;
|
||||
}
|
||||
this.view.propertyMove(this.column.id, {
|
||||
id: targetId,
|
||||
this.column.move({
|
||||
id: prev.id,
|
||||
before: true,
|
||||
});
|
||||
},
|
||||
@@ -161,16 +162,17 @@ export class RecordField extends SignalWatcher(
|
||||
${MoveRightIcon()}
|
||||
</div>`,
|
||||
hide: () =>
|
||||
properties.findIndex(v => v === this.column.id) ===
|
||||
properties.findIndex(
|
||||
property => property.id === this.column.id
|
||||
) ===
|
||||
properties.length - 1,
|
||||
select: () => {
|
||||
const index = properties.findIndex(v => v === this.column.id);
|
||||
const targetId = properties[index + 1];
|
||||
if (!targetId) {
|
||||
const next = this.column.next$.value;
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
this.view.propertyMove(this.column.id, {
|
||||
id: targetId,
|
||||
this.column.move({
|
||||
id: next.id,
|
||||
before: false,
|
||||
});
|
||||
},
|
||||
@@ -211,7 +213,7 @@ export class RecordField extends SignalWatcher(
|
||||
accessor rowId!: string;
|
||||
|
||||
cell$ = computed(() => {
|
||||
return this.column.cellGet(this.rowId);
|
||||
return this.column.cellGetOrCreate(this.rowId);
|
||||
});
|
||||
|
||||
changeEditing = (editing: boolean) => {
|
||||
|
||||
@@ -37,7 +37,11 @@ const GroupTitleMobile = (
|
||||
value: groupData.value,
|
||||
data: groupData.property.data$.value,
|
||||
updateData: groupData.manager.updateData,
|
||||
updateValue: value => groupData.manager.updateValue(groupData.rows, value),
|
||||
updateValue: value =>
|
||||
groupData.manager.updateValue(
|
||||
groupData.rows.map(row => row.rowId),
|
||||
value
|
||||
),
|
||||
readonly: ops.readonly,
|
||||
};
|
||||
|
||||
@@ -140,7 +144,11 @@ export const GroupTitle = (
|
||||
value: groupData.value,
|
||||
data: groupData.property.data$.value,
|
||||
updateData: groupData.manager.updateData,
|
||||
updateValue: value => groupData.manager.updateValue(groupData.rows, value),
|
||||
updateValue: value =>
|
||||
groupData.manager.updateValue(
|
||||
groupData.rows.map(row => row.rowId),
|
||||
value
|
||||
),
|
||||
readonly: ops.readonly,
|
||||
};
|
||||
|
||||
|
||||
@@ -189,22 +189,25 @@ export const selectGroupByProperty = (
|
||||
},
|
||||
items: [
|
||||
menu.group({
|
||||
items: view.propertiesWithoutFilter$.value
|
||||
.filter(id => {
|
||||
if (view.propertyGet(id).type$.value === 'title') {
|
||||
items: view.propertiesRaw$.value
|
||||
.filter(property => {
|
||||
if (property.type$.value === 'title') {
|
||||
return false;
|
||||
}
|
||||
return !!groupByMatcher.match(view.propertyGet(id).dataType$.value);
|
||||
const dataType = property.dataType$.value;
|
||||
if (!dataType) {
|
||||
return false;
|
||||
}
|
||||
return !!groupByMatcher.match(dataType);
|
||||
})
|
||||
.map<MenuConfig>(id => {
|
||||
const property = view.propertyGet(id);
|
||||
.map<MenuConfig>(property => {
|
||||
return menu.action({
|
||||
name: property.name$.value,
|
||||
isSelected: group.property$.value?.id === id,
|
||||
isSelected: group.property$.value?.id === property.id,
|
||||
prefix: html` <uni-lit .uni="${property.icon}"></uni-lit>`,
|
||||
select: () => {
|
||||
group.changeGroup(id);
|
||||
ops?.onSelect?.(id);
|
||||
group.changeGroup(property.id);
|
||||
ops?.onSelect?.(property.id);
|
||||
},
|
||||
});
|
||||
}),
|
||||
@@ -254,7 +257,7 @@ export const popGroupSetting = (
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const icon = view.propertyIconGet(type);
|
||||
const icon = groupProperty.icon;
|
||||
const menuHandler = popMenu(target, {
|
||||
options: {
|
||||
title: {
|
||||
|
||||
@@ -7,11 +7,12 @@ import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
import type { GroupBy, GroupProperty } from '../common/types.js';
|
||||
import type { TypeInstance } from '../logical/type.js';
|
||||
import { createTraitKey } from '../traits/key.js';
|
||||
import { computedLock } from '../utils/lock.js';
|
||||
import type { Property } from '../view-manager/property.js';
|
||||
import type { Row } from '../view-manager/row.js';
|
||||
import type { SingleView } from '../view-manager/single-view.js';
|
||||
import { defaultGroupBy } from './default.js';
|
||||
import { groupByMatcher } from './matcher.js';
|
||||
|
||||
export type GroupData = {
|
||||
manager: GroupTrait;
|
||||
property: Property;
|
||||
@@ -19,12 +20,10 @@ export type GroupData = {
|
||||
name: string;
|
||||
type: TypeInstance;
|
||||
value: unknown;
|
||||
rows: string[];
|
||||
rows: Row[];
|
||||
};
|
||||
|
||||
export class GroupTrait {
|
||||
private preDataList: GroupData[] | undefined;
|
||||
|
||||
config$ = computed(() => {
|
||||
const groupBy = this.groupBy$.value;
|
||||
if (!groupBy) {
|
||||
@@ -42,7 +41,7 @@ export class GroupTrait {
|
||||
if (!groupBy) {
|
||||
return;
|
||||
}
|
||||
return this.view.propertyGet(groupBy.columnId);
|
||||
return this.view.propertyGetOrCreate(groupBy.columnId);
|
||||
});
|
||||
|
||||
staticGroupDataMap$ = computed<
|
||||
@@ -81,8 +80,9 @@ export class GroupTrait {
|
||||
const groupMap: Record<string, GroupData> = Object.fromEntries(
|
||||
Object.entries(staticGroupMap).map(([k, v]) => [k, { ...v, rows: [] }])
|
||||
);
|
||||
this.view.rows$.value.forEach(id => {
|
||||
const value = this.view.cellJsonValueGet(id, groupBy.columnId);
|
||||
this.view.rows$.value.forEach(row => {
|
||||
const value = this.view.cellGetOrCreate(row.rowId, groupBy.columnId)
|
||||
.jsonValue$.value;
|
||||
const keys = config.valuesGroup(value, tType);
|
||||
keys.forEach(({ key, value }) => {
|
||||
if (!groupMap[key]) {
|
||||
@@ -96,40 +96,36 @@ export class GroupTrait {
|
||||
type: tType,
|
||||
};
|
||||
}
|
||||
groupMap[key].rows.push(id);
|
||||
groupMap[key].rows.push(row);
|
||||
});
|
||||
});
|
||||
return groupMap;
|
||||
});
|
||||
|
||||
private readonly _groupsDataList$ = computed(() => {
|
||||
const groupMap = this.groupDataMap$.value;
|
||||
if (!groupMap) {
|
||||
return;
|
||||
}
|
||||
const sortedGroup = this.ops.sortGroup(Object.keys(groupMap));
|
||||
sortedGroup.forEach(key => {
|
||||
if (!groupMap[key]) return;
|
||||
groupMap[key].rows = this.ops.sortRow(key, groupMap[key].rows);
|
||||
});
|
||||
return (this.preDataList = sortedGroup
|
||||
.map(key => groupMap[key])
|
||||
.filter((v): v is GroupData => v != null));
|
||||
});
|
||||
|
||||
groupsDataList$ = computed(() => {
|
||||
if (this.view.isLocked$.value) {
|
||||
return this.preDataList;
|
||||
}
|
||||
return (this.preDataList = this._groupsDataList$.value);
|
||||
});
|
||||
groupsDataList$ = computedLock(
|
||||
computed(() => {
|
||||
const groupMap = this.groupDataMap$.value;
|
||||
if (!groupMap) {
|
||||
return;
|
||||
}
|
||||
const sortedGroup = this.ops.sortGroup(Object.keys(groupMap));
|
||||
sortedGroup.forEach(key => {
|
||||
if (!groupMap[key]) return;
|
||||
groupMap[key].rows = this.ops.sortRow(key, groupMap[key].rows);
|
||||
});
|
||||
return sortedGroup
|
||||
.map(key => groupMap[key])
|
||||
.filter((v): v is GroupData => v != null);
|
||||
}),
|
||||
this.view.isLocked$
|
||||
);
|
||||
|
||||
updateData = (data: NonNullable<unknown>) => {
|
||||
const propertyId = this.propertyId;
|
||||
if (!propertyId) {
|
||||
return;
|
||||
}
|
||||
this.view.propertyDataSet(propertyId, data);
|
||||
this.view.propertyGetOrCreate(propertyId).dataUpdate(() => data);
|
||||
};
|
||||
|
||||
get addGroup() {
|
||||
@@ -137,7 +133,7 @@ export class GroupTrait {
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
return this.view.propertyMetaGet(type)?.config.addGroup;
|
||||
return this.view.manager.dataSource.propertyMetaGet(type)?.config.addGroup;
|
||||
}
|
||||
|
||||
get propertyId() {
|
||||
@@ -150,7 +146,7 @@ export class GroupTrait {
|
||||
private readonly ops: {
|
||||
groupBySet: (groupBy: GroupBy | undefined) => void;
|
||||
sortGroup: (keys: string[]) => string[];
|
||||
sortRow: (groupKey: string, rowIds: string[]) => string[];
|
||||
sortRow: (groupKey: string, rows: Row[]) => Row[];
|
||||
changeGroupSort: (keys: string[]) => void;
|
||||
changeRowSort: (
|
||||
groupKeys: string[],
|
||||
@@ -169,8 +165,11 @@ export class GroupTrait {
|
||||
const addTo = this.config$.value?.addToGroup ?? (value => value);
|
||||
const v = groupMap[key]?.value;
|
||||
if (v != null) {
|
||||
const newValue = addTo(v, this.view.cellJsonValueGet(rowId, propertyId));
|
||||
this.view.cellJsonValueSet(rowId, propertyId, newValue);
|
||||
const newValue = addTo(
|
||||
v,
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValue$.value
|
||||
);
|
||||
this.view.cellGetOrCreate(rowId, propertyId).valueSet(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,8 +190,10 @@ export class GroupTrait {
|
||||
this.ops.groupBySet(undefined);
|
||||
return;
|
||||
}
|
||||
const column = this.view.propertyGet(columnId);
|
||||
const propertyMeta = this.view.propertyMetaGet(column.type$.value);
|
||||
const column = this.view.propertyGetOrCreate(columnId);
|
||||
const propertyMeta = this.view.manager.dataSource.propertyMetaGet(
|
||||
column.type$.value
|
||||
);
|
||||
if (propertyMeta) {
|
||||
this.ops.groupBySet(
|
||||
defaultGroupBy(
|
||||
@@ -238,15 +239,18 @@ export class GroupTrait {
|
||||
if (group) {
|
||||
newValue = remove(
|
||||
group.value,
|
||||
this.view.cellJsonValueGet(rowId, propertyId)
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValue$.value
|
||||
);
|
||||
}
|
||||
const addTo = this.config$.value?.addToGroup ?? (value => value);
|
||||
newValue = addTo(groupMap[toGroupKey]?.value ?? null, newValue);
|
||||
this.view.cellJsonValueSet(rowId, propertyId, newValue);
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValueSet(newValue);
|
||||
}
|
||||
const rows = groupMap[toGroupKey]?.rows.filter(id => id !== rowId) ?? [];
|
||||
const index = insertPositionToIndex(position, rows, id => id);
|
||||
const rows =
|
||||
groupMap[toGroupKey]?.rows
|
||||
.filter(row => row.rowId !== rowId)
|
||||
.map(row => row.rowId) ?? [];
|
||||
const index = insertPositionToIndex(position, rows, row => row);
|
||||
rows.splice(index, 0, rowId);
|
||||
this.changeCardSort(toGroupKey, rows);
|
||||
}
|
||||
@@ -278,9 +282,9 @@ export class GroupTrait {
|
||||
const remove = this.config$.value?.removeFromGroup ?? (() => undefined);
|
||||
const newValue = remove(
|
||||
groupMap[key]?.value ?? null,
|
||||
this.view.cellJsonValueGet(rowId, propertyId)
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValue$.value
|
||||
);
|
||||
this.view.cellValueSet(rowId, propertyId, newValue);
|
||||
this.view.cellGetOrCreate(rowId, propertyId).valueSet(newValue);
|
||||
}
|
||||
|
||||
updateValue(rows: string[], value: unknown) {
|
||||
@@ -288,8 +292,8 @@ export class GroupTrait {
|
||||
if (!propertyId) {
|
||||
return;
|
||||
}
|
||||
rows.forEach(id => {
|
||||
this.view.cellJsonValueSet(id, propertyId, value);
|
||||
rows.forEach(rowId => {
|
||||
this.view.cellGetOrCreate(rowId, propertyId).jsonValueSet(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export type PropertyConfig<Data, RawValue = unknown, JsonValue = unknown> = {
|
||||
};
|
||||
fixed?: {
|
||||
defaultData: Data;
|
||||
defaultOrder?: string;
|
||||
defaultOrder?: 'start' | 'end';
|
||||
defaultShow?: boolean;
|
||||
};
|
||||
minWidth?: number;
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { DataTypeOf } from '../logical/data-type.js';
|
||||
import { t } from '../logical/index.js';
|
||||
import type { TypeInstance } from '../logical/type.js';
|
||||
import { typeSystem } from '../logical/type-system.js';
|
||||
import type { SingleView } from '../view-manager/index.js';
|
||||
import type { Row, SingleView } from '../view-manager/index.js';
|
||||
import type { Sort } from './types.js';
|
||||
|
||||
export const Compare = {
|
||||
@@ -16,14 +16,14 @@ const evalRef = (
|
||||
view: SingleView,
|
||||
ref: VariableRef
|
||||
):
|
||||
| ((row: string) => {
|
||||
| ((row: Row) => {
|
||||
value: unknown;
|
||||
ttype?: TypeInstance;
|
||||
})
|
||||
| undefined => {
|
||||
const ttype = view.propertyDataTypeGet(ref.name);
|
||||
const ttype = view.propertyGetOrCreate(ref.name).dataType$.value;
|
||||
return row => ({
|
||||
value: view.cellJsonValueGet(row, ref.name),
|
||||
value: view.cellGetOrCreate(row.rowId, ref.name).jsonValue$.value,
|
||||
ttype,
|
||||
});
|
||||
};
|
||||
@@ -153,7 +153,7 @@ const compare = (type: TypeInstance, a: unknown, b: unknown): CompareType => {
|
||||
export const evalSort = (
|
||||
sort: Sort,
|
||||
view: SingleView
|
||||
): ((rowA: string, rowB: string) => number) | undefined => {
|
||||
): ((rowA: Row, rowB: Row) => number) | undefined => {
|
||||
if (sort.sortBy.length) {
|
||||
const sortBy = sort.sortBy.map(sort => {
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
|
||||
import { createTraitKey } from '../traits/key.js';
|
||||
import type { SingleView } from '../view-manager/index.js';
|
||||
import type { Row, SingleView } from '../view-manager/index.js';
|
||||
import { evalSort } from './eval.js';
|
||||
import type { Sort, SortBy } from './types.js';
|
||||
|
||||
@@ -16,7 +16,7 @@ export class SortManager {
|
||||
});
|
||||
};
|
||||
|
||||
sort = (rows: string[]) => {
|
||||
sort = (rows: Row[]) => {
|
||||
if (!this.sort$.value) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
15
blocksuite/affine/data-view/src/core/utils/lock.ts
Normal file
15
blocksuite/affine/data-view/src/core/utils/lock.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
|
||||
export const computedLock = <T>(
|
||||
value$: ReadonlySignal<T>,
|
||||
lock$: ReadonlySignal<boolean>
|
||||
): ReadonlySignal<T> => {
|
||||
let previousValue: T;
|
||||
return computed(() => {
|
||||
if (lock$.value) {
|
||||
return previousValue ?? value$.value;
|
||||
}
|
||||
previousValue = value$.value;
|
||||
return previousValue;
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
|
||||
import { fromJson } from '../property/utils.js';
|
||||
import type { Property } from './property.js';
|
||||
import type { Row } from './row.js';
|
||||
import type { SingleView } from './single-view.js';
|
||||
@@ -9,18 +10,19 @@ export interface Cell<
|
||||
JsonValue = unknown,
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> {
|
||||
readonly rowId: string;
|
||||
readonly view: SingleView;
|
||||
readonly rowId: string;
|
||||
readonly row: Row;
|
||||
readonly propertyId: string;
|
||||
readonly property: Property<RawValue, JsonValue, Data>;
|
||||
readonly isEmpty$: ReadonlySignal<boolean>;
|
||||
readonly stringValue$: ReadonlySignal<string>;
|
||||
readonly jsonValue$: ReadonlySignal<JsonValue>;
|
||||
|
||||
readonly isEmpty$: ReadonlySignal<boolean>;
|
||||
readonly value$: ReadonlySignal<RawValue | undefined>;
|
||||
readonly jsonValue$: ReadonlySignal<JsonValue | undefined>;
|
||||
readonly stringValue$: ReadonlySignal<string | undefined>;
|
||||
|
||||
valueSet(value: RawValue | undefined): void;
|
||||
jsonValueSet(value: JsonValue | undefined): void;
|
||||
}
|
||||
|
||||
export class CellBase<
|
||||
@@ -29,10 +31,12 @@ export class CellBase<
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> implements Cell<RawValue, JsonValue, Data>
|
||||
{
|
||||
get dataSource() {
|
||||
return this.view.manager.dataSource;
|
||||
}
|
||||
|
||||
meta$ = computed(() => {
|
||||
return this.view.manager.dataSource.propertyMetaGet(
|
||||
this.property.type$.value
|
||||
);
|
||||
return this.dataSource.propertyMetaGet(this.property.type$.value);
|
||||
});
|
||||
|
||||
value$ = computed(() => {
|
||||
@@ -51,20 +55,37 @@ export class CellBase<
|
||||
);
|
||||
});
|
||||
|
||||
jsonValue$: ReadonlySignal<JsonValue> = computed(() => {
|
||||
return this.view.cellJsonValueGet(this.rowId, this.propertyId) as JsonValue;
|
||||
jsonValue$: ReadonlySignal<JsonValue | undefined> = computed(() => {
|
||||
const toJson = this.property.meta$.value?.config.rawValue.toJson;
|
||||
if (!toJson) {
|
||||
return undefined;
|
||||
}
|
||||
return (
|
||||
(toJson({
|
||||
value: this.value$.value,
|
||||
data: this.property.data$.value,
|
||||
dataSource: this.dataSource,
|
||||
}) as JsonValue) ?? undefined
|
||||
);
|
||||
});
|
||||
|
||||
property$ = computed(() => {
|
||||
return this.view.propertyGet(this.propertyId) as Property<
|
||||
return this.view.propertyGetOrCreate(this.propertyId) as Property<
|
||||
RawValue,
|
||||
JsonValue,
|
||||
Data
|
||||
>;
|
||||
});
|
||||
|
||||
stringValue$: ReadonlySignal<string> = computed(() => {
|
||||
return this.view.cellStringValueGet(this.rowId, this.propertyId)!;
|
||||
stringValue$: ReadonlySignal<string | undefined> = computed(() => {
|
||||
const toString = this.property.meta$.value?.config.rawValue.toString;
|
||||
if (!toString) {
|
||||
return;
|
||||
}
|
||||
return toString({
|
||||
value: this.value$.value,
|
||||
data: this.property.data$.value,
|
||||
});
|
||||
});
|
||||
|
||||
get property(): Property<RawValue, JsonValue, Data> {
|
||||
@@ -72,7 +93,7 @@ export class CellBase<
|
||||
}
|
||||
|
||||
get row(): Row {
|
||||
return this.view.rowGet(this.rowId);
|
||||
return this.view.rowGetOrCreate(this.rowId);
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -88,4 +109,17 @@ export class CellBase<
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
jsonValueSet(value: JsonValue | undefined): void {
|
||||
const meta = this.property.meta$.value;
|
||||
if (!meta) {
|
||||
return;
|
||||
}
|
||||
const rawValue = fromJson(meta.config, {
|
||||
value: value,
|
||||
data: this.property.data$.value,
|
||||
dataSource: this.view.manager.dataSource,
|
||||
});
|
||||
this.dataSource.cellValueChange(this.rowId, this.propertyId, rawValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { UniComponent } from '@blocksuite/affine-shared/types';
|
||||
import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
|
||||
import type { TypeInstance } from '../logical/type.js';
|
||||
import type { CellRenderer } from '../property/index.js';
|
||||
import type { CellRenderer, PropertyMetaConfig } from '../property/index.js';
|
||||
import type { PropertyDataUpdater } from '../types.js';
|
||||
import type { Cell } from './cell.js';
|
||||
import type { SingleView } from './single-view.js';
|
||||
@@ -13,14 +14,17 @@ export interface Property<
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> {
|
||||
readonly id: string;
|
||||
readonly index: number;
|
||||
readonly index$: ReadonlySignal<number | undefined>;
|
||||
readonly view: SingleView;
|
||||
readonly isFirst: boolean;
|
||||
readonly isLast: boolean;
|
||||
readonly isFirst$: ReadonlySignal<boolean>;
|
||||
readonly isLast$: ReadonlySignal<boolean>;
|
||||
readonly next$: ReadonlySignal<Property | undefined>;
|
||||
readonly prev$: ReadonlySignal<Property | undefined>;
|
||||
readonly readonly$: ReadonlySignal<boolean>;
|
||||
readonly renderer$: ReadonlySignal<CellRenderer | undefined>;
|
||||
readonly cells$: ReadonlySignal<Cell[]>;
|
||||
readonly dataType$: ReadonlySignal<TypeInstance>;
|
||||
readonly dataType$: ReadonlySignal<TypeInstance | undefined>;
|
||||
readonly meta$: ReadonlySignal<PropertyMetaConfig | undefined>;
|
||||
readonly icon?: UniComponent;
|
||||
|
||||
readonly delete?: () => void;
|
||||
@@ -29,7 +33,7 @@ export interface Property<
|
||||
readonly duplicate?: () => void;
|
||||
get canDuplicate(): boolean;
|
||||
|
||||
cellGet(rowId: string): Cell<RawValue, JsonValue, Data>;
|
||||
cellGetOrCreate(rowId: string): Cell<RawValue, JsonValue, Data>;
|
||||
|
||||
readonly data$: ReadonlySignal<Data>;
|
||||
dataUpdate(updater: PropertyDataUpdater<Data>): void;
|
||||
@@ -48,8 +52,16 @@ export interface Property<
|
||||
valueGet(rowId: string): RawValue | undefined;
|
||||
valueSet(rowId: string, value: RawValue | undefined): void;
|
||||
|
||||
stringValueGet(rowId: string): string;
|
||||
stringValueGet(rowId: string): string | undefined;
|
||||
valueSetFromString(rowId: string, value: string): void;
|
||||
parseValueFromString(value: string):
|
||||
| {
|
||||
value: unknown;
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
move(position: InsertToPosition): void;
|
||||
}
|
||||
|
||||
export abstract class PropertyBase<
|
||||
@@ -58,125 +70,195 @@ export abstract class PropertyBase<
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> implements Property<RawValue, JsonValue, Data>
|
||||
{
|
||||
meta$ = computed(() => {
|
||||
return this.dataSource.propertyMetaGet(this.type$.value);
|
||||
});
|
||||
|
||||
cells$ = computed(() => {
|
||||
return this.view.rows$.value.map(id => this.cellGet(id));
|
||||
return this.view.rows$.value.map(row =>
|
||||
this.view.cellGetOrCreate(row.rowId, this.id)
|
||||
);
|
||||
});
|
||||
|
||||
data$ = computed(() => {
|
||||
return this.view.propertyDataGet(this.id) as Data;
|
||||
return this.dataSource.propertyDataGet(this.id) as Data;
|
||||
});
|
||||
|
||||
dataType$ = computed(() => {
|
||||
return this.view.propertyDataTypeGet(this.id)!;
|
||||
const type = this.type$.value;
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const meta = this.dataSource.propertyMetaGet(type);
|
||||
if (!meta) {
|
||||
return;
|
||||
}
|
||||
return meta.config.jsonValue.type({
|
||||
data: this.data$.value,
|
||||
dataSource: this.dataSource,
|
||||
});
|
||||
});
|
||||
|
||||
hide$ = computed(() => {
|
||||
return this.view.propertyHideGet(this.id);
|
||||
});
|
||||
abstract hide$: ReadonlySignal<boolean>;
|
||||
|
||||
name$ = computed(() => {
|
||||
return this.view.propertyNameGet(this.id);
|
||||
return this.dataSource.propertyNameGet(this.id);
|
||||
});
|
||||
|
||||
readonly$ = computed(() => {
|
||||
return this.view.readonly$.value || this.view.propertyReadonlyGet(this.id);
|
||||
return (
|
||||
this.view.readonly$.value || this.dataSource.propertyReadonlyGet(this.id)
|
||||
);
|
||||
});
|
||||
|
||||
type$ = computed(() => {
|
||||
return this.view.propertyTypeGet(this.id)!;
|
||||
return this.dataSource.propertyTypeGet(this.id)!;
|
||||
});
|
||||
|
||||
renderer$ = computed(() => {
|
||||
return this.view.propertyMetaGet(this.type$.value)?.renderer.cellRenderer;
|
||||
return this.meta$.value?.renderer.cellRenderer;
|
||||
});
|
||||
|
||||
get delete(): (() => void) | undefined {
|
||||
return () => this.view.propertyDelete(this.id);
|
||||
return () => this.dataSource.propertyDelete(this.id);
|
||||
}
|
||||
|
||||
get duplicate(): (() => void) | undefined {
|
||||
return () => this.view.propertyDuplicate(this.id);
|
||||
return () => {
|
||||
const id = this.dataSource.propertyDuplicate(this.id);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
const property = this.view.propertyGetOrCreate(id);
|
||||
property.move({
|
||||
before: false,
|
||||
id: this.id,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
abstract move(position: InsertToPosition): void;
|
||||
|
||||
get icon(): UniComponent | undefined {
|
||||
if (!this.type$.value) return undefined;
|
||||
return this.view.propertyIconGet(this.type$.value);
|
||||
return this.dataSource.propertyMetaGet(this.type$.value)?.renderer.icon;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.propertyId;
|
||||
}
|
||||
|
||||
get index(): number {
|
||||
return this.view.propertyIndexGet(this.id);
|
||||
}
|
||||
index$ = computed(() => {
|
||||
const index = this.view.propertyIds$.value.indexOf(this.id);
|
||||
return index >= 0 ? index : undefined;
|
||||
});
|
||||
|
||||
get isFirst(): boolean {
|
||||
return this.view.propertyIndexGet(this.id) === 0;
|
||||
}
|
||||
isFirst$ = computed(() => {
|
||||
return this.index$.value === 0;
|
||||
});
|
||||
|
||||
get isLast(): boolean {
|
||||
return (
|
||||
this.view.propertyIndexGet(this.id) ===
|
||||
this.view.properties$.value.length - 1
|
||||
);
|
||||
}
|
||||
isLast$ = computed(() => {
|
||||
return this.index$.value === this.view.propertyIds$.value.length - 1;
|
||||
});
|
||||
|
||||
next$ = computed(() => {
|
||||
const properties = this.view.properties$.value;
|
||||
if (this.index$.value == null) {
|
||||
return;
|
||||
}
|
||||
return properties[this.index$.value + 1];
|
||||
});
|
||||
|
||||
prev$ = computed(() => {
|
||||
const properties = this.view.properties$.value;
|
||||
if (this.index$.value == null) {
|
||||
return;
|
||||
}
|
||||
return properties[this.index$.value - 1];
|
||||
});
|
||||
|
||||
get typeSet(): undefined | ((type: string) => void) {
|
||||
return type => this.view.propertyTypeSet(this.id, type);
|
||||
return type => this.dataSource.propertyTypeSet(this.id, type);
|
||||
}
|
||||
|
||||
constructor(
|
||||
public view: SingleView,
|
||||
public propertyId: string
|
||||
) {}
|
||||
protected get dataSource() {
|
||||
return this.view.manager.dataSource;
|
||||
}
|
||||
get canDelete(): boolean {
|
||||
return this.view.propertyCanDelete(this.id);
|
||||
return this.dataSource.propertyCanDelete(this.id);
|
||||
}
|
||||
get canDuplicate(): boolean {
|
||||
return this.view.propertyCanDuplicate(this.id);
|
||||
return this.dataSource.propertyCanDuplicate(this.id);
|
||||
}
|
||||
get typeCanSet(): boolean {
|
||||
return this.view.propertyTypeCanSet(this.id);
|
||||
return this.dataSource.propertyTypeCanSet(this.id);
|
||||
}
|
||||
get hideCanSet(): boolean {
|
||||
return this.view.propertyCanHide(this.id);
|
||||
return this.type$.value !== 'title';
|
||||
}
|
||||
|
||||
cellGet(rowId: string): Cell<RawValue, JsonValue, Data> {
|
||||
return this.view.cellGet(rowId, this.id) as Cell<RawValue, JsonValue, Data>;
|
||||
cellGetOrCreate(rowId: string): Cell<RawValue, JsonValue, Data> {
|
||||
return this.view.cellGetOrCreate(rowId, this.id) as Cell<
|
||||
RawValue,
|
||||
JsonValue,
|
||||
Data
|
||||
>;
|
||||
}
|
||||
|
||||
dataUpdate(updater: PropertyDataUpdater<Data>): void {
|
||||
const data = this.data$.value;
|
||||
this.view.propertyDataSet(this.id, {
|
||||
this.dataSource.propertyDataSet(this.id, {
|
||||
...data,
|
||||
...updater(data),
|
||||
});
|
||||
}
|
||||
|
||||
hideSet(hide: boolean): void {
|
||||
this.view.propertyHideSet(this.id, hide);
|
||||
}
|
||||
abstract hideSet(hide: boolean): void;
|
||||
|
||||
nameSet(name: string): void {
|
||||
this.view.propertyNameSet(this.id, name);
|
||||
this.dataSource.propertyNameSet(this.id, name);
|
||||
}
|
||||
|
||||
stringValueGet(rowId: string): string {
|
||||
return this.cellGet(rowId).stringValue$.value;
|
||||
stringValueGet(rowId: string): string | undefined {
|
||||
return this.cellGetOrCreate(rowId).stringValue$.value;
|
||||
}
|
||||
|
||||
valueGet(rowId: string): RawValue | undefined {
|
||||
return this.cellGet(rowId).value$.value;
|
||||
return this.cellGetOrCreate(rowId).value$.value;
|
||||
}
|
||||
|
||||
valueSet(rowId: string, value: RawValue | undefined): void {
|
||||
return this.cellGet(rowId).valueSet(value);
|
||||
return this.cellGetOrCreate(rowId).valueSet(value);
|
||||
}
|
||||
|
||||
parseValueFromString(value: string):
|
||||
| {
|
||||
value: unknown;
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
| undefined {
|
||||
const type = this.type$.value;
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const fromString =
|
||||
this.dataSource.propertyMetaGet(type)?.config.rawValue.fromString;
|
||||
if (!fromString) {
|
||||
return;
|
||||
}
|
||||
return fromString({
|
||||
value,
|
||||
data: this.data$.value,
|
||||
dataSource: this.dataSource,
|
||||
});
|
||||
}
|
||||
|
||||
valueSetFromString(rowId: string, value: string): void {
|
||||
const data = this.view.propertyParseValueFromString(this.id, value);
|
||||
const data = this.parseValueFromString(value);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
|
||||
import { type Cell, CellBase } from './cell.js';
|
||||
@@ -6,17 +7,59 @@ import type { SingleView } from './single-view.js';
|
||||
export interface Row {
|
||||
readonly cells$: ReadonlySignal<Cell[]>;
|
||||
readonly rowId: string;
|
||||
|
||||
index$: ReadonlySignal<number | undefined>;
|
||||
|
||||
prev$: ReadonlySignal<Row | undefined>;
|
||||
next$: ReadonlySignal<Row | undefined>;
|
||||
|
||||
delete(): void;
|
||||
|
||||
move(position: InsertToPosition): void;
|
||||
}
|
||||
|
||||
export class RowBase implements Row {
|
||||
cells$ = computed(() => {
|
||||
return this.singleView.propertyIds$.value.map(propertyId => {
|
||||
return new CellBase(this.singleView, propertyId, this.rowId);
|
||||
return this.singleView.propertiesRaw$.value.map(property => {
|
||||
return new CellBase(this.singleView, property.id, this.rowId);
|
||||
});
|
||||
});
|
||||
|
||||
index$ = computed(() => {
|
||||
const idx = this.singleView.rowIds$.value.indexOf(this.rowId);
|
||||
return idx >= 0 ? idx : undefined;
|
||||
});
|
||||
|
||||
prev$ = computed(() => {
|
||||
const index = this.index$.value;
|
||||
if (index == null) {
|
||||
return;
|
||||
}
|
||||
return this.singleView.rows$.value[index - 1];
|
||||
});
|
||||
|
||||
next$ = computed(() => {
|
||||
const index = this.index$.value;
|
||||
if (index == null) {
|
||||
return;
|
||||
}
|
||||
return this.singleView.rows$.value[index + 1];
|
||||
});
|
||||
|
||||
constructor(
|
||||
readonly singleView: SingleView,
|
||||
readonly rowId: string
|
||||
) {}
|
||||
|
||||
get dataSource() {
|
||||
return this.singleView.manager.dataSource;
|
||||
}
|
||||
|
||||
delete(): void {
|
||||
this.dataSource.rowDelete([this.rowId]);
|
||||
}
|
||||
|
||||
move(position: InsertToPosition): void {
|
||||
this.dataSource.rowMove(this.rowId, position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import type { UniComponent } from '@blocksuite/affine-shared/types';
|
||||
import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||
import { computed, type ReadonlySignal, signal } from '@preact/signals-core';
|
||||
|
||||
import type { DataViewContextKey } from '../data-source/context.js';
|
||||
import type { Variable } from '../expression/types.js';
|
||||
import type { TypeInstance } from '../logical/type.js';
|
||||
import type { PropertyMetaConfig } from '../property/property-config.js';
|
||||
import { fromJson } from '../property/utils';
|
||||
import type { TraitKey } from '../traits/key.js';
|
||||
import type { DatabaseFlags } from '../types.js';
|
||||
import { computedLock } from '../utils/lock.js';
|
||||
import type { DataViewDataType, ViewMeta } from '../view/data-view.js';
|
||||
import { type Cell, CellBase } from './cell.js';
|
||||
import type { Property } from './property.js';
|
||||
@@ -36,49 +34,25 @@ export interface SingleView {
|
||||
|
||||
nameSet(name: string): void;
|
||||
|
||||
readonly propertyIds$: ReadonlySignal<string[]>;
|
||||
readonly propertiesWithoutFilter$: ReadonlySignal<string[]>;
|
||||
readonly propertiesRaw$: ReadonlySignal<Property[]>;
|
||||
readonly propertyMap$: ReadonlySignal<Record<string, Property>>;
|
||||
readonly properties$: ReadonlySignal<Property[]>;
|
||||
readonly detailProperties$: ReadonlySignal<string[]>;
|
||||
readonly rows$: ReadonlySignal<string[]>;
|
||||
readonly propertyIds$: ReadonlySignal<string[]>;
|
||||
readonly detailProperties$: ReadonlySignal<Property[]>;
|
||||
readonly rowsRaw$: ReadonlySignal<Row[]>;
|
||||
readonly rows$: ReadonlySignal<Row[]>;
|
||||
readonly rowIds$: ReadonlySignal<string[]>;
|
||||
|
||||
readonly vars$: ReadonlySignal<Variable[]>;
|
||||
|
||||
readonly featureFlags$: ReadonlySignal<DatabaseFlags>;
|
||||
|
||||
cellValueGet(rowId: string, propertyId: string): unknown;
|
||||
|
||||
cellValueSet(rowId: string, propertyId: string, value: unknown): void;
|
||||
|
||||
cellJsonValueGet(rowId: string, propertyId: string): unknown | null;
|
||||
|
||||
cellJsonValueSet(rowId: string, propertyId: string, value: unknown): void;
|
||||
|
||||
cellStringValueGet(rowId: string, propertyId: string): string | undefined;
|
||||
|
||||
cellGet(rowId: string, propertyId: string): Cell;
|
||||
|
||||
propertyParseValueFromString(
|
||||
propertyId: string,
|
||||
value: string
|
||||
):
|
||||
| {
|
||||
value: unknown;
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
| undefined;
|
||||
propertyGetOrCreate(propertyId: string): Property;
|
||||
rowGetOrCreate(rowId: string): Row;
|
||||
cellGetOrCreate(rowId: string, propertyId: string): Cell;
|
||||
|
||||
rowAdd(insertPosition: InsertToPosition): string;
|
||||
|
||||
rowDelete(ids: string[]): void;
|
||||
|
||||
rowMove(rowId: string, position: InsertToPosition): void;
|
||||
|
||||
rowGet(rowId: string): Row;
|
||||
|
||||
rowPrevGet(rowId: string): string | undefined;
|
||||
|
||||
rowNextGet(rowId: string): string | undefined;
|
||||
rowsDelete(rows: string[]): void;
|
||||
|
||||
readonly propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
|
||||
|
||||
@@ -87,54 +61,6 @@ export interface SingleView {
|
||||
type?: string
|
||||
): string | undefined;
|
||||
|
||||
propertyDelete(propertyId: string): void;
|
||||
|
||||
propertyCanDelete(propertyId: string): boolean;
|
||||
|
||||
propertyDuplicate(propertyId: string): void;
|
||||
|
||||
propertyCanDuplicate(propertyId: string): boolean;
|
||||
|
||||
propertyGet(propertyId: string): Property;
|
||||
|
||||
propertyMetaGet(type: string): PropertyMetaConfig | undefined;
|
||||
|
||||
propertyPreGet(propertyId: string): Property | undefined;
|
||||
|
||||
propertyNextGet(propertyId: string): Property | undefined;
|
||||
|
||||
propertyNameGet(propertyId: string): string;
|
||||
|
||||
propertyNameSet(propertyId: string, name: string): void;
|
||||
|
||||
propertyTypeGet(propertyId: string): string | undefined;
|
||||
|
||||
propertyTypeSet(propertyId: string, type: string): void;
|
||||
|
||||
propertyTypeCanSet(propertyId: string): boolean;
|
||||
|
||||
propertyHideGet(propertyId: string): boolean;
|
||||
|
||||
propertyHideSet(propertyId: string, hide: boolean): void;
|
||||
|
||||
propertyCanHide(propertyId: string): boolean;
|
||||
|
||||
propertyDataGet(propertyId: string): Record<string, unknown>;
|
||||
|
||||
propertyDataSet(propertyId: string, data: Record<string, unknown>): void;
|
||||
|
||||
propertyDataTypeGet(propertyId: string): TypeInstance | undefined;
|
||||
|
||||
propertyIndexGet(propertyId: string): number;
|
||||
|
||||
propertyIdGetByIndex(index: number): string | undefined;
|
||||
|
||||
propertyReadonlyGet(propertyId: string): boolean;
|
||||
|
||||
propertyMove(propertyId: string, position: InsertToPosition): void;
|
||||
|
||||
propertyIconGet(type: string): UniComponent | undefined;
|
||||
|
||||
contextGet<T>(key: DataViewContextKey<T>): T;
|
||||
|
||||
traitGet<T>(key: TraitKey<T>): T | undefined;
|
||||
@@ -158,7 +84,7 @@ export abstract class SingleViewBase<
|
||||
return this.dataSource.viewDataGet(this.id) as ViewData | undefined;
|
||||
});
|
||||
|
||||
abstract detailProperties$: ReadonlySignal<string[]>;
|
||||
abstract detailProperties$: ReadonlySignal<Property[]>;
|
||||
|
||||
protected lockRows$ = signal(false);
|
||||
|
||||
@@ -172,43 +98,56 @@ export abstract class SingleViewBase<
|
||||
return this.data$.value?.name ?? '';
|
||||
});
|
||||
|
||||
preRows: string[] = [];
|
||||
|
||||
abstract propertyIds$: ReadonlySignal<string[]>;
|
||||
|
||||
properties$ = computed(() => {
|
||||
return this.propertyIds$.value.map(
|
||||
id => this.propertyGet(id) as ReturnType<this['propertyGet']>
|
||||
);
|
||||
propertyIds$: ReadonlySignal<string[]> = computed(() => {
|
||||
return this.properties$.value.map(v => v.id);
|
||||
});
|
||||
|
||||
abstract propertiesWithoutFilter$: ReadonlySignal<string[]>;
|
||||
propertyMap$: ReadonlySignal<Record<string, Property>> = computed(() => {
|
||||
return Object.fromEntries(this.properties$.value.map(v => [v.id, v]));
|
||||
});
|
||||
|
||||
abstract properties$: ReadonlySignal<Property[]>;
|
||||
|
||||
abstract propertiesRaw$: ReadonlySignal<Property[]>;
|
||||
|
||||
abstract readonly$: ReadonlySignal<boolean>;
|
||||
|
||||
rows$ = computed(() => {
|
||||
if (this.lockRows$.value) {
|
||||
return this.preRows;
|
||||
}
|
||||
return (this.preRows = this.rowsMapping(this.dataSource.rows$.value));
|
||||
rowsRaw$ = computed(() => {
|
||||
return this.dataSource.rows$.value.map(id => this.rowGetOrCreate(id));
|
||||
});
|
||||
|
||||
rows$ = computedLock(
|
||||
computed(() => {
|
||||
return this.rowsMapping(this.rowsRaw$.value);
|
||||
}),
|
||||
this.isLocked$
|
||||
);
|
||||
|
||||
rowsDelete(rows: string[]): void {
|
||||
this.dataSource.rowDelete(rows);
|
||||
}
|
||||
|
||||
rowIds$ = computed(() => {
|
||||
return this.rowsRaw$.value.map(v => v.rowId);
|
||||
});
|
||||
|
||||
vars$ = computed(() => {
|
||||
return this.propertiesWithoutFilter$.value.flatMap(id => {
|
||||
const v = this.propertyGet(id);
|
||||
const propertyMeta = this.dataSource.propertyMetaGet(v.type$.value);
|
||||
return this.propertiesRaw$.value.flatMap(property => {
|
||||
const propertyMeta = this.dataSource.propertyMetaGet(
|
||||
property.type$.value
|
||||
);
|
||||
if (!propertyMeta) {
|
||||
return [];
|
||||
}
|
||||
return {
|
||||
id: v.id,
|
||||
name: v.name$.value,
|
||||
id: property.id,
|
||||
name: property.name$.value,
|
||||
type: propertyMeta.config.jsonValue.type({
|
||||
data: v.data$.value,
|
||||
data: property.data$.value,
|
||||
dataSource: this.dataSource,
|
||||
}),
|
||||
icon: v.icon,
|
||||
propertyType: v.type$.value,
|
||||
icon: property.icon,
|
||||
propertyType: property.type$.value,
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -240,29 +179,13 @@ export abstract class SingleViewBase<
|
||||
public id: string
|
||||
) {}
|
||||
|
||||
propertyCanDelete(propertyId: string): boolean {
|
||||
return this.dataSource.propertyCanDelete(propertyId);
|
||||
}
|
||||
|
||||
propertyCanDuplicate(propertyId: string): boolean {
|
||||
return this.dataSource.propertyCanDuplicate(propertyId);
|
||||
}
|
||||
|
||||
propertyTypeCanSet(propertyId: string): boolean {
|
||||
return this.dataSource.propertyTypeCanSet(propertyId);
|
||||
}
|
||||
|
||||
propertyCanHide(propertyId: string): boolean {
|
||||
return this.propertyTypeGet(propertyId) !== 'title';
|
||||
}
|
||||
|
||||
private searchRowsMapping(rows: string[], searchString: string): string[] {
|
||||
return rows.filter(id => {
|
||||
private searchRowsMapping(rows: Row[], searchString: string): Row[] {
|
||||
return rows.filter(row => {
|
||||
if (searchString) {
|
||||
const containsSearchString = this.propertyIds$.value.some(
|
||||
propertyId => {
|
||||
return this.cellStringValueGet(id, propertyId)
|
||||
?.toLowerCase()
|
||||
return this.cellGetOrCreate(row.rowId, propertyId)
|
||||
.stringValue$.value?.toLowerCase()
|
||||
.includes(searchString?.toLowerCase());
|
||||
}
|
||||
);
|
||||
@@ -270,66 +193,14 @@ export abstract class SingleViewBase<
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this.isShow(id);
|
||||
return this.isShow(row.rowId);
|
||||
});
|
||||
}
|
||||
|
||||
cellGet(rowId: string, propertyId: string): Cell {
|
||||
cellGetOrCreate(rowId: string, propertyId: string): Cell {
|
||||
return new CellBase(this, propertyId, rowId);
|
||||
}
|
||||
|
||||
cellJsonValueGet(rowId: string, propertyId: string): unknown | null {
|
||||
const type = this.propertyTypeGet(propertyId);
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
this.dataSource.propertyMetaGet(type)?.config.rawValue.toJson({
|
||||
value: this.dataSource.cellValueGet(rowId, propertyId),
|
||||
data: this.propertyDataGet(propertyId),
|
||||
dataSource: this.dataSource,
|
||||
}) ?? null
|
||||
);
|
||||
}
|
||||
|
||||
cellJsonValueSet(rowId: string, propertyId: string, value: unknown): void {
|
||||
const type = this.propertyTypeGet(propertyId);
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const config = this.dataSource.propertyMetaGet(type)?.config;
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
const rawValue = fromJson(config, {
|
||||
value: value,
|
||||
data: this.propertyDataGet(propertyId),
|
||||
dataSource: this.dataSource,
|
||||
});
|
||||
this.dataSource.cellValueChange(rowId, propertyId, rawValue);
|
||||
}
|
||||
|
||||
cellStringValueGet(rowId: string, propertyId: string): string | undefined {
|
||||
const type = this.propertyTypeGet(propertyId);
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
this.dataSource.propertyMetaGet(type)?.config.rawValue.toString({
|
||||
value: this.dataSource.cellValueGet(rowId, propertyId),
|
||||
data: this.propertyDataGet(propertyId),
|
||||
}) ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
cellValueGet(rowId: string, propertyId: string): unknown {
|
||||
return this.dataSource.cellValueGet(rowId, propertyId);
|
||||
}
|
||||
|
||||
cellValueSet(rowId: string, propertyId: string, value: unknown): void {
|
||||
this.dataSource.cellValueChange(rowId, propertyId, value);
|
||||
}
|
||||
|
||||
contextGet<T>(key: DataViewContextKey<T>): T {
|
||||
return this.dataSource.contextGet(key);
|
||||
}
|
||||
@@ -365,144 +236,22 @@ export abstract class SingleViewBase<
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
this.propertyMove(id, position);
|
||||
const property = this.propertyGetOrCreate(id);
|
||||
property.move(position);
|
||||
return id;
|
||||
}
|
||||
|
||||
propertyDataGet(propertyId: string): Record<string, unknown> {
|
||||
return this.dataSource.propertyDataGet(propertyId);
|
||||
}
|
||||
|
||||
propertyDataSet(propertyId: string, data: Record<string, unknown>): void {
|
||||
this.dataSource.propertyDataSet(propertyId, data);
|
||||
}
|
||||
|
||||
propertyDataTypeGet(propertyId: string): TypeInstance | undefined {
|
||||
const type = this.propertyTypeGet(propertyId);
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const meta = this.dataSource.propertyMetaGet(type);
|
||||
if (!meta) {
|
||||
return;
|
||||
}
|
||||
return meta.config.jsonValue.type({
|
||||
data: this.propertyDataGet(propertyId),
|
||||
dataSource: this.dataSource,
|
||||
});
|
||||
}
|
||||
|
||||
propertyDelete(propertyId: string): void {
|
||||
this.dataSource.propertyDelete(propertyId);
|
||||
}
|
||||
|
||||
propertyDuplicate(propertyId: string): void {
|
||||
const id = this.dataSource.propertyDuplicate(propertyId);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
this.propertyMove(id, {
|
||||
before: false,
|
||||
id: propertyId,
|
||||
});
|
||||
}
|
||||
|
||||
abstract propertyGet(propertyId: string): Property;
|
||||
|
||||
abstract propertyHideGet(propertyId: string): boolean;
|
||||
|
||||
abstract propertyHideSet(propertyId: string, hide: boolean): void;
|
||||
|
||||
propertyIconGet(type: string): UniComponent | undefined {
|
||||
return this.dataSource.propertyMetaGet(type)?.renderer.icon;
|
||||
}
|
||||
|
||||
propertyIdGetByIndex(index: number): string | undefined {
|
||||
return this.propertyIds$.value[index];
|
||||
}
|
||||
|
||||
propertyIndexGet(propertyId: string): number {
|
||||
return this.propertyIds$.value.indexOf(propertyId);
|
||||
}
|
||||
|
||||
propertyMetaGet(type: string): PropertyMetaConfig | undefined {
|
||||
return this.dataSource.propertyMetaGet(type);
|
||||
}
|
||||
|
||||
abstract propertyMove(propertyId: string, position: InsertToPosition): void;
|
||||
|
||||
propertyNameGet(propertyId: string): string {
|
||||
return this.dataSource.propertyNameGet(propertyId);
|
||||
}
|
||||
|
||||
propertyNameSet(propertyId: string, name: string): void {
|
||||
this.dataSource.propertyNameSet(propertyId, name);
|
||||
}
|
||||
|
||||
propertyNextGet(propertyId: string): Property | undefined {
|
||||
const index = this.propertyIndexGet(propertyId);
|
||||
const nextId = this.propertyIdGetByIndex(index + 1);
|
||||
if (!nextId) return;
|
||||
return this.propertyGet(nextId);
|
||||
}
|
||||
|
||||
propertyParseValueFromString(propertyId: string, cellData: string) {
|
||||
const type = this.propertyTypeGet(propertyId);
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
const fromString =
|
||||
this.dataSource.propertyMetaGet(type)?.config.rawValue.fromString;
|
||||
if (!fromString) {
|
||||
return;
|
||||
}
|
||||
return fromString({
|
||||
value: cellData,
|
||||
data: this.propertyDataGet(propertyId),
|
||||
dataSource: this.dataSource,
|
||||
});
|
||||
}
|
||||
|
||||
propertyPreGet(propertyId: string): Property | undefined {
|
||||
const index = this.propertyIndexGet(propertyId);
|
||||
const prevId = this.propertyIdGetByIndex(index - 1);
|
||||
if (!prevId) return;
|
||||
return this.propertyGet(prevId);
|
||||
}
|
||||
|
||||
propertyReadonlyGet(propertyId: string): boolean {
|
||||
return this.dataSource.propertyReadonlyGet(propertyId);
|
||||
}
|
||||
|
||||
propertyTypeGet(propertyId: string): string | undefined {
|
||||
return this.dataSource.propertyTypeGet(propertyId);
|
||||
}
|
||||
|
||||
propertyTypeSet(propertyId: string, type: string): void {
|
||||
this.dataSource.propertyTypeSet(propertyId, type);
|
||||
}
|
||||
abstract propertyGetOrCreate(propertyId: string): Property;
|
||||
|
||||
rowAdd(insertPosition: InsertToPosition | number): string {
|
||||
return this.dataSource.rowAdd(insertPosition);
|
||||
}
|
||||
|
||||
rowDelete(ids: string[]): void {
|
||||
this.dataSource.rowDelete(ids);
|
||||
}
|
||||
|
||||
rowGet(rowId: string): Row {
|
||||
rowGetOrCreate(rowId: string): Row {
|
||||
return new RowBase(this, rowId);
|
||||
}
|
||||
|
||||
rowMove(rowId: string, position: InsertToPosition): void {
|
||||
this.dataSource.rowMove(rowId, position);
|
||||
}
|
||||
|
||||
abstract rowNextGet(rowId: string): string | undefined;
|
||||
|
||||
abstract rowPrevGet(rowId: string): string | undefined;
|
||||
|
||||
protected rowsMapping(rows: string[]): string[] {
|
||||
protected rowsMapping(rows: Row[]): Row[] {
|
||||
return this.searchRowsMapping(rows, this.searchString.value);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ export const kanbanViewModel = kanbanViewType.createModel<KanbanViewData>({
|
||||
return {
|
||||
columns: columns.map(id => ({
|
||||
id: id,
|
||||
hide: false,
|
||||
})),
|
||||
filter: {
|
||||
type: 'group',
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
insertPositionToIndex,
|
||||
type InsertToPosition,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
import { computed } from '@preact/signals-core';
|
||||
|
||||
import { evalFilter } from '../../core/filter/eval.js';
|
||||
import { generateDefaultValues } from '../../core/filter/generate-default-values.js';
|
||||
@@ -20,7 +20,7 @@ import { SingleViewBase } from '../../core/view-manager/single-view.js';
|
||||
import type { KanbanViewData } from './define.js';
|
||||
|
||||
export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
propertiesWithoutFilter$ = computed(() => {
|
||||
propertiesRaw$ = computed(() => {
|
||||
const needShow = new Set(this.dataSource.properties$.value);
|
||||
const result: string[] = [];
|
||||
this.data$.value?.columns.forEach(v => {
|
||||
@@ -30,12 +30,16 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
}
|
||||
});
|
||||
result.push(...needShow);
|
||||
return result;
|
||||
return result.map(id => this.propertyGetOrCreate(id));
|
||||
});
|
||||
|
||||
properties$ = computed(() => {
|
||||
return this.propertiesRaw$.value.filter(property => !property.hide$.value);
|
||||
});
|
||||
|
||||
detailProperties$ = computed(() => {
|
||||
return this.propertiesWithoutFilter$.value.filter(
|
||||
id => this.propertyTypeGet(id) !== 'title'
|
||||
return this.propertiesRaw$.value.filter(
|
||||
property => property.type$.value !== 'title'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -76,9 +80,13 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
v => v,
|
||||
this.view?.groupProperties.map(v => v.key) ?? []
|
||||
),
|
||||
sortRow: (key, ids) => {
|
||||
sortRow: (key, rows) => {
|
||||
const property = this.view?.groupProperties.find(v => v.key === key);
|
||||
return sortByManually(ids, v => v, property?.manuallyCardSort ?? []);
|
||||
return sortByManually(
|
||||
rows,
|
||||
v => v.rowId,
|
||||
property?.manuallyCardSort ?? []
|
||||
);
|
||||
},
|
||||
changeGroupSort: keys => {
|
||||
const map = new Map(this.view?.groupProperties.map(v => [v.key, v]));
|
||||
@@ -134,28 +142,20 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
mainProperties$ = computed(() => {
|
||||
return (
|
||||
this.data$.value?.header ?? {
|
||||
titleColumn: this.propertiesWithoutFilter$.value.find(
|
||||
id => this.propertyTypeGet(id) === 'title'
|
||||
),
|
||||
titleColumn: this.propertiesRaw$.value.find(
|
||||
property => property.type$.value === 'title'
|
||||
)?.id,
|
||||
iconColumn: 'type',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
propertyIds$: ReadonlySignal<string[]> = computed(() => {
|
||||
return this.propertiesWithoutFilter$.value.filter(
|
||||
id => !this.propertyHideGet(id)
|
||||
);
|
||||
});
|
||||
|
||||
readonly$ = computed(() => {
|
||||
return this.manager.readonly$.value;
|
||||
});
|
||||
|
||||
get columns(): string[] {
|
||||
return this.propertiesWithoutFilter$.value.filter(
|
||||
id => !this.propertyHideGet(id)
|
||||
);
|
||||
get columns(): KanbanColumn[] {
|
||||
return this.propertiesRaw$.value.filter(property => !property.hide$.value);
|
||||
}
|
||||
|
||||
get filter(): FilterGroup {
|
||||
@@ -182,8 +182,8 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
if (filter.conditions.length > 0) {
|
||||
const defaultValues = generateDefaultValues(filter, this.vars$.value);
|
||||
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
||||
const property = this.propertyGet(propertyId);
|
||||
const propertyMeta = this.propertyMetaGet(property.type$.value);
|
||||
const property = this.propertyGetOrCreate(propertyId);
|
||||
const propertyMeta = property.meta$.value;
|
||||
if (!propertyMeta) {
|
||||
return;
|
||||
}
|
||||
@@ -192,7 +192,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
data: property.data$.value,
|
||||
dataSource: this.dataSource,
|
||||
});
|
||||
this.cellValueSet(id, propertyId, value);
|
||||
this.cellGetOrCreate(id, propertyId).valueSet(value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
if (!columnId) {
|
||||
return;
|
||||
}
|
||||
return this.propertyGet(columnId);
|
||||
return this.propertyGetOrCreate(columnId);
|
||||
}
|
||||
|
||||
getHeaderIcon(_rowId: string): KanbanColumn | undefined {
|
||||
@@ -212,7 +212,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
if (!columnId) {
|
||||
return;
|
||||
}
|
||||
return this.propertyGet(columnId);
|
||||
return this.propertyGetOrCreate(columnId);
|
||||
}
|
||||
|
||||
getHeaderTitle(_rowId: string): KanbanColumn | undefined {
|
||||
@@ -220,7 +220,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
if (!columnId) {
|
||||
return;
|
||||
}
|
||||
return this.propertyGet(columnId);
|
||||
return this.propertyGetOrCreate(columnId);
|
||||
}
|
||||
|
||||
hasHeader(_rowId: string): boolean {
|
||||
@@ -248,7 +248,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
const rowMap = Object.fromEntries(
|
||||
this.properties$.value.map(column => [
|
||||
column.id,
|
||||
column.cellGet(rowId).jsonValue$.value,
|
||||
column.cellGetOrCreate(rowId).jsonValue$.value,
|
||||
])
|
||||
);
|
||||
return evalFilter(this.filter$.value, rowMap);
|
||||
@@ -256,32 +256,16 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
return true;
|
||||
}
|
||||
|
||||
propertyGet(columnId: string): KanbanColumn {
|
||||
propertyGetOrCreate(columnId: string): KanbanColumn {
|
||||
return new KanbanColumn(this, columnId);
|
||||
}
|
||||
}
|
||||
|
||||
propertyHideGet(columnId: string): boolean {
|
||||
return this.view?.columns.find(v => v.id === columnId)?.hide ?? false;
|
||||
}
|
||||
|
||||
propertyHideSet(columnId: string, hide: boolean): void {
|
||||
this.dataUpdate(view => {
|
||||
return {
|
||||
columns: view.columns.map(v =>
|
||||
v.id === columnId
|
||||
? {
|
||||
...v,
|
||||
hide,
|
||||
}
|
||||
: v
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
propertyMove(columnId: string, toAfterOfColumn: InsertToPosition): void {
|
||||
this.dataUpdate(view => {
|
||||
const columnIndex = view.columns.findIndex(v => v.id === columnId);
|
||||
type KanbanColumnData = KanbanViewData['columns'][number];
|
||||
export class KanbanColumn extends PropertyBase {
|
||||
override move(position: InsertToPosition): void {
|
||||
this.kanbanView.dataUpdate(view => {
|
||||
const columnIndex = view.columns.findIndex(v => v.id === this.id);
|
||||
if (columnIndex < 0) {
|
||||
return {};
|
||||
}
|
||||
@@ -290,7 +274,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
if (!column) {
|
||||
return {};
|
||||
}
|
||||
const index = insertPositionToIndex(toAfterOfColumn, columns);
|
||||
const index = insertPositionToIndex(position, columns);
|
||||
columns.splice(index, 0, column);
|
||||
return {
|
||||
columns,
|
||||
@@ -298,23 +282,47 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||
});
|
||||
}
|
||||
|
||||
override rowMove(rowId: string, position: InsertToPosition): void {
|
||||
this.dataSource.rowMove(rowId, position);
|
||||
override hideSet(hide: boolean): void {
|
||||
this.viewDataUpdate(data => {
|
||||
return {
|
||||
...data,
|
||||
hide,
|
||||
};
|
||||
});
|
||||
}
|
||||
hide$ = computed(() => {
|
||||
const hideFromViewData = this.viewData$.value?.hide;
|
||||
if (hideFromViewData != null) {
|
||||
return hideFromViewData;
|
||||
}
|
||||
const defaultShow = this.meta$.value?.config.fixed?.defaultShow;
|
||||
if (defaultShow != null) {
|
||||
return !defaultShow;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
viewData$ = computed(() => {
|
||||
return this.kanbanView.data$.value?.columns.find(v => v.id === this.id);
|
||||
});
|
||||
|
||||
viewDataUpdate(
|
||||
updater: (viewData: KanbanColumnData) => Partial<KanbanColumnData>
|
||||
): void {
|
||||
this.kanbanView.dataUpdate(data => {
|
||||
return {
|
||||
...data,
|
||||
columns: data.columns.map(v =>
|
||||
v.id === this.id ? { ...v, ...updater(v) } : v
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
override rowNextGet(rowId: string): string | undefined {
|
||||
const index = this.rows$.value.indexOf(rowId);
|
||||
return this.rows$.value[index + 1];
|
||||
}
|
||||
|
||||
override rowPrevGet(rowId: string): string | undefined {
|
||||
const index = this.rows$.value.indexOf(rowId);
|
||||
return this.rows$.value[index - 1];
|
||||
}
|
||||
}
|
||||
|
||||
export class KanbanColumn extends PropertyBase {
|
||||
constructor(dataViewManager: KanbanSingleView, columnId: string) {
|
||||
super(dataViewManager, columnId);
|
||||
constructor(
|
||||
private readonly kanbanView: KanbanSingleView,
|
||||
columnId: string
|
||||
) {
|
||||
super(kanbanView, columnId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ export class MobileKanbanCard extends SignalWatcher(
|
||||
return;
|
||||
}
|
||||
return html` <div class="mobile-card-header-icon">
|
||||
${icon.cellGet(this.cardId).value$.value}
|
||||
${icon.cellGetOrCreate(this.cardId).value$.value}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ export class MobileKanbanCell extends SignalWatcher(
|
||||
|
||||
override render() {
|
||||
const props: CellRenderProps = {
|
||||
cell: this.column.cellGet(this.cardId),
|
||||
cell: this.column.cellGetOrCreate(this.cardId),
|
||||
isEditing$: this.isEditing$,
|
||||
selectCurrentCell: this.selectCurrentCell,
|
||||
};
|
||||
|
||||
@@ -77,15 +77,15 @@ export class MobileKanbanGroup extends SignalWatcher(
|
||||
name: 'Ungroup',
|
||||
hide: () => this.group.value == null,
|
||||
select: () => {
|
||||
this.group.rows.forEach(id => {
|
||||
this.group.manager.removeFromGroup(id, this.group.key);
|
||||
this.group.rows.forEach(row => {
|
||||
this.group.manager.removeFromGroup(row.rowId, this.group.key);
|
||||
});
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.view.rowDelete(this.group.rows);
|
||||
this.view.rowsDelete(this.group.rows.map(row => row.rowId));
|
||||
},
|
||||
}),
|
||||
],
|
||||
@@ -106,15 +106,15 @@ export class MobileKanbanGroup extends SignalWatcher(
|
||||
<div class="mobile-group-body">
|
||||
${repeat(
|
||||
cards,
|
||||
id => id,
|
||||
id => {
|
||||
row => row.rowId,
|
||||
row => {
|
||||
return html`
|
||||
<mobile-kanban-card
|
||||
data-card-id="${id}"
|
||||
data-card-id="${row.rowId}"
|
||||
.groupKey="${this.group.key}"
|
||||
.dataViewEle="${this.dataViewEle}"
|
||||
.view="${this.view}"
|
||||
.cardId="${id}"
|
||||
.cardId="${row.rowId}"
|
||||
></mobile-kanban-card>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ export const popCardMenu = (
|
||||
},
|
||||
prefix: DeleteIcon(),
|
||||
select: () => {
|
||||
view.rowDelete([cardId]);
|
||||
view.rowsDelete([cardId]);
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -230,7 +230,7 @@ export class KanbanCard extends SignalWatcher(
|
||||
return;
|
||||
}
|
||||
return html` <div class="card-header-icon">
|
||||
${icon.cellGet(this.cardId).value$.value}
|
||||
${icon.cellGetOrCreate(this.cardId).value$.value}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ export class KanbanCell extends SignalWatcher(
|
||||
|
||||
override render() {
|
||||
const props: CellRenderProps = {
|
||||
cell: this.column.cellGet(this.cardId),
|
||||
cell: this.column.cellGetOrCreate(this.cardId),
|
||||
isEditing$: this.isEditing$,
|
||||
selectCurrentCell: this.selectCurrentCell,
|
||||
};
|
||||
|
||||
@@ -116,7 +116,7 @@ export class KanbanSelectionController implements ReactiveController {
|
||||
return;
|
||||
}
|
||||
if (selection.selectionType === 'card') {
|
||||
this.host.props.view.rowDelete(selection.cards.map(v => v.cardId));
|
||||
this.host.props.view.rowsDelete(selection.cards.map(v => v.cardId));
|
||||
this.selection = undefined;
|
||||
}
|
||||
}
|
||||
@@ -155,12 +155,13 @@ export class KanbanSelectionController implements ReactiveController {
|
||||
focusFirstCell() {
|
||||
const group = this.host.groupManager?.groupsDataList$.value?.[0];
|
||||
const card = group?.rows[0];
|
||||
const columnId = card && this.host.props.view.getHeaderTitle(card)?.id;
|
||||
const columnId =
|
||||
card && this.host.props.view.getHeaderTitle(card.rowId)?.id;
|
||||
if (group && card && columnId) {
|
||||
this.selection = {
|
||||
selectionType: 'cell',
|
||||
groupKey: group.key,
|
||||
cardId: card,
|
||||
cardId: card.rowId,
|
||||
columnId,
|
||||
isEditing: false,
|
||||
};
|
||||
|
||||
@@ -143,15 +143,15 @@ export class KanbanGroup extends SignalWatcher(
|
||||
name: 'Ungroup',
|
||||
hide: () => this.group.value == null,
|
||||
select: () => {
|
||||
this.group.rows.forEach(id => {
|
||||
this.group.manager.removeFromGroup(id, this.group.key);
|
||||
this.group.rows.forEach(row => {
|
||||
this.group.manager.removeFromGroup(row.rowId, this.group.key);
|
||||
});
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.view.rowDelete(this.group.rows);
|
||||
this.view.rowsDelete(this.group.rows.map(row => row.rowId));
|
||||
},
|
||||
}),
|
||||
]);
|
||||
@@ -170,15 +170,15 @@ export class KanbanGroup extends SignalWatcher(
|
||||
<div class="group-body">
|
||||
${repeat(
|
||||
cards,
|
||||
id => id,
|
||||
id => {
|
||||
row => row.rowId,
|
||||
row => {
|
||||
return html`
|
||||
<affine-data-view-kanban-card
|
||||
data-card-id="${id}"
|
||||
data-card-id="${row.rowId}"
|
||||
.groupKey="${this.group.key}"
|
||||
.dataViewEle="${this.dataViewEle}"
|
||||
.view="${this.view}"
|
||||
.cardId="${id}"
|
||||
.cardId="${row.rowId}"
|
||||
></affine-data-view-kanban-card>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
type SingleView,
|
||||
} from '../../../core/index.js';
|
||||
import { TableViewAreaSelection } from '../selection';
|
||||
import type { TableColumn } from '../table-view-manager.js';
|
||||
import type { TableProperty } from '../table-view-manager.js';
|
||||
|
||||
export class MobileTableCell extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
@@ -38,13 +38,13 @@ export class MobileTableCell extends SignalWatcher(
|
||||
private readonly _cell = signal<DataViewCellLifeCycle>();
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor rowId!: string;
|
||||
|
||||
cell$ = computed(() => {
|
||||
return this.column.cellGet(this.rowId);
|
||||
return this.column.cellGetOrCreate(this.rowId);
|
||||
});
|
||||
|
||||
isSelectionEditing$ = computed(() => {
|
||||
|
||||
@@ -25,7 +25,7 @@ import { inputConfig, typeConfig } from '../../../core/common/property-menu.js';
|
||||
import type { Property } from '../../../core/view-manager/property.js';
|
||||
import { numberFormats } from '../../../property-presets/number/utils/formats.js';
|
||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../consts.js';
|
||||
import type { TableColumn, TableSingleView } from '../table-view-manager.js';
|
||||
import type { TableProperty, TableSingleView } from '../table-view-manager.js';
|
||||
|
||||
export class MobileTableColumnHeader extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
@@ -174,16 +174,14 @@ export class MobileTableColumnHeader extends SignalWatcher(
|
||||
menu.action({
|
||||
name: 'Move Left',
|
||||
prefix: MoveLeftIcon(),
|
||||
hide: () => this.column.isFirst,
|
||||
hide: () => this.column.isFirst$.value,
|
||||
select: () => {
|
||||
const preId = this.tableViewManager.propertyPreGet(
|
||||
this.column.id
|
||||
)?.id;
|
||||
if (!preId) {
|
||||
const pre = this.column.prev$.value;
|
||||
if (!pre) {
|
||||
return;
|
||||
}
|
||||
this.tableViewManager.propertyMove(this.column.id, {
|
||||
id: preId,
|
||||
this.column.move({
|
||||
id: pre.id,
|
||||
before: true,
|
||||
});
|
||||
},
|
||||
@@ -191,16 +189,14 @@ export class MobileTableColumnHeader extends SignalWatcher(
|
||||
menu.action({
|
||||
name: 'Move Right',
|
||||
prefix: MoveRightIcon(),
|
||||
hide: () => this.column.isLast,
|
||||
hide: () => this.column.isLast$.value,
|
||||
select: () => {
|
||||
const nextId = this.tableViewManager.propertyNextGet(
|
||||
this.column.id
|
||||
)?.id;
|
||||
if (!nextId) {
|
||||
const next = this.column.next$.value;
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
this.tableViewManager.propertyMove(this.column.id, {
|
||||
id: nextId,
|
||||
this.column.move({
|
||||
id: next.id,
|
||||
before: false,
|
||||
});
|
||||
},
|
||||
@@ -256,7 +252,7 @@ export class MobileTableColumnHeader extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor tableViewManager!: TableSingleView;
|
||||
|
||||
@@ -14,6 +14,7 @@ import { repeat } from 'lit/directives/repeat.js';
|
||||
import type { DataViewRenderer } from '../../../core/data-view.js';
|
||||
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
||||
import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import type { Row } from '../../../core/index.js';
|
||||
import { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
|
||||
import type { DataViewTable } from '../pc/table-view.js';
|
||||
import { TableViewAreaSelection } from '../selection';
|
||||
@@ -100,15 +101,15 @@ export class MobileTableGroup extends SignalWatcher(
|
||||
name: 'Ungroup',
|
||||
hide: () => group.value == null,
|
||||
select: () => {
|
||||
group.rows.forEach(id => {
|
||||
group.manager.removeFromGroup(id, group.key);
|
||||
group.rows.forEach(row => {
|
||||
group.manager.removeFromGroup(row.rowId, group.key);
|
||||
});
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.view.rowDelete(group.rows);
|
||||
this.view.rowsDelete(group.rows.map(row => row.rowId));
|
||||
},
|
||||
}),
|
||||
]);
|
||||
@@ -135,7 +136,7 @@ export class MobileTableGroup extends SignalWatcher(
|
||||
return this.group?.rows ?? this.view.rows$.value;
|
||||
}
|
||||
|
||||
private renderRows(ids: string[]) {
|
||||
private renderRows(rows: Row[]) {
|
||||
return html`
|
||||
<mobile-table-header
|
||||
.renderGroupHeader="${this.renderGroupHeader}"
|
||||
@@ -143,15 +144,15 @@ export class MobileTableGroup extends SignalWatcher(
|
||||
></mobile-table-header>
|
||||
<div class="mobile-affine-table-body">
|
||||
${repeat(
|
||||
ids,
|
||||
id => id,
|
||||
(id, idx) => {
|
||||
rows,
|
||||
row => row.rowId,
|
||||
(row, idx) => {
|
||||
return html` <mobile-table-row
|
||||
data-row-index="${idx}"
|
||||
data-row-id="${id}"
|
||||
data-row-id="${row.rowId}"
|
||||
.dataViewEle="${this.dataViewEle}"
|
||||
.view="${this.view}"
|
||||
.rowId="${id}"
|
||||
.rowId="${row.rowId}"
|
||||
.rowIndex="${idx}"
|
||||
></mobile-table-row>`;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export const popMobileRowMenu = (
|
||||
class: { 'delete-item': true },
|
||||
prefix: DeleteIcon(),
|
||||
select: () => {
|
||||
view.rowDelete([rowId]);
|
||||
view.rowsDelete([rowId]);
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -30,7 +30,7 @@ export class TableClipboardController implements ReactiveController {
|
||||
.map(row => row.cells.map(cell => cell.stringValue$.value).join('\t'))
|
||||
.join('\n');
|
||||
const jsonResult: JsonAreaData = area.map(row =>
|
||||
row.cells.map(cell => cell.stringValue$.value)
|
||||
row.cells.map(cell => cell.stringValue$.value ?? '')
|
||||
);
|
||||
if (isCut) {
|
||||
const deleteRows: string[] = [];
|
||||
@@ -44,7 +44,7 @@ export class TableClipboardController implements ReactiveController {
|
||||
}
|
||||
}
|
||||
if (deleteRows.length) {
|
||||
this.props.view.rowDelete(deleteRows);
|
||||
this.props.view.rowsDelete(deleteRows);
|
||||
}
|
||||
}
|
||||
this.clipboard
|
||||
@@ -210,7 +210,7 @@ function getSelectedArea(
|
||||
.sort((a, b) => a.y - b.y)
|
||||
.map(v => v.row);
|
||||
return rows.map(r => {
|
||||
const row = view.rowGet(r.id);
|
||||
const row = view.rowGetOrCreate(r.id);
|
||||
return {
|
||||
row,
|
||||
cells: row.cells$.value,
|
||||
@@ -227,11 +227,11 @@ function getSelectedArea(
|
||||
return;
|
||||
}
|
||||
for (let i = rowsSelection.start; i <= rowsSelection.end; i++) {
|
||||
const row: SelectedArea[number] = {
|
||||
const rowArea: SelectedArea[number] = {
|
||||
cells: [],
|
||||
};
|
||||
const rowId = rows[i];
|
||||
if (rowId == null) {
|
||||
const row = rows[i];
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
for (let j = columnsSelection.start; j <= columnsSelection.end; j++) {
|
||||
@@ -239,10 +239,10 @@ function getSelectedArea(
|
||||
if (columnId == null) {
|
||||
continue;
|
||||
}
|
||||
const cell = view.cellGet(rowId, columnId);
|
||||
row.cells.push(cell);
|
||||
const cell = view.cellGetOrCreate(row.rowId, columnId);
|
||||
rowArea.cells.push(cell);
|
||||
}
|
||||
data.push(row);
|
||||
data.push(rowArea);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -95,7 +95,7 @@ export function fillSelectionWithFocusCellData(
|
||||
|
||||
const curCell = cellContainer.cell$.value;
|
||||
|
||||
if (t.richText.is(curCol.dataType$.value)) {
|
||||
if (curCol.dataType$.value && t.richText.is(curCol.dataType$.value)) {
|
||||
const focusCellText = focusData as Text | undefined;
|
||||
|
||||
const delta = focusCellText?.toDelta() ?? [{ insert: '' }];
|
||||
|
||||
@@ -25,7 +25,7 @@ export class TableHotkeysController implements ReactiveController {
|
||||
if (TableViewRowSelection.is(selection)) {
|
||||
const rows = TableViewRowSelection.rowsIds(selection);
|
||||
this.selectionController.selection = undefined;
|
||||
this.host.props.view.rowDelete(rows);
|
||||
this.host.props.view.rowsDelete(rows);
|
||||
return;
|
||||
}
|
||||
const {
|
||||
@@ -336,11 +336,14 @@ export class TableHotkeysController implements ReactiveController {
|
||||
rows:
|
||||
this.host.props.view.groupTrait.groupsDataList$.value?.flatMap(
|
||||
group =>
|
||||
group?.rows.map(id => ({ groupKey: group.key, id })) ?? []
|
||||
group?.rows.map(row => ({
|
||||
groupKey: group.key,
|
||||
id: row.rowId,
|
||||
})) ?? []
|
||||
) ??
|
||||
this.host.props.view.rows$.value.map(id => ({
|
||||
this.host.props.view.rows$.value.map(row => ({
|
||||
groupKey: undefined,
|
||||
id,
|
||||
id: row.rowId,
|
||||
})),
|
||||
});
|
||||
return true;
|
||||
|
||||
@@ -242,7 +242,7 @@ export class TableSelectionController implements ReactiveController {
|
||||
this.selection = TableViewAreaSelection.create({
|
||||
groupKey: groupKey,
|
||||
focus: {
|
||||
rowIndex: rows?.findIndex(v => v === id) ?? 0,
|
||||
rowIndex: rows?.findIndex(v => v.rowId === id) ?? 0,
|
||||
columnIndex: index,
|
||||
},
|
||||
isEditing: true,
|
||||
@@ -373,7 +373,7 @@ export class TableSelectionController implements ReactiveController {
|
||||
}
|
||||
|
||||
deleteRow(rowId: string) {
|
||||
this.view.rowDelete([rowId]);
|
||||
this.view.rowsDelete([rowId]);
|
||||
this.focusToCell('up');
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { typeSystem } from '../../../../../../core';
|
||||
import type { GroupData } from '../../../../../../core/group-by/trait';
|
||||
import { statsFunctions } from '../../../../../../core/statistics';
|
||||
import type { StatisticsConfig } from '../../../../../../core/statistics/types';
|
||||
import type { TableColumn } from '../../../../table-view-manager';
|
||||
import type { TableProperty } from '../../../../table-view-manager';
|
||||
|
||||
const styles = css`
|
||||
.stats-cell {
|
||||
@@ -73,12 +73,12 @@ export class VirtualDatabaseColumnStatsCell extends SignalWatcher(
|
||||
static override styles = styles;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
cellValues$ = computed(() => {
|
||||
if (this.group) {
|
||||
return this.group.rows.map(id => {
|
||||
return this.column.valueGet(id);
|
||||
return this.group.rows.map(row => {
|
||||
return this.column.valueGet(row.rowId);
|
||||
});
|
||||
}
|
||||
return this.column.cells$.value.map(cell => cell.jsonValue$.value);
|
||||
@@ -157,7 +157,7 @@ export class VirtualDatabaseColumnStatsCell extends SignalWatcher(
|
||||
values$ = signal<unknown[]>([]);
|
||||
|
||||
statsResult$ = computed(() => {
|
||||
const meta = this.column.view.propertyMetaGet(this.column.type$.value);
|
||||
const meta = this.column.meta$.value;
|
||||
if (!meta) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -86,15 +86,15 @@ export class TableGroupHeader extends SignalWatcher(
|
||||
name: 'Ungroup',
|
||||
hide: () => group.value == null,
|
||||
select: () => {
|
||||
group.rows.forEach(id => {
|
||||
group.manager.removeFromGroup(id, group.key);
|
||||
group.rows.forEach(row => {
|
||||
group.manager.removeFromGroup(row.rowId, group.key);
|
||||
});
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.tableViewManager.rowDelete(group.rows);
|
||||
this.tableViewManager.rowsDelete(group.rows.map(row => row.rowId));
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -48,7 +48,11 @@ export const GroupTitle = (
|
||||
value: groupData.value,
|
||||
data: groupData.property.data$.value,
|
||||
updateData: groupData.manager.updateData,
|
||||
updateValue: value => groupData.manager.updateValue(groupData.rows, value),
|
||||
updateValue: value =>
|
||||
groupData.manager.updateValue(
|
||||
groupData.rows.map(row => row.rowId),
|
||||
value
|
||||
),
|
||||
readonly: ops.readonly,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { baseTheme } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
|
||||
import {
|
||||
DEFAULT_ADD_BUTTON_WIDTH,
|
||||
DEFAULT_COLUMN_TITLE_HEIGHT,
|
||||
} from '../../../../consts';
|
||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../../../consts';
|
||||
|
||||
export const columnHeaderContainer = style({
|
||||
display: 'block',
|
||||
@@ -33,104 +29,6 @@ export const cell = style({
|
||||
userSelect: 'none',
|
||||
});
|
||||
|
||||
export const addColumnButton = style({
|
||||
flex: 1,
|
||||
minWidth: `${DEFAULT_ADD_BUTTON_WIDTH}px`,
|
||||
minHeight: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
export const columnContent = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: '6px',
|
||||
boxSizing: 'border-box',
|
||||
position: 'relative',
|
||||
});
|
||||
|
||||
export const columnText = style({
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
overflow: 'hidden',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
fontSize: '14px',
|
||||
position: 'relative',
|
||||
});
|
||||
|
||||
export const columnTypeIcon = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
borderRadius: '4px',
|
||||
padding: '2px',
|
||||
fontSize: '18px',
|
||||
color: cssVarV2.icon.primary,
|
||||
});
|
||||
|
||||
export const columnTextContent = style({
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden',
|
||||
});
|
||||
|
||||
export const columnTextInput = style({
|
||||
flex: 1,
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
fontWeight: 500,
|
||||
});
|
||||
|
||||
export const columnTextIcon = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
background: 'var(--affine-white)',
|
||||
border: `1px solid ${cssVarV2.layer.insideBorder.border}`,
|
||||
borderRadius: '4px',
|
||||
opacity: 0,
|
||||
});
|
||||
|
||||
export const columnTextSaveIcon = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
border: '1px solid transparent',
|
||||
borderRadius: '4px',
|
||||
fill: 'var(--affine-icon-color)',
|
||||
selectors: {
|
||||
'&:hover': {
|
||||
background: 'var(--affine-white)',
|
||||
borderColor: cssVarV2.layer.insideBorder.border,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const columnInput = style({
|
||||
width: '100%',
|
||||
height: '24px',
|
||||
padding: 0,
|
||||
border: 'none',
|
||||
color: 'inherit',
|
||||
fontWeight: 600,
|
||||
fontSize: '14px',
|
||||
fontFamily: baseTheme.fontSansFamily,
|
||||
background: 'transparent',
|
||||
selectors: {
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const columnMove = style({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
@@ -156,17 +54,6 @@ globalStyle(`${columnMove} svg`, {
|
||||
opacity: 0,
|
||||
});
|
||||
|
||||
export const databaseAddColumnButton = style({
|
||||
position: 'sticky',
|
||||
right: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '40px',
|
||||
height: '38px',
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
export const headerAddColumnButton = style({
|
||||
height: `${DEFAULT_COLUMN_TITLE_HEIGHT}px`,
|
||||
backgroundColor: 'var(--affine-background-primary-color)',
|
||||
@@ -178,18 +65,3 @@ export const headerAddColumnButton = style({
|
||||
fontSize: '18px',
|
||||
color: cssVarV2.icon.primary,
|
||||
});
|
||||
|
||||
export const columnTypeMenuIcon = style({
|
||||
border: `1px solid ${cssVarV2.layer.insideBorder.border}`,
|
||||
borderRadius: '4px',
|
||||
padding: '5px',
|
||||
backgroundColor: 'var(--affine-background-secondary-color)',
|
||||
});
|
||||
|
||||
export const columnMovePreview = style({
|
||||
position: 'fixed',
|
||||
zIndex: 100,
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
background: 'var(--affine-text-emphasis-color)',
|
||||
});
|
||||
|
||||
@@ -30,8 +30,6 @@ export class VirtualTableHeader extends SignalWatcher(
|
||||
column.editTitle();
|
||||
};
|
||||
|
||||
preMove = 0;
|
||||
|
||||
private get readonly() {
|
||||
return this.tableViewManager.readonly$.value;
|
||||
}
|
||||
@@ -41,10 +39,6 @@ export class VirtualTableHeader extends SignalWatcher(
|
||||
this.classList.add(styles.columnHeaderContainer);
|
||||
}
|
||||
|
||||
getScale() {
|
||||
return this.scaleDiv?.getBoundingClientRect().width ?? 1;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="${styles.columnHeader} database-row">
|
||||
|
||||
@@ -8,8 +8,9 @@ import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import type { GroupData } from '../../../../../../core/group-by/trait';
|
||||
import type { Row } from '../../../../../../core/view-manager/row';
|
||||
import type {
|
||||
TableColumn,
|
||||
TableProperty,
|
||||
TableSingleView,
|
||||
} from '../../../../table-view-manager';
|
||||
|
||||
@@ -29,8 +30,8 @@ export class DataViewColumnPreview extends SignalWatcher(
|
||||
return this.column.view as TableSingleView;
|
||||
}
|
||||
|
||||
private renderGroup(rows: string[]) {
|
||||
const columnIndex = this.tableViewManager.propertyIndexGet(this.column.id);
|
||||
private renderGroup(rows: Row[]) {
|
||||
const columnIndex = this.column.index$.value;
|
||||
return html`
|
||||
<div
|
||||
style="background-color: var(--affine-background-primary-color);border-top: 1px solid ${unsafeCSS(
|
||||
@@ -77,7 +78,7 @@ export class DataViewColumnPreview extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor container!: HTMLElement;
|
||||
|
||||
@@ -43,7 +43,7 @@ import { numberFormats } from '../../../../../../property-presets/number/utils/f
|
||||
import { ShowQuickSettingBarContextKey } from '../../../../../../widget-presets/quick-setting-bar/context';
|
||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../../../consts';
|
||||
import type {
|
||||
TableColumn,
|
||||
TableProperty,
|
||||
TableSingleView,
|
||||
} from '../../../../table-view-manager';
|
||||
import {
|
||||
@@ -86,9 +86,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
return menu.action({
|
||||
name: config.config.name,
|
||||
isSelected: config.type === this.column.type$.value,
|
||||
prefix: renderUniLit(
|
||||
this.tableViewManager.propertyIconGet(config.type)
|
||||
),
|
||||
prefix: renderUniLit(config.renderer.icon),
|
||||
select: () => {
|
||||
this.column.typeSet?.(config.type);
|
||||
},
|
||||
@@ -326,16 +324,14 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
menu.action({
|
||||
name: 'Move Left',
|
||||
prefix: MoveLeftIcon(),
|
||||
hide: () => this.column.isFirst,
|
||||
hide: () => this.column.isFirst$.value,
|
||||
select: () => {
|
||||
const preId = this.tableViewManager.propertyPreGet(
|
||||
this.column.id
|
||||
)?.id;
|
||||
if (!preId) {
|
||||
const pre = this.column.prev$.value;
|
||||
if (!pre) {
|
||||
return;
|
||||
}
|
||||
this.tableViewManager.propertyMove(this.column.id, {
|
||||
id: preId,
|
||||
this.column.move({
|
||||
id: pre.id,
|
||||
before: true,
|
||||
});
|
||||
},
|
||||
@@ -343,16 +339,14 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
menu.action({
|
||||
name: 'Move Right',
|
||||
prefix: MoveRightIcon(),
|
||||
hide: () => this.column.isLast,
|
||||
hide: () => this.column.isLast$.value,
|
||||
select: () => {
|
||||
const nextId = this.tableViewManager.propertyNextGet(
|
||||
this.column.id
|
||||
)?.id;
|
||||
if (!nextId) {
|
||||
const next = this.column.next$.value;
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
this.tableViewManager.propertyMove(this.column.id, {
|
||||
id: nextId,
|
||||
this.column.move({
|
||||
id: next.id,
|
||||
before: false,
|
||||
});
|
||||
},
|
||||
@@ -475,7 +469,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor grabStatus: 'grabStart' | 'grabEnd' | 'grabbing' = 'grabEnd';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { startDrag } from '../../../../../../core/utils/drag';
|
||||
import { getResultInRange } from '../../../../../../core/utils/utils';
|
||||
import type { TableColumn } from '../../../../table-view-manager';
|
||||
import type { TableProperty } from '../../../../table-view-manager';
|
||||
|
||||
export class TableVerticalIndicator extends WithDisposable(ShadowlessElement) {
|
||||
static override styles = css`
|
||||
@@ -101,7 +101,7 @@ export const startDragWidthAdjustmentBar = (
|
||||
evt: PointerEvent,
|
||||
ele: HTMLElement,
|
||||
width: number,
|
||||
column: TableColumn
|
||||
column: TableProperty
|
||||
) => {
|
||||
const scale = width / column.width$.value;
|
||||
const left = ele.getBoundingClientRect().left;
|
||||
|
||||
@@ -45,7 +45,7 @@ export class DatabaseCellContainer extends SignalWatcher(
|
||||
private readonly _cell = signal<DataViewCellLifeCycle>();
|
||||
|
||||
cell$ = computed(() => {
|
||||
return this.view.cellGet(this.rowId, this.columnId);
|
||||
return this.view.cellGetOrCreate(this.rowId, this.columnId);
|
||||
});
|
||||
|
||||
selectCurrentCell = (editing: boolean) => {
|
||||
|
||||
@@ -70,7 +70,7 @@ export const popRowMenu = (
|
||||
},
|
||||
prefix: DeleteIcon(),
|
||||
select: () => {
|
||||
selectionController.view.rowDelete(rows);
|
||||
selectionController.view.rowsDelete(rows);
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -134,12 +134,8 @@ export class VirtualTableView extends DataViewBase<
|
||||
moveTo: (id, evt) => {
|
||||
const result = this.dragController.getInsertPosition(evt);
|
||||
if (result) {
|
||||
this.props.view.rowMove(
|
||||
id,
|
||||
result.position,
|
||||
undefined,
|
||||
result.groupKey
|
||||
);
|
||||
const row = this.props.view.rowGetOrCreate(id);
|
||||
row.move(result.position, undefined, result.groupKey);
|
||||
}
|
||||
},
|
||||
getSelection: () => {
|
||||
@@ -181,13 +177,13 @@ export class VirtualTableView extends DataViewBase<
|
||||
return [
|
||||
{
|
||||
id: '',
|
||||
rows: this.props.view.rows$.value,
|
||||
rows: this.props.view.rowIds$.value,
|
||||
},
|
||||
];
|
||||
}
|
||||
return groupTrait.groupsDataList$.value.map(group => ({
|
||||
id: group.key,
|
||||
rows: group.rows,
|
||||
rows: group.rows.map(v => v.rowId),
|
||||
}));
|
||||
});
|
||||
virtualScroll$ = signal<TableGrid>();
|
||||
@@ -221,9 +217,10 @@ export class VirtualTableView extends DataViewBase<
|
||||
if (!selection || selection.selectionType !== 'row') {
|
||||
return false;
|
||||
}
|
||||
const groupId = row.group.groupId;
|
||||
return TableViewRowSelection.includes(selection, {
|
||||
id: row.rowId,
|
||||
groupKey: row.group.groupId,
|
||||
groupKey: groupId ? groupId : undefined,
|
||||
});
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
TableViewAreaSelection,
|
||||
type TableViewSelectionWithType,
|
||||
} from '../selection';
|
||||
import type { TableColumn } from '../table-view-manager.js';
|
||||
import type { TableProperty } from '../table-view-manager.js';
|
||||
import type { TableGroup } from './group.js';
|
||||
|
||||
export class DatabaseCellContainer extends SignalWatcher(
|
||||
@@ -42,13 +42,13 @@ export class DatabaseCellContainer extends SignalWatcher(
|
||||
private readonly _cell = signal<DataViewCellLifeCycle>();
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor rowId!: string;
|
||||
|
||||
cell$ = computed(() => {
|
||||
return this.column.cellGet(this.rowId);
|
||||
return this.column.cellGetOrCreate(this.rowId);
|
||||
});
|
||||
|
||||
selectCurrentCell = (editing: boolean) => {
|
||||
|
||||
@@ -30,7 +30,7 @@ export class TableClipboardController implements ReactiveController {
|
||||
.map(row => row.cells.map(cell => cell.stringValue$.value).join('\t'))
|
||||
.join('\n');
|
||||
const jsonResult: JsonAreaData = area.map(row =>
|
||||
row.cells.map(cell => cell.stringValue$.value)
|
||||
row.cells.map(cell => cell.stringValue$.value ?? '')
|
||||
);
|
||||
if (isCut) {
|
||||
const deleteRows: string[] = [];
|
||||
@@ -44,7 +44,7 @@ export class TableClipboardController implements ReactiveController {
|
||||
}
|
||||
}
|
||||
if (deleteRows.length) {
|
||||
this.props.view.rowDelete(deleteRows);
|
||||
this.props.view.rowsDelete(deleteRows);
|
||||
}
|
||||
}
|
||||
this.clipboard
|
||||
@@ -211,7 +211,7 @@ function getSelectedArea(
|
||||
.sort((a, b) => a.y - b.y)
|
||||
.map(v => v.row);
|
||||
return rows.map(r => {
|
||||
const row = view.rowGet(r.id);
|
||||
const row = view.rowGetOrCreate(r.id);
|
||||
return {
|
||||
row,
|
||||
cells: row.cells$.value,
|
||||
@@ -228,11 +228,11 @@ function getSelectedArea(
|
||||
return;
|
||||
}
|
||||
for (let i = rowsSelection.start; i <= rowsSelection.end; i++) {
|
||||
const row: SelectedArea[number] = {
|
||||
const rowArea: SelectedArea[number] = {
|
||||
cells: [],
|
||||
};
|
||||
const rowId = rows[i];
|
||||
if (rowId == null) {
|
||||
const row = rows[i];
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
for (let j = columnsSelection.start; j <= columnsSelection.end; j++) {
|
||||
@@ -240,10 +240,10 @@ function getSelectedArea(
|
||||
if (columnId == null) {
|
||||
continue;
|
||||
}
|
||||
const cell = view.cellGet(rowId, columnId);
|
||||
row.cells.push(cell);
|
||||
const cell = view.cellGetOrCreate(row.rowId, columnId);
|
||||
rowArea.cells.push(cell);
|
||||
}
|
||||
data.push(row);
|
||||
data.push(rowArea);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -88,7 +88,9 @@ export function fillSelectionWithFocusCellData(
|
||||
|
||||
const curCell = cellContainer.cell$.value;
|
||||
|
||||
if (t.richText.is(curCol.dataType$.value)) {
|
||||
const dataType = curCol.dataType$.value;
|
||||
|
||||
if (dataType && t.richText.is(dataType)) {
|
||||
const focusCellText = focusData as Text | undefined;
|
||||
|
||||
const delta = focusCellText?.toDelta() ?? [{ insert: '' }];
|
||||
|
||||
@@ -4,20 +4,20 @@ import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||
import type { ReactiveController } from 'lit';
|
||||
|
||||
import { startDrag } from '../../../../core/utils/drag.js';
|
||||
import { TableRow } from '../row/row.js';
|
||||
import { TableRowView } from '../row/row.js';
|
||||
import type { DataViewTable } from '../table-view.js';
|
||||
|
||||
export class TableDragController implements ReactiveController {
|
||||
dragStart = (row: TableRow, evt: PointerEvent) => {
|
||||
const eleRect = row.getBoundingClientRect();
|
||||
dragStart = (rowView: TableRowView, evt: PointerEvent) => {
|
||||
const eleRect = rowView.getBoundingClientRect();
|
||||
const offsetLeft = evt.x - eleRect.left;
|
||||
const offsetTop = evt.y - eleRect.top;
|
||||
const preview = createDragPreview(
|
||||
row,
|
||||
rowView,
|
||||
evt.x - offsetLeft,
|
||||
evt.y - offsetTop
|
||||
);
|
||||
const fromGroup = row.groupKey;
|
||||
const fromGroup = rowView.groupKey;
|
||||
|
||||
startDrag<
|
||||
| undefined
|
||||
@@ -38,7 +38,7 @@ export class TableDragController implements ReactiveController {
|
||||
this.dropPreview.remove();
|
||||
return {
|
||||
type: 'out',
|
||||
callback: callback(evt, row.rowId),
|
||||
callback: callback(evt, rowView.rowId),
|
||||
};
|
||||
}
|
||||
return;
|
||||
@@ -66,12 +66,8 @@ export class TableDragController implements ReactiveController {
|
||||
return;
|
||||
}
|
||||
if (result.type === 'self') {
|
||||
this.host.props.view.rowMove(
|
||||
row.rowId,
|
||||
result.position,
|
||||
fromGroup,
|
||||
result.groupKey
|
||||
);
|
||||
const row = this.host.props.view.rowGetOrCreate(rowView.rowId);
|
||||
row.move(result.position, fromGroup, result.groupKey);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -159,9 +155,9 @@ export class TableDragController implements ReactiveController {
|
||||
}
|
||||
}
|
||||
|
||||
const createDragPreview = (row: TableRow, x: number, y: number) => {
|
||||
const createDragPreview = (row: TableRowView, x: number, y: number) => {
|
||||
const div = document.createElement('div');
|
||||
const cloneRow = new TableRow();
|
||||
const cloneRow = new TableRowView();
|
||||
cloneRow.view = row.view;
|
||||
cloneRow.rowIndex = row.rowIndex;
|
||||
cloneRow.rowId = row.rowId;
|
||||
|
||||
@@ -25,7 +25,7 @@ export class TableHotkeysController implements ReactiveController {
|
||||
if (TableViewRowSelection.is(selection)) {
|
||||
const rows = TableViewRowSelection.rowsIds(selection);
|
||||
this.selectionController.selection = undefined;
|
||||
this.host.props.view.rowDelete(rows);
|
||||
this.host.props.view.rowsDelete(rows);
|
||||
return;
|
||||
}
|
||||
const {
|
||||
@@ -336,11 +336,14 @@ export class TableHotkeysController implements ReactiveController {
|
||||
rows:
|
||||
this.host.props.view.groupTrait.groupsDataList$.value?.flatMap(
|
||||
group =>
|
||||
group?.rows.map(id => ({ groupKey: group.key, id })) ?? []
|
||||
group?.rows.map(row => ({
|
||||
groupKey: group.key,
|
||||
id: row.rowId,
|
||||
})) ?? []
|
||||
) ??
|
||||
this.host.props.view.rows$.value.map(id => ({
|
||||
this.host.props.view.rows$.value.map(row => ({
|
||||
groupKey: undefined,
|
||||
id,
|
||||
id: row.rowId,
|
||||
})),
|
||||
});
|
||||
return true;
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
} from '../../selection';
|
||||
import type { DatabaseCellContainer } from '../cell.js';
|
||||
import type { TableGroup } from '../group.js';
|
||||
import type { TableRow } from '../row/row.js';
|
||||
import type { TableRowView } from '../row/row.js';
|
||||
import type { DataViewTable } from '../table-view.js';
|
||||
import {
|
||||
DragToFillElement,
|
||||
@@ -244,7 +244,7 @@ export class TableSelectionController implements ReactiveController {
|
||||
this.selection = TableViewAreaSelection.create({
|
||||
groupKey: groupKey,
|
||||
focus: {
|
||||
rowIndex: rows?.findIndex(v => v === id) ?? 0,
|
||||
rowIndex: rows?.findIndex(v => v.rowId === id) ?? 0,
|
||||
columnIndex: index,
|
||||
},
|
||||
isEditing: true,
|
||||
@@ -346,7 +346,7 @@ export class TableSelectionController implements ReactiveController {
|
||||
}
|
||||
|
||||
deleteRow(rowId: string) {
|
||||
this.view.rowDelete([rowId]);
|
||||
this.view.rowsDelete([rowId]);
|
||||
this.focusToCell('up');
|
||||
}
|
||||
|
||||
@@ -553,7 +553,7 @@ export class TableSelectionController implements ReactiveController {
|
||||
(
|
||||
this.getGroup(lastRow?.groupKey)?.querySelector(
|
||||
`data-view-table-row[data-row-id='${lastRow?.id}']`
|
||||
) as TableRow | null
|
||||
) as TableRowView | null
|
||||
)?.rowIndex ?? 0;
|
||||
const getRowByIndex = (index: number) => {
|
||||
const tableRow = this.rows(lastRow?.groupKey)?.item(index);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { DataViewColumnPreview } from './header/column-renderer.js';
|
||||
import { DatabaseHeaderColumn } from './header/database-header-column.js';
|
||||
import { DatabaseNumberFormatBar } from './header/number-format-bar.js';
|
||||
import { TableVerticalIndicator } from './header/vertical-indicator.js';
|
||||
import { TableRow } from './row/row.js';
|
||||
import { TableRowView } from './row/row.js';
|
||||
import { RowSelectCheckbox } from './row/row-select-checkbox.js';
|
||||
import { DataViewTable } from './table-view.js';
|
||||
|
||||
@@ -28,7 +28,7 @@ export function pcEffects() {
|
||||
'affine-database-number-format-bar',
|
||||
DatabaseNumberFormatBar
|
||||
);
|
||||
customElements.define('data-view-table-row', TableRow);
|
||||
customElements.define('data-view-table-row', TableRowView);
|
||||
customElements.define('row-select-checkbox', RowSelectCheckbox);
|
||||
customElements.define('data-view-table-selection', SelectionElement);
|
||||
customElements.define('data-view-drag-to-fill', DragToFillElement);
|
||||
|
||||
@@ -15,6 +15,7 @@ import { repeat } from 'lit/directives/repeat.js';
|
||||
import type { DataViewRenderer } from '../../../core/data-view.js';
|
||||
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
||||
import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import type { Row } from '../../../core/index.js';
|
||||
import { createDndContext } from '../../../core/utils/wc-dnd/dnd-context.js';
|
||||
import { defaultActivators } from '../../../core/utils/wc-dnd/sensors/index.js';
|
||||
import { linearMove } from '../../../core/utils/wc-dnd/utils/linear-move.js';
|
||||
@@ -117,15 +118,15 @@ export class TableGroup extends SignalWatcher(
|
||||
name: 'Ungroup',
|
||||
hide: () => group.value == null,
|
||||
select: () => {
|
||||
group.rows.forEach(id => {
|
||||
group.manager.removeFromGroup(id, group.key);
|
||||
group.rows.forEach(row => {
|
||||
group.manager.removeFromGroup(row.rowId, group.key);
|
||||
});
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.view.rowDelete(group.rows);
|
||||
this.view.rowsDelete(group.rows.map(row => row.rowId));
|
||||
},
|
||||
}),
|
||||
]);
|
||||
@@ -173,7 +174,7 @@ export class TableGroup extends SignalWatcher(
|
||||
const overIndex = this.view.properties$.value.findIndex(
|
||||
data => data.id === over.id
|
||||
);
|
||||
this.view.propertyMove(active.id, {
|
||||
this.view.propertyGetOrCreate(active.id).move({
|
||||
before: activeIndex > overIndex,
|
||||
id: over.id,
|
||||
});
|
||||
@@ -181,7 +182,7 @@ export class TableGroup extends SignalWatcher(
|
||||
},
|
||||
collisionDetection: linearMove(true),
|
||||
createOverlay: active => {
|
||||
const column = this.view.propertyGet(active.id);
|
||||
const column = this.view.propertyGetOrCreate(active.id);
|
||||
const preview = new DataViewColumnPreview();
|
||||
preview.column = column;
|
||||
preview.group = this.group;
|
||||
@@ -241,7 +242,7 @@ export class TableGroup extends SignalWatcher(
|
||||
return this.group?.rows ?? this.view.rows$.value;
|
||||
}
|
||||
|
||||
private renderRows(ids: string[]) {
|
||||
private renderRows(rows: Row[]) {
|
||||
return html`
|
||||
<affine-database-column-header
|
||||
.renderGroupHeader="${this.renderGroupHeader}"
|
||||
@@ -249,15 +250,15 @@ export class TableGroup extends SignalWatcher(
|
||||
></affine-database-column-header>
|
||||
<div class="affine-database-block-rows">
|
||||
${repeat(
|
||||
ids,
|
||||
id => id,
|
||||
(id, idx) => {
|
||||
rows,
|
||||
row => row.rowId,
|
||||
(row, idx) => {
|
||||
return html` <data-view-table-row
|
||||
data-row-index="${idx}"
|
||||
data-row-id="${id}"
|
||||
data-row-id="${row.rowId}"
|
||||
.dataViewEle="${this.dataViewEle}"
|
||||
.view="${this.view}"
|
||||
.rowId="${id}"
|
||||
.rowId="${row.rowId}"
|
||||
.rowIndex="${idx}"
|
||||
></data-view-table-row>`;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,11 @@ import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import type { GroupData } from '../../../../core/group-by/trait.js';
|
||||
import type { TableColumn, TableSingleView } from '../../table-view-manager.js';
|
||||
import type { Row } from '../../../../core/index.js';
|
||||
import type {
|
||||
TableProperty,
|
||||
TableSingleView,
|
||||
} from '../../table-view-manager.js';
|
||||
|
||||
export class DataViewColumnPreview extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
@@ -26,8 +30,8 @@ export class DataViewColumnPreview extends SignalWatcher(
|
||||
return this.column.view as TableSingleView;
|
||||
}
|
||||
|
||||
private renderGroup(rows: string[]) {
|
||||
const columnIndex = this.tableViewManager.propertyIndexGet(this.column.id);
|
||||
private renderGroup(rows: Row[]) {
|
||||
const columnIndex = this.column.index$.value;
|
||||
return html`
|
||||
<div
|
||||
style="background-color: var(--affine-background-primary-color);border-top: 1px solid ${unsafeCSS(
|
||||
@@ -74,7 +78,7 @@ export class DataViewColumnPreview extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor container!: HTMLElement;
|
||||
|
||||
@@ -42,7 +42,10 @@ import type { Property } from '../../../../core/view-manager/property.js';
|
||||
import { numberFormats } from '../../../../property-presets/number/utils/formats.js';
|
||||
import { ShowQuickSettingBarContextKey } from '../../../../widget-presets/quick-setting-bar/context.js';
|
||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../consts.js';
|
||||
import type { TableColumn, TableSingleView } from '../../table-view-manager.js';
|
||||
import type {
|
||||
TableProperty,
|
||||
TableSingleView,
|
||||
} from '../../table-view-manager.js';
|
||||
import {
|
||||
getTableGroupRect,
|
||||
getVerticalIndicator,
|
||||
@@ -83,9 +86,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
return menu.action({
|
||||
name: config.config.name,
|
||||
isSelected: config.type === this.column.type$.value,
|
||||
prefix: renderUniLit(
|
||||
this.tableViewManager.propertyIconGet(config.type)
|
||||
),
|
||||
prefix: renderUniLit(config.renderer.icon),
|
||||
select: () => {
|
||||
this.column.typeSet?.(config.type);
|
||||
},
|
||||
@@ -323,16 +324,14 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
menu.action({
|
||||
name: 'Move Left',
|
||||
prefix: MoveLeftIcon(),
|
||||
hide: () => this.column.isFirst,
|
||||
hide: () => this.column.isFirst$.value,
|
||||
select: () => {
|
||||
const preId = this.tableViewManager.propertyPreGet(
|
||||
this.column.id
|
||||
)?.id;
|
||||
if (!preId) {
|
||||
const prev = this.column.prev$.value;
|
||||
if (!prev) {
|
||||
return;
|
||||
}
|
||||
this.tableViewManager.propertyMove(this.column.id, {
|
||||
id: preId,
|
||||
this.column.move({
|
||||
id: prev.id,
|
||||
before: true,
|
||||
});
|
||||
},
|
||||
@@ -340,16 +339,14 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
menu.action({
|
||||
name: 'Move Right',
|
||||
prefix: MoveRightIcon(),
|
||||
hide: () => this.column.isLast,
|
||||
hide: () => this.column.isLast$.value,
|
||||
select: () => {
|
||||
const nextId = this.tableViewManager.propertyNextGet(
|
||||
this.column.id
|
||||
)?.id;
|
||||
if (!nextId) {
|
||||
const next = this.column.next$.value;
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
this.tableViewManager.propertyMove(this.column.id, {
|
||||
id: nextId,
|
||||
this.column.move({
|
||||
id: next.id,
|
||||
before: false,
|
||||
});
|
||||
},
|
||||
@@ -472,7 +469,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor grabStatus: 'grabStart' | 'grabEnd' | 'grabbing' = 'grabEnd';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { startDrag } from '../../../../core/utils/drag.js';
|
||||
import { getResultInRange } from '../../../../core/utils/utils.js';
|
||||
import type { TableColumn } from '../../table-view-manager.js';
|
||||
import type { TableProperty } from '../../table-view-manager.js';
|
||||
|
||||
export class TableVerticalIndicator extends WithDisposable(ShadowlessElement) {
|
||||
static override styles = css`
|
||||
@@ -95,7 +95,7 @@ export const startDragWidthAdjustmentBar = (
|
||||
evt: PointerEvent,
|
||||
ele: HTMLElement,
|
||||
width: number,
|
||||
column: TableColumn
|
||||
column: TableProperty
|
||||
) => {
|
||||
const scale = width / column.width$.value;
|
||||
const left = ele.getBoundingClientRect().left;
|
||||
|
||||
@@ -70,7 +70,7 @@ export const popRowMenu = (
|
||||
},
|
||||
prefix: DeleteIcon(),
|
||||
select: () => {
|
||||
selectionController.view.rowDelete(rows);
|
||||
selectionController.view.rowsDelete(rows);
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -18,7 +18,9 @@ import type { TableSingleView } from '../../table-view-manager.js';
|
||||
import type { TableGroup } from '../group.js';
|
||||
import { openDetail, popRowMenu } from '../menu.js';
|
||||
|
||||
export class TableRow extends SignalWatcher(WithDisposable(ShadowlessElement)) {
|
||||
export class TableRowView extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
) {
|
||||
static override styles = css`
|
||||
.affine-database-block-row:has(.row-select-checkbox.selected) {
|
||||
background: var(--affine-primary-color-04);
|
||||
@@ -294,6 +296,6 @@ export class TableRow extends SignalWatcher(WithDisposable(ShadowlessElement)) {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'data-view-table-row': TableRow;
|
||||
'data-view-table-row': TableRowView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,12 +237,8 @@ export class DataViewTable extends DataViewBase<
|
||||
moveTo: (id, evt) => {
|
||||
const result = this.dragController.getInsertPosition(evt);
|
||||
if (result) {
|
||||
this.props.view.rowMove(
|
||||
id,
|
||||
result.position,
|
||||
undefined,
|
||||
result.groupKey
|
||||
);
|
||||
const row = this.props.view.rowGetOrCreate(id);
|
||||
row.move(result.position, undefined, result.groupKey);
|
||||
}
|
||||
},
|
||||
getSelection: () => {
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { GroupData } from '../../../core/group-by/trait.js';
|
||||
import { typeSystem } from '../../../core/index.js';
|
||||
import { statsFunctions } from '../../../core/statistics/index.js';
|
||||
import type { StatisticsConfig } from '../../../core/statistics/types.js';
|
||||
import type { TableColumn } from '../table-view-manager.js';
|
||||
import type { TableProperty } from '../table-view-manager.js';
|
||||
|
||||
const styles = css`
|
||||
.stats-cell {
|
||||
@@ -73,12 +73,12 @@ export class DatabaseColumnStatsCell extends SignalWatcher(
|
||||
static override styles = styles;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor column!: TableColumn;
|
||||
accessor column!: TableProperty;
|
||||
|
||||
cellValues$ = computed(() => {
|
||||
if (this.group) {
|
||||
return this.group.rows.map(id => {
|
||||
return this.column.valueGet(id);
|
||||
return this.group.rows.map(row => {
|
||||
return this.column.valueGet(row.rowId);
|
||||
});
|
||||
}
|
||||
return this.column.cells$.value.map(cell => cell.jsonValue$.value);
|
||||
@@ -157,7 +157,7 @@ export class DatabaseColumnStatsCell extends SignalWatcher(
|
||||
values$ = signal<unknown[]>([]);
|
||||
|
||||
statsResult$ = computed(() => {
|
||||
const meta = this.column.view.propertyMetaGet(this.column.type$.value);
|
||||
const meta = this.column.meta$.value;
|
||||
if (!meta) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { fromJson } from '../../core/property/utils';
|
||||
import { SortManager, sortTraitKey } from '../../core/sort/manager.js';
|
||||
import { PropertyBase } from '../../core/view-manager/property.js';
|
||||
import { type Row, RowBase } from '../../core/view-manager/row.js';
|
||||
import {
|
||||
type SingleView,
|
||||
SingleViewBase,
|
||||
@@ -24,10 +25,9 @@ import {
|
||||
import type { ViewManager } from '../../core/view-manager/view-manager.js';
|
||||
import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_COLUMN_WIDTH } from './consts.js';
|
||||
import type { TableViewData } from './define.js';
|
||||
import type { StatCalcOpType } from './types.js';
|
||||
|
||||
export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
propertiesWithoutFilter$ = computed(() => {
|
||||
propertiesRaw$ = computed(() => {
|
||||
const needShow = new Set(this.dataSource.properties$.value);
|
||||
const result: string[] = [];
|
||||
this.data$.value?.columns.forEach(v => {
|
||||
@@ -37,19 +37,11 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
}
|
||||
});
|
||||
result.push(...needShow);
|
||||
return result;
|
||||
return result.map(id => this.propertyGetOrCreate(id));
|
||||
});
|
||||
|
||||
private readonly computedColumns$ = computed(() => {
|
||||
return this.propertiesWithoutFilter$.value.map(id => {
|
||||
const column = this.propertyGet(id);
|
||||
return {
|
||||
id: column.id,
|
||||
hide: column.hide$.value,
|
||||
width: column.width$.value,
|
||||
statCalcType: column.statCalcOp$.value,
|
||||
};
|
||||
});
|
||||
properties$ = computed(() => {
|
||||
return this.propertiesRaw$.value.filter(property => !property.hide$.value);
|
||||
});
|
||||
|
||||
private readonly filter$ = computed(() => {
|
||||
@@ -81,8 +73,8 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
);
|
||||
|
||||
detailProperties$ = computed(() => {
|
||||
return this.propertiesWithoutFilter$.value.filter(
|
||||
id => this.propertyTypeGet(id) !== 'title'
|
||||
return this.propertiesRaw$.value.filter(
|
||||
property => property.type$.value !== 'title'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -115,9 +107,13 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
v => v,
|
||||
this.groupProperties.map(v => v.key)
|
||||
),
|
||||
sortRow: (key, ids) => {
|
||||
sortRow: (key, rows) => {
|
||||
const property = this.groupProperties.find(v => v.key === key);
|
||||
return sortByManually(ids, v => v, property?.manuallyCardSort ?? []);
|
||||
return sortByManually(
|
||||
rows,
|
||||
v => v.rowId,
|
||||
property?.manuallyCardSort ?? []
|
||||
);
|
||||
},
|
||||
changeGroupSort: keys => {
|
||||
const map = new Map(this.groupProperties.map(v => [v.key, v]));
|
||||
@@ -173,20 +169,14 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
mainProperties$ = computed(() => {
|
||||
return (
|
||||
this.data$.value?.header ?? {
|
||||
titleColumn: this.propertiesWithoutFilter$.value.find(
|
||||
id => this.propertyTypeGet(id) === 'title'
|
||||
),
|
||||
titleColumn: this.propertiesRaw$.value.find(
|
||||
property => property.type$.value === 'title'
|
||||
)?.id,
|
||||
iconColumn: 'type',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
propertyIds$ = computed(() => {
|
||||
return this.propertiesWithoutFilter$.value.filter(
|
||||
id => !this.propertyHideGet(id)
|
||||
);
|
||||
});
|
||||
|
||||
readonly$ = computed(() => {
|
||||
return this.manager.readonly$.value;
|
||||
});
|
||||
@@ -207,58 +197,12 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
super(viewManager, viewId);
|
||||
}
|
||||
|
||||
columnGetStatCalcOp(columnId: string): StatCalcOpType {
|
||||
return this.data$.value?.columns.find(v => v.id === columnId)?.statCalcType;
|
||||
}
|
||||
|
||||
columnGetWidth(columnId: string): number {
|
||||
const column = this.data$.value?.columns.find(v => v.id === columnId);
|
||||
if (column?.width != null) {
|
||||
return column.width;
|
||||
}
|
||||
const type = this.propertyTypeGet(columnId);
|
||||
if (type === 'title') {
|
||||
return 260;
|
||||
}
|
||||
return DEFAULT_COLUMN_WIDTH;
|
||||
}
|
||||
|
||||
columnUpdateStatCalcOp(columnId: string, op?: string): void {
|
||||
this.dataUpdate(() => {
|
||||
return {
|
||||
columns: this.computedColumns$.value.map(v =>
|
||||
v.id === columnId
|
||||
? {
|
||||
...v,
|
||||
statCalcType: op,
|
||||
}
|
||||
: v
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
columnUpdateWidth(columnId: string, width: number): void {
|
||||
this.dataUpdate(() => {
|
||||
return {
|
||||
columns: this.computedColumns$.value.map(v =>
|
||||
v.id === columnId
|
||||
? {
|
||||
...v,
|
||||
width: width,
|
||||
}
|
||||
: v
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
isShow(rowId: string): boolean {
|
||||
if (this.filter$.value?.conditions.length) {
|
||||
const rowMap = Object.fromEntries(
|
||||
this.properties$.value.map(column => [
|
||||
column.id,
|
||||
column.cellGet(rowId).jsonValue$.value,
|
||||
column.cellGetOrCreate(rowId).jsonValue$.value,
|
||||
])
|
||||
);
|
||||
return evalFilter(this.filter$.value, rowMap);
|
||||
@@ -266,54 +210,12 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
return true;
|
||||
}
|
||||
|
||||
minWidthGet(type: string): number {
|
||||
return (
|
||||
this.propertyMetaGet(type)?.config.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH
|
||||
);
|
||||
propertyGetOrCreate(columnId: string): TableProperty {
|
||||
return new TableProperty(this, columnId);
|
||||
}
|
||||
|
||||
propertyGet(columnId: string): TableColumn {
|
||||
return new TableColumn(this, columnId);
|
||||
}
|
||||
|
||||
propertyHideGet(columnId: string): boolean {
|
||||
return (
|
||||
this.data$.value?.columns.find(v => v.id === columnId)?.hide ?? false
|
||||
);
|
||||
}
|
||||
|
||||
propertyHideSet(columnId: string, hide: boolean): void {
|
||||
this.dataUpdate(() => {
|
||||
return {
|
||||
columns: this.computedColumns$.value.map(v =>
|
||||
v.id === columnId
|
||||
? {
|
||||
...v,
|
||||
hide,
|
||||
}
|
||||
: v
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
propertyMove(columnId: string, toAfterOfColumn: InsertToPosition): void {
|
||||
this.dataUpdate(() => {
|
||||
const columnIndex = this.computedColumns$.value.findIndex(
|
||||
v => v.id === columnId
|
||||
);
|
||||
if (columnIndex < 0) {
|
||||
return {};
|
||||
}
|
||||
const columns = [...this.computedColumns$.value];
|
||||
const [column] = columns.splice(columnIndex, 1);
|
||||
if (!column) return {};
|
||||
const index = insertPositionToIndex(toAfterOfColumn, columns);
|
||||
columns.splice(index, 0, column);
|
||||
return {
|
||||
columns,
|
||||
};
|
||||
});
|
||||
override rowGetOrCreate(rowId: string): TableRow {
|
||||
return new TableRow(this, rowId);
|
||||
}
|
||||
|
||||
override rowAdd(
|
||||
@@ -326,15 +228,15 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
if (filter.conditions.length > 0) {
|
||||
const defaultValues = generateDefaultValues(filter, this.vars$.value);
|
||||
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
||||
const property = this.propertyGet(propertyId);
|
||||
const propertyMeta = this.propertyMetaGet(property.type$.value);
|
||||
const property = this.propertyGetOrCreate(propertyId);
|
||||
const propertyMeta = property.meta$.value;
|
||||
if (propertyMeta) {
|
||||
const value = fromJson(propertyMeta.config, {
|
||||
value: jsonValue,
|
||||
data: property.data$.value,
|
||||
dataSource: this.dataSource,
|
||||
});
|
||||
this.cellValueSet(id, propertyId, value);
|
||||
this.cellGetOrCreate(id, propertyId).valueSet(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -345,45 +247,84 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||
return id;
|
||||
}
|
||||
|
||||
override rowMove(
|
||||
rowId: string,
|
||||
position: InsertToPosition,
|
||||
fromGroup?: string,
|
||||
toGroup?: string
|
||||
) {
|
||||
if (toGroup == null) {
|
||||
super.rowMove(rowId, position);
|
||||
return;
|
||||
}
|
||||
this.groupTrait.moveCardTo(rowId, fromGroup, toGroup, position);
|
||||
}
|
||||
|
||||
override rowNextGet(rowId: string): string | undefined {
|
||||
const index = this.rows$.value.indexOf(rowId);
|
||||
return this.rows$.value[index + 1];
|
||||
}
|
||||
|
||||
override rowPrevGet(rowId: string): string | undefined {
|
||||
const index = this.rows$.value.indexOf(rowId);
|
||||
return this.rows$.value[index - 1];
|
||||
}
|
||||
|
||||
override rowsMapping(rows: string[]) {
|
||||
override rowsMapping(rows: Row[]) {
|
||||
return this.sortManager.sort(super.rowsMapping(rows));
|
||||
}
|
||||
|
||||
readonly computedProperties$: ReadonlySignal<TableColumnData[]> = computed(
|
||||
() => {
|
||||
return this.propertiesRaw$.value.map(property => {
|
||||
return {
|
||||
id: property.id,
|
||||
hide: property.hide$.value,
|
||||
width: property.width$.value,
|
||||
statCalcType: property.statCalcOp$.value,
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export class TableColumn extends PropertyBase {
|
||||
type TableColumnData = TableViewData['columns'][number];
|
||||
|
||||
export class TableProperty extends PropertyBase {
|
||||
override hideSet(hide: boolean): void {
|
||||
this.viewDataUpdate(data => {
|
||||
return {
|
||||
...data,
|
||||
hide,
|
||||
};
|
||||
});
|
||||
}
|
||||
override move(position: InsertToPosition): void {
|
||||
this.tableView.dataUpdate(() => {
|
||||
const columnIndex = this.tableView.computedProperties$.value.findIndex(
|
||||
v => v.id === this.id
|
||||
);
|
||||
if (columnIndex < 0) {
|
||||
return {};
|
||||
}
|
||||
const columns = [...this.tableView.computedProperties$.value];
|
||||
const [column] = columns.splice(columnIndex, 1);
|
||||
if (!column) return {};
|
||||
const index = insertPositionToIndex(position, columns);
|
||||
columns.splice(index, 0, column);
|
||||
return {
|
||||
columns,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
hide$ = computed(() => {
|
||||
const hideFromViewData = this.viewData$.value?.hide;
|
||||
if (hideFromViewData != null) {
|
||||
return hideFromViewData;
|
||||
}
|
||||
const defaultShow = this.meta$.value?.config.fixed?.defaultShow;
|
||||
if (defaultShow != null) {
|
||||
return !defaultShow;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
statCalcOp$ = computed(() => {
|
||||
return this.tableView.columnGetStatCalcOp(this.id);
|
||||
return this.viewData$.value?.statCalcType;
|
||||
});
|
||||
|
||||
width$: ReadonlySignal<number> = computed(() => {
|
||||
return this.tableView.columnGetWidth(this.id);
|
||||
const column = this.viewData$.value;
|
||||
if (column?.width != null) {
|
||||
return column.width;
|
||||
}
|
||||
const type = this.type$.value;
|
||||
if (type === 'title') {
|
||||
return 260;
|
||||
}
|
||||
return DEFAULT_COLUMN_WIDTH;
|
||||
});
|
||||
|
||||
get minWidth() {
|
||||
return this.tableView.minWidthGet(this.type$.value);
|
||||
return this.meta$.value?.config.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -393,11 +334,62 @@ export class TableColumn extends PropertyBase {
|
||||
super(tableView as SingleView, columnId);
|
||||
}
|
||||
|
||||
viewDataUpdate(
|
||||
updater: (viewData: TableColumnData) => Partial<TableColumnData>
|
||||
): void {
|
||||
this.tableView.dataUpdate(data => {
|
||||
return {
|
||||
...data,
|
||||
columns: this.tableView.computedProperties$.value.map(v =>
|
||||
v.id === this.id ? { ...v, ...updater(v) } : v
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
viewData$ = computed(() => {
|
||||
return this.tableView.data$.value?.columns.find(v => v.id === this.id);
|
||||
});
|
||||
|
||||
updateStatCalcOp(type?: string): void {
|
||||
return this.tableView.columnUpdateStatCalcOp(this.id, type);
|
||||
this.viewDataUpdate(data => {
|
||||
return {
|
||||
...data,
|
||||
statCalcType: type,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
updateWidth(width: number): void {
|
||||
this.tableView.columnUpdateWidth(this.id, width);
|
||||
this.viewDataUpdate(data => {
|
||||
return {
|
||||
...data,
|
||||
width,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
export class TableRow extends RowBase {
|
||||
override move(
|
||||
position: InsertToPosition,
|
||||
fromGroup?: string,
|
||||
toGroup?: string
|
||||
): void {
|
||||
if (toGroup == null) {
|
||||
super.move(position);
|
||||
return;
|
||||
}
|
||||
this.tableView.groupTrait.moveCardTo(
|
||||
this.rowId,
|
||||
fromGroup,
|
||||
toGroup,
|
||||
position
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
readonly tableView: TableSingleView,
|
||||
rowId: string
|
||||
) {
|
||||
super(tableView, rowId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user