refactor(editor): add runtime type checks to database cell values (#10770)

This commit is contained in:
zzj3720
2025-03-12 09:22:41 +00:00
parent fd3ce431fe
commit 01151ec18f
54 changed files with 775 additions and 629 deletions
@@ -13,7 +13,11 @@ import {
selectPropertyModelConfig,
} from './define.js';
export class SelectCell extends BaseCellRenderer<string, SelectPropertyData> {
export class SelectCell extends BaseCellRenderer<
string,
string,
SelectPropertyData
> {
closePopup?: () => void;
private readonly popTagSelect = () => {
this.closePopup = popTagSelect(popupTargetFromElement(this), {
@@ -2,23 +2,66 @@ 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 { type SelectTag, SelectTagSchema, 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 | undefined,
SelectPropertyData
>({
export const SelectPropertySchema = zod.object({
options: zod.array(SelectTagSchema),
});
export type SelectPropertyData = zod.infer<typeof SelectPropertySchema>;
export const selectPropertyModelConfig = selectPropertyType.modelConfig({
name: 'Select',
valueSchema: zod.string().optional(),
type: ({ data }) => t.tag.instance(data.options),
defaultData: () => ({
options: [],
}),
propertyData: {
schema: SelectPropertySchema,
default: () => ({
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 }) => {
return {
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,
});