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:
@@ -24,6 +24,7 @@
|
|||||||
"@blocksuite/icons": "^2.2.12",
|
"@blocksuite/icons": "^2.2.12",
|
||||||
"@blocksuite/std": "workspace:*",
|
"@blocksuite/std": "workspace:*",
|
||||||
"@blocksuite/store": "workspace:*",
|
"@blocksuite/store": "workspace:*",
|
||||||
|
"@emotion/css": "^11.13.5",
|
||||||
"@floating-ui/dom": "^1.6.13",
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type {
|
|||||||
ColumnDataType,
|
ColumnDataType,
|
||||||
ColumnUpdater,
|
ColumnUpdater,
|
||||||
DatabaseBlockModel,
|
DatabaseBlockModel,
|
||||||
|
ParagraphBlockModel,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands';
|
import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands';
|
||||||
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||||
@@ -51,7 +52,59 @@ import {
|
|||||||
databaseBlockViews,
|
databaseBlockViews,
|
||||||
} from './views/index.js';
|
} from './views/index.js';
|
||||||
|
|
||||||
|
type SpacialProperty = {
|
||||||
|
valueSet: (rowId: string, propertyId: string, value: unknown) => void;
|
||||||
|
valueGet: (rowId: string, propertyId: string) => unknown;
|
||||||
|
};
|
||||||
export class DatabaseBlockDataSource extends DataSourceBase {
|
export class DatabaseBlockDataSource extends DataSourceBase {
|
||||||
|
spacialProperties: Record<string, SpacialProperty> = {
|
||||||
|
'created-time': {
|
||||||
|
valueSet: () => {},
|
||||||
|
valueGet: (rowId: string) => {
|
||||||
|
const model = this.getModelById(rowId) as ParagraphBlockModel;
|
||||||
|
if (!model) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return model.props['meta:createdAt'];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'created-by': {
|
||||||
|
valueSet: () => {},
|
||||||
|
valueGet: (rowId: string) => {
|
||||||
|
const model = this.getModelById(rowId) as
|
||||||
|
| ParagraphBlockModel
|
||||||
|
| undefined;
|
||||||
|
return model ? model.props['meta:createdBy'] : null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
valueSet: () => {},
|
||||||
|
valueGet: (rowId: string) => {
|
||||||
|
const model = this.getModelById(rowId);
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return getIcon(model);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
valueSet: () => {},
|
||||||
|
valueGet: (rowId: string) => {
|
||||||
|
const model = this.getModelById(rowId);
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return model.text;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
isSpacialProperty(propertyId: string): boolean {
|
||||||
|
return this.spacialProperties[propertyId] !== undefined;
|
||||||
|
}
|
||||||
|
spacialValueGet(rowId: string, propertyId: string): unknown {
|
||||||
|
return this.spacialProperties[propertyId]?.valueGet(rowId, propertyId);
|
||||||
|
}
|
||||||
|
|
||||||
static externalProperties = signal<PropertyMetaConfig[]>([]);
|
static externalProperties = signal<PropertyMetaConfig[]>([]);
|
||||||
static propertiesList = computed(() => {
|
static propertiesList = computed(() => {
|
||||||
return [
|
return [
|
||||||
@@ -196,20 +249,15 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cellValueGet(rowId: string, propertyId: string): unknown {
|
cellValueGet(rowId: string, propertyId: string): unknown {
|
||||||
if (propertyId === 'type') {
|
if (this.isSpacialProperty(propertyId)) {
|
||||||
const model = this.getModelById(rowId);
|
return this.spacialValueGet(rowId, propertyId);
|
||||||
if (!model) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return getIcon(model);
|
|
||||||
}
|
}
|
||||||
const type = this.propertyTypeGet(propertyId);
|
const type = this.propertyTypeGet(propertyId);
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type === 'title') {
|
if (type === 'title') {
|
||||||
const model = this.getModelById(rowId);
|
return this.spacialValueGet(rowId, 'title');
|
||||||
return model?.text;
|
|
||||||
}
|
}
|
||||||
const meta = this.propertyMetaGet(type);
|
const meta = this.propertyMetaGet(type);
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export class BlockRenderer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return html` <div class="database-block-detail-header-icon">
|
return html` <div class="database-block-detail-header-icon">
|
||||||
${this.view.cellValueGet(this.rowId, iconColumn)}
|
${this.view.cellGetOrCreate(this.rowId, iconColumn).value$.value}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { DatabaseBlockComponent } from './database-block';
|
|||||||
import { DatabaseDndPreviewBlockComponent } from './database-dnd-preview-block';
|
import { DatabaseDndPreviewBlockComponent } from './database-dnd-preview-block';
|
||||||
import { BlockRenderer } from './detail-panel/block-renderer';
|
import { BlockRenderer } from './detail-panel/block-renderer';
|
||||||
import { NoteRenderer } from './detail-panel/note-renderer';
|
import { NoteRenderer } from './detail-panel/note-renderer';
|
||||||
|
import { CreatedTimeCell } from './properties/created-time/cell-renderer';
|
||||||
import { LinkCell } from './properties/link/cell-renderer';
|
import { LinkCell } from './properties/link/cell-renderer';
|
||||||
import { RichTextCell } from './properties/rich-text/cell-renderer';
|
import { RichTextCell } from './properties/rich-text/cell-renderer';
|
||||||
import { IconCell } from './properties/title/icon';
|
import { IconCell } from './properties/title/icon';
|
||||||
@@ -15,6 +16,7 @@ export function effects() {
|
|||||||
customElements.define('affine-database-link-cell', LinkCell);
|
customElements.define('affine-database-link-cell', LinkCell);
|
||||||
customElements.define('data-view-header-area-text', HeaderAreaTextCell);
|
customElements.define('data-view-header-area-text', HeaderAreaTextCell);
|
||||||
customElements.define('affine-database-rich-text-cell', RichTextCell);
|
customElements.define('affine-database-rich-text-cell', RichTextCell);
|
||||||
|
customElements.define('affine-database-created-time-cell', CreatedTimeCell);
|
||||||
customElements.define('center-peek', CenterPeek);
|
customElements.define('center-peek', CenterPeek);
|
||||||
customElements.define('database-datasource-note-renderer', NoteRenderer);
|
customElements.define('database-datasource-note-renderer', NoteRenderer);
|
||||||
customElements.define('database-datasource-block-renderer', BlockRenderer);
|
customElements.define('database-datasource-block-renderer', BlockRenderer);
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import {
|
||||||
|
BaseCellRenderer,
|
||||||
|
createFromBaseCellRenderer,
|
||||||
|
createIcon,
|
||||||
|
} from '@blocksuite/data-view';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { format } from 'date-fns/format';
|
||||||
|
import { html } from 'lit';
|
||||||
|
|
||||||
|
import { createdTimePropertyModelConfig } from './define.js';
|
||||||
|
const createdTimeCellStyle = css({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
const textStyle = css({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
export class CreatedTimeCell extends BaseCellRenderer<number, number> {
|
||||||
|
renderContent() {
|
||||||
|
const formattedDate = this.value
|
||||||
|
? format(this.value, 'yyyy-MM-dd HH:mm:ss')
|
||||||
|
: '';
|
||||||
|
return html`<div class="${textStyle}">${formattedDate}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
override connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.classList.add(createdTimeCellStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
override beforeEnterEditMode() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override render() {
|
||||||
|
return html`<div class="date-container">${this.renderContent()}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createdTimeColumnConfig =
|
||||||
|
createdTimePropertyModelConfig.createPropertyMeta({
|
||||||
|
icon: createIcon('DateTimeIcon'),
|
||||||
|
cellRenderer: {
|
||||||
|
view: createFromBaseCellRenderer(CreatedTimeCell),
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { propertyType, t } from '@blocksuite/data-view';
|
||||||
|
import { format } from 'date-fns/format';
|
||||||
|
import zod from 'zod';
|
||||||
|
|
||||||
|
export const createdTimeColumnType = propertyType('created-time');
|
||||||
|
export const createdTimePropertyModelConfig = createdTimeColumnType.modelConfig(
|
||||||
|
{
|
||||||
|
name: 'Created Time',
|
||||||
|
propertyData: {
|
||||||
|
schema: zod.object({}),
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
jsonValue: {
|
||||||
|
schema: zod.number().nullable(),
|
||||||
|
isEmpty: () => false,
|
||||||
|
type: () => t.date.instance(),
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.number().nullable(),
|
||||||
|
default: () => null,
|
||||||
|
toString: ({ value }) =>
|
||||||
|
value != null ? format(value, 'yyyy-MM-dd HH:mm:ss') : '',
|
||||||
|
fromString: () => {
|
||||||
|
return { value: null };
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
|
},
|
||||||
|
fixed: {
|
||||||
|
defaultData: {},
|
||||||
|
defaultShow: false,
|
||||||
|
defaultOrder: 'end',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { propertyPresets } from '@blocksuite/data-view/property-presets';
|
import { propertyPresets } from '@blocksuite/data-view/property-presets';
|
||||||
|
|
||||||
|
import { createdTimeColumnConfig } from './created-time/cell-renderer.js';
|
||||||
import { linkColumnConfig } from './link/cell-renderer.js';
|
import { linkColumnConfig } from './link/cell-renderer.js';
|
||||||
import { richTextColumnConfig } from './rich-text/cell-renderer.js';
|
import { richTextColumnConfig } from './rich-text/cell-renderer.js';
|
||||||
import { titleColumnConfig } from './title/cell-renderer.js';
|
import { titleColumnConfig } from './title/cell-renderer.js';
|
||||||
@@ -24,4 +25,5 @@ export const databaseBlockProperties = {
|
|||||||
linkColumnConfig,
|
linkColumnConfig,
|
||||||
richTextColumnConfig,
|
richTextColumnConfig,
|
||||||
titleColumnConfig,
|
titleColumnConfig,
|
||||||
|
createdTimeColumnConfig,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -310,7 +310,6 @@ export class RichTextCell extends BaseCellRenderer<Text, string> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(text);
|
|
||||||
inlineEditor.insertText(inlineRange, text);
|
inlineEditor.insertText(inlineRange, text);
|
||||||
inlineEditor.setInlineRange({
|
inlineEditor.setInlineRange({
|
||||||
index: inlineRange.index + text.length,
|
index: inlineRange.index + text.length,
|
||||||
|
|||||||
@@ -271,7 +271,8 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
|
|||||||
const iconColumn = this.view.mainProperties$.value.iconColumn;
|
const iconColumn = this.view.mainProperties$.value.iconColumn;
|
||||||
if (!iconColumn) return;
|
if (!iconColumn) return;
|
||||||
|
|
||||||
const icon = this.view.cellValueGet(this.cell.rowId, iconColumn) as string;
|
const icon = this.view.cellGetOrCreate(this.cell.rowId, iconColumn).value$
|
||||||
|
.value;
|
||||||
if (!icon) return;
|
if (!icon) return;
|
||||||
return icon;
|
return icon;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export class DataViewPropertiesSettingView extends SignalWatcher(
|
|||||||
accessor view!: SingleView;
|
accessor view!: SingleView;
|
||||||
|
|
||||||
items$ = computed(() => {
|
items$ = computed(() => {
|
||||||
return this.view.propertiesWithoutFilter$.value;
|
return this.view.propertiesRaw$.value.map(property => property.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
renderProperty = (property: Property) => {
|
renderProperty = (property: Property) => {
|
||||||
@@ -171,8 +171,7 @@ export class DataViewPropertiesSettingView extends SignalWatcher(
|
|||||||
const activeIndex = properties.findIndex(id => id === activeId);
|
const activeIndex = properties.findIndex(id => id === activeId);
|
||||||
const overIndex = properties.findIndex(id => id === over.id);
|
const overIndex = properties.findIndex(id => id === over.id);
|
||||||
|
|
||||||
this.view.propertyMove(
|
this.view.propertyGetOrCreate(activeId).move(
|
||||||
activeId,
|
|
||||||
activeIndex > overIndex
|
activeIndex > overIndex
|
||||||
? {
|
? {
|
||||||
before: true,
|
before: true,
|
||||||
@@ -198,9 +197,7 @@ export class DataViewPropertiesSettingView extends SignalWatcher(
|
|||||||
});
|
});
|
||||||
|
|
||||||
private itemsGroup() {
|
private itemsGroup() {
|
||||||
return this.view.propertiesWithoutFilter$.value.map(id =>
|
return this.view.propertiesRaw$.value;
|
||||||
this.view.propertyGet(id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
@@ -246,14 +243,12 @@ export const popPropertiesSetting = (
|
|||||||
text: 'Properties',
|
text: 'Properties',
|
||||||
onBack: props.onBack,
|
onBack: props.onBack,
|
||||||
postfix: () => {
|
postfix: () => {
|
||||||
const items = props.view.propertiesWithoutFilter$.value.map(id =>
|
const items = props.view.propertiesRaw$.value;
|
||||||
props.view.propertyGet(id)
|
const isAllShowed = items.every(property => !property.hide$.value);
|
||||||
);
|
|
||||||
const isAllShowed = items.every(v => !v.hide$.value);
|
|
||||||
const clickChangeAll = () => {
|
const clickChangeAll = () => {
|
||||||
props.view.propertiesWithoutFilter$.value.forEach(id => {
|
items.forEach(property => {
|
||||||
if (props.view.propertyCanHide(id)) {
|
if (property.hideCanSet) {
|
||||||
props.view.propertyHideSet(id, isAllShowed);
|
property.hideSet(isAllShowed);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,9 +58,7 @@ export const typeConfig = (property: Property) => {
|
|||||||
return menu.action({
|
return menu.action({
|
||||||
isSelected: config.type === property.type$.value,
|
isSelected: config.type === property.type$.value,
|
||||||
name: config.config.name,
|
name: config.config.name,
|
||||||
prefix: renderUniLit(
|
prefix: renderUniLit(config.renderer.icon),
|
||||||
property.view.propertyIconGet(config.type)
|
|
||||||
),
|
|
||||||
select: () => {
|
select: () => {
|
||||||
if (property.type$.value === config.type) {
|
if (property.type$.value === config.type) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export class RecordDetail extends SignalWatcher(
|
|||||||
items: this.view.propertyMetas$.value.map(meta => {
|
items: this.view.propertyMetas$.value.map(meta => {
|
||||||
return menu.action({
|
return menu.action({
|
||||||
name: meta.config.name,
|
name: meta.config.name,
|
||||||
prefix: renderUniLit(this.view.propertyIconGet(meta.type)),
|
prefix: renderUniLit(meta.renderer.icon),
|
||||||
select: () => {
|
select: () => {
|
||||||
this.view.propertyAdd('end', meta.type);
|
this.view.propertyAdd('end', meta.type);
|
||||||
},
|
},
|
||||||
@@ -136,9 +136,7 @@ export class RecordDetail extends SignalWatcher(
|
|||||||
accessor view!: SingleView;
|
accessor view!: SingleView;
|
||||||
|
|
||||||
properties$ = computed(() => {
|
properties$ = computed(() => {
|
||||||
return this.view.detailProperties$.value.map(id =>
|
return this.view.detailProperties$.value;
|
||||||
this.view.propertyGet(id)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
selection = new DetailSelection(this);
|
selection = new DetailSelection(this);
|
||||||
@@ -184,16 +182,20 @@ export class RecordDetail extends SignalWatcher(
|
|||||||
this.dataset.widgetId = 'affine-detail-widget';
|
this.dataset.widgetId = 'affine-detail-widget';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row$ = computed(() => {
|
||||||
|
return this.view.rowGetOrCreate(this.rowId);
|
||||||
|
});
|
||||||
|
|
||||||
hasNext() {
|
hasNext() {
|
||||||
return this.view.rowNextGet(this.rowId) != null;
|
return this.row$.value.next$.value != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPrev() {
|
hasPrev() {
|
||||||
return this.view.rowPrevGet(this.rowId) != null;
|
return this.row$.value.prev$.value != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextRow() {
|
nextRow() {
|
||||||
const rowId = this.view.rowNextGet(this.rowId);
|
const rowId = this.row$.value.next$.value?.rowId;
|
||||||
if (rowId == null) {
|
if (rowId == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -202,7 +204,7 @@ export class RecordDetail extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
prevRow() {
|
prevRow() {
|
||||||
const rowId = this.view.rowPrevGet(this.rowId);
|
const rowId = this.row$.value.prev$.value?.rowId;
|
||||||
if (rowId == null) {
|
if (rowId == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,15 +140,16 @@ export class RecordField extends SignalWatcher(
|
|||||||
${MoveLeftIcon()}
|
${MoveLeftIcon()}
|
||||||
</div>`,
|
</div>`,
|
||||||
hide: () =>
|
hide: () =>
|
||||||
properties.findIndex(v => v === this.column.id) === 0,
|
properties.findIndex(
|
||||||
|
property => property.id === this.column.id
|
||||||
|
) === 0,
|
||||||
select: () => {
|
select: () => {
|
||||||
const index = properties.findIndex(v => v === this.column.id);
|
const prev = this.column.prev$.value;
|
||||||
const targetId = properties[index - 1];
|
if (!prev) {
|
||||||
if (!targetId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.view.propertyMove(this.column.id, {
|
this.column.move({
|
||||||
id: targetId,
|
id: prev.id,
|
||||||
before: true,
|
before: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -161,16 +162,17 @@ export class RecordField extends SignalWatcher(
|
|||||||
${MoveRightIcon()}
|
${MoveRightIcon()}
|
||||||
</div>`,
|
</div>`,
|
||||||
hide: () =>
|
hide: () =>
|
||||||
properties.findIndex(v => v === this.column.id) ===
|
properties.findIndex(
|
||||||
|
property => property.id === this.column.id
|
||||||
|
) ===
|
||||||
properties.length - 1,
|
properties.length - 1,
|
||||||
select: () => {
|
select: () => {
|
||||||
const index = properties.findIndex(v => v === this.column.id);
|
const next = this.column.next$.value;
|
||||||
const targetId = properties[index + 1];
|
if (!next) {
|
||||||
if (!targetId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.view.propertyMove(this.column.id, {
|
this.column.move({
|
||||||
id: targetId,
|
id: next.id,
|
||||||
before: false,
|
before: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -211,7 +213,7 @@ export class RecordField extends SignalWatcher(
|
|||||||
accessor rowId!: string;
|
accessor rowId!: string;
|
||||||
|
|
||||||
cell$ = computed(() => {
|
cell$ = computed(() => {
|
||||||
return this.column.cellGet(this.rowId);
|
return this.column.cellGetOrCreate(this.rowId);
|
||||||
});
|
});
|
||||||
|
|
||||||
changeEditing = (editing: boolean) => {
|
changeEditing = (editing: boolean) => {
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ const GroupTitleMobile = (
|
|||||||
value: groupData.value,
|
value: groupData.value,
|
||||||
data: groupData.property.data$.value,
|
data: groupData.property.data$.value,
|
||||||
updateData: groupData.manager.updateData,
|
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,
|
readonly: ops.readonly,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -140,7 +144,11 @@ export const GroupTitle = (
|
|||||||
value: groupData.value,
|
value: groupData.value,
|
||||||
data: groupData.property.data$.value,
|
data: groupData.property.data$.value,
|
||||||
updateData: groupData.manager.updateData,
|
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,
|
readonly: ops.readonly,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -189,22 +189,25 @@ export const selectGroupByProperty = (
|
|||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
menu.group({
|
menu.group({
|
||||||
items: view.propertiesWithoutFilter$.value
|
items: view.propertiesRaw$.value
|
||||||
.filter(id => {
|
.filter(property => {
|
||||||
if (view.propertyGet(id).type$.value === 'title') {
|
if (property.type$.value === 'title') {
|
||||||
return false;
|
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 => {
|
.map<MenuConfig>(property => {
|
||||||
const property = view.propertyGet(id);
|
|
||||||
return menu.action({
|
return menu.action({
|
||||||
name: property.name$.value,
|
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>`,
|
prefix: html` <uni-lit .uni="${property.icon}"></uni-lit>`,
|
||||||
select: () => {
|
select: () => {
|
||||||
group.changeGroup(id);
|
group.changeGroup(property.id);
|
||||||
ops?.onSelect?.(id);
|
ops?.onSelect?.(property.id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
@@ -254,7 +257,7 @@ export const popGroupSetting = (
|
|||||||
if (!type) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const icon = view.propertyIconGet(type);
|
const icon = groupProperty.icon;
|
||||||
const menuHandler = popMenu(target, {
|
const menuHandler = popMenu(target, {
|
||||||
options: {
|
options: {
|
||||||
title: {
|
title: {
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ import { computed, type ReadonlySignal } from '@preact/signals-core';
|
|||||||
import type { GroupBy, GroupProperty } from '../common/types.js';
|
import type { GroupBy, GroupProperty } from '../common/types.js';
|
||||||
import type { TypeInstance } from '../logical/type.js';
|
import type { TypeInstance } from '../logical/type.js';
|
||||||
import { createTraitKey } from '../traits/key.js';
|
import { createTraitKey } from '../traits/key.js';
|
||||||
|
import { computedLock } from '../utils/lock.js';
|
||||||
import type { Property } from '../view-manager/property.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 type { SingleView } from '../view-manager/single-view.js';
|
||||||
import { defaultGroupBy } from './default.js';
|
import { defaultGroupBy } from './default.js';
|
||||||
import { groupByMatcher } from './matcher.js';
|
import { groupByMatcher } from './matcher.js';
|
||||||
|
|
||||||
export type GroupData = {
|
export type GroupData = {
|
||||||
manager: GroupTrait;
|
manager: GroupTrait;
|
||||||
property: Property;
|
property: Property;
|
||||||
@@ -19,12 +20,10 @@ export type GroupData = {
|
|||||||
name: string;
|
name: string;
|
||||||
type: TypeInstance;
|
type: TypeInstance;
|
||||||
value: unknown;
|
value: unknown;
|
||||||
rows: string[];
|
rows: Row[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class GroupTrait {
|
export class GroupTrait {
|
||||||
private preDataList: GroupData[] | undefined;
|
|
||||||
|
|
||||||
config$ = computed(() => {
|
config$ = computed(() => {
|
||||||
const groupBy = this.groupBy$.value;
|
const groupBy = this.groupBy$.value;
|
||||||
if (!groupBy) {
|
if (!groupBy) {
|
||||||
@@ -42,7 +41,7 @@ export class GroupTrait {
|
|||||||
if (!groupBy) {
|
if (!groupBy) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.view.propertyGet(groupBy.columnId);
|
return this.view.propertyGetOrCreate(groupBy.columnId);
|
||||||
});
|
});
|
||||||
|
|
||||||
staticGroupDataMap$ = computed<
|
staticGroupDataMap$ = computed<
|
||||||
@@ -81,8 +80,9 @@ export class GroupTrait {
|
|||||||
const groupMap: Record<string, GroupData> = Object.fromEntries(
|
const groupMap: Record<string, GroupData> = Object.fromEntries(
|
||||||
Object.entries(staticGroupMap).map(([k, v]) => [k, { ...v, rows: [] }])
|
Object.entries(staticGroupMap).map(([k, v]) => [k, { ...v, rows: [] }])
|
||||||
);
|
);
|
||||||
this.view.rows$.value.forEach(id => {
|
this.view.rows$.value.forEach(row => {
|
||||||
const value = this.view.cellJsonValueGet(id, groupBy.columnId);
|
const value = this.view.cellGetOrCreate(row.rowId, groupBy.columnId)
|
||||||
|
.jsonValue$.value;
|
||||||
const keys = config.valuesGroup(value, tType);
|
const keys = config.valuesGroup(value, tType);
|
||||||
keys.forEach(({ key, value }) => {
|
keys.forEach(({ key, value }) => {
|
||||||
if (!groupMap[key]) {
|
if (!groupMap[key]) {
|
||||||
@@ -96,40 +96,36 @@ export class GroupTrait {
|
|||||||
type: tType,
|
type: tType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
groupMap[key].rows.push(id);
|
groupMap[key].rows.push(row);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return groupMap;
|
return groupMap;
|
||||||
});
|
});
|
||||||
|
|
||||||
private readonly _groupsDataList$ = computed(() => {
|
groupsDataList$ = computedLock(
|
||||||
const groupMap = this.groupDataMap$.value;
|
computed(() => {
|
||||||
if (!groupMap) {
|
const groupMap = this.groupDataMap$.value;
|
||||||
return;
|
if (!groupMap) {
|
||||||
}
|
return;
|
||||||
const sortedGroup = this.ops.sortGroup(Object.keys(groupMap));
|
}
|
||||||
sortedGroup.forEach(key => {
|
const sortedGroup = this.ops.sortGroup(Object.keys(groupMap));
|
||||||
if (!groupMap[key]) return;
|
sortedGroup.forEach(key => {
|
||||||
groupMap[key].rows = this.ops.sortRow(key, groupMap[key].rows);
|
if (!groupMap[key]) return;
|
||||||
});
|
groupMap[key].rows = this.ops.sortRow(key, groupMap[key].rows);
|
||||||
return (this.preDataList = sortedGroup
|
});
|
||||||
.map(key => groupMap[key])
|
return sortedGroup
|
||||||
.filter((v): v is GroupData => v != null));
|
.map(key => groupMap[key])
|
||||||
});
|
.filter((v): v is GroupData => v != null);
|
||||||
|
}),
|
||||||
groupsDataList$ = computed(() => {
|
this.view.isLocked$
|
||||||
if (this.view.isLocked$.value) {
|
);
|
||||||
return this.preDataList;
|
|
||||||
}
|
|
||||||
return (this.preDataList = this._groupsDataList$.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
updateData = (data: NonNullable<unknown>) => {
|
updateData = (data: NonNullable<unknown>) => {
|
||||||
const propertyId = this.propertyId;
|
const propertyId = this.propertyId;
|
||||||
if (!propertyId) {
|
if (!propertyId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.view.propertyDataSet(propertyId, data);
|
this.view.propertyGetOrCreate(propertyId).dataUpdate(() => data);
|
||||||
};
|
};
|
||||||
|
|
||||||
get addGroup() {
|
get addGroup() {
|
||||||
@@ -137,7 +133,7 @@ export class GroupTrait {
|
|||||||
if (!type) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.view.propertyMetaGet(type)?.config.addGroup;
|
return this.view.manager.dataSource.propertyMetaGet(type)?.config.addGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
get propertyId() {
|
get propertyId() {
|
||||||
@@ -150,7 +146,7 @@ export class GroupTrait {
|
|||||||
private readonly ops: {
|
private readonly ops: {
|
||||||
groupBySet: (groupBy: GroupBy | undefined) => void;
|
groupBySet: (groupBy: GroupBy | undefined) => void;
|
||||||
sortGroup: (keys: string[]) => string[];
|
sortGroup: (keys: string[]) => string[];
|
||||||
sortRow: (groupKey: string, rowIds: string[]) => string[];
|
sortRow: (groupKey: string, rows: Row[]) => Row[];
|
||||||
changeGroupSort: (keys: string[]) => void;
|
changeGroupSort: (keys: string[]) => void;
|
||||||
changeRowSort: (
|
changeRowSort: (
|
||||||
groupKeys: string[],
|
groupKeys: string[],
|
||||||
@@ -169,8 +165,11 @@ export class GroupTrait {
|
|||||||
const addTo = this.config$.value?.addToGroup ?? (value => value);
|
const addTo = this.config$.value?.addToGroup ?? (value => value);
|
||||||
const v = groupMap[key]?.value;
|
const v = groupMap[key]?.value;
|
||||||
if (v != null) {
|
if (v != null) {
|
||||||
const newValue = addTo(v, this.view.cellJsonValueGet(rowId, propertyId));
|
const newValue = addTo(
|
||||||
this.view.cellJsonValueSet(rowId, propertyId, newValue);
|
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);
|
this.ops.groupBySet(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const column = this.view.propertyGet(columnId);
|
const column = this.view.propertyGetOrCreate(columnId);
|
||||||
const propertyMeta = this.view.propertyMetaGet(column.type$.value);
|
const propertyMeta = this.view.manager.dataSource.propertyMetaGet(
|
||||||
|
column.type$.value
|
||||||
|
);
|
||||||
if (propertyMeta) {
|
if (propertyMeta) {
|
||||||
this.ops.groupBySet(
|
this.ops.groupBySet(
|
||||||
defaultGroupBy(
|
defaultGroupBy(
|
||||||
@@ -238,15 +239,18 @@ export class GroupTrait {
|
|||||||
if (group) {
|
if (group) {
|
||||||
newValue = remove(
|
newValue = remove(
|
||||||
group.value,
|
group.value,
|
||||||
this.view.cellJsonValueGet(rowId, propertyId)
|
this.view.cellGetOrCreate(rowId, propertyId).jsonValue$.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const addTo = this.config$.value?.addToGroup ?? (value => value);
|
const addTo = this.config$.value?.addToGroup ?? (value => value);
|
||||||
newValue = addTo(groupMap[toGroupKey]?.value ?? null, newValue);
|
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 rows =
|
||||||
const index = insertPositionToIndex(position, rows, id => id);
|
groupMap[toGroupKey]?.rows
|
||||||
|
.filter(row => row.rowId !== rowId)
|
||||||
|
.map(row => row.rowId) ?? [];
|
||||||
|
const index = insertPositionToIndex(position, rows, row => row);
|
||||||
rows.splice(index, 0, rowId);
|
rows.splice(index, 0, rowId);
|
||||||
this.changeCardSort(toGroupKey, rows);
|
this.changeCardSort(toGroupKey, rows);
|
||||||
}
|
}
|
||||||
@@ -278,9 +282,9 @@ export class GroupTrait {
|
|||||||
const remove = this.config$.value?.removeFromGroup ?? (() => undefined);
|
const remove = this.config$.value?.removeFromGroup ?? (() => undefined);
|
||||||
const newValue = remove(
|
const newValue = remove(
|
||||||
groupMap[key]?.value ?? null,
|
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) {
|
updateValue(rows: string[], value: unknown) {
|
||||||
@@ -288,8 +292,8 @@ export class GroupTrait {
|
|||||||
if (!propertyId) {
|
if (!propertyId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rows.forEach(id => {
|
rows.forEach(rowId => {
|
||||||
this.view.cellJsonValueSet(id, propertyId, value);
|
this.view.cellGetOrCreate(rowId, propertyId).jsonValueSet(value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export type PropertyConfig<Data, RawValue = unknown, JsonValue = unknown> = {
|
|||||||
};
|
};
|
||||||
fixed?: {
|
fixed?: {
|
||||||
defaultData: Data;
|
defaultData: Data;
|
||||||
defaultOrder?: string;
|
defaultOrder?: 'start' | 'end';
|
||||||
defaultShow?: boolean;
|
defaultShow?: boolean;
|
||||||
};
|
};
|
||||||
minWidth?: number;
|
minWidth?: number;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { DataTypeOf } from '../logical/data-type.js';
|
|||||||
import { t } from '../logical/index.js';
|
import { t } from '../logical/index.js';
|
||||||
import type { TypeInstance } from '../logical/type.js';
|
import type { TypeInstance } from '../logical/type.js';
|
||||||
import { typeSystem } from '../logical/type-system.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';
|
import type { Sort } from './types.js';
|
||||||
|
|
||||||
export const Compare = {
|
export const Compare = {
|
||||||
@@ -16,14 +16,14 @@ const evalRef = (
|
|||||||
view: SingleView,
|
view: SingleView,
|
||||||
ref: VariableRef
|
ref: VariableRef
|
||||||
):
|
):
|
||||||
| ((row: string) => {
|
| ((row: Row) => {
|
||||||
value: unknown;
|
value: unknown;
|
||||||
ttype?: TypeInstance;
|
ttype?: TypeInstance;
|
||||||
})
|
})
|
||||||
| undefined => {
|
| undefined => {
|
||||||
const ttype = view.propertyDataTypeGet(ref.name);
|
const ttype = view.propertyGetOrCreate(ref.name).dataType$.value;
|
||||||
return row => ({
|
return row => ({
|
||||||
value: view.cellJsonValueGet(row, ref.name),
|
value: view.cellGetOrCreate(row.rowId, ref.name).jsonValue$.value,
|
||||||
ttype,
|
ttype,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -153,7 +153,7 @@ const compare = (type: TypeInstance, a: unknown, b: unknown): CompareType => {
|
|||||||
export const evalSort = (
|
export const evalSort = (
|
||||||
sort: Sort,
|
sort: Sort,
|
||||||
view: SingleView
|
view: SingleView
|
||||||
): ((rowA: string, rowB: string) => number) | undefined => {
|
): ((rowA: Row, rowB: Row) => number) | undefined => {
|
||||||
if (sort.sortBy.length) {
|
if (sort.sortBy.length) {
|
||||||
const sortBy = sort.sortBy.map(sort => {
|
const sortBy = sort.sortBy.map(sort => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||||
|
|
||||||
import { createTraitKey } from '../traits/key.js';
|
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 { evalSort } from './eval.js';
|
||||||
import type { Sort, SortBy } from './types.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) {
|
if (!this.sort$.value) {
|
||||||
return rows;
|
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 { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||||
|
|
||||||
|
import { fromJson } from '../property/utils.js';
|
||||||
import type { Property } from './property.js';
|
import type { Property } from './property.js';
|
||||||
import type { Row } from './row.js';
|
import type { Row } from './row.js';
|
||||||
import type { SingleView } from './single-view.js';
|
import type { SingleView } from './single-view.js';
|
||||||
@@ -9,18 +10,19 @@ export interface Cell<
|
|||||||
JsonValue = unknown,
|
JsonValue = unknown,
|
||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
> {
|
> {
|
||||||
readonly rowId: string;
|
|
||||||
readonly view: SingleView;
|
readonly view: SingleView;
|
||||||
|
readonly rowId: string;
|
||||||
readonly row: Row;
|
readonly row: Row;
|
||||||
readonly propertyId: string;
|
readonly propertyId: string;
|
||||||
readonly property: Property<RawValue, JsonValue, Data>;
|
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 value$: ReadonlySignal<RawValue | undefined>;
|
||||||
|
readonly jsonValue$: ReadonlySignal<JsonValue | undefined>;
|
||||||
|
readonly stringValue$: ReadonlySignal<string | undefined>;
|
||||||
|
|
||||||
valueSet(value: RawValue | undefined): void;
|
valueSet(value: RawValue | undefined): void;
|
||||||
|
jsonValueSet(value: JsonValue | undefined): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CellBase<
|
export class CellBase<
|
||||||
@@ -29,10 +31,12 @@ export class CellBase<
|
|||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
> implements Cell<RawValue, JsonValue, Data>
|
> implements Cell<RawValue, JsonValue, Data>
|
||||||
{
|
{
|
||||||
|
get dataSource() {
|
||||||
|
return this.view.manager.dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
meta$ = computed(() => {
|
meta$ = computed(() => {
|
||||||
return this.view.manager.dataSource.propertyMetaGet(
|
return this.dataSource.propertyMetaGet(this.property.type$.value);
|
||||||
this.property.type$.value
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
value$ = computed(() => {
|
value$ = computed(() => {
|
||||||
@@ -51,20 +55,37 @@ export class CellBase<
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonValue$: ReadonlySignal<JsonValue> = computed(() => {
|
jsonValue$: ReadonlySignal<JsonValue | undefined> = computed(() => {
|
||||||
return this.view.cellJsonValueGet(this.rowId, this.propertyId) as JsonValue;
|
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(() => {
|
property$ = computed(() => {
|
||||||
return this.view.propertyGet(this.propertyId) as Property<
|
return this.view.propertyGetOrCreate(this.propertyId) as Property<
|
||||||
RawValue,
|
RawValue,
|
||||||
JsonValue,
|
JsonValue,
|
||||||
Data
|
Data
|
||||||
>;
|
>;
|
||||||
});
|
});
|
||||||
|
|
||||||
stringValue$: ReadonlySignal<string> = computed(() => {
|
stringValue$: ReadonlySignal<string | undefined> = computed(() => {
|
||||||
return this.view.cellStringValueGet(this.rowId, this.propertyId)!;
|
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> {
|
get property(): Property<RawValue, JsonValue, Data> {
|
||||||
@@ -72,7 +93,7 @@ export class CellBase<
|
|||||||
}
|
}
|
||||||
|
|
||||||
get row(): Row {
|
get row(): Row {
|
||||||
return this.view.rowGet(this.rowId);
|
return this.view.rowGetOrCreate(this.rowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -88,4 +109,17 @@ export class CellBase<
|
|||||||
value
|
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 { UniComponent } from '@blocksuite/affine-shared/types';
|
||||||
|
import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||||
|
|
||||||
import type { TypeInstance } from '../logical/type.js';
|
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 { PropertyDataUpdater } from '../types.js';
|
||||||
import type { Cell } from './cell.js';
|
import type { Cell } from './cell.js';
|
||||||
import type { SingleView } from './single-view.js';
|
import type { SingleView } from './single-view.js';
|
||||||
@@ -13,14 +14,17 @@ export interface Property<
|
|||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
> {
|
> {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly index: number;
|
readonly index$: ReadonlySignal<number | undefined>;
|
||||||
readonly view: SingleView;
|
readonly view: SingleView;
|
||||||
readonly isFirst: boolean;
|
readonly isFirst$: ReadonlySignal<boolean>;
|
||||||
readonly isLast: boolean;
|
readonly isLast$: ReadonlySignal<boolean>;
|
||||||
|
readonly next$: ReadonlySignal<Property | undefined>;
|
||||||
|
readonly prev$: ReadonlySignal<Property | undefined>;
|
||||||
readonly readonly$: ReadonlySignal<boolean>;
|
readonly readonly$: ReadonlySignal<boolean>;
|
||||||
readonly renderer$: ReadonlySignal<CellRenderer | undefined>;
|
readonly renderer$: ReadonlySignal<CellRenderer | undefined>;
|
||||||
readonly cells$: ReadonlySignal<Cell[]>;
|
readonly cells$: ReadonlySignal<Cell[]>;
|
||||||
readonly dataType$: ReadonlySignal<TypeInstance>;
|
readonly dataType$: ReadonlySignal<TypeInstance | undefined>;
|
||||||
|
readonly meta$: ReadonlySignal<PropertyMetaConfig | undefined>;
|
||||||
readonly icon?: UniComponent;
|
readonly icon?: UniComponent;
|
||||||
|
|
||||||
readonly delete?: () => void;
|
readonly delete?: () => void;
|
||||||
@@ -29,7 +33,7 @@ export interface Property<
|
|||||||
readonly duplicate?: () => void;
|
readonly duplicate?: () => void;
|
||||||
get canDuplicate(): boolean;
|
get canDuplicate(): boolean;
|
||||||
|
|
||||||
cellGet(rowId: string): Cell<RawValue, JsonValue, Data>;
|
cellGetOrCreate(rowId: string): Cell<RawValue, JsonValue, Data>;
|
||||||
|
|
||||||
readonly data$: ReadonlySignal<Data>;
|
readonly data$: ReadonlySignal<Data>;
|
||||||
dataUpdate(updater: PropertyDataUpdater<Data>): void;
|
dataUpdate(updater: PropertyDataUpdater<Data>): void;
|
||||||
@@ -48,8 +52,16 @@ export interface Property<
|
|||||||
valueGet(rowId: string): RawValue | undefined;
|
valueGet(rowId: string): RawValue | undefined;
|
||||||
valueSet(rowId: string, value: RawValue | undefined): void;
|
valueSet(rowId: string, value: RawValue | undefined): void;
|
||||||
|
|
||||||
stringValueGet(rowId: string): string;
|
stringValueGet(rowId: string): string | undefined;
|
||||||
valueSetFromString(rowId: string, value: string): void;
|
valueSetFromString(rowId: string, value: string): void;
|
||||||
|
parseValueFromString(value: string):
|
||||||
|
| {
|
||||||
|
value: unknown;
|
||||||
|
data?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
move(position: InsertToPosition): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class PropertyBase<
|
export abstract class PropertyBase<
|
||||||
@@ -58,125 +70,195 @@ export abstract class PropertyBase<
|
|||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
> implements Property<RawValue, JsonValue, Data>
|
> implements Property<RawValue, JsonValue, Data>
|
||||||
{
|
{
|
||||||
|
meta$ = computed(() => {
|
||||||
|
return this.dataSource.propertyMetaGet(this.type$.value);
|
||||||
|
});
|
||||||
|
|
||||||
cells$ = computed(() => {
|
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(() => {
|
data$ = computed(() => {
|
||||||
return this.view.propertyDataGet(this.id) as Data;
|
return this.dataSource.propertyDataGet(this.id) as Data;
|
||||||
});
|
});
|
||||||
|
|
||||||
dataType$ = computed(() => {
|
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(() => {
|
abstract hide$: ReadonlySignal<boolean>;
|
||||||
return this.view.propertyHideGet(this.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
name$ = computed(() => {
|
name$ = computed(() => {
|
||||||
return this.view.propertyNameGet(this.id);
|
return this.dataSource.propertyNameGet(this.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
readonly$ = computed(() => {
|
readonly$ = computed(() => {
|
||||||
return this.view.readonly$.value || this.view.propertyReadonlyGet(this.id);
|
return (
|
||||||
|
this.view.readonly$.value || this.dataSource.propertyReadonlyGet(this.id)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
type$ = computed(() => {
|
type$ = computed(() => {
|
||||||
return this.view.propertyTypeGet(this.id)!;
|
return this.dataSource.propertyTypeGet(this.id)!;
|
||||||
});
|
});
|
||||||
|
|
||||||
renderer$ = computed(() => {
|
renderer$ = computed(() => {
|
||||||
return this.view.propertyMetaGet(this.type$.value)?.renderer.cellRenderer;
|
return this.meta$.value?.renderer.cellRenderer;
|
||||||
});
|
});
|
||||||
|
|
||||||
get delete(): (() => void) | undefined {
|
get delete(): (() => void) | undefined {
|
||||||
return () => this.view.propertyDelete(this.id);
|
return () => this.dataSource.propertyDelete(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
get duplicate(): (() => void) | undefined {
|
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 {
|
get icon(): UniComponent | undefined {
|
||||||
if (!this.type$.value) return 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 {
|
get id(): string {
|
||||||
return this.propertyId;
|
return this.propertyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
get index(): number {
|
index$ = computed(() => {
|
||||||
return this.view.propertyIndexGet(this.id);
|
const index = this.view.propertyIds$.value.indexOf(this.id);
|
||||||
}
|
return index >= 0 ? index : undefined;
|
||||||
|
});
|
||||||
|
|
||||||
get isFirst(): boolean {
|
isFirst$ = computed(() => {
|
||||||
return this.view.propertyIndexGet(this.id) === 0;
|
return this.index$.value === 0;
|
||||||
}
|
});
|
||||||
|
|
||||||
get isLast(): boolean {
|
isLast$ = computed(() => {
|
||||||
return (
|
return this.index$.value === this.view.propertyIds$.value.length - 1;
|
||||||
this.view.propertyIndexGet(this.id) ===
|
});
|
||||||
this.view.properties$.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) {
|
get typeSet(): undefined | ((type: string) => void) {
|
||||||
return type => this.view.propertyTypeSet(this.id, type);
|
return type => this.dataSource.propertyTypeSet(this.id, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public view: SingleView,
|
public view: SingleView,
|
||||||
public propertyId: string
|
public propertyId: string
|
||||||
) {}
|
) {}
|
||||||
|
protected get dataSource() {
|
||||||
|
return this.view.manager.dataSource;
|
||||||
|
}
|
||||||
get canDelete(): boolean {
|
get canDelete(): boolean {
|
||||||
return this.view.propertyCanDelete(this.id);
|
return this.dataSource.propertyCanDelete(this.id);
|
||||||
}
|
}
|
||||||
get canDuplicate(): boolean {
|
get canDuplicate(): boolean {
|
||||||
return this.view.propertyCanDuplicate(this.id);
|
return this.dataSource.propertyCanDuplicate(this.id);
|
||||||
}
|
}
|
||||||
get typeCanSet(): boolean {
|
get typeCanSet(): boolean {
|
||||||
return this.view.propertyTypeCanSet(this.id);
|
return this.dataSource.propertyTypeCanSet(this.id);
|
||||||
}
|
}
|
||||||
get hideCanSet(): boolean {
|
get hideCanSet(): boolean {
|
||||||
return this.view.propertyCanHide(this.id);
|
return this.type$.value !== 'title';
|
||||||
}
|
}
|
||||||
|
|
||||||
cellGet(rowId: string): Cell<RawValue, JsonValue, Data> {
|
cellGetOrCreate(rowId: string): Cell<RawValue, JsonValue, Data> {
|
||||||
return this.view.cellGet(rowId, this.id) as Cell<RawValue, JsonValue, Data>;
|
return this.view.cellGetOrCreate(rowId, this.id) as Cell<
|
||||||
|
RawValue,
|
||||||
|
JsonValue,
|
||||||
|
Data
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
dataUpdate(updater: PropertyDataUpdater<Data>): void {
|
dataUpdate(updater: PropertyDataUpdater<Data>): void {
|
||||||
const data = this.data$.value;
|
const data = this.data$.value;
|
||||||
this.view.propertyDataSet(this.id, {
|
this.dataSource.propertyDataSet(this.id, {
|
||||||
...data,
|
...data,
|
||||||
...updater(data),
|
...updater(data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
hideSet(hide: boolean): void {
|
abstract hideSet(hide: boolean): void;
|
||||||
this.view.propertyHideSet(this.id, hide);
|
|
||||||
}
|
|
||||||
|
|
||||||
nameSet(name: string): void {
|
nameSet(name: string): void {
|
||||||
this.view.propertyNameSet(this.id, name);
|
this.dataSource.propertyNameSet(this.id, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
stringValueGet(rowId: string): string {
|
stringValueGet(rowId: string): string | undefined {
|
||||||
return this.cellGet(rowId).stringValue$.value;
|
return this.cellGetOrCreate(rowId).stringValue$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueGet(rowId: string): RawValue | undefined {
|
valueGet(rowId: string): RawValue | undefined {
|
||||||
return this.cellGet(rowId).value$.value;
|
return this.cellGetOrCreate(rowId).value$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueSet(rowId: string, value: RawValue | undefined): void {
|
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 {
|
valueSetFromString(rowId: string, value: string): void {
|
||||||
const data = this.view.propertyParseValueFromString(this.id, value);
|
const data = this.parseValueFromString(value);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||||
|
|
||||||
import { type Cell, CellBase } from './cell.js';
|
import { type Cell, CellBase } from './cell.js';
|
||||||
@@ -6,17 +7,59 @@ import type { SingleView } from './single-view.js';
|
|||||||
export interface Row {
|
export interface Row {
|
||||||
readonly cells$: ReadonlySignal<Cell[]>;
|
readonly cells$: ReadonlySignal<Cell[]>;
|
||||||
readonly rowId: string;
|
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 {
|
export class RowBase implements Row {
|
||||||
cells$ = computed(() => {
|
cells$ = computed(() => {
|
||||||
return this.singleView.propertyIds$.value.map(propertyId => {
|
return this.singleView.propertiesRaw$.value.map(property => {
|
||||||
return new CellBase(this.singleView, propertyId, this.rowId);
|
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(
|
constructor(
|
||||||
readonly singleView: SingleView,
|
readonly singleView: SingleView,
|
||||||
readonly rowId: string
|
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 type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
||||||
import { computed, type ReadonlySignal, signal } from '@preact/signals-core';
|
import { computed, type ReadonlySignal, signal } from '@preact/signals-core';
|
||||||
|
|
||||||
import type { DataViewContextKey } from '../data-source/context.js';
|
import type { DataViewContextKey } from '../data-source/context.js';
|
||||||
import type { Variable } from '../expression/types.js';
|
import type { Variable } from '../expression/types.js';
|
||||||
import type { TypeInstance } from '../logical/type.js';
|
|
||||||
import type { PropertyMetaConfig } from '../property/property-config.js';
|
import type { PropertyMetaConfig } from '../property/property-config.js';
|
||||||
import { fromJson } from '../property/utils';
|
|
||||||
import type { TraitKey } from '../traits/key.js';
|
import type { TraitKey } from '../traits/key.js';
|
||||||
import type { DatabaseFlags } from '../types.js';
|
import type { DatabaseFlags } from '../types.js';
|
||||||
|
import { computedLock } from '../utils/lock.js';
|
||||||
import type { DataViewDataType, ViewMeta } from '../view/data-view.js';
|
import type { DataViewDataType, ViewMeta } from '../view/data-view.js';
|
||||||
import { type Cell, CellBase } from './cell.js';
|
import { type Cell, CellBase } from './cell.js';
|
||||||
import type { Property } from './property.js';
|
import type { Property } from './property.js';
|
||||||
@@ -36,49 +34,25 @@ export interface SingleView {
|
|||||||
|
|
||||||
nameSet(name: string): void;
|
nameSet(name: string): void;
|
||||||
|
|
||||||
readonly propertyIds$: ReadonlySignal<string[]>;
|
readonly propertiesRaw$: ReadonlySignal<Property[]>;
|
||||||
readonly propertiesWithoutFilter$: ReadonlySignal<string[]>;
|
readonly propertyMap$: ReadonlySignal<Record<string, Property>>;
|
||||||
readonly properties$: ReadonlySignal<Property[]>;
|
readonly properties$: ReadonlySignal<Property[]>;
|
||||||
readonly detailProperties$: ReadonlySignal<string[]>;
|
readonly propertyIds$: ReadonlySignal<string[]>;
|
||||||
readonly rows$: ReadonlySignal<string[]>;
|
readonly detailProperties$: ReadonlySignal<Property[]>;
|
||||||
|
readonly rowsRaw$: ReadonlySignal<Row[]>;
|
||||||
|
readonly rows$: ReadonlySignal<Row[]>;
|
||||||
|
readonly rowIds$: ReadonlySignal<string[]>;
|
||||||
|
|
||||||
readonly vars$: ReadonlySignal<Variable[]>;
|
readonly vars$: ReadonlySignal<Variable[]>;
|
||||||
|
|
||||||
readonly featureFlags$: ReadonlySignal<DatabaseFlags>;
|
readonly featureFlags$: ReadonlySignal<DatabaseFlags>;
|
||||||
|
|
||||||
cellValueGet(rowId: string, propertyId: string): unknown;
|
propertyGetOrCreate(propertyId: string): Property;
|
||||||
|
rowGetOrCreate(rowId: string): Row;
|
||||||
cellValueSet(rowId: string, propertyId: string, value: unknown): void;
|
cellGetOrCreate(rowId: string, propertyId: string): Cell;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
rowAdd(insertPosition: InsertToPosition): string;
|
rowAdd(insertPosition: InsertToPosition): string;
|
||||||
|
rowsDelete(rows: string[]): void;
|
||||||
rowDelete(ids: string[]): void;
|
|
||||||
|
|
||||||
rowMove(rowId: string, position: InsertToPosition): void;
|
|
||||||
|
|
||||||
rowGet(rowId: string): Row;
|
|
||||||
|
|
||||||
rowPrevGet(rowId: string): string | undefined;
|
|
||||||
|
|
||||||
rowNextGet(rowId: string): string | undefined;
|
|
||||||
|
|
||||||
readonly propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
|
readonly propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
|
||||||
|
|
||||||
@@ -87,54 +61,6 @@ export interface SingleView {
|
|||||||
type?: string
|
type?: string
|
||||||
): string | undefined;
|
): 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;
|
contextGet<T>(key: DataViewContextKey<T>): T;
|
||||||
|
|
||||||
traitGet<T>(key: TraitKey<T>): T | undefined;
|
traitGet<T>(key: TraitKey<T>): T | undefined;
|
||||||
@@ -158,7 +84,7 @@ export abstract class SingleViewBase<
|
|||||||
return this.dataSource.viewDataGet(this.id) as ViewData | undefined;
|
return this.dataSource.viewDataGet(this.id) as ViewData | undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
abstract detailProperties$: ReadonlySignal<string[]>;
|
abstract detailProperties$: ReadonlySignal<Property[]>;
|
||||||
|
|
||||||
protected lockRows$ = signal(false);
|
protected lockRows$ = signal(false);
|
||||||
|
|
||||||
@@ -172,43 +98,56 @@ export abstract class SingleViewBase<
|
|||||||
return this.data$.value?.name ?? '';
|
return this.data$.value?.name ?? '';
|
||||||
});
|
});
|
||||||
|
|
||||||
preRows: string[] = [];
|
propertyIds$: ReadonlySignal<string[]> = computed(() => {
|
||||||
|
return this.properties$.value.map(v => v.id);
|
||||||
abstract propertyIds$: ReadonlySignal<string[]>;
|
|
||||||
|
|
||||||
properties$ = computed(() => {
|
|
||||||
return this.propertyIds$.value.map(
|
|
||||||
id => this.propertyGet(id) as ReturnType<this['propertyGet']>
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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>;
|
abstract readonly$: ReadonlySignal<boolean>;
|
||||||
|
|
||||||
rows$ = computed(() => {
|
rowsRaw$ = computed(() => {
|
||||||
if (this.lockRows$.value) {
|
return this.dataSource.rows$.value.map(id => this.rowGetOrCreate(id));
|
||||||
return this.preRows;
|
});
|
||||||
}
|
|
||||||
return (this.preRows = this.rowsMapping(this.dataSource.rows$.value));
|
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(() => {
|
vars$ = computed(() => {
|
||||||
return this.propertiesWithoutFilter$.value.flatMap(id => {
|
return this.propertiesRaw$.value.flatMap(property => {
|
||||||
const v = this.propertyGet(id);
|
const propertyMeta = this.dataSource.propertyMetaGet(
|
||||||
const propertyMeta = this.dataSource.propertyMetaGet(v.type$.value);
|
property.type$.value
|
||||||
|
);
|
||||||
if (!propertyMeta) {
|
if (!propertyMeta) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: v.id,
|
id: property.id,
|
||||||
name: v.name$.value,
|
name: property.name$.value,
|
||||||
type: propertyMeta.config.jsonValue.type({
|
type: propertyMeta.config.jsonValue.type({
|
||||||
data: v.data$.value,
|
data: property.data$.value,
|
||||||
dataSource: this.dataSource,
|
dataSource: this.dataSource,
|
||||||
}),
|
}),
|
||||||
icon: v.icon,
|
icon: property.icon,
|
||||||
propertyType: v.type$.value,
|
propertyType: property.type$.value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -240,29 +179,13 @@ export abstract class SingleViewBase<
|
|||||||
public id: string
|
public id: string
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
propertyCanDelete(propertyId: string): boolean {
|
private searchRowsMapping(rows: Row[], searchString: string): Row[] {
|
||||||
return this.dataSource.propertyCanDelete(propertyId);
|
return rows.filter(row => {
|
||||||
}
|
|
||||||
|
|
||||||
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 => {
|
|
||||||
if (searchString) {
|
if (searchString) {
|
||||||
const containsSearchString = this.propertyIds$.value.some(
|
const containsSearchString = this.propertyIds$.value.some(
|
||||||
propertyId => {
|
propertyId => {
|
||||||
return this.cellStringValueGet(id, propertyId)
|
return this.cellGetOrCreate(row.rowId, propertyId)
|
||||||
?.toLowerCase()
|
.stringValue$.value?.toLowerCase()
|
||||||
.includes(searchString?.toLowerCase());
|
.includes(searchString?.toLowerCase());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -270,66 +193,14 @@ export abstract class SingleViewBase<
|
|||||||
return false;
|
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);
|
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 {
|
contextGet<T>(key: DataViewContextKey<T>): T {
|
||||||
return this.dataSource.contextGet(key);
|
return this.dataSource.contextGet(key);
|
||||||
}
|
}
|
||||||
@@ -365,144 +236,22 @@ export abstract class SingleViewBase<
|
|||||||
if (!id) {
|
if (!id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.propertyMove(id, position);
|
const property = this.propertyGetOrCreate(id);
|
||||||
|
property.move(position);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyDataGet(propertyId: string): Record<string, unknown> {
|
abstract propertyGetOrCreate(propertyId: string): Property;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
rowAdd(insertPosition: InsertToPosition | number): string {
|
rowAdd(insertPosition: InsertToPosition | number): string {
|
||||||
return this.dataSource.rowAdd(insertPosition);
|
return this.dataSource.rowAdd(insertPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
rowDelete(ids: string[]): void {
|
rowGetOrCreate(rowId: string): Row {
|
||||||
this.dataSource.rowDelete(ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
rowGet(rowId: string): Row {
|
|
||||||
return new RowBase(this, rowId);
|
return new RowBase(this, rowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
rowMove(rowId: string, position: InsertToPosition): void {
|
protected rowsMapping(rows: Row[]): Row[] {
|
||||||
this.dataSource.rowMove(rowId, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract rowNextGet(rowId: string): string | undefined;
|
|
||||||
|
|
||||||
abstract rowPrevGet(rowId: string): string | undefined;
|
|
||||||
|
|
||||||
protected rowsMapping(rows: string[]): string[] {
|
|
||||||
return this.searchRowsMapping(rows, this.searchString.value);
|
return this.searchRowsMapping(rows, this.searchString.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export const kanbanViewModel = kanbanViewType.createModel<KanbanViewData>({
|
|||||||
return {
|
return {
|
||||||
columns: columns.map(id => ({
|
columns: columns.map(id => ({
|
||||||
id: id,
|
id: id,
|
||||||
hide: false,
|
|
||||||
})),
|
})),
|
||||||
filter: {
|
filter: {
|
||||||
type: 'group',
|
type: 'group',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
insertPositionToIndex,
|
insertPositionToIndex,
|
||||||
type InsertToPosition,
|
type InsertToPosition,
|
||||||
} from '@blocksuite/affine-shared/utils';
|
} 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 { evalFilter } from '../../core/filter/eval.js';
|
||||||
import { generateDefaultValues } from '../../core/filter/generate-default-values.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';
|
import type { KanbanViewData } from './define.js';
|
||||||
|
|
||||||
export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
||||||
propertiesWithoutFilter$ = computed(() => {
|
propertiesRaw$ = computed(() => {
|
||||||
const needShow = new Set(this.dataSource.properties$.value);
|
const needShow = new Set(this.dataSource.properties$.value);
|
||||||
const result: string[] = [];
|
const result: string[] = [];
|
||||||
this.data$.value?.columns.forEach(v => {
|
this.data$.value?.columns.forEach(v => {
|
||||||
@@ -30,12 +30,16 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
result.push(...needShow);
|
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(() => {
|
detailProperties$ = computed(() => {
|
||||||
return this.propertiesWithoutFilter$.value.filter(
|
return this.propertiesRaw$.value.filter(
|
||||||
id => this.propertyTypeGet(id) !== 'title'
|
property => property.type$.value !== 'title'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,9 +80,13 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
v => v,
|
v => v,
|
||||||
this.view?.groupProperties.map(v => v.key) ?? []
|
this.view?.groupProperties.map(v => v.key) ?? []
|
||||||
),
|
),
|
||||||
sortRow: (key, ids) => {
|
sortRow: (key, rows) => {
|
||||||
const property = this.view?.groupProperties.find(v => v.key === key);
|
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 => {
|
changeGroupSort: keys => {
|
||||||
const map = new Map(this.view?.groupProperties.map(v => [v.key, v]));
|
const map = new Map(this.view?.groupProperties.map(v => [v.key, v]));
|
||||||
@@ -134,28 +142,20 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
mainProperties$ = computed(() => {
|
mainProperties$ = computed(() => {
|
||||||
return (
|
return (
|
||||||
this.data$.value?.header ?? {
|
this.data$.value?.header ?? {
|
||||||
titleColumn: this.propertiesWithoutFilter$.value.find(
|
titleColumn: this.propertiesRaw$.value.find(
|
||||||
id => this.propertyTypeGet(id) === 'title'
|
property => property.type$.value === 'title'
|
||||||
),
|
)?.id,
|
||||||
iconColumn: 'type',
|
iconColumn: 'type',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
propertyIds$: ReadonlySignal<string[]> = computed(() => {
|
|
||||||
return this.propertiesWithoutFilter$.value.filter(
|
|
||||||
id => !this.propertyHideGet(id)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
readonly$ = computed(() => {
|
readonly$ = computed(() => {
|
||||||
return this.manager.readonly$.value;
|
return this.manager.readonly$.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
get columns(): string[] {
|
get columns(): KanbanColumn[] {
|
||||||
return this.propertiesWithoutFilter$.value.filter(
|
return this.propertiesRaw$.value.filter(property => !property.hide$.value);
|
||||||
id => !this.propertyHideGet(id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get filter(): FilterGroup {
|
get filter(): FilterGroup {
|
||||||
@@ -182,8 +182,8 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
if (filter.conditions.length > 0) {
|
if (filter.conditions.length > 0) {
|
||||||
const defaultValues = generateDefaultValues(filter, this.vars$.value);
|
const defaultValues = generateDefaultValues(filter, this.vars$.value);
|
||||||
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
||||||
const property = this.propertyGet(propertyId);
|
const property = this.propertyGetOrCreate(propertyId);
|
||||||
const propertyMeta = this.propertyMetaGet(property.type$.value);
|
const propertyMeta = property.meta$.value;
|
||||||
if (!propertyMeta) {
|
if (!propertyMeta) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
data: property.data$.value,
|
data: property.data$.value,
|
||||||
dataSource: this.dataSource,
|
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) {
|
if (!columnId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.propertyGet(columnId);
|
return this.propertyGetOrCreate(columnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHeaderIcon(_rowId: string): KanbanColumn | undefined {
|
getHeaderIcon(_rowId: string): KanbanColumn | undefined {
|
||||||
@@ -212,7 +212,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
if (!columnId) {
|
if (!columnId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.propertyGet(columnId);
|
return this.propertyGetOrCreate(columnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHeaderTitle(_rowId: string): KanbanColumn | undefined {
|
getHeaderTitle(_rowId: string): KanbanColumn | undefined {
|
||||||
@@ -220,7 +220,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
if (!columnId) {
|
if (!columnId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.propertyGet(columnId);
|
return this.propertyGetOrCreate(columnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasHeader(_rowId: string): boolean {
|
hasHeader(_rowId: string): boolean {
|
||||||
@@ -248,7 +248,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
const rowMap = Object.fromEntries(
|
const rowMap = Object.fromEntries(
|
||||||
this.properties$.value.map(column => [
|
this.properties$.value.map(column => [
|
||||||
column.id,
|
column.id,
|
||||||
column.cellGet(rowId).jsonValue$.value,
|
column.cellGetOrCreate(rowId).jsonValue$.value,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
return evalFilter(this.filter$.value, rowMap);
|
return evalFilter(this.filter$.value, rowMap);
|
||||||
@@ -256,32 +256,16 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyGet(columnId: string): KanbanColumn {
|
propertyGetOrCreate(columnId: string): KanbanColumn {
|
||||||
return new KanbanColumn(this, columnId);
|
return new KanbanColumn(this, columnId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
propertyHideGet(columnId: string): boolean {
|
type KanbanColumnData = KanbanViewData['columns'][number];
|
||||||
return this.view?.columns.find(v => v.id === columnId)?.hide ?? false;
|
export class KanbanColumn extends PropertyBase {
|
||||||
}
|
override move(position: InsertToPosition): void {
|
||||||
|
this.kanbanView.dataUpdate(view => {
|
||||||
propertyHideSet(columnId: string, hide: boolean): void {
|
const columnIndex = view.columns.findIndex(v => v.id === this.id);
|
||||||
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);
|
|
||||||
if (columnIndex < 0) {
|
if (columnIndex < 0) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -290,7 +274,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
if (!column) {
|
if (!column) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const index = insertPositionToIndex(toAfterOfColumn, columns);
|
const index = insertPositionToIndex(position, columns);
|
||||||
columns.splice(index, 0, column);
|
columns.splice(index, 0, column);
|
||||||
return {
|
return {
|
||||||
columns,
|
columns,
|
||||||
@@ -298,23 +282,47 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
override rowMove(rowId: string, position: InsertToPosition): void {
|
override hideSet(hide: boolean): void {
|
||||||
this.dataSource.rowMove(rowId, position);
|
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 {
|
constructor(
|
||||||
const index = this.rows$.value.indexOf(rowId);
|
private readonly kanbanView: KanbanSingleView,
|
||||||
return this.rows$.value[index + 1];
|
columnId: string
|
||||||
}
|
) {
|
||||||
|
super(kanbanView, columnId);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ export class MobileKanbanCard extends SignalWatcher(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return html` <div class="mobile-card-header-icon">
|
return html` <div class="mobile-card-header-icon">
|
||||||
${icon.cellGet(this.cardId).value$.value}
|
${icon.cellGetOrCreate(this.cardId).value$.value}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ export class MobileKanbanCell extends SignalWatcher(
|
|||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
const props: CellRenderProps = {
|
const props: CellRenderProps = {
|
||||||
cell: this.column.cellGet(this.cardId),
|
cell: this.column.cellGetOrCreate(this.cardId),
|
||||||
isEditing$: this.isEditing$,
|
isEditing$: this.isEditing$,
|
||||||
selectCurrentCell: this.selectCurrentCell,
|
selectCurrentCell: this.selectCurrentCell,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -77,15 +77,15 @@ export class MobileKanbanGroup extends SignalWatcher(
|
|||||||
name: 'Ungroup',
|
name: 'Ungroup',
|
||||||
hide: () => this.group.value == null,
|
hide: () => this.group.value == null,
|
||||||
select: () => {
|
select: () => {
|
||||||
this.group.rows.forEach(id => {
|
this.group.rows.forEach(row => {
|
||||||
this.group.manager.removeFromGroup(id, this.group.key);
|
this.group.manager.removeFromGroup(row.rowId, this.group.key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
menu.action({
|
menu.action({
|
||||||
name: 'Delete Cards',
|
name: 'Delete Cards',
|
||||||
select: () => {
|
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">
|
<div class="mobile-group-body">
|
||||||
${repeat(
|
${repeat(
|
||||||
cards,
|
cards,
|
||||||
id => id,
|
row => row.rowId,
|
||||||
id => {
|
row => {
|
||||||
return html`
|
return html`
|
||||||
<mobile-kanban-card
|
<mobile-kanban-card
|
||||||
data-card-id="${id}"
|
data-card-id="${row.rowId}"
|
||||||
.groupKey="${this.group.key}"
|
.groupKey="${this.group.key}"
|
||||||
.dataViewEle="${this.dataViewEle}"
|
.dataViewEle="${this.dataViewEle}"
|
||||||
.view="${this.view}"
|
.view="${this.view}"
|
||||||
.cardId="${id}"
|
.cardId="${row.rowId}"
|
||||||
></mobile-kanban-card>
|
></mobile-kanban-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export const popCardMenu = (
|
|||||||
},
|
},
|
||||||
prefix: DeleteIcon(),
|
prefix: DeleteIcon(),
|
||||||
select: () => {
|
select: () => {
|
||||||
view.rowDelete([cardId]);
|
view.rowsDelete([cardId]);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ export class KanbanCard extends SignalWatcher(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return html` <div class="card-header-icon">
|
return html` <div class="card-header-icon">
|
||||||
${icon.cellGet(this.cardId).value$.value}
|
${icon.cellGetOrCreate(this.cardId).value$.value}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export class KanbanCell extends SignalWatcher(
|
|||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
const props: CellRenderProps = {
|
const props: CellRenderProps = {
|
||||||
cell: this.column.cellGet(this.cardId),
|
cell: this.column.cellGetOrCreate(this.cardId),
|
||||||
isEditing$: this.isEditing$,
|
isEditing$: this.isEditing$,
|
||||||
selectCurrentCell: this.selectCurrentCell,
|
selectCurrentCell: this.selectCurrentCell,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export class KanbanSelectionController implements ReactiveController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selection.selectionType === 'card') {
|
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;
|
this.selection = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,12 +155,13 @@ export class KanbanSelectionController implements ReactiveController {
|
|||||||
focusFirstCell() {
|
focusFirstCell() {
|
||||||
const group = this.host.groupManager?.groupsDataList$.value?.[0];
|
const group = this.host.groupManager?.groupsDataList$.value?.[0];
|
||||||
const card = group?.rows[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) {
|
if (group && card && columnId) {
|
||||||
this.selection = {
|
this.selection = {
|
||||||
selectionType: 'cell',
|
selectionType: 'cell',
|
||||||
groupKey: group.key,
|
groupKey: group.key,
|
||||||
cardId: card,
|
cardId: card.rowId,
|
||||||
columnId,
|
columnId,
|
||||||
isEditing: false,
|
isEditing: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -143,15 +143,15 @@ export class KanbanGroup extends SignalWatcher(
|
|||||||
name: 'Ungroup',
|
name: 'Ungroup',
|
||||||
hide: () => this.group.value == null,
|
hide: () => this.group.value == null,
|
||||||
select: () => {
|
select: () => {
|
||||||
this.group.rows.forEach(id => {
|
this.group.rows.forEach(row => {
|
||||||
this.group.manager.removeFromGroup(id, this.group.key);
|
this.group.manager.removeFromGroup(row.rowId, this.group.key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
menu.action({
|
menu.action({
|
||||||
name: 'Delete Cards',
|
name: 'Delete Cards',
|
||||||
select: () => {
|
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">
|
<div class="group-body">
|
||||||
${repeat(
|
${repeat(
|
||||||
cards,
|
cards,
|
||||||
id => id,
|
row => row.rowId,
|
||||||
id => {
|
row => {
|
||||||
return html`
|
return html`
|
||||||
<affine-data-view-kanban-card
|
<affine-data-view-kanban-card
|
||||||
data-card-id="${id}"
|
data-card-id="${row.rowId}"
|
||||||
.groupKey="${this.group.key}"
|
.groupKey="${this.group.key}"
|
||||||
.dataViewEle="${this.dataViewEle}"
|
.dataViewEle="${this.dataViewEle}"
|
||||||
.view="${this.view}"
|
.view="${this.view}"
|
||||||
.cardId="${id}"
|
.cardId="${row.rowId}"
|
||||||
></affine-data-view-kanban-card>
|
></affine-data-view-kanban-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
type SingleView,
|
type SingleView,
|
||||||
} from '../../../core/index.js';
|
} from '../../../core/index.js';
|
||||||
import { TableViewAreaSelection } from '../selection';
|
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(
|
export class MobileTableCell extends SignalWatcher(
|
||||||
WithDisposable(ShadowlessElement)
|
WithDisposable(ShadowlessElement)
|
||||||
@@ -38,13 +38,13 @@ export class MobileTableCell extends SignalWatcher(
|
|||||||
private readonly _cell = signal<DataViewCellLifeCycle>();
|
private readonly _cell = signal<DataViewCellLifeCycle>();
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor rowId!: string;
|
accessor rowId!: string;
|
||||||
|
|
||||||
cell$ = computed(() => {
|
cell$ = computed(() => {
|
||||||
return this.column.cellGet(this.rowId);
|
return this.column.cellGetOrCreate(this.rowId);
|
||||||
});
|
});
|
||||||
|
|
||||||
isSelectionEditing$ = computed(() => {
|
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 type { Property } from '../../../core/view-manager/property.js';
|
||||||
import { numberFormats } from '../../../property-presets/number/utils/formats.js';
|
import { numberFormats } from '../../../property-presets/number/utils/formats.js';
|
||||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../consts.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(
|
export class MobileTableColumnHeader extends SignalWatcher(
|
||||||
WithDisposable(ShadowlessElement)
|
WithDisposable(ShadowlessElement)
|
||||||
@@ -174,16 +174,14 @@ export class MobileTableColumnHeader extends SignalWatcher(
|
|||||||
menu.action({
|
menu.action({
|
||||||
name: 'Move Left',
|
name: 'Move Left',
|
||||||
prefix: MoveLeftIcon(),
|
prefix: MoveLeftIcon(),
|
||||||
hide: () => this.column.isFirst,
|
hide: () => this.column.isFirst$.value,
|
||||||
select: () => {
|
select: () => {
|
||||||
const preId = this.tableViewManager.propertyPreGet(
|
const pre = this.column.prev$.value;
|
||||||
this.column.id
|
if (!pre) {
|
||||||
)?.id;
|
|
||||||
if (!preId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tableViewManager.propertyMove(this.column.id, {
|
this.column.move({
|
||||||
id: preId,
|
id: pre.id,
|
||||||
before: true,
|
before: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -191,16 +189,14 @@ export class MobileTableColumnHeader extends SignalWatcher(
|
|||||||
menu.action({
|
menu.action({
|
||||||
name: 'Move Right',
|
name: 'Move Right',
|
||||||
prefix: MoveRightIcon(),
|
prefix: MoveRightIcon(),
|
||||||
hide: () => this.column.isLast,
|
hide: () => this.column.isLast$.value,
|
||||||
select: () => {
|
select: () => {
|
||||||
const nextId = this.tableViewManager.propertyNextGet(
|
const next = this.column.next$.value;
|
||||||
this.column.id
|
if (!next) {
|
||||||
)?.id;
|
|
||||||
if (!nextId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tableViewManager.propertyMove(this.column.id, {
|
this.column.move({
|
||||||
id: nextId,
|
id: next.id,
|
||||||
before: false,
|
before: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -256,7 +252,7 @@ export class MobileTableColumnHeader extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor tableViewManager!: TableSingleView;
|
accessor tableViewManager!: TableSingleView;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { repeat } from 'lit/directives/repeat.js';
|
|||||||
import type { DataViewRenderer } from '../../../core/data-view.js';
|
import type { DataViewRenderer } from '../../../core/data-view.js';
|
||||||
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
||||||
import type { GroupData } from '../../../core/group-by/trait.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 { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
|
||||||
import type { DataViewTable } from '../pc/table-view.js';
|
import type { DataViewTable } from '../pc/table-view.js';
|
||||||
import { TableViewAreaSelection } from '../selection';
|
import { TableViewAreaSelection } from '../selection';
|
||||||
@@ -100,15 +101,15 @@ export class MobileTableGroup extends SignalWatcher(
|
|||||||
name: 'Ungroup',
|
name: 'Ungroup',
|
||||||
hide: () => group.value == null,
|
hide: () => group.value == null,
|
||||||
select: () => {
|
select: () => {
|
||||||
group.rows.forEach(id => {
|
group.rows.forEach(row => {
|
||||||
group.manager.removeFromGroup(id, group.key);
|
group.manager.removeFromGroup(row.rowId, group.key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
menu.action({
|
menu.action({
|
||||||
name: 'Delete Cards',
|
name: 'Delete Cards',
|
||||||
select: () => {
|
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;
|
return this.group?.rows ?? this.view.rows$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderRows(ids: string[]) {
|
private renderRows(rows: Row[]) {
|
||||||
return html`
|
return html`
|
||||||
<mobile-table-header
|
<mobile-table-header
|
||||||
.renderGroupHeader="${this.renderGroupHeader}"
|
.renderGroupHeader="${this.renderGroupHeader}"
|
||||||
@@ -143,15 +144,15 @@ export class MobileTableGroup extends SignalWatcher(
|
|||||||
></mobile-table-header>
|
></mobile-table-header>
|
||||||
<div class="mobile-affine-table-body">
|
<div class="mobile-affine-table-body">
|
||||||
${repeat(
|
${repeat(
|
||||||
ids,
|
rows,
|
||||||
id => id,
|
row => row.rowId,
|
||||||
(id, idx) => {
|
(row, idx) => {
|
||||||
return html` <mobile-table-row
|
return html` <mobile-table-row
|
||||||
data-row-index="${idx}"
|
data-row-index="${idx}"
|
||||||
data-row-id="${id}"
|
data-row-id="${row.rowId}"
|
||||||
.dataViewEle="${this.dataViewEle}"
|
.dataViewEle="${this.dataViewEle}"
|
||||||
.view="${this.view}"
|
.view="${this.view}"
|
||||||
.rowId="${id}"
|
.rowId="${row.rowId}"
|
||||||
.rowIndex="${idx}"
|
.rowIndex="${idx}"
|
||||||
></mobile-table-row>`;
|
></mobile-table-row>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export const popMobileRowMenu = (
|
|||||||
class: { 'delete-item': true },
|
class: { 'delete-item': true },
|
||||||
prefix: DeleteIcon(),
|
prefix: DeleteIcon(),
|
||||||
select: () => {
|
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'))
|
.map(row => row.cells.map(cell => cell.stringValue$.value).join('\t'))
|
||||||
.join('\n');
|
.join('\n');
|
||||||
const jsonResult: JsonAreaData = area.map(row =>
|
const jsonResult: JsonAreaData = area.map(row =>
|
||||||
row.cells.map(cell => cell.stringValue$.value)
|
row.cells.map(cell => cell.stringValue$.value ?? '')
|
||||||
);
|
);
|
||||||
if (isCut) {
|
if (isCut) {
|
||||||
const deleteRows: string[] = [];
|
const deleteRows: string[] = [];
|
||||||
@@ -44,7 +44,7 @@ export class TableClipboardController implements ReactiveController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deleteRows.length) {
|
if (deleteRows.length) {
|
||||||
this.props.view.rowDelete(deleteRows);
|
this.props.view.rowsDelete(deleteRows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.clipboard
|
this.clipboard
|
||||||
@@ -210,7 +210,7 @@ function getSelectedArea(
|
|||||||
.sort((a, b) => a.y - b.y)
|
.sort((a, b) => a.y - b.y)
|
||||||
.map(v => v.row);
|
.map(v => v.row);
|
||||||
return rows.map(r => {
|
return rows.map(r => {
|
||||||
const row = view.rowGet(r.id);
|
const row = view.rowGetOrCreate(r.id);
|
||||||
return {
|
return {
|
||||||
row,
|
row,
|
||||||
cells: row.cells$.value,
|
cells: row.cells$.value,
|
||||||
@@ -227,11 +227,11 @@ function getSelectedArea(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let i = rowsSelection.start; i <= rowsSelection.end; i++) {
|
for (let i = rowsSelection.start; i <= rowsSelection.end; i++) {
|
||||||
const row: SelectedArea[number] = {
|
const rowArea: SelectedArea[number] = {
|
||||||
cells: [],
|
cells: [],
|
||||||
};
|
};
|
||||||
const rowId = rows[i];
|
const row = rows[i];
|
||||||
if (rowId == null) {
|
if (row == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (let j = columnsSelection.start; j <= columnsSelection.end; j++) {
|
for (let j = columnsSelection.start; j <= columnsSelection.end; j++) {
|
||||||
@@ -239,10 +239,10 @@ function getSelectedArea(
|
|||||||
if (columnId == null) {
|
if (columnId == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const cell = view.cellGet(rowId, columnId);
|
const cell = view.cellGetOrCreate(row.rowId, columnId);
|
||||||
row.cells.push(cell);
|
rowArea.cells.push(cell);
|
||||||
}
|
}
|
||||||
data.push(row);
|
data.push(rowArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export function fillSelectionWithFocusCellData(
|
|||||||
|
|
||||||
const curCell = cellContainer.cell$.value;
|
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 focusCellText = focusData as Text | undefined;
|
||||||
|
|
||||||
const delta = focusCellText?.toDelta() ?? [{ insert: '' }];
|
const delta = focusCellText?.toDelta() ?? [{ insert: '' }];
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class TableHotkeysController implements ReactiveController {
|
|||||||
if (TableViewRowSelection.is(selection)) {
|
if (TableViewRowSelection.is(selection)) {
|
||||||
const rows = TableViewRowSelection.rowsIds(selection);
|
const rows = TableViewRowSelection.rowsIds(selection);
|
||||||
this.selectionController.selection = undefined;
|
this.selectionController.selection = undefined;
|
||||||
this.host.props.view.rowDelete(rows);
|
this.host.props.view.rowsDelete(rows);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
@@ -336,11 +336,14 @@ export class TableHotkeysController implements ReactiveController {
|
|||||||
rows:
|
rows:
|
||||||
this.host.props.view.groupTrait.groupsDataList$.value?.flatMap(
|
this.host.props.view.groupTrait.groupsDataList$.value?.flatMap(
|
||||||
group =>
|
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,
|
groupKey: undefined,
|
||||||
id,
|
id: row.rowId,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ export class TableSelectionController implements ReactiveController {
|
|||||||
this.selection = TableViewAreaSelection.create({
|
this.selection = TableViewAreaSelection.create({
|
||||||
groupKey: groupKey,
|
groupKey: groupKey,
|
||||||
focus: {
|
focus: {
|
||||||
rowIndex: rows?.findIndex(v => v === id) ?? 0,
|
rowIndex: rows?.findIndex(v => v.rowId === id) ?? 0,
|
||||||
columnIndex: index,
|
columnIndex: index,
|
||||||
},
|
},
|
||||||
isEditing: true,
|
isEditing: true,
|
||||||
@@ -373,7 +373,7 @@ export class TableSelectionController implements ReactiveController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteRow(rowId: string) {
|
deleteRow(rowId: string) {
|
||||||
this.view.rowDelete([rowId]);
|
this.view.rowsDelete([rowId]);
|
||||||
this.focusToCell('up');
|
this.focusToCell('up');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { typeSystem } from '../../../../../../core';
|
|||||||
import type { GroupData } from '../../../../../../core/group-by/trait';
|
import type { GroupData } from '../../../../../../core/group-by/trait';
|
||||||
import { statsFunctions } from '../../../../../../core/statistics';
|
import { statsFunctions } from '../../../../../../core/statistics';
|
||||||
import type { StatisticsConfig } from '../../../../../../core/statistics/types';
|
import type { StatisticsConfig } from '../../../../../../core/statistics/types';
|
||||||
import type { TableColumn } from '../../../../table-view-manager';
|
import type { TableProperty } from '../../../../table-view-manager';
|
||||||
|
|
||||||
const styles = css`
|
const styles = css`
|
||||||
.stats-cell {
|
.stats-cell {
|
||||||
@@ -73,12 +73,12 @@ export class VirtualDatabaseColumnStatsCell extends SignalWatcher(
|
|||||||
static override styles = styles;
|
static override styles = styles;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
cellValues$ = computed(() => {
|
cellValues$ = computed(() => {
|
||||||
if (this.group) {
|
if (this.group) {
|
||||||
return this.group.rows.map(id => {
|
return this.group.rows.map(row => {
|
||||||
return this.column.valueGet(id);
|
return this.column.valueGet(row.rowId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this.column.cells$.value.map(cell => cell.jsonValue$.value);
|
return this.column.cells$.value.map(cell => cell.jsonValue$.value);
|
||||||
@@ -157,7 +157,7 @@ export class VirtualDatabaseColumnStatsCell extends SignalWatcher(
|
|||||||
values$ = signal<unknown[]>([]);
|
values$ = signal<unknown[]>([]);
|
||||||
|
|
||||||
statsResult$ = computed(() => {
|
statsResult$ = computed(() => {
|
||||||
const meta = this.column.view.propertyMetaGet(this.column.type$.value);
|
const meta = this.column.meta$.value;
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,15 +86,15 @@ export class TableGroupHeader extends SignalWatcher(
|
|||||||
name: 'Ungroup',
|
name: 'Ungroup',
|
||||||
hide: () => group.value == null,
|
hide: () => group.value == null,
|
||||||
select: () => {
|
select: () => {
|
||||||
group.rows.forEach(id => {
|
group.rows.forEach(row => {
|
||||||
group.manager.removeFromGroup(id, group.key);
|
group.manager.removeFromGroup(row.rowId, group.key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
menu.action({
|
menu.action({
|
||||||
name: 'Delete Cards',
|
name: 'Delete Cards',
|
||||||
select: () => {
|
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,
|
value: groupData.value,
|
||||||
data: groupData.property.data$.value,
|
data: groupData.property.data$.value,
|
||||||
updateData: groupData.manager.updateData,
|
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,
|
readonly: ops.readonly,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import { baseTheme } from '@toeverything/theme';
|
|
||||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
import { globalStyle, style } from '@vanilla-extract/css';
|
import { globalStyle, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
import {
|
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../../../consts';
|
||||||
DEFAULT_ADD_BUTTON_WIDTH,
|
|
||||||
DEFAULT_COLUMN_TITLE_HEIGHT,
|
|
||||||
} from '../../../../consts';
|
|
||||||
|
|
||||||
export const columnHeaderContainer = style({
|
export const columnHeaderContainer = style({
|
||||||
display: 'block',
|
display: 'block',
|
||||||
@@ -33,104 +29,6 @@ export const cell = style({
|
|||||||
userSelect: 'none',
|
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({
|
export const columnMove = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -156,17 +54,6 @@ globalStyle(`${columnMove} svg`, {
|
|||||||
opacity: 0,
|
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({
|
export const headerAddColumnButton = style({
|
||||||
height: `${DEFAULT_COLUMN_TITLE_HEIGHT}px`,
|
height: `${DEFAULT_COLUMN_TITLE_HEIGHT}px`,
|
||||||
backgroundColor: 'var(--affine-background-primary-color)',
|
backgroundColor: 'var(--affine-background-primary-color)',
|
||||||
@@ -178,18 +65,3 @@ export const headerAddColumnButton = style({
|
|||||||
fontSize: '18px',
|
fontSize: '18px',
|
||||||
color: cssVarV2.icon.primary,
|
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();
|
column.editTitle();
|
||||||
};
|
};
|
||||||
|
|
||||||
preMove = 0;
|
|
||||||
|
|
||||||
private get readonly() {
|
private get readonly() {
|
||||||
return this.tableViewManager.readonly$.value;
|
return this.tableViewManager.readonly$.value;
|
||||||
}
|
}
|
||||||
@@ -41,10 +39,6 @@ export class VirtualTableHeader extends SignalWatcher(
|
|||||||
this.classList.add(styles.columnHeaderContainer);
|
this.classList.add(styles.columnHeaderContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
getScale() {
|
|
||||||
return this.scaleDiv?.getBoundingClientRect().width ?? 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="${styles.columnHeader} database-row">
|
<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 { html } from 'lit/static-html.js';
|
||||||
|
|
||||||
import type { GroupData } from '../../../../../../core/group-by/trait';
|
import type { GroupData } from '../../../../../../core/group-by/trait';
|
||||||
|
import type { Row } from '../../../../../../core/view-manager/row';
|
||||||
import type {
|
import type {
|
||||||
TableColumn,
|
TableProperty,
|
||||||
TableSingleView,
|
TableSingleView,
|
||||||
} from '../../../../table-view-manager';
|
} from '../../../../table-view-manager';
|
||||||
|
|
||||||
@@ -29,8 +30,8 @@ export class DataViewColumnPreview extends SignalWatcher(
|
|||||||
return this.column.view as TableSingleView;
|
return this.column.view as TableSingleView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderGroup(rows: string[]) {
|
private renderGroup(rows: Row[]) {
|
||||||
const columnIndex = this.tableViewManager.propertyIndexGet(this.column.id);
|
const columnIndex = this.column.index$.value;
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
style="background-color: var(--affine-background-primary-color);border-top: 1px solid ${unsafeCSS(
|
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 })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor container!: HTMLElement;
|
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 { ShowQuickSettingBarContextKey } from '../../../../../../widget-presets/quick-setting-bar/context';
|
||||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../../../consts';
|
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../../../consts';
|
||||||
import type {
|
import type {
|
||||||
TableColumn,
|
TableProperty,
|
||||||
TableSingleView,
|
TableSingleView,
|
||||||
} from '../../../../table-view-manager';
|
} from '../../../../table-view-manager';
|
||||||
import {
|
import {
|
||||||
@@ -86,9 +86,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
return menu.action({
|
return menu.action({
|
||||||
name: config.config.name,
|
name: config.config.name,
|
||||||
isSelected: config.type === this.column.type$.value,
|
isSelected: config.type === this.column.type$.value,
|
||||||
prefix: renderUniLit(
|
prefix: renderUniLit(config.renderer.icon),
|
||||||
this.tableViewManager.propertyIconGet(config.type)
|
|
||||||
),
|
|
||||||
select: () => {
|
select: () => {
|
||||||
this.column.typeSet?.(config.type);
|
this.column.typeSet?.(config.type);
|
||||||
},
|
},
|
||||||
@@ -326,16 +324,14 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
menu.action({
|
menu.action({
|
||||||
name: 'Move Left',
|
name: 'Move Left',
|
||||||
prefix: MoveLeftIcon(),
|
prefix: MoveLeftIcon(),
|
||||||
hide: () => this.column.isFirst,
|
hide: () => this.column.isFirst$.value,
|
||||||
select: () => {
|
select: () => {
|
||||||
const preId = this.tableViewManager.propertyPreGet(
|
const pre = this.column.prev$.value;
|
||||||
this.column.id
|
if (!pre) {
|
||||||
)?.id;
|
|
||||||
if (!preId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tableViewManager.propertyMove(this.column.id, {
|
this.column.move({
|
||||||
id: preId,
|
id: pre.id,
|
||||||
before: true,
|
before: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -343,16 +339,14 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
menu.action({
|
menu.action({
|
||||||
name: 'Move Right',
|
name: 'Move Right',
|
||||||
prefix: MoveRightIcon(),
|
prefix: MoveRightIcon(),
|
||||||
hide: () => this.column.isLast,
|
hide: () => this.column.isLast$.value,
|
||||||
select: () => {
|
select: () => {
|
||||||
const nextId = this.tableViewManager.propertyNextGet(
|
const next = this.column.next$.value;
|
||||||
this.column.id
|
if (!next) {
|
||||||
)?.id;
|
|
||||||
if (!nextId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tableViewManager.propertyMove(this.column.id, {
|
this.column.move({
|
||||||
id: nextId,
|
id: next.id,
|
||||||
before: false,
|
before: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -475,7 +469,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor grabStatus: 'grabStart' | 'grabEnd' | 'grabbing' = 'grabEnd';
|
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 { startDrag } from '../../../../../../core/utils/drag';
|
||||||
import { getResultInRange } from '../../../../../../core/utils/utils';
|
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) {
|
export class TableVerticalIndicator extends WithDisposable(ShadowlessElement) {
|
||||||
static override styles = css`
|
static override styles = css`
|
||||||
@@ -101,7 +101,7 @@ export const startDragWidthAdjustmentBar = (
|
|||||||
evt: PointerEvent,
|
evt: PointerEvent,
|
||||||
ele: HTMLElement,
|
ele: HTMLElement,
|
||||||
width: number,
|
width: number,
|
||||||
column: TableColumn
|
column: TableProperty
|
||||||
) => {
|
) => {
|
||||||
const scale = width / column.width$.value;
|
const scale = width / column.width$.value;
|
||||||
const left = ele.getBoundingClientRect().left;
|
const left = ele.getBoundingClientRect().left;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export class DatabaseCellContainer extends SignalWatcher(
|
|||||||
private readonly _cell = signal<DataViewCellLifeCycle>();
|
private readonly _cell = signal<DataViewCellLifeCycle>();
|
||||||
|
|
||||||
cell$ = computed(() => {
|
cell$ = computed(() => {
|
||||||
return this.view.cellGet(this.rowId, this.columnId);
|
return this.view.cellGetOrCreate(this.rowId, this.columnId);
|
||||||
});
|
});
|
||||||
|
|
||||||
selectCurrentCell = (editing: boolean) => {
|
selectCurrentCell = (editing: boolean) => {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const popRowMenu = (
|
|||||||
},
|
},
|
||||||
prefix: DeleteIcon(),
|
prefix: DeleteIcon(),
|
||||||
select: () => {
|
select: () => {
|
||||||
selectionController.view.rowDelete(rows);
|
selectionController.view.rowsDelete(rows);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -134,12 +134,8 @@ export class VirtualTableView extends DataViewBase<
|
|||||||
moveTo: (id, evt) => {
|
moveTo: (id, evt) => {
|
||||||
const result = this.dragController.getInsertPosition(evt);
|
const result = this.dragController.getInsertPosition(evt);
|
||||||
if (result) {
|
if (result) {
|
||||||
this.props.view.rowMove(
|
const row = this.props.view.rowGetOrCreate(id);
|
||||||
id,
|
row.move(result.position, undefined, result.groupKey);
|
||||||
result.position,
|
|
||||||
undefined,
|
|
||||||
result.groupKey
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getSelection: () => {
|
getSelection: () => {
|
||||||
@@ -181,13 +177,13 @@ export class VirtualTableView extends DataViewBase<
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: '',
|
id: '',
|
||||||
rows: this.props.view.rows$.value,
|
rows: this.props.view.rowIds$.value,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return groupTrait.groupsDataList$.value.map(group => ({
|
return groupTrait.groupsDataList$.value.map(group => ({
|
||||||
id: group.key,
|
id: group.key,
|
||||||
rows: group.rows,
|
rows: group.rows.map(v => v.rowId),
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
virtualScroll$ = signal<TableGrid>();
|
virtualScroll$ = signal<TableGrid>();
|
||||||
@@ -221,9 +217,10 @@ export class VirtualTableView extends DataViewBase<
|
|||||||
if (!selection || selection.selectionType !== 'row') {
|
if (!selection || selection.selectionType !== 'row') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const groupId = row.group.groupId;
|
||||||
return TableViewRowSelection.includes(selection, {
|
return TableViewRowSelection.includes(selection, {
|
||||||
id: row.rowId,
|
id: row.rowId,
|
||||||
groupKey: row.group.groupId,
|
groupKey: groupId ? groupId : undefined,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
TableViewAreaSelection,
|
TableViewAreaSelection,
|
||||||
type TableViewSelectionWithType,
|
type TableViewSelectionWithType,
|
||||||
} from '../selection';
|
} from '../selection';
|
||||||
import type { TableColumn } from '../table-view-manager.js';
|
import type { TableProperty } from '../table-view-manager.js';
|
||||||
import type { TableGroup } from './group.js';
|
import type { TableGroup } from './group.js';
|
||||||
|
|
||||||
export class DatabaseCellContainer extends SignalWatcher(
|
export class DatabaseCellContainer extends SignalWatcher(
|
||||||
@@ -42,13 +42,13 @@ export class DatabaseCellContainer extends SignalWatcher(
|
|||||||
private readonly _cell = signal<DataViewCellLifeCycle>();
|
private readonly _cell = signal<DataViewCellLifeCycle>();
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor rowId!: string;
|
accessor rowId!: string;
|
||||||
|
|
||||||
cell$ = computed(() => {
|
cell$ = computed(() => {
|
||||||
return this.column.cellGet(this.rowId);
|
return this.column.cellGetOrCreate(this.rowId);
|
||||||
});
|
});
|
||||||
|
|
||||||
selectCurrentCell = (editing: boolean) => {
|
selectCurrentCell = (editing: boolean) => {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export class TableClipboardController implements ReactiveController {
|
|||||||
.map(row => row.cells.map(cell => cell.stringValue$.value).join('\t'))
|
.map(row => row.cells.map(cell => cell.stringValue$.value).join('\t'))
|
||||||
.join('\n');
|
.join('\n');
|
||||||
const jsonResult: JsonAreaData = area.map(row =>
|
const jsonResult: JsonAreaData = area.map(row =>
|
||||||
row.cells.map(cell => cell.stringValue$.value)
|
row.cells.map(cell => cell.stringValue$.value ?? '')
|
||||||
);
|
);
|
||||||
if (isCut) {
|
if (isCut) {
|
||||||
const deleteRows: string[] = [];
|
const deleteRows: string[] = [];
|
||||||
@@ -44,7 +44,7 @@ export class TableClipboardController implements ReactiveController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deleteRows.length) {
|
if (deleteRows.length) {
|
||||||
this.props.view.rowDelete(deleteRows);
|
this.props.view.rowsDelete(deleteRows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.clipboard
|
this.clipboard
|
||||||
@@ -211,7 +211,7 @@ function getSelectedArea(
|
|||||||
.sort((a, b) => a.y - b.y)
|
.sort((a, b) => a.y - b.y)
|
||||||
.map(v => v.row);
|
.map(v => v.row);
|
||||||
return rows.map(r => {
|
return rows.map(r => {
|
||||||
const row = view.rowGet(r.id);
|
const row = view.rowGetOrCreate(r.id);
|
||||||
return {
|
return {
|
||||||
row,
|
row,
|
||||||
cells: row.cells$.value,
|
cells: row.cells$.value,
|
||||||
@@ -228,11 +228,11 @@ function getSelectedArea(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let i = rowsSelection.start; i <= rowsSelection.end; i++) {
|
for (let i = rowsSelection.start; i <= rowsSelection.end; i++) {
|
||||||
const row: SelectedArea[number] = {
|
const rowArea: SelectedArea[number] = {
|
||||||
cells: [],
|
cells: [],
|
||||||
};
|
};
|
||||||
const rowId = rows[i];
|
const row = rows[i];
|
||||||
if (rowId == null) {
|
if (row == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (let j = columnsSelection.start; j <= columnsSelection.end; j++) {
|
for (let j = columnsSelection.start; j <= columnsSelection.end; j++) {
|
||||||
@@ -240,10 +240,10 @@ function getSelectedArea(
|
|||||||
if (columnId == null) {
|
if (columnId == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const cell = view.cellGet(rowId, columnId);
|
const cell = view.cellGetOrCreate(row.rowId, columnId);
|
||||||
row.cells.push(cell);
|
rowArea.cells.push(cell);
|
||||||
}
|
}
|
||||||
data.push(row);
|
data.push(rowArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ export function fillSelectionWithFocusCellData(
|
|||||||
|
|
||||||
const curCell = cellContainer.cell$.value;
|
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 focusCellText = focusData as Text | undefined;
|
||||||
|
|
||||||
const delta = focusCellText?.toDelta() ?? [{ insert: '' }];
|
const delta = focusCellText?.toDelta() ?? [{ insert: '' }];
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
|
|||||||
import type { ReactiveController } from 'lit';
|
import type { ReactiveController } from 'lit';
|
||||||
|
|
||||||
import { startDrag } from '../../../../core/utils/drag.js';
|
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';
|
import type { DataViewTable } from '../table-view.js';
|
||||||
|
|
||||||
export class TableDragController implements ReactiveController {
|
export class TableDragController implements ReactiveController {
|
||||||
dragStart = (row: TableRow, evt: PointerEvent) => {
|
dragStart = (rowView: TableRowView, evt: PointerEvent) => {
|
||||||
const eleRect = row.getBoundingClientRect();
|
const eleRect = rowView.getBoundingClientRect();
|
||||||
const offsetLeft = evt.x - eleRect.left;
|
const offsetLeft = evt.x - eleRect.left;
|
||||||
const offsetTop = evt.y - eleRect.top;
|
const offsetTop = evt.y - eleRect.top;
|
||||||
const preview = createDragPreview(
|
const preview = createDragPreview(
|
||||||
row,
|
rowView,
|
||||||
evt.x - offsetLeft,
|
evt.x - offsetLeft,
|
||||||
evt.y - offsetTop
|
evt.y - offsetTop
|
||||||
);
|
);
|
||||||
const fromGroup = row.groupKey;
|
const fromGroup = rowView.groupKey;
|
||||||
|
|
||||||
startDrag<
|
startDrag<
|
||||||
| undefined
|
| undefined
|
||||||
@@ -38,7 +38,7 @@ export class TableDragController implements ReactiveController {
|
|||||||
this.dropPreview.remove();
|
this.dropPreview.remove();
|
||||||
return {
|
return {
|
||||||
type: 'out',
|
type: 'out',
|
||||||
callback: callback(evt, row.rowId),
|
callback: callback(evt, rowView.rowId),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -66,12 +66,8 @@ export class TableDragController implements ReactiveController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result.type === 'self') {
|
if (result.type === 'self') {
|
||||||
this.host.props.view.rowMove(
|
const row = this.host.props.view.rowGetOrCreate(rowView.rowId);
|
||||||
row.rowId,
|
row.move(result.position, fromGroup, result.groupKey);
|
||||||
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 div = document.createElement('div');
|
||||||
const cloneRow = new TableRow();
|
const cloneRow = new TableRowView();
|
||||||
cloneRow.view = row.view;
|
cloneRow.view = row.view;
|
||||||
cloneRow.rowIndex = row.rowIndex;
|
cloneRow.rowIndex = row.rowIndex;
|
||||||
cloneRow.rowId = row.rowId;
|
cloneRow.rowId = row.rowId;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class TableHotkeysController implements ReactiveController {
|
|||||||
if (TableViewRowSelection.is(selection)) {
|
if (TableViewRowSelection.is(selection)) {
|
||||||
const rows = TableViewRowSelection.rowsIds(selection);
|
const rows = TableViewRowSelection.rowsIds(selection);
|
||||||
this.selectionController.selection = undefined;
|
this.selectionController.selection = undefined;
|
||||||
this.host.props.view.rowDelete(rows);
|
this.host.props.view.rowsDelete(rows);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
@@ -336,11 +336,14 @@ export class TableHotkeysController implements ReactiveController {
|
|||||||
rows:
|
rows:
|
||||||
this.host.props.view.groupTrait.groupsDataList$.value?.flatMap(
|
this.host.props.view.groupTrait.groupsDataList$.value?.flatMap(
|
||||||
group =>
|
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,
|
groupKey: undefined,
|
||||||
id,
|
id: row.rowId,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
} from '../../selection';
|
} from '../../selection';
|
||||||
import type { DatabaseCellContainer } from '../cell.js';
|
import type { DatabaseCellContainer } from '../cell.js';
|
||||||
import type { TableGroup } from '../group.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 type { DataViewTable } from '../table-view.js';
|
||||||
import {
|
import {
|
||||||
DragToFillElement,
|
DragToFillElement,
|
||||||
@@ -244,7 +244,7 @@ export class TableSelectionController implements ReactiveController {
|
|||||||
this.selection = TableViewAreaSelection.create({
|
this.selection = TableViewAreaSelection.create({
|
||||||
groupKey: groupKey,
|
groupKey: groupKey,
|
||||||
focus: {
|
focus: {
|
||||||
rowIndex: rows?.findIndex(v => v === id) ?? 0,
|
rowIndex: rows?.findIndex(v => v.rowId === id) ?? 0,
|
||||||
columnIndex: index,
|
columnIndex: index,
|
||||||
},
|
},
|
||||||
isEditing: true,
|
isEditing: true,
|
||||||
@@ -346,7 +346,7 @@ export class TableSelectionController implements ReactiveController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteRow(rowId: string) {
|
deleteRow(rowId: string) {
|
||||||
this.view.rowDelete([rowId]);
|
this.view.rowsDelete([rowId]);
|
||||||
this.focusToCell('up');
|
this.focusToCell('up');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,7 +553,7 @@ export class TableSelectionController implements ReactiveController {
|
|||||||
(
|
(
|
||||||
this.getGroup(lastRow?.groupKey)?.querySelector(
|
this.getGroup(lastRow?.groupKey)?.querySelector(
|
||||||
`data-view-table-row[data-row-id='${lastRow?.id}']`
|
`data-view-table-row[data-row-id='${lastRow?.id}']`
|
||||||
) as TableRow | null
|
) as TableRowView | null
|
||||||
)?.rowIndex ?? 0;
|
)?.rowIndex ?? 0;
|
||||||
const getRowByIndex = (index: number) => {
|
const getRowByIndex = (index: number) => {
|
||||||
const tableRow = this.rows(lastRow?.groupKey)?.item(index);
|
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 { DatabaseHeaderColumn } from './header/database-header-column.js';
|
||||||
import { DatabaseNumberFormatBar } from './header/number-format-bar.js';
|
import { DatabaseNumberFormatBar } from './header/number-format-bar.js';
|
||||||
import { TableVerticalIndicator } from './header/vertical-indicator.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 { RowSelectCheckbox } from './row/row-select-checkbox.js';
|
||||||
import { DataViewTable } from './table-view.js';
|
import { DataViewTable } from './table-view.js';
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ export function pcEffects() {
|
|||||||
'affine-database-number-format-bar',
|
'affine-database-number-format-bar',
|
||||||
DatabaseNumberFormatBar
|
DatabaseNumberFormatBar
|
||||||
);
|
);
|
||||||
customElements.define('data-view-table-row', TableRow);
|
customElements.define('data-view-table-row', TableRowView);
|
||||||
customElements.define('row-select-checkbox', RowSelectCheckbox);
|
customElements.define('row-select-checkbox', RowSelectCheckbox);
|
||||||
customElements.define('data-view-table-selection', SelectionElement);
|
customElements.define('data-view-table-selection', SelectionElement);
|
||||||
customElements.define('data-view-drag-to-fill', DragToFillElement);
|
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 type { DataViewRenderer } from '../../../core/data-view.js';
|
||||||
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
import { GroupTitle } from '../../../core/group-by/group-title.js';
|
||||||
import type { GroupData } from '../../../core/group-by/trait.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 { createDndContext } from '../../../core/utils/wc-dnd/dnd-context.js';
|
||||||
import { defaultActivators } from '../../../core/utils/wc-dnd/sensors/index.js';
|
import { defaultActivators } from '../../../core/utils/wc-dnd/sensors/index.js';
|
||||||
import { linearMove } from '../../../core/utils/wc-dnd/utils/linear-move.js';
|
import { linearMove } from '../../../core/utils/wc-dnd/utils/linear-move.js';
|
||||||
@@ -117,15 +118,15 @@ export class TableGroup extends SignalWatcher(
|
|||||||
name: 'Ungroup',
|
name: 'Ungroup',
|
||||||
hide: () => group.value == null,
|
hide: () => group.value == null,
|
||||||
select: () => {
|
select: () => {
|
||||||
group.rows.forEach(id => {
|
group.rows.forEach(row => {
|
||||||
group.manager.removeFromGroup(id, group.key);
|
group.manager.removeFromGroup(row.rowId, group.key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
menu.action({
|
menu.action({
|
||||||
name: 'Delete Cards',
|
name: 'Delete Cards',
|
||||||
select: () => {
|
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(
|
const overIndex = this.view.properties$.value.findIndex(
|
||||||
data => data.id === over.id
|
data => data.id === over.id
|
||||||
);
|
);
|
||||||
this.view.propertyMove(active.id, {
|
this.view.propertyGetOrCreate(active.id).move({
|
||||||
before: activeIndex > overIndex,
|
before: activeIndex > overIndex,
|
||||||
id: over.id,
|
id: over.id,
|
||||||
});
|
});
|
||||||
@@ -181,7 +182,7 @@ export class TableGroup extends SignalWatcher(
|
|||||||
},
|
},
|
||||||
collisionDetection: linearMove(true),
|
collisionDetection: linearMove(true),
|
||||||
createOverlay: active => {
|
createOverlay: active => {
|
||||||
const column = this.view.propertyGet(active.id);
|
const column = this.view.propertyGetOrCreate(active.id);
|
||||||
const preview = new DataViewColumnPreview();
|
const preview = new DataViewColumnPreview();
|
||||||
preview.column = column;
|
preview.column = column;
|
||||||
preview.group = this.group;
|
preview.group = this.group;
|
||||||
@@ -241,7 +242,7 @@ export class TableGroup extends SignalWatcher(
|
|||||||
return this.group?.rows ?? this.view.rows$.value;
|
return this.group?.rows ?? this.view.rows$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderRows(ids: string[]) {
|
private renderRows(rows: Row[]) {
|
||||||
return html`
|
return html`
|
||||||
<affine-database-column-header
|
<affine-database-column-header
|
||||||
.renderGroupHeader="${this.renderGroupHeader}"
|
.renderGroupHeader="${this.renderGroupHeader}"
|
||||||
@@ -249,15 +250,15 @@ export class TableGroup extends SignalWatcher(
|
|||||||
></affine-database-column-header>
|
></affine-database-column-header>
|
||||||
<div class="affine-database-block-rows">
|
<div class="affine-database-block-rows">
|
||||||
${repeat(
|
${repeat(
|
||||||
ids,
|
rows,
|
||||||
id => id,
|
row => row.rowId,
|
||||||
(id, idx) => {
|
(row, idx) => {
|
||||||
return html` <data-view-table-row
|
return html` <data-view-table-row
|
||||||
data-row-index="${idx}"
|
data-row-index="${idx}"
|
||||||
data-row-id="${id}"
|
data-row-id="${row.rowId}"
|
||||||
.dataViewEle="${this.dataViewEle}"
|
.dataViewEle="${this.dataViewEle}"
|
||||||
.view="${this.view}"
|
.view="${this.view}"
|
||||||
.rowId="${id}"
|
.rowId="${row.rowId}"
|
||||||
.rowIndex="${idx}"
|
.rowIndex="${idx}"
|
||||||
></data-view-table-row>`;
|
></data-view-table-row>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ import { styleMap } from 'lit/directives/style-map.js';
|
|||||||
import { html } from 'lit/static-html.js';
|
import { html } from 'lit/static-html.js';
|
||||||
|
|
||||||
import type { GroupData } from '../../../../core/group-by/trait.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(
|
export class DataViewColumnPreview extends SignalWatcher(
|
||||||
WithDisposable(ShadowlessElement)
|
WithDisposable(ShadowlessElement)
|
||||||
@@ -26,8 +30,8 @@ export class DataViewColumnPreview extends SignalWatcher(
|
|||||||
return this.column.view as TableSingleView;
|
return this.column.view as TableSingleView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderGroup(rows: string[]) {
|
private renderGroup(rows: Row[]) {
|
||||||
const columnIndex = this.tableViewManager.propertyIndexGet(this.column.id);
|
const columnIndex = this.column.index$.value;
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
style="background-color: var(--affine-background-primary-color);border-top: 1px solid ${unsafeCSS(
|
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 })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor container!: HTMLElement;
|
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 { numberFormats } from '../../../../property-presets/number/utils/formats.js';
|
||||||
import { ShowQuickSettingBarContextKey } from '../../../../widget-presets/quick-setting-bar/context.js';
|
import { ShowQuickSettingBarContextKey } from '../../../../widget-presets/quick-setting-bar/context.js';
|
||||||
import { DEFAULT_COLUMN_TITLE_HEIGHT } from '../../consts.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 {
|
import {
|
||||||
getTableGroupRect,
|
getTableGroupRect,
|
||||||
getVerticalIndicator,
|
getVerticalIndicator,
|
||||||
@@ -83,9 +86,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
return menu.action({
|
return menu.action({
|
||||||
name: config.config.name,
|
name: config.config.name,
|
||||||
isSelected: config.type === this.column.type$.value,
|
isSelected: config.type === this.column.type$.value,
|
||||||
prefix: renderUniLit(
|
prefix: renderUniLit(config.renderer.icon),
|
||||||
this.tableViewManager.propertyIconGet(config.type)
|
|
||||||
),
|
|
||||||
select: () => {
|
select: () => {
|
||||||
this.column.typeSet?.(config.type);
|
this.column.typeSet?.(config.type);
|
||||||
},
|
},
|
||||||
@@ -323,16 +324,14 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
menu.action({
|
menu.action({
|
||||||
name: 'Move Left',
|
name: 'Move Left',
|
||||||
prefix: MoveLeftIcon(),
|
prefix: MoveLeftIcon(),
|
||||||
hide: () => this.column.isFirst,
|
hide: () => this.column.isFirst$.value,
|
||||||
select: () => {
|
select: () => {
|
||||||
const preId = this.tableViewManager.propertyPreGet(
|
const prev = this.column.prev$.value;
|
||||||
this.column.id
|
if (!prev) {
|
||||||
)?.id;
|
|
||||||
if (!preId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tableViewManager.propertyMove(this.column.id, {
|
this.column.move({
|
||||||
id: preId,
|
id: prev.id,
|
||||||
before: true,
|
before: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -340,16 +339,14 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
menu.action({
|
menu.action({
|
||||||
name: 'Move Right',
|
name: 'Move Right',
|
||||||
prefix: MoveRightIcon(),
|
prefix: MoveRightIcon(),
|
||||||
hide: () => this.column.isLast,
|
hide: () => this.column.isLast$.value,
|
||||||
select: () => {
|
select: () => {
|
||||||
const nextId = this.tableViewManager.propertyNextGet(
|
const next = this.column.next$.value;
|
||||||
this.column.id
|
if (!next) {
|
||||||
)?.id;
|
|
||||||
if (!nextId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.tableViewManager.propertyMove(this.column.id, {
|
this.column.move({
|
||||||
id: nextId,
|
id: next.id,
|
||||||
before: false,
|
before: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -472,7 +469,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor grabStatus: 'grabStart' | 'grabEnd' | 'grabbing' = 'grabEnd';
|
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 { startDrag } from '../../../../core/utils/drag.js';
|
||||||
import { getResultInRange } from '../../../../core/utils/utils.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) {
|
export class TableVerticalIndicator extends WithDisposable(ShadowlessElement) {
|
||||||
static override styles = css`
|
static override styles = css`
|
||||||
@@ -95,7 +95,7 @@ export const startDragWidthAdjustmentBar = (
|
|||||||
evt: PointerEvent,
|
evt: PointerEvent,
|
||||||
ele: HTMLElement,
|
ele: HTMLElement,
|
||||||
width: number,
|
width: number,
|
||||||
column: TableColumn
|
column: TableProperty
|
||||||
) => {
|
) => {
|
||||||
const scale = width / column.width$.value;
|
const scale = width / column.width$.value;
|
||||||
const left = ele.getBoundingClientRect().left;
|
const left = ele.getBoundingClientRect().left;
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const popRowMenu = (
|
|||||||
},
|
},
|
||||||
prefix: DeleteIcon(),
|
prefix: DeleteIcon(),
|
||||||
select: () => {
|
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 type { TableGroup } from '../group.js';
|
||||||
import { openDetail, popRowMenu } from '../menu.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`
|
static override styles = css`
|
||||||
.affine-database-block-row:has(.row-select-checkbox.selected) {
|
.affine-database-block-row:has(.row-select-checkbox.selected) {
|
||||||
background: var(--affine-primary-color-04);
|
background: var(--affine-primary-color-04);
|
||||||
@@ -294,6 +296,6 @@ export class TableRow extends SignalWatcher(WithDisposable(ShadowlessElement)) {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
'data-view-table-row': TableRow;
|
'data-view-table-row': TableRowView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,12 +237,8 @@ export class DataViewTable extends DataViewBase<
|
|||||||
moveTo: (id, evt) => {
|
moveTo: (id, evt) => {
|
||||||
const result = this.dragController.getInsertPosition(evt);
|
const result = this.dragController.getInsertPosition(evt);
|
||||||
if (result) {
|
if (result) {
|
||||||
this.props.view.rowMove(
|
const row = this.props.view.rowGetOrCreate(id);
|
||||||
id,
|
row.move(result.position, undefined, result.groupKey);
|
||||||
result.position,
|
|
||||||
undefined,
|
|
||||||
result.groupKey
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getSelection: () => {
|
getSelection: () => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import type { GroupData } from '../../../core/group-by/trait.js';
|
|||||||
import { typeSystem } from '../../../core/index.js';
|
import { typeSystem } from '../../../core/index.js';
|
||||||
import { statsFunctions } from '../../../core/statistics/index.js';
|
import { statsFunctions } from '../../../core/statistics/index.js';
|
||||||
import type { StatisticsConfig } from '../../../core/statistics/types.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`
|
const styles = css`
|
||||||
.stats-cell {
|
.stats-cell {
|
||||||
@@ -73,12 +73,12 @@ export class DatabaseColumnStatsCell extends SignalWatcher(
|
|||||||
static override styles = styles;
|
static override styles = styles;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor column!: TableColumn;
|
accessor column!: TableProperty;
|
||||||
|
|
||||||
cellValues$ = computed(() => {
|
cellValues$ = computed(() => {
|
||||||
if (this.group) {
|
if (this.group) {
|
||||||
return this.group.rows.map(id => {
|
return this.group.rows.map(row => {
|
||||||
return this.column.valueGet(id);
|
return this.column.valueGet(row.rowId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this.column.cells$.value.map(cell => cell.jsonValue$.value);
|
return this.column.cells$.value.map(cell => cell.jsonValue$.value);
|
||||||
@@ -157,7 +157,7 @@ export class DatabaseColumnStatsCell extends SignalWatcher(
|
|||||||
values$ = signal<unknown[]>([]);
|
values$ = signal<unknown[]>([]);
|
||||||
|
|
||||||
statsResult$ = computed(() => {
|
statsResult$ = computed(() => {
|
||||||
const meta = this.column.view.propertyMetaGet(this.column.type$.value);
|
const meta = this.column.meta$.value;
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
import { fromJson } from '../../core/property/utils';
|
import { fromJson } from '../../core/property/utils';
|
||||||
import { SortManager, sortTraitKey } from '../../core/sort/manager.js';
|
import { SortManager, sortTraitKey } from '../../core/sort/manager.js';
|
||||||
import { PropertyBase } from '../../core/view-manager/property.js';
|
import { PropertyBase } from '../../core/view-manager/property.js';
|
||||||
|
import { type Row, RowBase } from '../../core/view-manager/row.js';
|
||||||
import {
|
import {
|
||||||
type SingleView,
|
type SingleView,
|
||||||
SingleViewBase,
|
SingleViewBase,
|
||||||
@@ -24,10 +25,9 @@ import {
|
|||||||
import type { ViewManager } from '../../core/view-manager/view-manager.js';
|
import type { ViewManager } from '../../core/view-manager/view-manager.js';
|
||||||
import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_COLUMN_WIDTH } from './consts.js';
|
import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_COLUMN_WIDTH } from './consts.js';
|
||||||
import type { TableViewData } from './define.js';
|
import type { TableViewData } from './define.js';
|
||||||
import type { StatCalcOpType } from './types.js';
|
|
||||||
|
|
||||||
export class TableSingleView extends SingleViewBase<TableViewData> {
|
export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||||
propertiesWithoutFilter$ = computed(() => {
|
propertiesRaw$ = computed(() => {
|
||||||
const needShow = new Set(this.dataSource.properties$.value);
|
const needShow = new Set(this.dataSource.properties$.value);
|
||||||
const result: string[] = [];
|
const result: string[] = [];
|
||||||
this.data$.value?.columns.forEach(v => {
|
this.data$.value?.columns.forEach(v => {
|
||||||
@@ -37,19 +37,11 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
result.push(...needShow);
|
result.push(...needShow);
|
||||||
return result;
|
return result.map(id => this.propertyGetOrCreate(id));
|
||||||
});
|
});
|
||||||
|
|
||||||
private readonly computedColumns$ = computed(() => {
|
properties$ = computed(() => {
|
||||||
return this.propertiesWithoutFilter$.value.map(id => {
|
return this.propertiesRaw$.value.filter(property => !property.hide$.value);
|
||||||
const column = this.propertyGet(id);
|
|
||||||
return {
|
|
||||||
id: column.id,
|
|
||||||
hide: column.hide$.value,
|
|
||||||
width: column.width$.value,
|
|
||||||
statCalcType: column.statCalcOp$.value,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
private readonly filter$ = computed(() => {
|
private readonly filter$ = computed(() => {
|
||||||
@@ -81,8 +73,8 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
detailProperties$ = computed(() => {
|
detailProperties$ = computed(() => {
|
||||||
return this.propertiesWithoutFilter$.value.filter(
|
return this.propertiesRaw$.value.filter(
|
||||||
id => this.propertyTypeGet(id) !== 'title'
|
property => property.type$.value !== 'title'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,9 +107,13 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
v => v,
|
v => v,
|
||||||
this.groupProperties.map(v => v.key)
|
this.groupProperties.map(v => v.key)
|
||||||
),
|
),
|
||||||
sortRow: (key, ids) => {
|
sortRow: (key, rows) => {
|
||||||
const property = this.groupProperties.find(v => v.key === key);
|
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 => {
|
changeGroupSort: keys => {
|
||||||
const map = new Map(this.groupProperties.map(v => [v.key, v]));
|
const map = new Map(this.groupProperties.map(v => [v.key, v]));
|
||||||
@@ -173,20 +169,14 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
mainProperties$ = computed(() => {
|
mainProperties$ = computed(() => {
|
||||||
return (
|
return (
|
||||||
this.data$.value?.header ?? {
|
this.data$.value?.header ?? {
|
||||||
titleColumn: this.propertiesWithoutFilter$.value.find(
|
titleColumn: this.propertiesRaw$.value.find(
|
||||||
id => this.propertyTypeGet(id) === 'title'
|
property => property.type$.value === 'title'
|
||||||
),
|
)?.id,
|
||||||
iconColumn: 'type',
|
iconColumn: 'type',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
propertyIds$ = computed(() => {
|
|
||||||
return this.propertiesWithoutFilter$.value.filter(
|
|
||||||
id => !this.propertyHideGet(id)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
readonly$ = computed(() => {
|
readonly$ = computed(() => {
|
||||||
return this.manager.readonly$.value;
|
return this.manager.readonly$.value;
|
||||||
});
|
});
|
||||||
@@ -207,58 +197,12 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
super(viewManager, viewId);
|
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 {
|
isShow(rowId: string): boolean {
|
||||||
if (this.filter$.value?.conditions.length) {
|
if (this.filter$.value?.conditions.length) {
|
||||||
const rowMap = Object.fromEntries(
|
const rowMap = Object.fromEntries(
|
||||||
this.properties$.value.map(column => [
|
this.properties$.value.map(column => [
|
||||||
column.id,
|
column.id,
|
||||||
column.cellGet(rowId).jsonValue$.value,
|
column.cellGetOrCreate(rowId).jsonValue$.value,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
return evalFilter(this.filter$.value, rowMap);
|
return evalFilter(this.filter$.value, rowMap);
|
||||||
@@ -266,54 +210,12 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
minWidthGet(type: string): number {
|
propertyGetOrCreate(columnId: string): TableProperty {
|
||||||
return (
|
return new TableProperty(this, columnId);
|
||||||
this.propertyMetaGet(type)?.config.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyGet(columnId: string): TableColumn {
|
override rowGetOrCreate(rowId: string): TableRow {
|
||||||
return new TableColumn(this, columnId);
|
return new TableRow(this, rowId);
|
||||||
}
|
|
||||||
|
|
||||||
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 rowAdd(
|
override rowAdd(
|
||||||
@@ -326,15 +228,15 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
if (filter.conditions.length > 0) {
|
if (filter.conditions.length > 0) {
|
||||||
const defaultValues = generateDefaultValues(filter, this.vars$.value);
|
const defaultValues = generateDefaultValues(filter, this.vars$.value);
|
||||||
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
||||||
const property = this.propertyGet(propertyId);
|
const property = this.propertyGetOrCreate(propertyId);
|
||||||
const propertyMeta = this.propertyMetaGet(property.type$.value);
|
const propertyMeta = property.meta$.value;
|
||||||
if (propertyMeta) {
|
if (propertyMeta) {
|
||||||
const value = fromJson(propertyMeta.config, {
|
const value = fromJson(propertyMeta.config, {
|
||||||
value: jsonValue,
|
value: jsonValue,
|
||||||
data: property.data$.value,
|
data: property.data$.value,
|
||||||
dataSource: this.dataSource,
|
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;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
override rowMove(
|
override rowsMapping(rows: Row[]) {
|
||||||
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[]) {
|
|
||||||
return this.sortManager.sort(super.rowsMapping(rows));
|
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(() => {
|
statCalcOp$ = computed(() => {
|
||||||
return this.tableView.columnGetStatCalcOp(this.id);
|
return this.viewData$.value?.statCalcType;
|
||||||
});
|
});
|
||||||
|
|
||||||
width$: ReadonlySignal<number> = computed(() => {
|
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() {
|
get minWidth() {
|
||||||
return this.tableView.minWidthGet(this.type$.value);
|
return this.meta$.value?.config.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -393,11 +334,62 @@ export class TableColumn extends PropertyBase {
|
|||||||
super(tableView as SingleView, columnId);
|
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 {
|
updateStatCalcOp(type?: string): void {
|
||||||
return this.tableView.columnUpdateStatCalcOp(this.id, type);
|
this.viewDataUpdate(data => {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
statCalcType: type,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateWidth(width: number): void {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"@blocksuite/icons": "^2.2.13",
|
"@blocksuite/icons": "^2.2.13",
|
||||||
"@dotlottie/player-component": "^2.7.12",
|
"@dotlottie/player-component": "^2.7.12",
|
||||||
"@emotion/cache": "^11.14.0",
|
"@emotion/cache": "^11.14.0",
|
||||||
|
"@emotion/css": "^11.13.5",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@floating-ui/dom": "^1.6.13",
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { propertyType, t } from '@blocksuite/affine/blocks/database';
|
||||||
|
import zod from 'zod';
|
||||||
|
|
||||||
|
export const createdByColumnType = propertyType('created-by');
|
||||||
|
export const createdByPropertyModelConfig = createdByColumnType.modelConfig({
|
||||||
|
name: 'Created By',
|
||||||
|
propertyData: {
|
||||||
|
schema: zod.object({}),
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
fixed: {
|
||||||
|
defaultData: {},
|
||||||
|
defaultOrder: 'end',
|
||||||
|
defaultShow: false,
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.string().nullable(),
|
||||||
|
default: () => null,
|
||||||
|
toString: ({ value }) => value ?? '',
|
||||||
|
fromString: () => {
|
||||||
|
return { value: null };
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
|
},
|
||||||
|
jsonValue: {
|
||||||
|
schema: zod.string().nullable(),
|
||||||
|
isEmpty: () => false,
|
||||||
|
type: () => t.string.instance(),
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import { Avatar, uniReactRoot } from '@affine/component';
|
||||||
|
import {
|
||||||
|
type CellRenderProps,
|
||||||
|
createIcon,
|
||||||
|
type DataViewCellLifeCycle,
|
||||||
|
HostContextKey,
|
||||||
|
} from '@blocksuite/affine/blocks/database';
|
||||||
|
import {
|
||||||
|
UserProvider,
|
||||||
|
type UserService,
|
||||||
|
} from '@blocksuite/affine/shared/services';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import {
|
||||||
|
forwardRef,
|
||||||
|
type ForwardRefRenderFunction,
|
||||||
|
type ReactNode,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import { useSignalValue } from '../../../../modules/doc-info/utils';
|
||||||
|
import { createdByPropertyModelConfig } from './define';
|
||||||
|
|
||||||
|
const cellContainer = css({
|
||||||
|
width: '100%',
|
||||||
|
position: 'relative',
|
||||||
|
gap: '6px',
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
});
|
||||||
|
const memberPreviewContainer = css({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '4px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
});
|
||||||
|
const memberName = css({
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
fontSize: '14px',
|
||||||
|
lineHeight: '22px',
|
||||||
|
});
|
||||||
|
const avatar = css({
|
||||||
|
flexShrink: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const CreatedByCellComponent: ForwardRefRenderFunction<
|
||||||
|
DataViewCellLifeCycle,
|
||||||
|
CellRenderProps<{}, string | null, string | null>
|
||||||
|
> = (props, ref): ReactNode => {
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
beforeEnterEditMode: () => {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
beforeExitEditingMode: () => {},
|
||||||
|
afterEnterEditingMode: () => {},
|
||||||
|
focusCell: () => true,
|
||||||
|
blurCell: () => true,
|
||||||
|
forceUpdate: () => {},
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const host = props.cell.view.contextGet(HostContextKey);
|
||||||
|
const userService = host?.std.getOptional(UserProvider);
|
||||||
|
const memberId = useSignalValue(props.cell.value$);
|
||||||
|
if (!memberId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div style={{ overflow: 'hidden' }}>
|
||||||
|
<div className={cellContainer}>
|
||||||
|
<MemberPreview
|
||||||
|
key={memberId}
|
||||||
|
memberId={memberId}
|
||||||
|
userService={userService}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useMemberInfo = (
|
||||||
|
id: string,
|
||||||
|
userService: UserService | null | undefined
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
userService?.revalidateUserInfo(id);
|
||||||
|
}, [id, userService]);
|
||||||
|
return useSignalValue(userService?.userInfo$(id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const MemberPreview = ({
|
||||||
|
memberId,
|
||||||
|
userService,
|
||||||
|
}: {
|
||||||
|
memberId: string;
|
||||||
|
userService: UserService | null | undefined;
|
||||||
|
}) => {
|
||||||
|
const userInfo = useMemberInfo(memberId, userService);
|
||||||
|
if (!userInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={memberPreviewContainer}>
|
||||||
|
<Avatar
|
||||||
|
name={userInfo.removed ? undefined : (userInfo.name ?? undefined)}
|
||||||
|
className={avatar}
|
||||||
|
url={!userInfo.removed ? userInfo.avatar : undefined}
|
||||||
|
size={24}
|
||||||
|
/>
|
||||||
|
<div className={memberName}>
|
||||||
|
{userInfo.removed ? 'Deleted user' : userInfo.name || 'Unnamed'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CreatedByCell = forwardRef(CreatedByCellComponent);
|
||||||
|
|
||||||
|
export const createdByPropertyConfig =
|
||||||
|
createdByPropertyModelConfig.createPropertyMeta({
|
||||||
|
icon: createIcon('UserIcon'),
|
||||||
|
cellRenderer: {
|
||||||
|
view: uniReactRoot.createUniComponent(CreatedByCell),
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import type { PropertyMetaConfig } from '@blocksuite/affine/blocks/database';
|
import type { PropertyMetaConfig } from '@blocksuite/affine/blocks/database';
|
||||||
|
|
||||||
|
import { createdByPropertyConfig } from './created-by/view';
|
||||||
import { filePropertyConfig } from './file/view';
|
import { filePropertyConfig } from './file/view';
|
||||||
import { memberPropertyConfig } from './member/view';
|
import { memberPropertyConfig } from './member/view';
|
||||||
|
|
||||||
export const propertiesPresets: PropertyMetaConfig<string, any, any, any>[] = [
|
export const propertiesPresets: PropertyMetaConfig<string, any, any, any>[] = [
|
||||||
filePropertyConfig,
|
filePropertyConfig,
|
||||||
memberPropertyConfig,
|
memberPropertyConfig,
|
||||||
|
createdByPropertyConfig,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -205,7 +205,11 @@ export const MemberListItem = (props: {
|
|||||||
data-selected={isSelected ? 'true' : 'false'}
|
data-selected={isSelected ? 'true' : 'false'}
|
||||||
>
|
>
|
||||||
<div className={styles.avatar}>
|
<div className={styles.avatar}>
|
||||||
<Avatar url={member.avatar} size={24} />
|
<Avatar
|
||||||
|
name={member.removed ? undefined : (member.name ?? undefined)}
|
||||||
|
url={member.avatar}
|
||||||
|
size={24}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.memberName}>{member.name}</div>
|
<div className={styles.memberName}>{member.name}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -228,6 +232,7 @@ export const MemberPreview = ({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.memberPreviewContainer}>
|
<div className={styles.memberPreviewContainer}>
|
||||||
<Avatar
|
<Avatar
|
||||||
|
name={userInfo.removed ? undefined : (userInfo.name ?? undefined)}
|
||||||
className={styles.avatar}
|
className={styles.avatar}
|
||||||
url={!userInfo.removed ? userInfo.avatar : undefined}
|
url={!userInfo.removed ? userInfo.avatar : undefined}
|
||||||
size={16}
|
size={16}
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ const MemberPreview = ({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.memberPreviewContainer}>
|
<div className={styles.memberPreviewContainer}>
|
||||||
<Avatar
|
<Avatar
|
||||||
|
name={userInfo.removed ? undefined : (userInfo.name ?? undefined)}
|
||||||
className={styles.avatar}
|
className={styles.avatar}
|
||||||
url={!userInfo.removed ? userInfo.avatar : undefined}
|
url={!userInfo.removed ? userInfo.avatar : undefined}
|
||||||
size={24}
|
size={24}
|
||||||
|
|||||||
17
yarn.lock
17
yarn.lock
@@ -407,6 +407,7 @@ __metadata:
|
|||||||
"@blocksuite/icons": "npm:^2.2.13"
|
"@blocksuite/icons": "npm:^2.2.13"
|
||||||
"@dotlottie/player-component": "npm:^2.7.12"
|
"@dotlottie/player-component": "npm:^2.7.12"
|
||||||
"@emotion/cache": "npm:^11.14.0"
|
"@emotion/cache": "npm:^11.14.0"
|
||||||
|
"@emotion/css": "npm:^11.13.5"
|
||||||
"@emotion/react": "npm:^11.14.0"
|
"@emotion/react": "npm:^11.14.0"
|
||||||
"@floating-ui/dom": "npm:^1.6.13"
|
"@floating-ui/dom": "npm:^1.6.13"
|
||||||
"@juggle/resize-observer": "npm:^3.4.0"
|
"@juggle/resize-observer": "npm:^3.4.0"
|
||||||
@@ -2537,6 +2538,7 @@ __metadata:
|
|||||||
"@blocksuite/icons": "npm:^2.2.12"
|
"@blocksuite/icons": "npm:^2.2.12"
|
||||||
"@blocksuite/std": "workspace:*"
|
"@blocksuite/std": "workspace:*"
|
||||||
"@blocksuite/store": "workspace:*"
|
"@blocksuite/store": "workspace:*"
|
||||||
|
"@emotion/css": "npm:^11.13.5"
|
||||||
"@floating-ui/dom": "npm:^1.6.13"
|
"@floating-ui/dom": "npm:^1.6.13"
|
||||||
"@lit/context": "npm:^1.1.2"
|
"@lit/context": "npm:^1.1.2"
|
||||||
"@preact/signals-core": "npm:^1.8.0"
|
"@preact/signals-core": "npm:^1.8.0"
|
||||||
@@ -5330,7 +5332,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@emotion/cache@npm:^11.14.0":
|
"@emotion/cache@npm:^11.13.5, @emotion/cache@npm:^11.14.0":
|
||||||
version: 11.14.0
|
version: 11.14.0
|
||||||
resolution: "@emotion/cache@npm:11.14.0"
|
resolution: "@emotion/cache@npm:11.14.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5343,6 +5345,19 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@emotion/css@npm:^11.13.5":
|
||||||
|
version: 11.13.5
|
||||||
|
resolution: "@emotion/css@npm:11.13.5"
|
||||||
|
dependencies:
|
||||||
|
"@emotion/babel-plugin": "npm:^11.13.5"
|
||||||
|
"@emotion/cache": "npm:^11.13.5"
|
||||||
|
"@emotion/serialize": "npm:^1.3.3"
|
||||||
|
"@emotion/sheet": "npm:^1.4.0"
|
||||||
|
"@emotion/utils": "npm:^1.4.2"
|
||||||
|
checksum: 10/267acf535e6c82e5835563f38a2c49ddc320ba005aeee1b8d1b04a2a217ce6d45639601e95617a78f3746a424d92bbbc837f785dbc29c401e4b614c0c50766d9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@emotion/hash@npm:^0.9.0, @emotion/hash@npm:^0.9.2":
|
"@emotion/hash@npm:^0.9.0, @emotion/hash@npm:^0.9.2":
|
||||||
version: 0.9.2
|
version: 0.9.2
|
||||||
resolution: "@emotion/hash@npm:0.9.2"
|
resolution: "@emotion/hash@npm:0.9.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user