mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-30 00:29:46 +08:00
refactor(editor): add runtime type checks to database cell values (#10770)
This commit is contained in:
@@ -4,16 +4,17 @@ import type { Block, BlockModel } from '@blocksuite/store';
|
|||||||
|
|
||||||
type PropertyMeta<
|
type PropertyMeta<
|
||||||
T extends BlockModel = BlockModel,
|
T extends BlockModel = BlockModel,
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
ColumnData extends NonNullable<unknown> = NonNullable<unknown>,
|
ColumnData extends NonNullable<unknown> = NonNullable<unknown>,
|
||||||
> = {
|
> = {
|
||||||
name: string;
|
name: string;
|
||||||
key: string;
|
key: string;
|
||||||
metaConfig: PropertyMetaConfig<string, ColumnData, Value>;
|
metaConfig: PropertyMetaConfig<string, ColumnData, RawValue, JsonValue>;
|
||||||
getColumnData?: (block: T) => ColumnData;
|
getColumnData?: (block: T) => ColumnData;
|
||||||
setColumnData?: (block: T, data: ColumnData) => void;
|
setColumnData?: (block: T, data: ColumnData) => void;
|
||||||
get: (block: T) => Value;
|
get: (block: T) => RawValue;
|
||||||
set?: (block: T, value: Value) => void;
|
set?: (block: T, value: RawValue) => void;
|
||||||
updated: (block: T, callback: () => void) => DisposableMember;
|
updated: (block: T, callback: () => void) => DisposableMember;
|
||||||
};
|
};
|
||||||
export type BlockMeta<T extends BlockModel = BlockModel> = {
|
export type BlockMeta<T extends BlockModel = BlockModel> = {
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { richTextColumnConfig } from '@blocksuite/affine-block-database';
|
|
||||||
import { type ListBlockModel, ListBlockSchema } from '@blocksuite/affine-model';
|
import { type ListBlockModel, ListBlockSchema } from '@blocksuite/affine-model';
|
||||||
import { propertyPresets } from '@blocksuite/data-view/property-presets';
|
|
||||||
|
|
||||||
import { createBlockMeta } from './base.js';
|
import { createBlockMeta } from './base.js';
|
||||||
|
|
||||||
@@ -13,48 +11,3 @@ export const todoMeta = createBlockMeta<ListBlockModel>({
|
|||||||
return (block.model as ListBlockModel).type === 'todo';
|
return (block.model as ListBlockModel).type === 'todo';
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
todoMeta.addProperty({
|
|
||||||
name: 'Content',
|
|
||||||
key: 'todo-title',
|
|
||||||
metaConfig: richTextColumnConfig,
|
|
||||||
get: block => block.text.yText,
|
|
||||||
set: (_block, _value) => {
|
|
||||||
//
|
|
||||||
},
|
|
||||||
updated: (block, callback) => {
|
|
||||||
block.text?.yText.observe(callback);
|
|
||||||
return {
|
|
||||||
dispose: () => {
|
|
||||||
block.text?.yText.unobserve(callback);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
todoMeta.addProperty({
|
|
||||||
name: 'Checked',
|
|
||||||
key: 'todo-checked',
|
|
||||||
metaConfig: propertyPresets.checkboxPropertyConfig,
|
|
||||||
get: block => block.checked,
|
|
||||||
set: (block, value) => {
|
|
||||||
block.checked = value ?? false;
|
|
||||||
},
|
|
||||||
updated: (block, callback) => {
|
|
||||||
return block.propsUpdated.subscribe(({ key }) => {
|
|
||||||
if (key === 'checked') {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
todoMeta.addProperty({
|
|
||||||
name: 'Source',
|
|
||||||
key: 'todo-source',
|
|
||||||
metaConfig: propertyPresets.textPropertyConfig,
|
|
||||||
get: block => block.doc.meta?.title ?? '',
|
|
||||||
updated: (block, callback) => {
|
|
||||||
return block.doc.workspace.slots.docListUpdated.subscribe(() => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ export const queryBlockColumns = [
|
|||||||
propertyPresets.multiSelectPropertyConfig,
|
propertyPresets.multiSelectPropertyConfig,
|
||||||
propertyPresets.checkboxPropertyConfig,
|
propertyPresets.checkboxPropertyConfig,
|
||||||
];
|
];
|
||||||
export const queryBlockHiddenColumns: PropertyMetaConfig<string, any, any>[] = [
|
export const queryBlockHiddenColumns: PropertyMetaConfig<
|
||||||
richTextColumnConfig,
|
string,
|
||||||
];
|
any,
|
||||||
|
any,
|
||||||
|
any
|
||||||
|
>[] = [richTextColumnConfig];
|
||||||
const queryBlockAllColumns = [...queryBlockColumns, ...queryBlockHiddenColumns];
|
const queryBlockAllColumns = [...queryBlockColumns, ...queryBlockHiddenColumns];
|
||||||
export const queryBlockAllColumnMap = Object.fromEntries(
|
export const queryBlockAllColumnMap = Object.fromEntries(
|
||||||
queryBlockAllColumns.map(v => [v.type, v as PropertyMetaConfig])
|
queryBlockAllColumns.map(v => [v.type, v as PropertyMetaConfig])
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ export class BlockQueryDataSource extends DataSourceBase {
|
|||||||
const property = this.getProperty(propertyId);
|
const property = this.getProperty(propertyId);
|
||||||
return (
|
return (
|
||||||
property.getColumnData?.(this.blocks[0].model) ??
|
property.getColumnData?.(this.blocks[0].model) ??
|
||||||
property.metaConfig.config.defaultData()
|
property.metaConfig.config.propertyData.default()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +284,8 @@ export class BlockQueryDataSource extends DataSourceBase {
|
|||||||
|
|
||||||
currentCells as any
|
currentCells as any
|
||||||
) ?? {
|
) ?? {
|
||||||
property: databaseBlockAllPropertyMap[toType].config.defaultData(),
|
property:
|
||||||
|
databaseBlockAllPropertyMap[toType].config.propertyData.default(),
|
||||||
cells: currentCells.map(() => undefined),
|
cells: currentCells.map(() => undefined),
|
||||||
};
|
};
|
||||||
this.block.doc.captureSync();
|
this.block.doc.captureSync();
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export const processTable = (
|
|||||||
if (isDelta(cell.value)) {
|
if (isDelta(cell.value)) {
|
||||||
value = cell.value;
|
value = cell.value;
|
||||||
} else {
|
} else {
|
||||||
value = property.config.cellToString({
|
value = property.config.rawValue.toString({
|
||||||
value: cell.value,
|
value: cell.value,
|
||||||
data: col.data,
|
data: col.data,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
return this._model.doc;
|
return this._model.doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
allPropertyMetas$ = computed<PropertyMetaConfig<any, any, any>[]>(() => {
|
allPropertyMetas$ = computed<PropertyMetaConfig<any, any, any, any>[]>(() => {
|
||||||
return databaseBlockPropertyList;
|
return databaseBlockPropertyList;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -151,23 +151,27 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
this._runCapture();
|
this._runCapture();
|
||||||
|
|
||||||
const type = this.propertyTypeGet(propertyId);
|
const type = this.propertyTypeGet(propertyId);
|
||||||
const update = this.propertyMetaGet(type).config.valueUpdate;
|
const update = this.propertyMetaGet(type)?.config.rawValue.setValue;
|
||||||
let newValue = value;
|
const old = this.cellValueGet(rowId, propertyId);
|
||||||
if (update) {
|
const updateFn =
|
||||||
const old = this.cellValueGet(rowId, propertyId);
|
update ??
|
||||||
newValue = update({
|
(({ setValue, newValue }) => {
|
||||||
value: old,
|
setValue(newValue);
|
||||||
data: this.propertyDataGet(propertyId),
|
|
||||||
dataSource: this,
|
|
||||||
newValue: value,
|
|
||||||
});
|
});
|
||||||
}
|
updateFn({
|
||||||
if (this._model.columns$.value.some(v => v.id === propertyId)) {
|
value: old,
|
||||||
updateCell(this._model, rowId, {
|
data: this.propertyDataGet(propertyId),
|
||||||
columnId: propertyId,
|
dataSource: this,
|
||||||
value: newValue,
|
newValue: value,
|
||||||
});
|
setValue: newValue => {
|
||||||
}
|
if (this._model.columns$.value.some(v => v.id === propertyId)) {
|
||||||
|
updateCell(this._model, rowId, {
|
||||||
|
columnId: propertyId,
|
||||||
|
value: newValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cellValueGet(rowId: string, propertyId: string): unknown {
|
cellValueGet(rowId: string, propertyId: string): unknown {
|
||||||
@@ -183,14 +187,32 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
const model = this.getModelById(rowId);
|
const model = this.getModelById(rowId);
|
||||||
return model?.text;
|
return model?.text;
|
||||||
}
|
}
|
||||||
return getCell(this._model, rowId, propertyId)?.value;
|
const meta = this.propertyMetaGet(type);
|
||||||
|
if (!meta) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rawValue =
|
||||||
|
getCell(this._model, rowId, propertyId)?.value ??
|
||||||
|
meta.config.rawValue.default();
|
||||||
|
const schema = meta.config.rawValue.schema;
|
||||||
|
const result = schema.safeParse(rawValue);
|
||||||
|
if (result.success) {
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyAdd(insertToPosition: InsertToPosition, type?: string): string {
|
propertyAdd(
|
||||||
|
insertToPosition: InsertToPosition,
|
||||||
|
type?: string
|
||||||
|
): string | undefined {
|
||||||
this.doc.captureSync();
|
this.doc.captureSync();
|
||||||
const property = this.propertyMetaGet(
|
const property = this.propertyMetaGet(
|
||||||
type ?? propertyPresets.multiSelectPropertyConfig.type
|
type ?? propertyPresets.multiSelectPropertyConfig.type
|
||||||
);
|
);
|
||||||
|
if (!property) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const result = addProperty(
|
const result = addProperty(
|
||||||
this._model,
|
this._model,
|
||||||
insertToPosition,
|
insertToPosition,
|
||||||
@@ -233,6 +255,9 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
}
|
}
|
||||||
if (this.isFixedProperty(propertyId)) {
|
if (this.isFixedProperty(propertyId)) {
|
||||||
const meta = this.propertyMetaGet(propertyId);
|
const meta = this.propertyMetaGet(propertyId);
|
||||||
|
if (!meta) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const defaultData = meta.config.fixed?.defaultData ?? {};
|
const defaultData = meta.config.fixed?.defaultData ?? {};
|
||||||
return {
|
return {
|
||||||
column: {
|
column: {
|
||||||
@@ -288,7 +313,10 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
}
|
}
|
||||||
const { column } = result;
|
const { column } = result;
|
||||||
const meta = this.propertyMetaGet(column.type);
|
const meta = this.propertyMetaGet(column.type);
|
||||||
return meta.config.type({
|
if (!meta) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return meta.config?.jsonValue.type({
|
||||||
data: column.data,
|
data: column.data,
|
||||||
dataSource: this,
|
dataSource: this,
|
||||||
});
|
});
|
||||||
@@ -335,15 +363,8 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyMetaGet(type: string): PropertyMetaConfig {
|
propertyMetaGet(type: string): PropertyMetaConfig | undefined {
|
||||||
const property = databaseBlockAllPropertyMap[type];
|
return databaseBlockAllPropertyMap[type];
|
||||||
if (!property) {
|
|
||||||
throw new BlockSuiteError(
|
|
||||||
ErrorCode.DatabaseBlockError,
|
|
||||||
`Unknown property type: ${type}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return property;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyNameGet(propertyId: string): string {
|
propertyNameGet(propertyId: string): string {
|
||||||
@@ -382,6 +403,10 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
if (this.isFixedProperty(propertyId)) {
|
if (this.isFixedProperty(propertyId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const meta = this.propertyMetaGet(toType);
|
||||||
|
if (!meta) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const currentType = this.propertyTypeGet(propertyId);
|
const currentType = this.propertyTypeGet(propertyId);
|
||||||
const currentData = this.propertyDataGet(propertyId);
|
const currentData = this.propertyDataGet(propertyId);
|
||||||
const rows = this.rows$.value;
|
const rows = this.rows$.value;
|
||||||
@@ -396,7 +421,7 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
|
|
||||||
currentCells as any
|
currentCells as any
|
||||||
) ?? {
|
) ?? {
|
||||||
property: this.propertyMetaGet(toType).config.defaultData(),
|
property: meta.config.propertyData.default(),
|
||||||
cells: currentCells.map(() => undefined),
|
cells: currentCells.map(() => undefined),
|
||||||
};
|
};
|
||||||
this.doc.captureSync();
|
this.doc.captureSync();
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
} from './cell-renderer.css.js';
|
} from './cell-renderer.css.js';
|
||||||
import { linkPropertyModelConfig } from './define.js';
|
import { linkPropertyModelConfig } from './define.js';
|
||||||
|
|
||||||
export class LinkCell extends BaseCellRenderer<string> {
|
export class LinkCell extends BaseCellRenderer<string, string> {
|
||||||
protected override firstUpdated(_changedProperties: PropertyValues) {
|
protected override firstUpdated(_changedProperties: PropertyValues) {
|
||||||
super.firstUpdated(_changedProperties);
|
super.firstUpdated(_changedProperties);
|
||||||
this.classList.add(linkCellStyle);
|
this.classList.add(linkCellStyle);
|
||||||
|
|||||||
@@ -3,17 +3,23 @@ import zod from 'zod';
|
|||||||
export const linkColumnType = propertyType('link');
|
export const linkColumnType = propertyType('link');
|
||||||
export const linkPropertyModelConfig = linkColumnType.modelConfig({
|
export const linkPropertyModelConfig = linkColumnType.modelConfig({
|
||||||
name: 'Link',
|
name: 'Link',
|
||||||
valueSchema: zod.string().optional(),
|
propertyData: {
|
||||||
type: () => t.string.instance(),
|
schema: zod.object({}),
|
||||||
defaultData: () => ({}),
|
default: () => ({}),
|
||||||
cellToString: ({ value }) => value?.toString() ?? '',
|
},
|
||||||
cellFromString: ({ value }) => {
|
jsonValue: {
|
||||||
return {
|
schema: zod.string(),
|
||||||
value: value,
|
type: () => t.string.instance(),
|
||||||
};
|
isEmpty: ({ value }) => !value,
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.string(),
|
||||||
|
default: () => '',
|
||||||
|
toString: ({ value }) => value,
|
||||||
|
fromString: ({ value }) => {
|
||||||
|
return { value: value };
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
},
|
},
|
||||||
cellToJson: ({ value }) => value ?? null,
|
|
||||||
cellFromJson: ({ value }) => (typeof value !== 'string' ? undefined : value),
|
|
||||||
|
|
||||||
isEmpty: ({ value }) => value == null || value.length == 0,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ function toggleStyle(
|
|||||||
inlineEditor.syncInlineRange();
|
inlineEditor.syncInlineRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RichTextCell extends BaseCellRenderer<Text> {
|
export class RichTextCell extends BaseCellRenderer<Text, string> {
|
||||||
inlineEditor$ = computed(() => {
|
inlineEditor$ = computed(() => {
|
||||||
return this.richText$.value?.inlineEditor;
|
return this.richText$.value?.inlineEditor;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,51 +19,59 @@ export const toYText = (text?: RichTextCellType): undefined | Text['yText'] => {
|
|||||||
|
|
||||||
export const richTextPropertyModelConfig = richTextColumnType.modelConfig({
|
export const richTextPropertyModelConfig = richTextColumnType.modelConfig({
|
||||||
name: 'Text',
|
name: 'Text',
|
||||||
valueSchema: zod
|
propertyData: {
|
||||||
.custom<RichTextCellType>(
|
schema: zod.object({}),
|
||||||
data => data instanceof Text || data instanceof Y.Text
|
default: () => ({}),
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
type: () => t.richText.instance(),
|
|
||||||
defaultData: () => ({}),
|
|
||||||
cellToString: ({ value }) => value?.toString() ?? '',
|
|
||||||
cellFromString: ({ value }) => {
|
|
||||||
return {
|
|
||||||
value: new Text(value),
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
cellToJson: ({ value, dataSource }) => {
|
jsonValue: {
|
||||||
if (!value) return null;
|
schema: zod.string(),
|
||||||
const host = dataSource.contextGet(HostContextKey);
|
type: () => t.richText.instance(),
|
||||||
if (host) {
|
isEmpty: ({ value }) => !value,
|
||||||
const collection = host.std.workspace;
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod
|
||||||
|
.custom<RichTextCellType>(
|
||||||
|
data => data instanceof Text || data instanceof Y.Text
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
default: () => undefined,
|
||||||
|
toString: ({ value }) => value?.toString() ?? '',
|
||||||
|
fromString: ({ value }) => {
|
||||||
|
return {
|
||||||
|
value: new Text(value),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
toJson: ({ value, dataSource }) => {
|
||||||
|
if (!value) return null;
|
||||||
|
const host = dataSource.contextGet(HostContextKey);
|
||||||
|
if (host) {
|
||||||
|
const collection = host.std.workspace;
|
||||||
|
const yText = toYText(value);
|
||||||
|
const deltas = yText?.toDelta();
|
||||||
|
const text = deltas
|
||||||
|
.map((delta: DeltaInsert<AffineTextAttributes>) => {
|
||||||
|
if (isLinkedDoc(delta)) {
|
||||||
|
const linkedDocId = delta.attributes?.reference?.pageId as string;
|
||||||
|
return collection.getDoc(linkedDocId)?.meta?.title;
|
||||||
|
}
|
||||||
|
return delta.insert;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return value?.toString() ?? null;
|
||||||
|
},
|
||||||
|
fromJson: ({ value }) =>
|
||||||
|
typeof value !== 'string' ? undefined : new Text(value),
|
||||||
|
onUpdate: ({ value, callback }) => {
|
||||||
const yText = toYText(value);
|
const yText = toYText(value);
|
||||||
const deltas = yText?.toDelta();
|
yText?.observe(callback);
|
||||||
const text = deltas
|
callback();
|
||||||
.map((delta: DeltaInsert<AffineTextAttributes>) => {
|
return {
|
||||||
if (isLinkedDoc(delta)) {
|
dispose: () => {
|
||||||
const linkedDocId = delta.attributes?.reference?.pageId as string;
|
yText?.unobserve(callback);
|
||||||
return collection.getDoc(linkedDocId)?.meta?.title;
|
},
|
||||||
}
|
};
|
||||||
return delta.insert;
|
},
|
||||||
})
|
|
||||||
.join('');
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
return value?.toString() ?? null;
|
|
||||||
},
|
},
|
||||||
cellFromJson: ({ value }) =>
|
|
||||||
typeof value !== 'string' ? undefined : new Text(value),
|
|
||||||
onUpdate: ({ value, callback }) => {
|
|
||||||
const yText = toYText(value);
|
|
||||||
yText?.observe(callback);
|
|
||||||
callback();
|
|
||||||
return {
|
|
||||||
dispose: () => {
|
|
||||||
yText?.unobserve(callback);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
isEmpty: ({ value }) => value == null || value.length === 0,
|
|
||||||
values: ({ value }) => (value?.toString() ? [value.toString()] : []),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { propertyType, t } from '@blocksuite/data-view';
|
import { propertyType, t } from '@blocksuite/data-view';
|
||||||
import { Text } from '@blocksuite/store';
|
import { Text } from '@blocksuite/store';
|
||||||
|
import { Doc } from 'yjs';
|
||||||
import zod from 'zod';
|
import zod from 'zod';
|
||||||
|
|
||||||
import { HostContextKey } from '../../context/host-context.js';
|
import { HostContextKey } from '../../context/host-context.js';
|
||||||
@@ -9,61 +10,74 @@ export const titleColumnType = propertyType('title');
|
|||||||
|
|
||||||
export const titlePropertyModelConfig = titleColumnType.modelConfig({
|
export const titlePropertyModelConfig = titleColumnType.modelConfig({
|
||||||
name: 'Title',
|
name: 'Title',
|
||||||
valueSchema: zod.custom<Text>(data => data instanceof Text).optional(),
|
propertyData: {
|
||||||
|
schema: zod.object({}),
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
jsonValue: {
|
||||||
|
schema: zod.string(),
|
||||||
|
type: () => t.richText.instance(),
|
||||||
|
isEmpty: ({ value }) => !value,
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.custom<Text>(data => data instanceof Text).optional(),
|
||||||
|
default: () => undefined,
|
||||||
|
toString: ({ value }) => value?.toString() ?? '',
|
||||||
|
fromString: ({ value }) => {
|
||||||
|
return { value: new Text(value) };
|
||||||
|
},
|
||||||
|
toJson: ({ value, dataSource }) => {
|
||||||
|
if (!value) return '';
|
||||||
|
const host = dataSource.contextGet(HostContextKey);
|
||||||
|
if (host) {
|
||||||
|
const collection = host.std.workspace;
|
||||||
|
const deltas = value.deltas$.value;
|
||||||
|
const text = deltas
|
||||||
|
.map(delta => {
|
||||||
|
if (isLinkedDoc(delta)) {
|
||||||
|
const linkedDocId = delta.attributes?.reference?.pageId as string;
|
||||||
|
return collection.getDoc(linkedDocId)?.meta?.title;
|
||||||
|
}
|
||||||
|
return delta.insert;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return value?.toString() ?? '';
|
||||||
|
},
|
||||||
|
fromJson: ({ value }) => new Text(value),
|
||||||
|
onUpdate: ({ value, callback }) => {
|
||||||
|
value?.yText.observe(callback);
|
||||||
|
callback();
|
||||||
|
return {
|
||||||
|
dispose: () => {
|
||||||
|
value?.yText.unobserve(callback);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
setValue: ({ value, newValue }) => {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const v = newValue as unknown;
|
||||||
|
if (v == null) {
|
||||||
|
value.replace(0, value.length, '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
value.replace(0, value.length, v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newValue instanceof Text) {
|
||||||
|
new Doc().getMap('root').set('text', newValue.yText);
|
||||||
|
value.clear();
|
||||||
|
value.applyDelta(newValue.toDelta());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
fixed: {
|
fixed: {
|
||||||
defaultData: {},
|
defaultData: {},
|
||||||
defaultShow: true,
|
defaultShow: true,
|
||||||
},
|
},
|
||||||
type: () => t.richText.instance(),
|
|
||||||
defaultData: () => ({}),
|
|
||||||
cellToString: ({ value }) => value?.toString() ?? '',
|
|
||||||
cellFromString: ({ value }) => {
|
|
||||||
return {
|
|
||||||
value: value,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
cellToJson: ({ value, dataSource }) => {
|
|
||||||
if (!value) return null;
|
|
||||||
const host = dataSource.contextGet(HostContextKey);
|
|
||||||
if (host) {
|
|
||||||
const collection = host.std.workspace;
|
|
||||||
const deltas = value.deltas$.value;
|
|
||||||
const text = deltas
|
|
||||||
.map(delta => {
|
|
||||||
if (isLinkedDoc(delta)) {
|
|
||||||
const linkedDocId = delta.attributes?.reference?.pageId as string;
|
|
||||||
return collection.getDoc(linkedDocId)?.meta?.title;
|
|
||||||
}
|
|
||||||
return delta.insert;
|
|
||||||
})
|
|
||||||
.join('');
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
return value?.toString() ?? null;
|
|
||||||
},
|
|
||||||
cellFromJson: ({ value }) =>
|
|
||||||
typeof value !== 'string' ? undefined : new Text(value),
|
|
||||||
onUpdate: ({ value, callback }) => {
|
|
||||||
value?.yText.observe(callback);
|
|
||||||
callback();
|
|
||||||
return {
|
|
||||||
dispose: () => {
|
|
||||||
value?.yText.unobserve(callback);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
valueUpdate: ({ value, newValue }) => {
|
|
||||||
const v = newValue as unknown;
|
|
||||||
if (typeof v === 'string') {
|
|
||||||
value?.replace(0, value.length, v);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (v == null) {
|
|
||||||
value?.replace(0, value.length, '');
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return newValue;
|
|
||||||
},
|
|
||||||
isEmpty: ({ value }) => value == null || value.length === 0,
|
|
||||||
values: ({ value }) => (value?.toString() ? [value.toString()] : []),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
titleRichTextStyle,
|
titleRichTextStyle,
|
||||||
} from './cell-renderer.css.js';
|
} from './cell-renderer.css.js';
|
||||||
|
|
||||||
export class HeaderAreaTextCell extends BaseCellRenderer<Text> {
|
export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
|
||||||
activity = true;
|
activity = true;
|
||||||
|
|
||||||
docId$ = signal<string>();
|
docId$ = signal<string>();
|
||||||
|
|||||||
@@ -53,8 +53,11 @@ export interface DataSource {
|
|||||||
propertyReadonlyGet(propertyId: string): boolean;
|
propertyReadonlyGet(propertyId: string): boolean;
|
||||||
propertyReadonlyGet$(propertyId: string): ReadonlySignal<boolean>;
|
propertyReadonlyGet$(propertyId: string): ReadonlySignal<boolean>;
|
||||||
|
|
||||||
propertyMetaGet(type: string): PropertyMetaConfig;
|
propertyMetaGet(type: string): PropertyMetaConfig | undefined;
|
||||||
propertyAdd(insertToPosition: InsertToPosition, type?: string): string;
|
propertyAdd(
|
||||||
|
insertToPosition: InsertToPosition,
|
||||||
|
type?: string
|
||||||
|
): string | undefined;
|
||||||
|
|
||||||
propertyDuplicate(propertyId: string): string | undefined;
|
propertyDuplicate(propertyId: string): string | undefined;
|
||||||
propertyCanDuplicate(propertyId: string): boolean;
|
propertyCanDuplicate(propertyId: string): boolean;
|
||||||
@@ -152,7 +155,7 @@ export abstract class DataSourceBase implements DataSource {
|
|||||||
abstract propertyAdd(
|
abstract propertyAdd(
|
||||||
insertToPosition: InsertToPosition,
|
insertToPosition: InsertToPosition,
|
||||||
type?: string
|
type?: string
|
||||||
): string;
|
): string | undefined;
|
||||||
|
|
||||||
abstract propertyDataGet(propertyId: string): Record<string, unknown>;
|
abstract propertyDataGet(propertyId: string): Record<string, unknown>;
|
||||||
|
|
||||||
@@ -179,7 +182,7 @@ export abstract class DataSourceBase implements DataSource {
|
|||||||
|
|
||||||
abstract propertyDuplicate(propertyId: string): string | undefined;
|
abstract propertyDuplicate(propertyId: string): string | undefined;
|
||||||
|
|
||||||
abstract propertyMetaGet(type: string): PropertyMetaConfig;
|
abstract propertyMetaGet(type: string): PropertyMetaConfig | undefined;
|
||||||
|
|
||||||
abstract propertyNameGet(propertyId: string): string;
|
abstract propertyNameGet(propertyId: string): string;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const defaultGroupBy = (
|
|||||||
data: NonNullable<unknown>
|
data: NonNullable<unknown>
|
||||||
): GroupBy | undefined => {
|
): GroupBy | undefined => {
|
||||||
const name = groupByMatcher.match(
|
const name = groupByMatcher.match(
|
||||||
propertyMeta.config.type({ data, dataSource })
|
propertyMeta.config.jsonValue.type({ data, dataSource })
|
||||||
)?.name;
|
)?.name;
|
||||||
return name != null
|
return name != null
|
||||||
? {
|
? {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ 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 type { DVJSON } from '../property/types.js';
|
|
||||||
import { createTraitKey } from '../traits/key.js';
|
import { createTraitKey } from '../traits/key.js';
|
||||||
import type { Property } from '../view-manager/property.js';
|
import type { Property } from '../view-manager/property.js';
|
||||||
import type { SingleView } from '../view-manager/single-view.js';
|
import type { SingleView } from '../view-manager/single-view.js';
|
||||||
@@ -19,7 +18,7 @@ export type GroupData = {
|
|||||||
key: string;
|
key: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: TypeInstance;
|
type: TypeInstance;
|
||||||
value: DVJSON;
|
value: unknown;
|
||||||
rows: string[];
|
rows: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -235,7 +234,7 @@ export class GroupTrait {
|
|||||||
}
|
}
|
||||||
const remove = this.config$.value?.removeFromGroup ?? (() => null);
|
const remove = this.config$.value?.removeFromGroup ?? (() => null);
|
||||||
const group = fromGroupKey != null ? groupMap[fromGroupKey] : undefined;
|
const group = fromGroupKey != null ? groupMap[fromGroupKey] : undefined;
|
||||||
let newValue: DVJSON = null;
|
let newValue: unknown = null;
|
||||||
if (group) {
|
if (group) {
|
||||||
newValue = remove(
|
newValue = remove(
|
||||||
group.value,
|
group.value,
|
||||||
@@ -284,7 +283,7 @@ export class GroupTrait {
|
|||||||
this.view.cellValueSet(rowId, propertyId, newValue);
|
this.view.cellValueSet(rowId, propertyId, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateValue(rows: string[], value: DVJSON) {
|
updateValue(rows: string[], value: unknown) {
|
||||||
const propertyId = this.propertyId;
|
const propertyId = this.propertyId;
|
||||||
if (!propertyId) {
|
if (!propertyId) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,34 +1,35 @@
|
|||||||
import type { UniComponent } from '@blocksuite/affine-shared/types';
|
import type { UniComponent } from '@blocksuite/affine-shared/types';
|
||||||
|
|
||||||
import type { TypeInstance } from '../logical/type.js';
|
import type { TypeInstance } from '../logical/type.js';
|
||||||
import type { DVJSON } from '../property/types.js';
|
|
||||||
|
|
||||||
export interface GroupRenderProps<
|
export interface GroupRenderProps<
|
||||||
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
||||||
Value = DVJSON,
|
JsonValue = unknown,
|
||||||
> {
|
> {
|
||||||
data: Data;
|
data: Data;
|
||||||
updateData?: (data: Data) => void;
|
updateData?: (data: Data) => void;
|
||||||
value: Value;
|
value: JsonValue;
|
||||||
updateValue?: (value: Value) => void;
|
updateValue?: (value: JsonValue) => void;
|
||||||
readonly: boolean;
|
readonly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GroupByConfig = {
|
export type GroupByConfig<
|
||||||
|
JsonValue = unknown,
|
||||||
|
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
||||||
|
> = {
|
||||||
name: string;
|
name: string;
|
||||||
groupName: (type: TypeInstance, value: unknown) => string;
|
groupName: (type: TypeInstance, value: unknown) => string;
|
||||||
defaultKeys: (type: TypeInstance) => {
|
defaultKeys: (type: TypeInstance) => {
|
||||||
key: string;
|
key: string;
|
||||||
value: DVJSON;
|
value: JsonValue;
|
||||||
}[];
|
}[];
|
||||||
valuesGroup: (
|
valuesGroup: (
|
||||||
value: unknown,
|
value: unknown,
|
||||||
type: TypeInstance
|
type: TypeInstance
|
||||||
) => {
|
) => {
|
||||||
key: string;
|
key: string;
|
||||||
value: DVJSON;
|
value: JsonValue;
|
||||||
}[];
|
}[];
|
||||||
addToGroup?: (value: DVJSON, oldValue: DVJSON) => DVJSON;
|
addToGroup?: (value: JsonValue, oldValue: JsonValue) => JsonValue;
|
||||||
removeFromGroup?: (value: DVJSON, oldValue: DVJSON) => DVJSON;
|
removeFromGroup?: (value: JsonValue, oldValue: JsonValue) => JsonValue;
|
||||||
view: UniComponent<GroupRenderProps>;
|
view: UniComponent<GroupRenderProps<Data, JsonValue>>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export const SelectTagSchema = Zod.object({
|
|||||||
id: Zod.string(),
|
id: Zod.string(),
|
||||||
color: Zod.string(),
|
color: Zod.string(),
|
||||||
value: Zod.string(),
|
value: Zod.string(),
|
||||||
parentId: Zod.string().optional(),
|
|
||||||
});
|
});
|
||||||
export const unknown = defineDataType('Unknown', zod.never(), zod.unknown());
|
export const unknown = defineDataType('Unknown', zod.never(), zod.unknown());
|
||||||
export const dt = {
|
export const dt = {
|
||||||
|
|||||||
@@ -8,18 +8,19 @@ import type { Cell } from '../view-manager/cell.js';
|
|||||||
import type { CellRenderProps, DataViewCellLifeCycle } from './manager.js';
|
import type { CellRenderProps, DataViewCellLifeCycle } from './manager.js';
|
||||||
|
|
||||||
export abstract class BaseCellRenderer<
|
export abstract class BaseCellRenderer<
|
||||||
Value,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
>
|
>
|
||||||
extends SignalWatcher(WithDisposable(ShadowlessElement))
|
extends SignalWatcher(WithDisposable(ShadowlessElement))
|
||||||
implements DataViewCellLifeCycle, CellRenderProps<Data, Value>
|
implements DataViewCellLifeCycle, CellRenderProps<Data, RawValue, JsonValue>
|
||||||
{
|
{
|
||||||
get expose() {
|
get expose() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor cell!: Cell<Value, Data>;
|
accessor cell!: Cell<RawValue, JsonValue, Data>;
|
||||||
|
|
||||||
readonly$ = computed(() => {
|
readonly$ = computed(() => {
|
||||||
return this.cell.property.readonly$.value;
|
return this.cell.property.readonly$.value;
|
||||||
@@ -101,11 +102,11 @@ export abstract class BaseCellRenderer<
|
|||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
valueSetImmediate(value: Value | undefined): void {
|
valueSetImmediate(value: RawValue | undefined): void {
|
||||||
this.cell.valueSet(value);
|
this.cell.valueSet(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
valueSetNextTick(value: Value | undefined) {
|
valueSetNextTick(value: RawValue | undefined) {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this.cell.valueSet(value);
|
this.cell.valueSet(value);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { PropertyModel } from './property-config.js';
|
import type { PropertyModel } from './property-config.js';
|
||||||
import type {
|
import type {
|
||||||
GetCellDataFromConfig,
|
|
||||||
GetPropertyDataFromConfig,
|
GetPropertyDataFromConfig,
|
||||||
|
GetRawValueFromConfig,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
|
|
||||||
export type ConvertFunction<
|
export type ConvertFunction<
|
||||||
@@ -9,14 +9,14 @@ export type ConvertFunction<
|
|||||||
To extends PropertyModel = PropertyModel,
|
To extends PropertyModel = PropertyModel,
|
||||||
> = (
|
> = (
|
||||||
property: GetPropertyDataFromConfig<From['config']>,
|
property: GetPropertyDataFromConfig<From['config']>,
|
||||||
cells: (GetCellDataFromConfig<From['config']> | undefined)[]
|
cells: (GetRawValueFromConfig<From['config']> | undefined)[]
|
||||||
) => {
|
) => {
|
||||||
property: GetPropertyDataFromConfig<To['config']>;
|
property: GetPropertyDataFromConfig<To['config']>;
|
||||||
cells: (GetCellDataFromConfig<To['config']> | undefined)[];
|
cells: (GetRawValueFromConfig<To['config']> | undefined)[];
|
||||||
};
|
};
|
||||||
export const createPropertyConvert = <
|
export const createPropertyConvert = <
|
||||||
From extends PropertyModel<any, any, any>,
|
From extends PropertyModel<any, any, any, any>,
|
||||||
To extends PropertyModel<any, any, any>,
|
To extends PropertyModel<any, any, any, any>,
|
||||||
>(
|
>(
|
||||||
from: From,
|
from: From,
|
||||||
to: To,
|
to: To,
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import type { Cell } from '../view-manager/cell.js';
|
|||||||
|
|
||||||
export interface CellRenderProps<
|
export interface CellRenderProps<
|
||||||
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
> {
|
> {
|
||||||
cell: Cell<Value, Data>;
|
cell: Cell<RawValue, JsonValue, Data>;
|
||||||
isEditing$: ReadonlySignal<boolean>;
|
isEditing$: ReadonlySignal<boolean>;
|
||||||
selectCurrentCell: (editing: boolean) => void;
|
selectCurrentCell: (editing: boolean) => void;
|
||||||
}
|
}
|
||||||
@@ -27,12 +28,17 @@ export interface DataViewCellLifeCycle {
|
|||||||
|
|
||||||
export type DataViewCellComponent<
|
export type DataViewCellComponent<
|
||||||
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
> = UniComponent<CellRenderProps<Data, Value>, DataViewCellLifeCycle>;
|
JsonValue = unknown,
|
||||||
|
> = UniComponent<
|
||||||
|
CellRenderProps<Data, RawValue, JsonValue>,
|
||||||
|
DataViewCellLifeCycle
|
||||||
|
>;
|
||||||
|
|
||||||
export type CellRenderer<
|
export type CellRenderer<
|
||||||
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
> = {
|
> = {
|
||||||
view: DataViewCellComponent<Data, Value>;
|
view: DataViewCellComponent<Data, RawValue, JsonValue>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,20 +4,22 @@ import type { PropertyConfig } from './types.js';
|
|||||||
export type PropertyMetaConfig<
|
export type PropertyMetaConfig<
|
||||||
Type extends string = string,
|
Type extends string = string,
|
||||||
PropertyData extends NonNullable<unknown> = NonNullable<unknown>,
|
PropertyData extends NonNullable<unknown> = NonNullable<unknown>,
|
||||||
CellData = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
> = {
|
> = {
|
||||||
type: Type;
|
type: Type;
|
||||||
config: PropertyConfig<PropertyData, CellData>;
|
config: PropertyConfig<PropertyData, RawValue, JsonValue>;
|
||||||
create: Create<PropertyData>;
|
create: Create<PropertyData>;
|
||||||
renderer: Renderer<PropertyData, CellData>;
|
renderer: Renderer<PropertyData, RawValue, JsonValue>;
|
||||||
};
|
};
|
||||||
type CreatePropertyMeta<
|
type CreatePropertyMeta<
|
||||||
Type extends string = string,
|
Type extends string = string,
|
||||||
PropertyData extends Record<string, unknown> = Record<string, never>,
|
PropertyData extends Record<string, unknown> = Record<string, never>,
|
||||||
CellData = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
> = (
|
> = (
|
||||||
renderer: Omit<Renderer<PropertyData, CellData>, 'type'>
|
renderer: Omit<Renderer<PropertyData, RawValue, JsonValue>, 'type'>
|
||||||
) => PropertyMetaConfig<Type, PropertyData, CellData>;
|
) => PropertyMetaConfig<Type, PropertyData, RawValue, JsonValue>;
|
||||||
type Create<
|
type Create<
|
||||||
PropertyData extends Record<string, unknown> = Record<string, never>,
|
PropertyData extends Record<string, unknown> = Record<string, never>,
|
||||||
> = (
|
> = (
|
||||||
@@ -32,26 +34,33 @@ type Create<
|
|||||||
export type PropertyModel<
|
export type PropertyModel<
|
||||||
Type extends string = string,
|
Type extends string = string,
|
||||||
PropertyData extends Record<string, unknown> = Record<string, unknown>,
|
PropertyData extends Record<string, unknown> = Record<string, unknown>,
|
||||||
CellData = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
> = {
|
> = {
|
||||||
type: Type;
|
type: Type;
|
||||||
config: PropertyConfig<PropertyData, CellData>;
|
config: PropertyConfig<PropertyData, RawValue, JsonValue>;
|
||||||
create: Create<PropertyData>;
|
create: Create<PropertyData>;
|
||||||
createPropertyMeta: CreatePropertyMeta<Type, PropertyData, CellData>;
|
createPropertyMeta: CreatePropertyMeta<
|
||||||
|
Type,
|
||||||
|
PropertyData,
|
||||||
|
RawValue,
|
||||||
|
JsonValue
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
export const propertyType = <Type extends string>(type: Type) => ({
|
export const propertyType = <Type extends string>(type: Type) => ({
|
||||||
type: type,
|
type: type,
|
||||||
modelConfig: <
|
modelConfig: <
|
||||||
CellData,
|
|
||||||
PropertyData extends Record<string, unknown> = Record<string, never>,
|
PropertyData extends Record<string, unknown> = Record<string, never>,
|
||||||
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
>(
|
>(
|
||||||
ops: PropertyConfig<PropertyData, CellData>
|
ops: PropertyConfig<PropertyData, RawValue, JsonValue>
|
||||||
): PropertyModel<Type, PropertyData, CellData> => {
|
): PropertyModel<Type, PropertyData, RawValue, JsonValue> => {
|
||||||
const create: Create<PropertyData> = (name, data) => {
|
const create: Create<PropertyData> = (name, data) => {
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
name,
|
name,
|
||||||
data: data ?? ops.defaultData(),
|
data: data ?? ops.propertyData.default(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -6,18 +6,20 @@ import type { CellRenderer, DataViewCellComponent } from './manager.js';
|
|||||||
|
|
||||||
export interface Renderer<
|
export interface Renderer<
|
||||||
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
> {
|
> {
|
||||||
type: string;
|
type: string;
|
||||||
icon?: UniComponent;
|
icon?: UniComponent;
|
||||||
cellRenderer: CellRenderer<Data, Value>;
|
cellRenderer: CellRenderer<Data, RawValue, JsonValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createFromBaseCellRenderer = <
|
export const createFromBaseCellRenderer = <
|
||||||
Value,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
>(
|
>(
|
||||||
renderer: new () => BaseCellRenderer<Value, Data>
|
renderer: new () => BaseCellRenderer<RawValue, JsonValue, Data>
|
||||||
): DataViewCellComponent => {
|
): DataViewCellComponent => {
|
||||||
return createUniComponentFromWebComponent(renderer as never) as never;
|
return createUniComponentFromWebComponent(renderer as never) as never;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,86 +8,84 @@ export type WithCommonPropertyConfig<T = {}> = T & {
|
|||||||
dataSource: DataSource;
|
dataSource: DataSource;
|
||||||
};
|
};
|
||||||
export type GetPropertyDataFromConfig<T> =
|
export type GetPropertyDataFromConfig<T> =
|
||||||
T extends PropertyConfig<infer R, any> ? R : never;
|
T extends PropertyConfig<infer R, any, any> ? R : never;
|
||||||
export type GetCellDataFromConfig<T> =
|
export type GetRawValueFromConfig<T> =
|
||||||
T extends PropertyConfig<any, infer R> ? R : never;
|
T extends PropertyConfig<any, infer R, any> ? R : never;
|
||||||
export type PropertyConfig<
|
export type GetJsonValueFromConfig<T> =
|
||||||
Data extends NonNullable<unknown> = NonNullable<unknown>,
|
T extends PropertyConfig<any, any, infer R> ? R : never;
|
||||||
Value = unknown,
|
export type PropertyConfig<Data, RawValue = unknown, JsonValue = unknown> = {
|
||||||
> = {
|
|
||||||
name: string;
|
name: string;
|
||||||
valueSchema: ZodType<Value>;
|
|
||||||
hide?: boolean;
|
hide?: boolean;
|
||||||
|
propertyData: {
|
||||||
|
schema: ZodType<Data>;
|
||||||
|
default: () => Data;
|
||||||
|
};
|
||||||
|
rawValue: {
|
||||||
|
schema: ZodType<RawValue>;
|
||||||
|
default: () => RawValue;
|
||||||
|
toString: (config: { value: RawValue; data: Data }) => string;
|
||||||
|
fromString: (
|
||||||
|
config: WithCommonPropertyConfig<{
|
||||||
|
value: string;
|
||||||
|
data: Data;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
value: unknown;
|
||||||
|
data?: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
toJson: (
|
||||||
|
config: WithCommonPropertyConfig<{
|
||||||
|
value: RawValue;
|
||||||
|
data: Data;
|
||||||
|
}>
|
||||||
|
) => JsonValue;
|
||||||
|
fromJson: (
|
||||||
|
config: WithCommonPropertyConfig<{
|
||||||
|
value: JsonValue;
|
||||||
|
data: Data;
|
||||||
|
}>
|
||||||
|
) => RawValue | undefined;
|
||||||
|
setValue?: (
|
||||||
|
config: WithCommonPropertyConfig<{
|
||||||
|
data: Data;
|
||||||
|
value: RawValue;
|
||||||
|
newValue: RawValue;
|
||||||
|
setValue: (value: RawValue) => void;
|
||||||
|
}>
|
||||||
|
) => void;
|
||||||
|
onUpdate?: (
|
||||||
|
config: WithCommonPropertyConfig<{
|
||||||
|
value: RawValue;
|
||||||
|
data: Data;
|
||||||
|
callback: () => void;
|
||||||
|
}>
|
||||||
|
) => Disposable;
|
||||||
|
};
|
||||||
|
jsonValue: {
|
||||||
|
schema: ZodType<JsonValue>;
|
||||||
|
type: (
|
||||||
|
config: WithCommonPropertyConfig<{
|
||||||
|
data: Data;
|
||||||
|
}>
|
||||||
|
) => TypeInstance;
|
||||||
|
isEmpty: (
|
||||||
|
config: WithCommonPropertyConfig<{
|
||||||
|
value: JsonValue;
|
||||||
|
}>
|
||||||
|
) => boolean;
|
||||||
|
};
|
||||||
fixed?: {
|
fixed?: {
|
||||||
defaultData: Data;
|
defaultData: Data;
|
||||||
defaultOrder?: string;
|
defaultOrder?: string;
|
||||||
defaultShow?: boolean;
|
defaultShow?: boolean;
|
||||||
};
|
};
|
||||||
defaultData: () => Data;
|
|
||||||
type: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
data: Data;
|
|
||||||
}>
|
|
||||||
) => TypeInstance;
|
|
||||||
formatValue?: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
value: Value;
|
|
||||||
data: Data;
|
|
||||||
}>
|
|
||||||
) => Value;
|
|
||||||
isEmpty: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
value?: Value;
|
|
||||||
}>
|
|
||||||
) => boolean;
|
|
||||||
minWidth?: number;
|
minWidth?: number;
|
||||||
values?: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
value?: Value;
|
|
||||||
}>
|
|
||||||
) => unknown[];
|
|
||||||
cellToString: (config: { value: Value; data: Data }) => string;
|
|
||||||
cellFromString: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
value: string;
|
|
||||||
data: Data;
|
|
||||||
}>
|
|
||||||
) => {
|
|
||||||
value: unknown;
|
|
||||||
data?: Record<string, unknown>;
|
|
||||||
};
|
|
||||||
cellToJson: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
value?: Value;
|
|
||||||
data: Data;
|
|
||||||
}>
|
|
||||||
) => DVJSON;
|
|
||||||
cellFromJson: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
value: DVJSON;
|
|
||||||
data: Data;
|
|
||||||
}>
|
|
||||||
) => Value | undefined;
|
|
||||||
addGroup?: (
|
addGroup?: (
|
||||||
config: WithCommonPropertyConfig<{
|
config: WithCommonPropertyConfig<{
|
||||||
text: string;
|
text: string;
|
||||||
oldData: Data;
|
oldData: Data;
|
||||||
}>
|
}>
|
||||||
) => Data;
|
) => Data;
|
||||||
onUpdate?: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
value: Value;
|
|
||||||
data: Data;
|
|
||||||
callback: () => void;
|
|
||||||
}>
|
|
||||||
) => Disposable;
|
|
||||||
valueUpdate?: (
|
|
||||||
config: WithCommonPropertyConfig<{
|
|
||||||
value: Value;
|
|
||||||
data: Data;
|
|
||||||
newValue: Value;
|
|
||||||
}>
|
|
||||||
) => Value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DVJSON =
|
export type DVJSON =
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import type { DataSource } from '../data-source/base';
|
||||||
|
import type { PropertyConfig } from './types';
|
||||||
|
|
||||||
|
export const fromJson = <Data, RawValue, JsonValue>(
|
||||||
|
config: PropertyConfig<Data, RawValue, JsonValue>,
|
||||||
|
{
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
dataSource,
|
||||||
|
}: {
|
||||||
|
value: unknown;
|
||||||
|
data: Data;
|
||||||
|
dataSource: DataSource;
|
||||||
|
}
|
||||||
|
): RawValue | undefined => {
|
||||||
|
const fromJson = config.rawValue.fromJson;
|
||||||
|
const jsonSchema = config.jsonValue.schema;
|
||||||
|
if (!fromJson || !jsonSchema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const jsonResult = jsonSchema.safeParse(value);
|
||||||
|
if (!jsonResult.success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return fromJson({
|
||||||
|
value: jsonResult.data,
|
||||||
|
data,
|
||||||
|
dataSource,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -19,16 +19,14 @@ export const anyTypeStatsFunctions: StatisticsConfig[] = [
|
|||||||
displayName: 'Values',
|
displayName: 'Values',
|
||||||
type: 'count-values',
|
type: 'count-values',
|
||||||
dataType: t.unknown.instance(),
|
dataType: t.unknown.instance(),
|
||||||
impl: (data, { meta, dataSource }) => {
|
impl: data => {
|
||||||
const values = data
|
const values = data.reduce((acc: number, v) => {
|
||||||
.flatMap(v => {
|
if (Array.isArray(v)) {
|
||||||
if (meta.config.values) {
|
return acc + v.length;
|
||||||
return meta.config.values({ value: v, dataSource });
|
}
|
||||||
}
|
return acc + (v == null ? 0 : 1);
|
||||||
return v;
|
}, 0);
|
||||||
})
|
return values.toString();
|
||||||
.filter(v => v != null);
|
|
||||||
return values.length.toString();
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
createStatisticConfig({
|
createStatisticConfig({
|
||||||
@@ -37,13 +35,13 @@ export const anyTypeStatsFunctions: StatisticsConfig[] = [
|
|||||||
displayName: 'Unique Values',
|
displayName: 'Unique Values',
|
||||||
type: 'count-unique-values',
|
type: 'count-unique-values',
|
||||||
dataType: t.unknown.instance(),
|
dataType: t.unknown.instance(),
|
||||||
impl: (data, { meta, dataSource }) => {
|
impl: data => {
|
||||||
const values = data
|
const values = data
|
||||||
.flatMap(v => {
|
.flatMap(v => {
|
||||||
if (meta.config.values) {
|
if (Array.isArray(v)) {
|
||||||
return meta.config.values({ value: v, dataSource });
|
return v;
|
||||||
}
|
}
|
||||||
return v;
|
return [v];
|
||||||
})
|
})
|
||||||
.filter(v => v != null);
|
.filter(v => v != null);
|
||||||
return new Set(values).size.toString();
|
return new Set(values).size.toString();
|
||||||
@@ -57,7 +55,7 @@ export const anyTypeStatsFunctions: StatisticsConfig[] = [
|
|||||||
dataType: t.unknown.instance(),
|
dataType: t.unknown.instance(),
|
||||||
impl: (data, { meta, dataSource }) => {
|
impl: (data, { meta, dataSource }) => {
|
||||||
const emptyList = data.filter(value =>
|
const emptyList = data.filter(value =>
|
||||||
meta.config.isEmpty({ value, dataSource })
|
meta.config.jsonValue.isEmpty({ value, dataSource })
|
||||||
);
|
);
|
||||||
return emptyList.length.toString();
|
return emptyList.length.toString();
|
||||||
},
|
},
|
||||||
@@ -70,7 +68,7 @@ export const anyTypeStatsFunctions: StatisticsConfig[] = [
|
|||||||
dataType: t.unknown.instance(),
|
dataType: t.unknown.instance(),
|
||||||
impl: (data, { meta, dataSource }) => {
|
impl: (data, { meta, dataSource }) => {
|
||||||
const notEmptyList = data.filter(
|
const notEmptyList = data.filter(
|
||||||
value => !meta.config.isEmpty({ value, dataSource })
|
value => !meta.config.jsonValue.isEmpty({ value, dataSource })
|
||||||
);
|
);
|
||||||
return notEmptyList.length.toString();
|
return notEmptyList.length.toString();
|
||||||
},
|
},
|
||||||
@@ -84,7 +82,7 @@ export const anyTypeStatsFunctions: StatisticsConfig[] = [
|
|||||||
impl: (data, { meta, dataSource }) => {
|
impl: (data, { meta, dataSource }) => {
|
||||||
if (data.length === 0) return '';
|
if (data.length === 0) return '';
|
||||||
const emptyList = data.filter(value =>
|
const emptyList = data.filter(value =>
|
||||||
meta.config.isEmpty({ value, dataSource })
|
meta.config.jsonValue.isEmpty({ value, dataSource })
|
||||||
);
|
);
|
||||||
return ((emptyList.length / data.length) * 100).toFixed(2) + '%';
|
return ((emptyList.length / data.length) * 100).toFixed(2) + '%';
|
||||||
},
|
},
|
||||||
@@ -98,7 +96,7 @@ export const anyTypeStatsFunctions: StatisticsConfig[] = [
|
|||||||
impl: (data, { meta, dataSource }) => {
|
impl: (data, { meta, dataSource }) => {
|
||||||
if (data.length === 0) return '';
|
if (data.length === 0) return '';
|
||||||
const notEmptyList = data.filter(
|
const notEmptyList = data.filter(
|
||||||
value => !meta.config.isEmpty({ value, dataSource })
|
value => !meta.config.jsonValue.isEmpty({ value, dataSource })
|
||||||
);
|
);
|
||||||
return ((notEmptyList.length / data.length) * 100).toFixed(2) + '%';
|
return ((notEmptyList.length / data.length) * 100).toFixed(2) + '%';
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,26 +5,29 @@ import type { Row } from './row.js';
|
|||||||
import type { SingleView } from './single-view.js';
|
import type { SingleView } from './single-view.js';
|
||||||
|
|
||||||
export interface Cell<
|
export interface Cell<
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
> {
|
> {
|
||||||
readonly rowId: string;
|
readonly rowId: string;
|
||||||
readonly view: SingleView;
|
readonly view: SingleView;
|
||||||
readonly row: Row;
|
readonly row: Row;
|
||||||
readonly propertyId: string;
|
readonly propertyId: string;
|
||||||
readonly property: Property<Value, Data>;
|
readonly property: Property<RawValue, JsonValue, Data>;
|
||||||
readonly isEmpty$: ReadonlySignal<boolean>;
|
readonly isEmpty$: ReadonlySignal<boolean>;
|
||||||
readonly stringValue$: ReadonlySignal<string>;
|
readonly stringValue$: ReadonlySignal<string>;
|
||||||
readonly jsonValue$: ReadonlySignal<unknown>;
|
readonly jsonValue$: ReadonlySignal<JsonValue>;
|
||||||
|
|
||||||
readonly value$: ReadonlySignal<Value | undefined>;
|
readonly value$: ReadonlySignal<RawValue | undefined>;
|
||||||
valueSet(value: Value | undefined): void;
|
|
||||||
|
valueSet(value: RawValue | undefined): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CellBase<
|
export class CellBase<
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
> implements Cell<Value, Data>
|
> implements Cell<RawValue, JsonValue, Data>
|
||||||
{
|
{
|
||||||
meta$ = computed(() => {
|
meta$ = computed(() => {
|
||||||
return this.view.manager.dataSource.propertyMetaGet(
|
return this.view.manager.dataSource.propertyMetaGet(
|
||||||
@@ -36,29 +39,35 @@ export class CellBase<
|
|||||||
return this.view.manager.dataSource.cellValueGet(
|
return this.view.manager.dataSource.cellValueGet(
|
||||||
this.rowId,
|
this.rowId,
|
||||||
this.propertyId
|
this.propertyId
|
||||||
) as Value;
|
) as RawValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
isEmpty$: ReadonlySignal<boolean> = computed(() => {
|
isEmpty$: ReadonlySignal<boolean> = computed(() => {
|
||||||
return this.meta$.value.config.isEmpty({
|
return (
|
||||||
value: this.value$.value,
|
this.meta$.value?.config.jsonValue.isEmpty({
|
||||||
dataSource: this.view.manager.dataSource,
|
value: this.jsonValue$.value,
|
||||||
});
|
dataSource: this.view.manager.dataSource,
|
||||||
|
}) ?? true
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonValue$: ReadonlySignal<unknown> = computed(() => {
|
jsonValue$: ReadonlySignal<JsonValue> = computed(() => {
|
||||||
return this.view.cellJsonValueGet(this.rowId, this.propertyId);
|
return this.view.cellJsonValueGet(this.rowId, this.propertyId) as JsonValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
property$ = computed(() => {
|
property$ = computed(() => {
|
||||||
return this.view.propertyGet(this.propertyId) as Property<Value, Data>;
|
return this.view.propertyGet(this.propertyId) as Property<
|
||||||
|
RawValue,
|
||||||
|
JsonValue,
|
||||||
|
Data
|
||||||
|
>;
|
||||||
});
|
});
|
||||||
|
|
||||||
stringValue$: ReadonlySignal<string> = computed(() => {
|
stringValue$: ReadonlySignal<string> = computed(() => {
|
||||||
return this.view.cellStringValueGet(this.rowId, this.propertyId)!;
|
return this.view.cellStringValueGet(this.rowId, this.propertyId)!;
|
||||||
});
|
});
|
||||||
|
|
||||||
get property(): Property<Value, Data> {
|
get property(): Property<RawValue, JsonValue, Data> {
|
||||||
return this.property$.value;
|
return this.property$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +81,7 @@ export class CellBase<
|
|||||||
public rowId: string
|
public rowId: string
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
valueSet(value: unknown | undefined): void {
|
valueSet(value: RawValue | undefined): void {
|
||||||
this.view.manager.dataSource.cellValueChange(
|
this.view.manager.dataSource.cellValueChange(
|
||||||
this.rowId,
|
this.rowId,
|
||||||
this.propertyId,
|
this.propertyId,
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import type { Cell } from './cell.js';
|
|||||||
import type { SingleView } from './single-view.js';
|
import type { SingleView } from './single-view.js';
|
||||||
|
|
||||||
export interface Property<
|
export interface Property<
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
> {
|
> {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
@@ -28,7 +29,7 @@ export interface Property<
|
|||||||
readonly duplicate?: () => void;
|
readonly duplicate?: () => void;
|
||||||
get canDuplicate(): boolean;
|
get canDuplicate(): boolean;
|
||||||
|
|
||||||
cellGet(rowId: string): Cell<Value>;
|
cellGet(rowId: string): Cell<RawValue, JsonValue, Data>;
|
||||||
|
|
||||||
readonly data$: ReadonlySignal<Data>;
|
readonly data$: ReadonlySignal<Data>;
|
||||||
dataUpdate(updater: PropertyDataUpdater<Data>): void;
|
dataUpdate(updater: PropertyDataUpdater<Data>): void;
|
||||||
@@ -44,17 +45,18 @@ export interface Property<
|
|||||||
hideSet(hide: boolean): void;
|
hideSet(hide: boolean): void;
|
||||||
get hideCanSet(): boolean;
|
get hideCanSet(): boolean;
|
||||||
|
|
||||||
valueGet(rowId: string): Value | undefined;
|
valueGet(rowId: string): RawValue | undefined;
|
||||||
valueSet(rowId: string, value: Value | undefined): void;
|
valueSet(rowId: string, value: RawValue | undefined): void;
|
||||||
|
|
||||||
stringValueGet(rowId: string): string;
|
stringValueGet(rowId: string): string;
|
||||||
valueSetFromString(rowId: string, value: string): void;
|
valueSetFromString(rowId: string, value: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class PropertyBase<
|
export abstract class PropertyBase<
|
||||||
Value = unknown,
|
RawValue = unknown,
|
||||||
|
JsonValue = unknown,
|
||||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||||
> implements Property<Value, Data>
|
> implements Property<RawValue, JsonValue, Data>
|
||||||
{
|
{
|
||||||
cells$ = computed(() => {
|
cells$ = computed(() => {
|
||||||
return this.view.rows$.value.map(id => this.cellGet(id));
|
return this.view.rows$.value.map(id => this.cellGet(id));
|
||||||
@@ -141,8 +143,8 @@ export abstract class PropertyBase<
|
|||||||
return this.view.propertyCanHide(this.id);
|
return this.view.propertyCanHide(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
cellGet(rowId: string): Cell<Value> {
|
cellGet(rowId: string): Cell<RawValue, JsonValue, Data> {
|
||||||
return this.view.cellGet(rowId, this.id) as Cell<Value>;
|
return this.view.cellGet(rowId, this.id) as Cell<RawValue, JsonValue, Data>;
|
||||||
}
|
}
|
||||||
|
|
||||||
dataUpdate(updater: PropertyDataUpdater<Data>): void {
|
dataUpdate(updater: PropertyDataUpdater<Data>): void {
|
||||||
@@ -165,11 +167,11 @@ export abstract class PropertyBase<
|
|||||||
return this.cellGet(rowId).stringValue$.value;
|
return this.cellGet(rowId).stringValue$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueGet(rowId: string): Value | undefined {
|
valueGet(rowId: string): RawValue | undefined {
|
||||||
return this.cellGet(rowId).value$.value;
|
return this.cellGet(rowId).value$.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueSet(rowId: string, value: Value | undefined): void {
|
valueSet(rowId: string, value: RawValue | undefined): void {
|
||||||
return this.cellGet(rowId).valueSet(value);
|
return this.cellGet(rowId).valueSet(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +183,6 @@ export abstract class PropertyBase<
|
|||||||
if (data.data) {
|
if (data.data) {
|
||||||
this.dataUpdate(() => data.data as Data);
|
this.dataUpdate(() => data.data as Data);
|
||||||
}
|
}
|
||||||
this.valueSet(rowId, data.value as Value);
|
this.valueSet(rowId, data.value as RawValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ 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 { DVJSON } from '../index.js';
|
|
||||||
import type { TypeInstance } from '../logical/type.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 type { DataViewDataType, ViewMeta } from '../view/data-view.js';
|
import type { DataViewDataType, ViewMeta } from '../view/data-view.js';
|
||||||
@@ -50,9 +50,9 @@ export interface SingleView {
|
|||||||
|
|
||||||
cellValueSet(rowId: string, propertyId: string, value: unknown): void;
|
cellValueSet(rowId: string, propertyId: string, value: unknown): void;
|
||||||
|
|
||||||
cellJsonValueGet(rowId: string, propertyId: string): DVJSON;
|
cellJsonValueGet(rowId: string, propertyId: string): unknown | null;
|
||||||
|
|
||||||
cellJsonValueSet(rowId: string, propertyId: string, value: DVJSON): void;
|
cellJsonValueSet(rowId: string, propertyId: string, value: unknown): void;
|
||||||
|
|
||||||
cellStringValueGet(rowId: string, propertyId: string): string | undefined;
|
cellStringValueGet(rowId: string, propertyId: string): string | undefined;
|
||||||
|
|
||||||
@@ -82,12 +82,17 @@ export interface SingleView {
|
|||||||
|
|
||||||
readonly propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
|
readonly propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
|
||||||
|
|
||||||
propertyAdd(toAfterOfProperty: InsertToPosition, type?: string): string;
|
propertyAdd(
|
||||||
|
toAfterOfProperty: InsertToPosition,
|
||||||
|
type?: string
|
||||||
|
): string | undefined;
|
||||||
|
|
||||||
propertyDelete(propertyId: string): void;
|
propertyDelete(propertyId: string): void;
|
||||||
|
|
||||||
propertyCanDelete(propertyId: string): boolean;
|
propertyCanDelete(propertyId: string): boolean;
|
||||||
|
|
||||||
propertyDuplicate(propertyId: string): void;
|
propertyDuplicate(propertyId: string): void;
|
||||||
|
|
||||||
propertyCanDuplicate(propertyId: string): boolean;
|
propertyCanDuplicate(propertyId: string): boolean;
|
||||||
|
|
||||||
propertyGet(propertyId: string): Property;
|
propertyGet(propertyId: string): Property;
|
||||||
@@ -105,11 +110,13 @@ export interface SingleView {
|
|||||||
propertyTypeGet(propertyId: string): string | undefined;
|
propertyTypeGet(propertyId: string): string | undefined;
|
||||||
|
|
||||||
propertyTypeSet(propertyId: string, type: string): void;
|
propertyTypeSet(propertyId: string, type: string): void;
|
||||||
|
|
||||||
propertyTypeCanSet(propertyId: string): boolean;
|
propertyTypeCanSet(propertyId: string): boolean;
|
||||||
|
|
||||||
propertyHideGet(propertyId: string): boolean;
|
propertyHideGet(propertyId: string): boolean;
|
||||||
|
|
||||||
propertyHideSet(propertyId: string, hide: boolean): void;
|
propertyHideSet(propertyId: string, hide: boolean): void;
|
||||||
|
|
||||||
propertyCanHide(propertyId: string): boolean;
|
propertyCanHide(propertyId: string): boolean;
|
||||||
|
|
||||||
propertyDataGet(propertyId: string): Record<string, unknown>;
|
propertyDataGet(propertyId: string): Record<string, unknown>;
|
||||||
@@ -187,13 +194,16 @@ export abstract class SingleViewBase<
|
|||||||
});
|
});
|
||||||
|
|
||||||
vars$ = computed(() => {
|
vars$ = computed(() => {
|
||||||
return this.propertiesWithoutFilter$.value.map(id => {
|
return this.propertiesWithoutFilter$.value.flatMap(id => {
|
||||||
const v = this.propertyGet(id);
|
const v = this.propertyGet(id);
|
||||||
const propertyMeta = this.dataSource.propertyMetaGet(v.type$.value);
|
const propertyMeta = this.dataSource.propertyMetaGet(v.type$.value);
|
||||||
|
if (!propertyMeta) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
id: v.id,
|
id: v.id,
|
||||||
name: v.name$.value,
|
name: v.name$.value,
|
||||||
type: propertyMeta.config.type({
|
type: propertyMeta.config.jsonValue.type({
|
||||||
data: v.data$.value,
|
data: v.data$.value,
|
||||||
dataSource: this.dataSource,
|
dataSource: this.dataSource,
|
||||||
}),
|
}),
|
||||||
@@ -229,15 +239,19 @@ export abstract class SingleViewBase<
|
|||||||
public manager: ViewManager,
|
public manager: ViewManager,
|
||||||
public id: string
|
public id: string
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
propertyCanDelete(propertyId: string): boolean {
|
propertyCanDelete(propertyId: string): boolean {
|
||||||
return this.dataSource.propertyCanDelete(propertyId);
|
return this.dataSource.propertyCanDelete(propertyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyCanDuplicate(propertyId: string): boolean {
|
propertyCanDuplicate(propertyId: string): boolean {
|
||||||
return this.dataSource.propertyCanDuplicate(propertyId);
|
return this.dataSource.propertyCanDuplicate(propertyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyTypeCanSet(propertyId: string): boolean {
|
propertyTypeCanSet(propertyId: string): boolean {
|
||||||
return this.dataSource.propertyTypeCanSet(propertyId);
|
return this.dataSource.propertyTypeCanSet(propertyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyCanHide(propertyId: string): boolean {
|
propertyCanHide(propertyId: string): boolean {
|
||||||
return this.propertyTypeGet(propertyId) !== 'title';
|
return this.propertyTypeGet(propertyId) !== 'title';
|
||||||
}
|
}
|
||||||
@@ -264,33 +278,35 @@ export abstract class SingleViewBase<
|
|||||||
return new CellBase(this, propertyId, rowId);
|
return new CellBase(this, propertyId, rowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
cellJsonValueGet(rowId: string, propertyId: string): DVJSON {
|
cellJsonValueGet(rowId: string, propertyId: string): unknown | null {
|
||||||
const type = this.propertyTypeGet(propertyId);
|
const type = this.propertyTypeGet(propertyId);
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.dataSource.propertyMetaGet(type).config.cellToJson({
|
return (
|
||||||
value: this.dataSource.cellValueGet(rowId, propertyId),
|
this.dataSource.propertyMetaGet(type)?.config.rawValue.toJson({
|
||||||
data: this.propertyDataGet(propertyId),
|
value: this.dataSource.cellValueGet(rowId, propertyId),
|
||||||
dataSource: this.dataSource,
|
data: this.propertyDataGet(propertyId),
|
||||||
});
|
dataSource: this.dataSource,
|
||||||
|
}) ?? null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
cellJsonValueSet(rowId: string, propertyId: string, value: DVJSON): void {
|
cellJsonValueSet(rowId: string, propertyId: string, value: unknown): void {
|
||||||
const type = this.propertyTypeGet(propertyId);
|
const type = this.propertyTypeGet(propertyId);
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fromJson = this.dataSource.propertyMetaGet(type).config.cellFromJson;
|
const config = this.dataSource.propertyMetaGet(type)?.config;
|
||||||
this.dataSource.cellValueChange(
|
if (!config) {
|
||||||
rowId,
|
return;
|
||||||
propertyId,
|
}
|
||||||
fromJson({
|
const rawValue = fromJson(config, {
|
||||||
value,
|
value: value,
|
||||||
data: this.propertyDataGet(propertyId),
|
data: this.propertyDataGet(propertyId),
|
||||||
dataSource: this.dataSource,
|
dataSource: this.dataSource,
|
||||||
})
|
});
|
||||||
);
|
this.dataSource.cellValueChange(rowId, propertyId, rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
cellStringValueGet(rowId: string, propertyId: string): string | undefined {
|
cellStringValueGet(rowId: string, propertyId: string): string | undefined {
|
||||||
@@ -299,7 +315,7 @@ export abstract class SingleViewBase<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
this.dataSource.propertyMetaGet(type).config.cellToString({
|
this.dataSource.propertyMetaGet(type)?.config.rawValue.toString({
|
||||||
value: this.dataSource.cellValueGet(rowId, propertyId),
|
value: this.dataSource.cellValueGet(rowId, propertyId),
|
||||||
data: this.propertyDataGet(propertyId),
|
data: this.propertyDataGet(propertyId),
|
||||||
}) ?? ''
|
}) ?? ''
|
||||||
@@ -311,14 +327,7 @@ export abstract class SingleViewBase<
|
|||||||
if (!type) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const cellValue = this.dataSource.cellValueGet(rowId, propertyId);
|
return this.dataSource.cellValueGet(rowId, propertyId);
|
||||||
return (
|
|
||||||
this.dataSource.propertyMetaGet(type).config.formatValue?.({
|
|
||||||
value: cellValue,
|
|
||||||
data: this.propertyDataGet(propertyId),
|
|
||||||
dataSource: this.dataSource,
|
|
||||||
}) ?? cellValue
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cellValueSet(rowId: string, propertyId: string, value: unknown): void {
|
cellValueSet(rowId: string, propertyId: string, value: unknown): void {
|
||||||
@@ -355,8 +364,11 @@ export abstract class SingleViewBase<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyAdd(position: InsertToPosition, type?: string): string {
|
propertyAdd(position: InsertToPosition, type?: string): string | undefined {
|
||||||
const id = this.dataSource.propertyAdd(position, type);
|
const id = this.dataSource.propertyAdd(position, type);
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.propertyMove(id, position);
|
this.propertyMove(id, position);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -374,7 +386,11 @@ export abstract class SingleViewBase<
|
|||||||
if (!type) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.dataSource.propertyMetaGet(type).config.type({
|
const meta = this.dataSource.propertyMetaGet(type);
|
||||||
|
if (!meta) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return meta.config.jsonValue.type({
|
||||||
data: this.propertyDataGet(propertyId),
|
data: this.propertyDataGet(propertyId),
|
||||||
dataSource: this.dataSource,
|
dataSource: this.dataSource,
|
||||||
});
|
});
|
||||||
@@ -402,7 +418,7 @@ export abstract class SingleViewBase<
|
|||||||
abstract propertyHideSet(propertyId: string, hide: boolean): void;
|
abstract propertyHideSet(propertyId: string, hide: boolean): void;
|
||||||
|
|
||||||
propertyIconGet(type: string): UniComponent | undefined {
|
propertyIconGet(type: string): UniComponent | undefined {
|
||||||
return this.dataSource.propertyMetaGet(type).renderer.icon;
|
return this.dataSource.propertyMetaGet(type)?.renderer.icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyIdGetByIndex(index: number): string | undefined {
|
propertyIdGetByIndex(index: number): string | undefined {
|
||||||
@@ -413,7 +429,7 @@ export abstract class SingleViewBase<
|
|||||||
return this.propertyIds$.value.indexOf(propertyId);
|
return this.propertyIds$.value.indexOf(propertyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyMetaGet(type: string): PropertyMetaConfig {
|
propertyMetaGet(type: string): PropertyMetaConfig | undefined {
|
||||||
return this.dataSource.propertyMetaGet(type);
|
return this.dataSource.propertyMetaGet(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,13 +455,16 @@ export abstract class SingleViewBase<
|
|||||||
if (!type) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return (
|
const fromString =
|
||||||
this.dataSource.propertyMetaGet(type).config.cellFromString({
|
this.dataSource.propertyMetaGet(type)?.config.rawValue.fromString;
|
||||||
value: cellData,
|
if (!fromString) {
|
||||||
data: this.propertyDataGet(propertyId),
|
return;
|
||||||
dataSource: this.dataSource,
|
}
|
||||||
}) ?? ''
|
return fromString({
|
||||||
);
|
value: cellData,
|
||||||
|
data: this.propertyDataGet(propertyId),
|
||||||
|
dataSource: this.dataSource,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyPreGet(propertyId: string): Property | undefined {
|
propertyPreGet(propertyId: string): Property | undefined {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { AffineLitIcon, UniAnyRender, UniLit } from './core/index.js';
|
|||||||
import { AnyRender } from './core/utils/uni-component/render-template.js';
|
import { AnyRender } from './core/utils/uni-component/render-template.js';
|
||||||
import { CheckboxCell } from './property-presets/checkbox/cell-renderer.js';
|
import { CheckboxCell } from './property-presets/checkbox/cell-renderer.js';
|
||||||
import { DateCell } from './property-presets/date/cell-renderer.js';
|
import { DateCell } from './property-presets/date/cell-renderer.js';
|
||||||
import { TextCell as ImageTextCell } from './property-presets/image/cell-renderer.js';
|
import { ImageCell } from './property-presets/image/cell-renderer.js';
|
||||||
import { MultiSelectCell } from './property-presets/multi-select/cell-renderer.js';
|
import { MultiSelectCell } from './property-presets/multi-select/cell-renderer.js';
|
||||||
import { NumberCell } from './property-presets/number/cell-renderer.js';
|
import { NumberCell } from './property-presets/number/cell-renderer.js';
|
||||||
import { ProgressCell } from './property-presets/progress/cell-renderer.js';
|
import { ProgressCell } from './property-presets/progress/cell-renderer.js';
|
||||||
@@ -74,7 +74,7 @@ export function effects() {
|
|||||||
customElements.define('mobile-table-cell', MobileTableCell);
|
customElements.define('mobile-table-cell', MobileTableCell);
|
||||||
customElements.define('affine-data-view-renderer', DataViewRenderer);
|
customElements.define('affine-data-view-renderer', DataViewRenderer);
|
||||||
customElements.define('any-render', AnyRender);
|
customElements.define('any-render', AnyRender);
|
||||||
customElements.define('affine-database-image-cell', ImageTextCell);
|
customElements.define('affine-database-image-cell', ImageCell);
|
||||||
customElements.define('affine-database-date-cell', DateCell);
|
customElements.define('affine-database-date-cell', DateCell);
|
||||||
customElements.define(
|
customElements.define(
|
||||||
'data-view-properties-setting',
|
'data-view-properties-setting',
|
||||||
|
|||||||
@@ -21,15 +21,24 @@ const FALSE_VALUES = new Set([
|
|||||||
|
|
||||||
export const checkboxPropertyModelConfig = checkboxPropertyType.modelConfig({
|
export const checkboxPropertyModelConfig = checkboxPropertyType.modelConfig({
|
||||||
name: 'Checkbox',
|
name: 'Checkbox',
|
||||||
valueSchema: zod.boolean().optional(),
|
propertyData: {
|
||||||
type: () => t.boolean.instance(),
|
schema: zod.object({}),
|
||||||
defaultData: () => ({}),
|
default: () => ({}),
|
||||||
cellToString: ({ value }) => (value ? 'True' : 'False'),
|
},
|
||||||
cellFromString: ({ value }) => ({
|
jsonValue: {
|
||||||
value: !FALSE_VALUES.has((value?.trim() ?? '').toLowerCase()),
|
schema: zod.boolean(),
|
||||||
}),
|
isEmpty: () => false,
|
||||||
cellToJson: ({ value }) => value ?? null,
|
type: () => t.boolean.instance(),
|
||||||
cellFromJson: ({ value }) => (typeof value !== 'boolean' ? undefined : value),
|
},
|
||||||
isEmpty: () => false,
|
rawValue: {
|
||||||
|
schema: zod.boolean(),
|
||||||
|
default: () => false,
|
||||||
|
fromString: ({ value }) => ({
|
||||||
|
value: !FALSE_VALUES.has((value?.trim() ?? '').toLowerCase()),
|
||||||
|
}),
|
||||||
|
toString: ({ value }) => (value ? 'True' : 'False'),
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
|
},
|
||||||
minWidth: 34,
|
minWidth: 34,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
} from './cell-renderer.css.js';
|
} from './cell-renderer.css.js';
|
||||||
import { datePropertyModelConfig } from './define.js';
|
import { datePropertyModelConfig } from './define.js';
|
||||||
|
|
||||||
export class DateCell extends BaseCellRenderer<number> {
|
export class DateCell extends BaseCellRenderer<number, number> {
|
||||||
private _prevPortalAbortController: AbortController | null = null;
|
private _prevPortalAbortController: AbortController | null = null;
|
||||||
|
|
||||||
private readonly openDatePicker = () => {
|
private readonly openDatePicker = () => {
|
||||||
|
|||||||
@@ -7,19 +7,24 @@ import { propertyType } from '../../core/property/property-config.js';
|
|||||||
export const datePropertyType = propertyType('date');
|
export const datePropertyType = propertyType('date');
|
||||||
export const datePropertyModelConfig = datePropertyType.modelConfig({
|
export const datePropertyModelConfig = datePropertyType.modelConfig({
|
||||||
name: 'Date',
|
name: 'Date',
|
||||||
type: () => t.date.instance(),
|
propertyData: {
|
||||||
valueSchema: zod.number().optional(),
|
schema: zod.object({}),
|
||||||
defaultData: () => ({}),
|
default: () => ({}),
|
||||||
cellToString: ({ value }) =>
|
},
|
||||||
value != null ? format(value, 'yyyy-MM-dd') : '',
|
jsonValue: {
|
||||||
cellFromString: ({ value }) => {
|
schema: zod.number().nullable(),
|
||||||
const date = parse(value, 'yyyy-MM-dd', new Date());
|
isEmpty: () => false,
|
||||||
|
type: () => t.date.instance(),
|
||||||
return {
|
},
|
||||||
value: +date,
|
rawValue: {
|
||||||
};
|
schema: zod.number().nullable(),
|
||||||
|
default: () => null,
|
||||||
|
toString: ({ value }) => (value != null ? format(value, 'yyyy-MM-dd') : ''),
|
||||||
|
fromString: ({ value }) => {
|
||||||
|
const date = parse(value, 'yyyy-MM-dd', new Date());
|
||||||
|
return { value: +date };
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
},
|
},
|
||||||
cellToJson: ({ value }) => value ?? null,
|
|
||||||
cellFromJson: ({ value }) => (typeof value !== 'number' ? undefined : value),
|
|
||||||
isEmpty: ({ value }) => value == null,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { createFromBaseCellRenderer } from '../../core/property/renderer.js';
|
|||||||
import { createIcon } from '../../core/utils/uni-icon.js';
|
import { createIcon } from '../../core/utils/uni-icon.js';
|
||||||
import { imagePropertyModelConfig } from './define.js';
|
import { imagePropertyModelConfig } from './define.js';
|
||||||
|
|
||||||
export class TextCell extends BaseCellRenderer<string> {
|
export class ImageCell extends BaseCellRenderer<string, string> {
|
||||||
static override styles = css`
|
static override styles = css`
|
||||||
affine-database-image-cell {
|
affine-database-image-cell {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -27,6 +27,6 @@ export class TextCell extends BaseCellRenderer<string> {
|
|||||||
export const imagePropertyConfig = imagePropertyModelConfig.createPropertyMeta({
|
export const imagePropertyConfig = imagePropertyModelConfig.createPropertyMeta({
|
||||||
icon: createIcon('ImageIcon'),
|
icon: createIcon('ImageIcon'),
|
||||||
cellRenderer: {
|
cellRenderer: {
|
||||||
view: createFromBaseCellRenderer(TextCell),
|
view: createFromBaseCellRenderer(ImageCell),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,21 +2,31 @@ import zod from 'zod';
|
|||||||
|
|
||||||
import { t } from '../../core/logical/type-presets.js';
|
import { t } from '../../core/logical/type-presets.js';
|
||||||
import { propertyType } from '../../core/property/property-config.js';
|
import { propertyType } from '../../core/property/property-config.js';
|
||||||
|
|
||||||
export const imagePropertyType = propertyType('image');
|
export const imagePropertyType = propertyType('image');
|
||||||
|
|
||||||
export const imagePropertyModelConfig = imagePropertyType.modelConfig({
|
export const imagePropertyModelConfig = imagePropertyType.modelConfig({
|
||||||
name: 'image',
|
name: 'image',
|
||||||
valueSchema: zod.string().optional(),
|
propertyData: {
|
||||||
hide: true,
|
schema: zod.object({}),
|
||||||
type: () => t.image.instance(),
|
default: () => ({}),
|
||||||
defaultData: () => ({}),
|
|
||||||
cellToString: ({ value }) => value ?? '',
|
|
||||||
cellFromString: ({ value }) => {
|
|
||||||
return {
|
|
||||||
value: value,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
cellToJson: ({ value }) => value ?? null,
|
jsonValue: {
|
||||||
cellFromJson: ({ value }) => (typeof value !== 'string' ? undefined : value),
|
schema: zod.string().nullable(),
|
||||||
isEmpty: ({ value }) => value == null,
|
isEmpty: ({ value }) => value == null,
|
||||||
|
type: () => t.image.instance(),
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.string().nullable(),
|
||||||
|
default: () => null,
|
||||||
|
toString: ({ value }) => value ?? '',
|
||||||
|
fromString: ({ value }) => {
|
||||||
|
return {
|
||||||
|
value: value,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
|
},
|
||||||
|
hide: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { multiSelectStyle } from './cell-renderer.css.js';
|
|||||||
import { multiSelectPropertyModelConfig } from './define.js';
|
import { multiSelectPropertyModelConfig } from './define.js';
|
||||||
|
|
||||||
export class MultiSelectCell extends BaseCellRenderer<
|
export class MultiSelectCell extends BaseCellRenderer<
|
||||||
|
string[],
|
||||||
string[],
|
string[],
|
||||||
SelectPropertyData
|
SelectPropertyData
|
||||||
> {
|
> {
|
||||||
|
|||||||
@@ -4,40 +4,29 @@ import zod from 'zod';
|
|||||||
import { getTagColor } from '../../core/component/tags/colors.js';
|
import { getTagColor } from '../../core/component/tags/colors.js';
|
||||||
import { type SelectTag, t } from '../../core/index.js';
|
import { type SelectTag, t } from '../../core/index.js';
|
||||||
import { propertyType } from '../../core/property/property-config.js';
|
import { propertyType } from '../../core/property/property-config.js';
|
||||||
import type { SelectPropertyData } from '../select/define.js';
|
import { SelectPropertySchema } from '../select/define.js';
|
||||||
export const multiSelectPropertyType = propertyType('multi-select');
|
export const multiSelectPropertyType = propertyType('multi-select');
|
||||||
|
|
||||||
export const multiSelectPropertyModelConfig =
|
export const multiSelectPropertyModelConfig =
|
||||||
multiSelectPropertyType.modelConfig<string[] | undefined, SelectPropertyData>(
|
multiSelectPropertyType.modelConfig({
|
||||||
{
|
name: 'Multi-select',
|
||||||
name: 'Multi-select',
|
propertyData: {
|
||||||
valueSchema: zod.array(zod.string()).optional(),
|
schema: SelectPropertySchema,
|
||||||
type: ({ data }) => t.array.instance(t.tag.instance(data.options)),
|
default: () => ({
|
||||||
defaultData: () => ({
|
|
||||||
options: [],
|
options: [],
|
||||||
}),
|
}),
|
||||||
addGroup: ({ text, oldData }) => {
|
},
|
||||||
return {
|
jsonValue: {
|
||||||
options: [
|
schema: zod.array(zod.string()),
|
||||||
...(oldData.options ?? []),
|
isEmpty: ({ value }) => value.length === 0,
|
||||||
{
|
type: ({ data }) => t.array.instance(t.tag.instance(data.options)),
|
||||||
id: nanoid(),
|
},
|
||||||
value: text,
|
rawValue: {
|
||||||
color: getTagColor(),
|
schema: zod.array(zod.string()),
|
||||||
},
|
default: () => [],
|
||||||
],
|
toString: ({ value, data }) =>
|
||||||
};
|
value.map(id => data.options.find(v => v.id === id)?.value).join(','),
|
||||||
},
|
fromString: ({ value: oldValue, data }) => {
|
||||||
formatValue: ({ value }) => {
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return value.filter(v => v != null);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
cellToString: ({ value, data }) =>
|
|
||||||
value
|
|
||||||
?.map(id => data.options.find(v => v.id === id)?.value)
|
|
||||||
.join(',') ?? '',
|
|
||||||
cellFromString: ({ value: oldValue, data }) => {
|
|
||||||
const optionMap = Object.fromEntries(
|
const optionMap = Object.fromEntries(
|
||||||
data.options.map(v => [v.value, v])
|
data.options.map(v => [v.value, v])
|
||||||
);
|
);
|
||||||
@@ -66,11 +55,22 @@ export const multiSelectPropertyModelConfig =
|
|||||||
data: data,
|
data: data,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
cellToJson: ({ value }) => value ?? null,
|
toJson: ({ value }) => value ?? null,
|
||||||
cellFromJson: ({ value }) =>
|
fromJson: ({ value }) =>
|
||||||
Array.isArray(value) && value.every(v => typeof v === 'string')
|
Array.isArray(value) && value.every(v => typeof v === 'string')
|
||||||
? value
|
? value
|
||||||
: undefined,
|
: undefined,
|
||||||
isEmpty: ({ value }) => value == null || value.length === 0,
|
},
|
||||||
}
|
addGroup: ({ text, oldData }) => {
|
||||||
);
|
return {
|
||||||
|
options: [
|
||||||
|
...(oldData.options ?? []),
|
||||||
|
{
|
||||||
|
id: nanoid(),
|
||||||
|
value: text,
|
||||||
|
color: getTagColor(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
} from './utils/formatter.js';
|
} from './utils/formatter.js';
|
||||||
|
|
||||||
export class NumberCell extends BaseCellRenderer<
|
export class NumberCell extends BaseCellRenderer<
|
||||||
|
number,
|
||||||
number,
|
number,
|
||||||
NumberPropertyDataType
|
NumberPropertyDataType
|
||||||
> {
|
> {
|
||||||
|
|||||||
@@ -2,25 +2,29 @@ import zod from 'zod';
|
|||||||
|
|
||||||
import { t } from '../../core/logical/type-presets.js';
|
import { t } from '../../core/logical/type-presets.js';
|
||||||
import { propertyType } from '../../core/property/property-config.js';
|
import { propertyType } from '../../core/property/property-config.js';
|
||||||
import type { NumberPropertyDataType } from './types.js';
|
import { NumberPropertySchema } from './types.js';
|
||||||
export const numberPropertyType = propertyType('number');
|
export const numberPropertyType = propertyType('number');
|
||||||
|
|
||||||
export const numberPropertyModelConfig = numberPropertyType.modelConfig<
|
export const numberPropertyModelConfig = numberPropertyType.modelConfig({
|
||||||
number | undefined,
|
|
||||||
NumberPropertyDataType
|
|
||||||
>({
|
|
||||||
name: 'Number',
|
name: 'Number',
|
||||||
valueSchema: zod.number().optional(),
|
propertyData: {
|
||||||
type: () => t.number.instance(),
|
schema: NumberPropertySchema,
|
||||||
defaultData: () => ({ decimal: 0, format: 'number' }),
|
default: () => ({ decimal: 0, format: 'number' }) as const,
|
||||||
cellToString: ({ value }) => value?.toString() ?? '',
|
},
|
||||||
cellFromString: ({ value }) => {
|
jsonValue: {
|
||||||
const num = value ? Number(value) : NaN;
|
schema: zod.number().nullable(),
|
||||||
return {
|
isEmpty: ({ value }) => value == null,
|
||||||
value: isNaN(num) ? null : num,
|
type: () => t.number.instance(),
|
||||||
};
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.number().nullable(),
|
||||||
|
default: () => null,
|
||||||
|
toString: ({ value }) => value?.toString() ?? '',
|
||||||
|
fromString: ({ value }) => {
|
||||||
|
const num = value ? Number(value) : NaN;
|
||||||
|
return { value: isNaN(num) ? null : num };
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value ?? null,
|
||||||
|
fromJson: ({ value }) => (typeof value !== 'number' ? null : value),
|
||||||
},
|
},
|
||||||
cellToJson: ({ value }) => value ?? null,
|
|
||||||
cellFromJson: ({ value }) => (typeof value !== 'number' ? undefined : value),
|
|
||||||
isEmpty: ({ value }) => value == null,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import type { NumberFormat } from './utils/formatter.js';
|
import zod from 'zod';
|
||||||
|
|
||||||
export type NumberPropertyDataType = {
|
import { NumberFormatSchema } from './utils/formatter.js';
|
||||||
decimal?: number;
|
|
||||||
format?: NumberFormat;
|
export const NumberPropertySchema = zod.object({
|
||||||
};
|
decimal: zod.number().optional(),
|
||||||
|
format: NumberFormatSchema,
|
||||||
|
});
|
||||||
|
export type NumberPropertyDataType = zod.infer<typeof NumberPropertySchema>;
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
export type NumberFormat =
|
import zod from 'zod';
|
||||||
| 'number'
|
export const NumberFormatSchema = zod.enum([
|
||||||
| 'numberWithCommas'
|
'number',
|
||||||
| 'percent'
|
'numberWithCommas',
|
||||||
| 'currencyYen'
|
'percent',
|
||||||
| 'currencyINR'
|
'currencyYen',
|
||||||
| 'currencyCNY'
|
'currencyINR',
|
||||||
| 'currencyUSD'
|
'currencyCNY',
|
||||||
| 'currencyEUR'
|
'currencyUSD',
|
||||||
| 'currencyGBP';
|
'currencyEUR',
|
||||||
|
'currencyGBP',
|
||||||
|
]);
|
||||||
|
export type NumberFormat = zod.infer<typeof NumberFormatSchema>;
|
||||||
|
|
||||||
const currency = (currency: string): Intl.NumberFormatOptions => ({
|
const currency = (currency: string): Intl.NumberFormatOptions => ({
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const progressColors = {
|
|||||||
success: 'var(--affine-success-color)',
|
success: 'var(--affine-success-color)',
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ProgressCell extends BaseCellRenderer<number> {
|
export class ProgressCell extends BaseCellRenderer<number, number> {
|
||||||
startDrag = (event: MouseEvent) => {
|
startDrag = (event: MouseEvent) => {
|
||||||
if (!this.isEditing$.value) return;
|
if (!this.isEditing$.value) return;
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,24 @@ export const progressPropertyType = propertyType('progress');
|
|||||||
|
|
||||||
export const progressPropertyModelConfig = progressPropertyType.modelConfig({
|
export const progressPropertyModelConfig = progressPropertyType.modelConfig({
|
||||||
name: 'Progress',
|
name: 'Progress',
|
||||||
valueSchema: zod.number().optional(),
|
propertyData: {
|
||||||
type: () => t.number.instance(),
|
schema: zod.object({}),
|
||||||
defaultData: () => ({}),
|
default: () => ({}),
|
||||||
cellToString: ({ value }) => value?.toString() ?? '',
|
|
||||||
cellFromString: ({ value }) => {
|
|
||||||
const num = value ? Number(value) : NaN;
|
|
||||||
return {
|
|
||||||
value: isNaN(num) ? null : num,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
cellToJson: ({ value }) => value ?? null,
|
jsonValue: {
|
||||||
cellFromJson: ({ value }) => {
|
schema: zod.number(),
|
||||||
if (typeof value !== 'number') return undefined;
|
isEmpty: () => false,
|
||||||
return value;
|
type: () => t.number.instance(),
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.number(),
|
||||||
|
default: () => 0,
|
||||||
|
toString: ({ value }) => value.toString(),
|
||||||
|
fromString: ({ value }) => {
|
||||||
|
const num = value ? Number(value) : NaN;
|
||||||
|
return { value: isNaN(num) ? 0 : num };
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
},
|
},
|
||||||
isEmpty: () => false,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ import {
|
|||||||
selectPropertyModelConfig,
|
selectPropertyModelConfig,
|
||||||
} from './define.js';
|
} from './define.js';
|
||||||
|
|
||||||
export class SelectCell extends BaseCellRenderer<string, SelectPropertyData> {
|
export class SelectCell extends BaseCellRenderer<
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
SelectPropertyData
|
||||||
|
> {
|
||||||
closePopup?: () => void;
|
closePopup?: () => void;
|
||||||
private readonly popTagSelect = () => {
|
private readonly popTagSelect = () => {
|
||||||
this.closePopup = popTagSelect(popupTargetFromElement(this), {
|
this.closePopup = popTagSelect(popupTargetFromElement(this), {
|
||||||
|
|||||||
@@ -2,23 +2,66 @@ import { nanoid } from '@blocksuite/store';
|
|||||||
import zod from 'zod';
|
import zod from 'zod';
|
||||||
|
|
||||||
import { getTagColor } from '../../core/component/tags/colors.js';
|
import { getTagColor } from '../../core/component/tags/colors.js';
|
||||||
import { type SelectTag, t } from '../../core/index.js';
|
import { type SelectTag, SelectTagSchema, t } from '../../core/index.js';
|
||||||
import { propertyType } from '../../core/property/property-config.js';
|
import { propertyType } from '../../core/property/property-config.js';
|
||||||
export const selectPropertyType = propertyType('select');
|
export const selectPropertyType = propertyType('select');
|
||||||
|
export const SelectPropertySchema = zod.object({
|
||||||
export type SelectPropertyData = {
|
options: zod.array(SelectTagSchema),
|
||||||
options: SelectTag[];
|
});
|
||||||
};
|
export type SelectPropertyData = zod.infer<typeof SelectPropertySchema>;
|
||||||
export const selectPropertyModelConfig = selectPropertyType.modelConfig<
|
export const selectPropertyModelConfig = selectPropertyType.modelConfig({
|
||||||
string | undefined,
|
|
||||||
SelectPropertyData
|
|
||||||
>({
|
|
||||||
name: 'Select',
|
name: 'Select',
|
||||||
valueSchema: zod.string().optional(),
|
propertyData: {
|
||||||
type: ({ data }) => t.tag.instance(data.options),
|
schema: SelectPropertySchema,
|
||||||
defaultData: () => ({
|
default: () => ({
|
||||||
options: [],
|
options: [],
|
||||||
}),
|
}),
|
||||||
|
},
|
||||||
|
jsonValue: {
|
||||||
|
schema: zod.string().nullable(),
|
||||||
|
isEmpty: ({ value }) => value == null,
|
||||||
|
type: ({ data }) => t.tag.instance(data.options),
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.string().nullable(),
|
||||||
|
default: () => null,
|
||||||
|
toString: ({ value, data }) =>
|
||||||
|
data.options.find(v => v.id === value)?.value ?? '',
|
||||||
|
fromString: ({ value: oldValue, data }) => {
|
||||||
|
if (!oldValue) {
|
||||||
|
return { value: null, data: data };
|
||||||
|
}
|
||||||
|
const optionMap = Object.fromEntries(data.options.map(v => [v.value, v]));
|
||||||
|
const name = oldValue
|
||||||
|
.split(',')
|
||||||
|
.map(v => v.trim())
|
||||||
|
.filter(v => v)[0];
|
||||||
|
if (!name) {
|
||||||
|
return { value: null, data: data };
|
||||||
|
}
|
||||||
|
|
||||||
|
let value: string | undefined;
|
||||||
|
const option = optionMap[name];
|
||||||
|
if (!option) {
|
||||||
|
const newOption: SelectTag = {
|
||||||
|
id: nanoid(),
|
||||||
|
value: name,
|
||||||
|
color: getTagColor(),
|
||||||
|
};
|
||||||
|
data.options.push(newOption);
|
||||||
|
value = newOption.id;
|
||||||
|
} else {
|
||||||
|
value = option.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
data: data,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
|
},
|
||||||
addGroup: ({ text, oldData }) => {
|
addGroup: ({ text, oldData }) => {
|
||||||
return {
|
return {
|
||||||
options: [
|
options: [
|
||||||
@@ -27,41 +70,4 @@ export const selectPropertyModelConfig = selectPropertyType.modelConfig<
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
cellToString: ({ value, data }) =>
|
|
||||||
data.options.find(v => v.id === value)?.value ?? '',
|
|
||||||
cellFromString: ({ value: oldValue, data }) => {
|
|
||||||
if (!oldValue) {
|
|
||||||
return { value: null, data: data };
|
|
||||||
}
|
|
||||||
const optionMap = Object.fromEntries(data.options.map(v => [v.value, v]));
|
|
||||||
const name = oldValue
|
|
||||||
.split(',')
|
|
||||||
.map(v => v.trim())
|
|
||||||
.filter(v => v)[0];
|
|
||||||
if (!name) {
|
|
||||||
return { value: null, data: data };
|
|
||||||
}
|
|
||||||
|
|
||||||
let value: string | undefined;
|
|
||||||
const option = optionMap[name];
|
|
||||||
if (!option) {
|
|
||||||
const newOption: SelectTag = {
|
|
||||||
id: nanoid(),
|
|
||||||
value: name,
|
|
||||||
color: getTagColor(),
|
|
||||||
};
|
|
||||||
data.options.push(newOption);
|
|
||||||
value = newOption.id;
|
|
||||||
} else {
|
|
||||||
value = option.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
data: data,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
cellToJson: ({ value }) => value ?? null,
|
|
||||||
cellFromJson: ({ value }) => (typeof value !== 'string' ? undefined : value),
|
|
||||||
isEmpty: ({ value }) => value == null,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { createIcon } from '../../core/utils/uni-icon.js';
|
|||||||
import { textInputStyle, textStyle } from './cell-renderer.css.js';
|
import { textInputStyle, textStyle } from './cell-renderer.css.js';
|
||||||
import { textPropertyModelConfig } from './define.js';
|
import { textPropertyModelConfig } from './define.js';
|
||||||
|
|
||||||
export class TextCell extends BaseCellRenderer<string> {
|
export class TextCell extends BaseCellRenderer<string, string> {
|
||||||
@query('input')
|
@query('input')
|
||||||
private accessor _inputEle!: HTMLInputElement;
|
private accessor _inputEle!: HTMLInputElement;
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,24 @@ export const textPropertyType = propertyType('text');
|
|||||||
|
|
||||||
export const textPropertyModelConfig = textPropertyType.modelConfig({
|
export const textPropertyModelConfig = textPropertyType.modelConfig({
|
||||||
name: 'Plain-Text',
|
name: 'Plain-Text',
|
||||||
valueSchema: zod.string().optional(),
|
propertyData: {
|
||||||
type: () => t.string.instance(),
|
schema: zod.object({}),
|
||||||
defaultData: () => ({}),
|
default: () => ({}),
|
||||||
cellToString: ({ value }) => value ?? '',
|
|
||||||
cellFromString: ({ value }) => {
|
|
||||||
return {
|
|
||||||
value: value,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
cellToJson: ({ value }) => value ?? null,
|
jsonValue: {
|
||||||
cellFromJson: ({ value }) => (typeof value !== 'string' ? undefined : value),
|
schema: zod.string(),
|
||||||
isEmpty: ({ value }) => value == null || value.length === 0,
|
type: () => t.string.instance(),
|
||||||
|
isEmpty: ({ value }) => !value,
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
schema: zod.string(),
|
||||||
|
default: () => '',
|
||||||
|
toString: ({ value }) => value,
|
||||||
|
fromString: ({ value }) => {
|
||||||
|
return { value: value };
|
||||||
|
},
|
||||||
|
toJson: ({ value }) => value,
|
||||||
|
fromJson: ({ value }) => value,
|
||||||
|
},
|
||||||
|
hide: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
groupTraitKey,
|
groupTraitKey,
|
||||||
sortByManually,
|
sortByManually,
|
||||||
} from '../../core/group-by/trait.js';
|
} from '../../core/group-by/trait.js';
|
||||||
|
import { fromJson } from '../../core/property/utils';
|
||||||
import { PropertyBase } from '../../core/view-manager/property.js';
|
import { PropertyBase } from '../../core/view-manager/property.js';
|
||||||
import { SingleViewBase } from '../../core/view-manager/single-view.js';
|
import { SingleViewBase } from '../../core/view-manager/single-view.js';
|
||||||
import type { KanbanViewData } from './define.js';
|
import type { KanbanViewData } from './define.js';
|
||||||
@@ -183,14 +184,15 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
||||||
const property = this.propertyGet(propertyId);
|
const property = this.propertyGet(propertyId);
|
||||||
const propertyMeta = this.propertyMetaGet(property.type$.value);
|
const propertyMeta = this.propertyMetaGet(property.type$.value);
|
||||||
if (propertyMeta?.config.cellFromJson) {
|
if (!propertyMeta) {
|
||||||
const value = propertyMeta.config.cellFromJson({
|
return;
|
||||||
value: jsonValue,
|
|
||||||
data: property.data$.value,
|
|
||||||
dataSource: this.dataSource,
|
|
||||||
});
|
|
||||||
this.cellValueSet(id, propertyId, value);
|
|
||||||
}
|
}
|
||||||
|
const value = fromJson(propertyMeta.config, {
|
||||||
|
value: jsonValue,
|
||||||
|
data: property.data$.value,
|
||||||
|
dataSource: this.dataSource,
|
||||||
|
});
|
||||||
|
this.cellValueSet(id, propertyId, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import { html } from 'lit/static-html.js';
|
|||||||
|
|
||||||
import { inputConfig, typeConfig } from '../../../core/common/property-menu.js';
|
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 type { NumberPropertyDataType } from '../../../property-presets/index.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 { TableColumn, TableSingleView } from '../table-view-manager.js';
|
||||||
@@ -91,12 +90,7 @@ export class MobileTableColumnHeader extends SignalWatcher(
|
|||||||
items: [
|
items: [
|
||||||
numberFormatConfig(this.column),
|
numberFormatConfig(this.column),
|
||||||
...numberFormats.map(format => {
|
...numberFormats.map(format => {
|
||||||
const data = (
|
const data = this.column.data$.value;
|
||||||
this.column as Property<
|
|
||||||
number,
|
|
||||||
NumberPropertyDataType
|
|
||||||
>
|
|
||||||
).data$.value;
|
|
||||||
return menu.action({
|
return menu.action({
|
||||||
isSelected: data.format === format.type,
|
isSelected: data.format === format.type,
|
||||||
prefix: html`<span
|
prefix: html`<span
|
||||||
|
|||||||
+1
-7
@@ -39,7 +39,6 @@ import {
|
|||||||
droppable,
|
droppable,
|
||||||
} from '../../../../core/utils/wc-dnd/dnd-context.js';
|
} from '../../../../core/utils/wc-dnd/dnd-context.js';
|
||||||
import type { Property } from '../../../../core/view-manager/property.js';
|
import type { Property } from '../../../../core/view-manager/property.js';
|
||||||
import type { NumberPropertyDataType } from '../../../../property-presets/index.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';
|
||||||
@@ -222,12 +221,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
|
|||||||
items: [
|
items: [
|
||||||
numberFormatConfig(this.column),
|
numberFormatConfig(this.column),
|
||||||
...numberFormats.map(format => {
|
...numberFormats.map(format => {
|
||||||
const data = (
|
const data = this.column.data$.value;
|
||||||
this.column as Property<
|
|
||||||
number,
|
|
||||||
NumberPropertyDataType
|
|
||||||
>
|
|
||||||
).data$.value;
|
|
||||||
return menu.action({
|
return menu.action({
|
||||||
isSelected: data.format === format.type,
|
isSelected: data.format === format.type,
|
||||||
prefix: html`<span
|
prefix: html`<span
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export class DatabaseColumnStatsCell extends SignalWatcher(
|
|||||||
return this.column.valueGet(id);
|
return this.column.valueGet(id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this.column.cells$.value.map(cell => cell.value$.value);
|
return this.column.cells$.value.map(cell => cell.jsonValue$.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
groups$ = computed(() => {
|
groups$ = computed(() => {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
groupTraitKey,
|
groupTraitKey,
|
||||||
sortByManually,
|
sortByManually,
|
||||||
} from '../../core/group-by/trait.js';
|
} from '../../core/group-by/trait.js';
|
||||||
|
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 {
|
import {
|
||||||
@@ -327,8 +328,8 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
Object.entries(defaultValues).forEach(([propertyId, jsonValue]) => {
|
||||||
const property = this.propertyGet(propertyId);
|
const property = this.propertyGet(propertyId);
|
||||||
const propertyMeta = this.propertyMetaGet(property.type$.value);
|
const propertyMeta = this.propertyMetaGet(property.type$.value);
|
||||||
if (propertyMeta?.config.cellFromJson) {
|
if (propertyMeta) {
|
||||||
const value = propertyMeta.config.cellFromJson({
|
const value = fromJson(propertyMeta.config, {
|
||||||
value: jsonValue,
|
value: jsonValue,
|
||||||
data: property.data$.value,
|
data: property.data$.value,
|
||||||
dataSource: this.dataSource,
|
dataSource: this.dataSource,
|
||||||
|
|||||||
@@ -79,11 +79,13 @@ export const database: InitFn = (collection: Workspace, id: string) => {
|
|||||||
{ type: type, text: new Text(`Paragraph type ${type}`) },
|
{ type: type, text: new Text(`Paragraph type ${type}`) },
|
||||||
databaseId
|
databaseId
|
||||||
);
|
);
|
||||||
datasource.cellValueChange(
|
if (richTextId) {
|
||||||
id,
|
datasource.cellValueChange(
|
||||||
richTextId,
|
id,
|
||||||
new Text(`Paragraph type ${type}`)
|
richTextId,
|
||||||
);
|
new Text(`Paragraph type ${type}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const listTypes: ListType[] = ['numbered', 'bulleted', 'todo', 'toggle'];
|
const listTypes: ListType[] = ['numbered', 'bulleted', 'todo', 'toggle'];
|
||||||
|
|
||||||
@@ -93,11 +95,13 @@ export const database: InitFn = (collection: Workspace, id: string) => {
|
|||||||
{ type: type, text: new Text(`List type ${type}`) },
|
{ type: type, text: new Text(`List type ${type}`) },
|
||||||
databaseId
|
databaseId
|
||||||
);
|
);
|
||||||
datasource.cellValueChange(
|
if (richTextId) {
|
||||||
id,
|
datasource.cellValueChange(
|
||||||
richTextId,
|
id,
|
||||||
new Text(`List type ${type}`)
|
richTextId,
|
||||||
);
|
new Text(`List type ${type}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Add a paragraph after database
|
// Add a paragraph after database
|
||||||
doc.addBlock('affine:paragraph', {}, noteId);
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
|
|||||||
@@ -531,8 +531,11 @@ test.describe('readonly mode', () => {
|
|||||||
});
|
});
|
||||||
await cell.click();
|
await cell.click();
|
||||||
await pressEnter(page);
|
await pressEnter(page);
|
||||||
|
await waitNextFrame(page, 100);
|
||||||
await type(page, '123');
|
await type(page, '123');
|
||||||
|
await waitNextFrame(page, 100);
|
||||||
await pressEnter(page);
|
await pressEnter(page);
|
||||||
|
await waitNextFrame(page, 100);
|
||||||
await assertDatabaseCellRichTexts(page, { text: '123' });
|
await assertDatabaseCellRichTexts(page, { text: '123' });
|
||||||
|
|
||||||
await switchReadonly(page);
|
await switchReadonly(page);
|
||||||
|
|||||||
@@ -386,6 +386,9 @@ export async function initKanbanViewState(
|
|||||||
});
|
});
|
||||||
config.columns.forEach(column => {
|
config.columns.forEach(column => {
|
||||||
const columnId = datasource.propertyAdd('end', column.type);
|
const columnId = datasource.propertyAdd('end', column.type);
|
||||||
|
if (!columnId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
datasource.propertyNameSet(columnId, column.type);
|
datasource.propertyNameSet(columnId, column.type);
|
||||||
rowIds.forEach((rowId, index) => {
|
rowIds.forEach((rowId, index) => {
|
||||||
const value = column.value?.[index];
|
const value = column.value?.[index];
|
||||||
|
|||||||
Reference in New Issue
Block a user