Files
AFFiNE-Mirror/blocksuite/affine/shared/src/utils/zod-schema.ts
T
L-Sun 459972fe6c fix(editor): add missing zod schema for edgeless frame (#10024)
Related to https://github.com/toeverything/AFFiNE/pull/9970#discussion_r1944971309

### What changes:
- Add missing zod shcema for edgeless basic props
- Change `applyLastProps` to generic function for better return type inference of
- Fix: add `ZodIntersection` case to `makeDeepOptional`
2025-02-07 12:49:59 +00:00

202 lines
5.1 KiB
TypeScript

import {
ColorSchema,
ConnectorMode,
DEFAULT_FRONT_END_POINT_STYLE,
DEFAULT_REAR_END_POINT_STYLE,
DEFAULT_ROUGHNESS,
DefaultTheme,
EdgelessTextZodSchema,
FontFamily,
FontFamilySchema,
FontStyle,
FontStyleSchema,
FontWeight,
FontWeightSchema,
FrameZodSchema,
LayoutType,
LineWidth,
MindmapStyle,
NoteZodSchema,
PointStyle,
ShapeStyle,
StrokeStyle,
StrokeStyleSchema,
TextAlign,
TextAlignSchema,
TextVerticalAlign,
} from '@blocksuite/affine-model';
import {
z,
ZodDefault,
ZodIntersection,
ZodObject,
type ZodTypeAny,
ZodUnion,
} from 'zod';
const ConnectorEndpointSchema = z.nativeEnum(PointStyle);
const LineWidthSchema = z.nativeEnum(LineWidth);
const ShapeStyleSchema = z.nativeEnum(ShapeStyle);
const TextVerticalAlignSchema = z.nativeEnum(TextVerticalAlign);
const ConnectorModeSchema = z.nativeEnum(ConnectorMode);
const LayoutTypeSchema = z.nativeEnum(LayoutType);
const MindmapStyleSchema = z.nativeEnum(MindmapStyle);
export const ConnectorSchema = z
.object({
frontEndpointStyle: ConnectorEndpointSchema,
rearEndpointStyle: ConnectorEndpointSchema,
stroke: ColorSchema,
strokeStyle: StrokeStyleSchema,
strokeWidth: LineWidthSchema,
rough: z.boolean(),
mode: ConnectorModeSchema,
labelStyle: z.object({
color: ColorSchema,
fontSize: z.number(),
fontFamily: FontFamilySchema,
fontWeight: FontWeightSchema,
fontStyle: FontStyleSchema,
textAlign: TextAlignSchema,
}),
})
.default({
frontEndpointStyle: DEFAULT_FRONT_END_POINT_STYLE,
rearEndpointStyle: DEFAULT_REAR_END_POINT_STYLE,
stroke: DefaultTheme.connectorColor,
strokeStyle: StrokeStyle.Solid,
strokeWidth: LineWidth.Two,
rough: false,
mode: ConnectorMode.Curve,
labelStyle: {
color: DefaultTheme.black,
fontSize: 16,
fontFamily: FontFamily.Inter,
fontWeight: FontWeight.Regular,
fontStyle: FontStyle.Normal,
textAlign: TextAlign.Center,
},
});
export const BrushSchema = z
.object({
color: ColorSchema,
lineWidth: LineWidthSchema,
})
.default({
color: DefaultTheme.black,
lineWidth: LineWidth.Four,
});
const DEFAULT_SHAPE = {
color: DefaultTheme.shapeTextColor,
fillColor: DefaultTheme.shapeFillColor,
strokeColor: DefaultTheme.shapeStrokeColor,
strokeStyle: StrokeStyle.Solid,
strokeWidth: LineWidth.Two,
shapeStyle: ShapeStyle.General,
filled: true,
radius: 0,
fontSize: 20,
fontFamily: FontFamily.Inter,
fontWeight: FontWeight.Regular,
fontStyle: FontStyle.Normal,
textAlign: TextAlign.Center,
roughness: DEFAULT_ROUGHNESS,
};
const ShapeObject = {
color: ColorSchema,
fillColor: ColorSchema,
strokeColor: ColorSchema,
strokeStyle: StrokeStyleSchema,
strokeWidth: z.number(),
shapeStyle: ShapeStyleSchema,
filled: z.boolean(),
radius: z.number(),
fontSize: z.number(),
fontFamily: FontFamilySchema,
fontWeight: FontWeightSchema,
fontStyle: FontStyleSchema,
textAlign: TextAlignSchema,
textHorizontalAlign: TextAlignSchema.optional(),
textVerticalAlign: TextVerticalAlignSchema.optional(),
roughness: z.number(),
};
export const ShapeSchema = z.object(ShapeObject).default(DEFAULT_SHAPE);
export const RoundedShapeSchema = z
.object(ShapeObject)
.default({ ...DEFAULT_SHAPE, radius: 0.1 });
export const TextSchema = z
.object({
color: ColorSchema,
fontSize: z.number(),
fontFamily: FontFamilySchema,
fontWeight: FontWeightSchema,
fontStyle: FontStyleSchema,
textAlign: TextAlignSchema,
})
.default({
color: DefaultTheme.textColor,
fontSize: 24,
fontFamily: FontFamily.Inter,
fontWeight: FontWeight.Regular,
fontStyle: FontStyle.Normal,
textAlign: TextAlign.Left,
});
export const MindmapSchema = z
.object({
layoutType: LayoutTypeSchema,
style: MindmapStyleSchema,
})
.default({
layoutType: LayoutType.RIGHT,
style: MindmapStyle.ONE,
});
export const NodePropsSchema = z.object({
connector: ConnectorSchema,
brush: BrushSchema,
text: TextSchema,
mindmap: MindmapSchema,
'affine:edgeless-text': EdgelessTextZodSchema,
'affine:note': NoteZodSchema,
'affine:frame': FrameZodSchema,
// shapes
'shape:diamond': ShapeSchema,
'shape:ellipse': ShapeSchema,
'shape:rect': ShapeSchema,
'shape:triangle': ShapeSchema,
'shape:roundedRect': RoundedShapeSchema,
});
export type NodeProps = z.infer<typeof NodePropsSchema>;
export function makeDeepOptional(schema: ZodTypeAny): ZodTypeAny {
if (schema instanceof ZodDefault) {
return makeDeepOptional(schema._def.innerType);
}
if (schema instanceof ZodObject) {
const shape = schema.shape;
const deepOptionalShape = Object.fromEntries(
Object.entries(shape).map(([key, value]) => {
return [key, makeDeepOptional(value as ZodTypeAny)];
})
);
return z.object(deepOptionalShape).optional();
} else if (schema instanceof ZodUnion) {
return schema.or(z.undefined());
} else if (schema instanceof ZodIntersection) {
return z.intersection(
makeDeepOptional(schema._def.left),
makeDeepOptional(schema._def.right)
);
} else {
return schema.optional();
}
}