mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 19:02:23 +08:00
refactor(editor): add schema for value of database block properties (#10749)
This commit is contained in:
@@ -197,7 +197,7 @@ export abstract class DataSourceBase implements DataSource {
|
||||
return computed(() => this.propertyReadonlyGet(propertyId));
|
||||
}
|
||||
|
||||
abstract propertyTypeGet(propertyId: string): string;
|
||||
abstract propertyTypeGet(propertyId: string): string | undefined;
|
||||
|
||||
propertyTypeGet$(propertyId: string): ReadonlySignal<string | undefined> {
|
||||
return computed(() => this.propertyTypeGet(propertyId));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Disposable } from '@blocksuite/global/slot';
|
||||
import type { ZodType } from 'zod';
|
||||
|
||||
import type { DataSource } from '../data-source/base.js';
|
||||
import type { TypeInstance } from '../logical/type.js';
|
||||
@@ -15,6 +16,7 @@ export type PropertyConfig<
|
||||
Value = unknown,
|
||||
> = {
|
||||
name: string;
|
||||
valueSchema: ZodType<Value>;
|
||||
hide?: boolean;
|
||||
fixed?: {
|
||||
defaultData: Data;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import zod from 'zod';
|
||||
|
||||
import { t } from '../../core/logical/type-presets.js';
|
||||
import { propertyType } from '../../core/property/property-config.js';
|
||||
|
||||
export const checkboxPropertyType = propertyType('checkbox');
|
||||
|
||||
const FALSE_VALUES = new Set([
|
||||
@@ -18,18 +19,17 @@ const FALSE_VALUES = new Set([
|
||||
'关闭',
|
||||
]);
|
||||
|
||||
export const checkboxPropertyModelConfig =
|
||||
checkboxPropertyType.modelConfig<boolean>({
|
||||
name: 'Checkbox',
|
||||
type: () => t.boolean.instance(),
|
||||
defaultData: () => ({}),
|
||||
cellToString: ({ value }) => (value ? 'True' : 'False'),
|
||||
cellFromString: ({ value }) => ({
|
||||
value: !FALSE_VALUES.has((value?.trim() ?? '').toLowerCase()),
|
||||
}),
|
||||
cellToJson: ({ value }) => value ?? null,
|
||||
cellFromJson: ({ value }) =>
|
||||
typeof value !== 'boolean' ? undefined : value,
|
||||
isEmpty: () => false,
|
||||
minWidth: 34,
|
||||
});
|
||||
export const checkboxPropertyModelConfig = checkboxPropertyType.modelConfig({
|
||||
name: 'Checkbox',
|
||||
valueSchema: zod.boolean().optional(),
|
||||
type: () => t.boolean.instance(),
|
||||
defaultData: () => ({}),
|
||||
cellToString: ({ value }) => (value ? 'True' : 'False'),
|
||||
cellFromString: ({ value }) => ({
|
||||
value: !FALSE_VALUES.has((value?.trim() ?? '').toLowerCase()),
|
||||
}),
|
||||
cellToJson: ({ value }) => value ?? null,
|
||||
cellFromJson: ({ value }) => (typeof value !== 'boolean' ? undefined : value),
|
||||
isEmpty: () => false,
|
||||
minWidth: 34,
|
||||
});
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { format } from 'date-fns/format';
|
||||
import { parse } from 'date-fns/parse';
|
||||
import zod from 'zod';
|
||||
|
||||
import { t } from '../../core/logical/type-presets.js';
|
||||
import { propertyType } from '../../core/property/property-config.js';
|
||||
|
||||
export const datePropertyType = propertyType('date');
|
||||
export const datePropertyModelConfig = datePropertyType.modelConfig<number>({
|
||||
export const datePropertyModelConfig = datePropertyType.modelConfig({
|
||||
name: 'Date',
|
||||
type: () => t.date.instance(),
|
||||
valueSchema: zod.number().optional(),
|
||||
defaultData: () => ({}),
|
||||
cellToString: ({ value }) => format(value, 'yyyy-MM-dd'),
|
||||
cellToString: ({ value }) =>
|
||||
value != null ? format(value, 'yyyy-MM-dd') : '',
|
||||
cellFromString: ({ value }) => {
|
||||
const date = parse(value, 'yyyy-MM-dd', new Date());
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import zod from 'zod';
|
||||
|
||||
import { t } from '../../core/logical/type-presets.js';
|
||||
import { propertyType } from '../../core/property/property-config.js';
|
||||
|
||||
export const imagePropertyType = propertyType('image');
|
||||
|
||||
export const imagePropertyModelConfig = imagePropertyType.modelConfig<string>({
|
||||
export const imagePropertyModelConfig = imagePropertyType.modelConfig({
|
||||
name: 'image',
|
||||
valueSchema: zod.string().optional(),
|
||||
hide: true,
|
||||
type: () => t.image.instance(),
|
||||
defaultData: () => ({}),
|
||||
|
||||
@@ -1,69 +1,76 @@
|
||||
import { nanoid } from '@blocksuite/store';
|
||||
import zod from 'zod';
|
||||
|
||||
import { getTagColor } from '../../core/component/tags/colors.js';
|
||||
import { type SelectTag, t } from '../../core/index.js';
|
||||
import { propertyType } from '../../core/property/property-config.js';
|
||||
import type { SelectPropertyData } from '../select/define.js';
|
||||
|
||||
export const multiSelectPropertyType = propertyType('multi-select');
|
||||
export const multiSelectPropertyModelConfig =
|
||||
multiSelectPropertyType.modelConfig<string[], SelectPropertyData>({
|
||||
name: 'Multi-select',
|
||||
type: ({ data }) => t.array.instance(t.tag.instance(data.options)),
|
||||
defaultData: () => ({
|
||||
options: [],
|
||||
}),
|
||||
addGroup: ({ text, oldData }) => {
|
||||
return {
|
||||
options: [
|
||||
...(oldData.options ?? []),
|
||||
{
|
||||
id: nanoid(),
|
||||
value: text,
|
||||
color: getTagColor(),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
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(data.options.map(v => [v.value, v]));
|
||||
const optionNames = oldValue
|
||||
.split(',')
|
||||
.map(v => v.trim())
|
||||
.filter(v => v);
|
||||
|
||||
const value: string[] = [];
|
||||
optionNames.forEach(name => {
|
||||
if (!optionMap[name]) {
|
||||
const newOption: SelectTag = {
|
||||
id: nanoid(),
|
||||
value: name,
|
||||
color: getTagColor(),
|
||||
};
|
||||
data.options.push(newOption);
|
||||
value.push(newOption.id);
|
||||
} else {
|
||||
value.push(optionMap[name].id);
|
||||
multiSelectPropertyType.modelConfig<string[] | undefined, SelectPropertyData>(
|
||||
{
|
||||
name: 'Multi-select',
|
||||
valueSchema: zod.array(zod.string()).optional(),
|
||||
type: ({ data }) => t.array.instance(t.tag.instance(data.options)),
|
||||
defaultData: () => ({
|
||||
options: [],
|
||||
}),
|
||||
addGroup: ({ text, oldData }) => {
|
||||
return {
|
||||
options: [
|
||||
...(oldData.options ?? []),
|
||||
{
|
||||
id: nanoid(),
|
||||
value: text,
|
||||
color: getTagColor(),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
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(
|
||||
data.options.map(v => [v.value, v])
|
||||
);
|
||||
const optionNames = oldValue
|
||||
.split(',')
|
||||
.map(v => v.trim())
|
||||
.filter(v => v);
|
||||
|
||||
return {
|
||||
value,
|
||||
data: data,
|
||||
};
|
||||
},
|
||||
cellToJson: ({ value }) => value ?? null,
|
||||
cellFromJson: ({ value }) =>
|
||||
Array.isArray(value) && value.every(v => typeof v === 'string')
|
||||
? value
|
||||
: undefined,
|
||||
isEmpty: ({ value }) => value == null || value.length === 0,
|
||||
});
|
||||
const value: string[] = [];
|
||||
optionNames.forEach(name => {
|
||||
if (!optionMap[name]) {
|
||||
const newOption: SelectTag = {
|
||||
id: nanoid(),
|
||||
value: name,
|
||||
color: getTagColor(),
|
||||
};
|
||||
data.options.push(newOption);
|
||||
value.push(newOption.id);
|
||||
} else {
|
||||
value.push(optionMap[name].id);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
value,
|
||||
data: data,
|
||||
};
|
||||
},
|
||||
cellToJson: ({ value }) => value ?? null,
|
||||
cellFromJson: ({ value }) =>
|
||||
Array.isArray(value) && value.every(v => typeof v === 'string')
|
||||
? value
|
||||
: undefined,
|
||||
isEmpty: ({ value }) => value == null || value.length === 0,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import zod from 'zod';
|
||||
|
||||
import { t } from '../../core/logical/type-presets.js';
|
||||
import { propertyType } from '../../core/property/property-config.js';
|
||||
import type { NumberPropertyDataType } from './types.js';
|
||||
|
||||
export const numberPropertyType = propertyType('number');
|
||||
|
||||
export const numberPropertyModelConfig = numberPropertyType.modelConfig<
|
||||
number,
|
||||
number | undefined,
|
||||
NumberPropertyDataType
|
||||
>({
|
||||
name: 'Number',
|
||||
valueSchema: zod.number().optional(),
|
||||
type: () => t.number.instance(),
|
||||
defaultData: () => ({ decimal: 0, format: 'number' }),
|
||||
cellToString: ({ value }) => value?.toString() ?? '',
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import zod from 'zod';
|
||||
|
||||
import { t } from '../../core/logical/type-presets.js';
|
||||
import { propertyType } from '../../core/property/property-config.js';
|
||||
|
||||
export const progressPropertyType = propertyType('progress');
|
||||
|
||||
export const progressPropertyModelConfig =
|
||||
progressPropertyType.modelConfig<number>({
|
||||
name: 'Progress',
|
||||
type: () => t.number.instance(),
|
||||
defaultData: () => ({}),
|
||||
cellToString: ({ value }) => value?.toString() ?? '',
|
||||
cellFromString: ({ value }) => {
|
||||
const num = value ? Number(value) : NaN;
|
||||
return {
|
||||
value: isNaN(num) ? null : num,
|
||||
};
|
||||
},
|
||||
cellToJson: ({ value }) => value ?? null,
|
||||
cellFromJson: ({ value }) => {
|
||||
if (typeof value !== 'number') return undefined;
|
||||
return value;
|
||||
},
|
||||
isEmpty: () => false,
|
||||
});
|
||||
export const progressPropertyModelConfig = progressPropertyType.modelConfig({
|
||||
name: 'Progress',
|
||||
valueSchema: zod.number().optional(),
|
||||
type: () => t.number.instance(),
|
||||
defaultData: () => ({}),
|
||||
cellToString: ({ value }) => value?.toString() ?? '',
|
||||
cellFromString: ({ value }) => {
|
||||
const num = value ? Number(value) : NaN;
|
||||
return {
|
||||
value: isNaN(num) ? null : num,
|
||||
};
|
||||
},
|
||||
cellToJson: ({ value }) => value ?? null,
|
||||
cellFromJson: ({ value }) => {
|
||||
if (typeof value !== 'number') return undefined;
|
||||
return value;
|
||||
},
|
||||
isEmpty: () => false,
|
||||
});
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { nanoid } from '@blocksuite/store';
|
||||
import zod from 'zod';
|
||||
|
||||
import { getTagColor } from '../../core/component/tags/colors.js';
|
||||
import { type SelectTag, t } from '../../core/index.js';
|
||||
import { propertyType } from '../../core/property/property-config.js';
|
||||
|
||||
export const selectPropertyType = propertyType('select');
|
||||
|
||||
export type SelectPropertyData = {
|
||||
options: SelectTag[];
|
||||
};
|
||||
export const selectPropertyModelConfig = selectPropertyType.modelConfig<
|
||||
string,
|
||||
string | undefined,
|
||||
SelectPropertyData
|
||||
>({
|
||||
name: 'Select',
|
||||
valueSchema: zod.string().optional(),
|
||||
type: ({ data }) => t.tag.instance(data.options),
|
||||
defaultData: () => ({
|
||||
options: [],
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import zod from 'zod';
|
||||
|
||||
import { t } from '../../core/index.js';
|
||||
import { propertyType } from '../../core/property/property-config.js';
|
||||
|
||||
export const textPropertyType = propertyType('text');
|
||||
|
||||
export const textPropertyModelConfig = textPropertyType.modelConfig<string>({
|
||||
export const textPropertyModelConfig = textPropertyType.modelConfig({
|
||||
name: 'Plain-Text',
|
||||
valueSchema: zod.string().optional(),
|
||||
type: () => t.string.instance(),
|
||||
defaultData: () => ({}),
|
||||
cellToString: ({ value }) => value ?? '',
|
||||
|
||||
Reference in New Issue
Block a user