Files
webstudio/packages/css-data/src/schema.ts
T
61f164d40e (feat): Ability to add box-shadow to nodes. (#1634)
## Description

This is a WIP PR, and will be adding more details about the feature and
the changes that are going in...

## Steps for reproduction

1. click button
2. expect xyz

## Code Review

- [ ] hi, I need you to do
  - conceptual review (architecture, feature-correctness)
  - detailed review (read every line)
  - test it on preview

## Before requesting a review

- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")

## Before merging

- [ ] tested locally and on preview environment (preview dev login:
5de6)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio-builder/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env.example`
and the `builder/env-check.js` if mandatory

---------

Co-authored-by: Bogdan Chadkin <trysound@yandex.ru>
Co-authored-by: Oleg Isonen <oleg008@gmail.com>
2023-07-02 16:31:26 +02:00

194 lines
4.9 KiB
TypeScript

import { units } from "./__generated__/units";
import type { properties } from "./__generated__/properties";
import { z } from "zod";
type Properties = typeof properties & {
[custom: CustomProperty]: {
appliesTo: "allElements";
initial: string;
inherited: boolean;
};
};
export type StyleProperty = keyof Properties;
type CustomProperty = `--${string}`;
export type AppliesTo = Properties[StyleProperty]["appliesTo"];
export type UnitGroup = keyof typeof units;
type UnitEnum = (typeof units)[UnitGroup][number];
const Unit = z.union([
// expected tuple with at least single element
// so cast to tuple with single union element to get correct inference
z.enum(Object.values(units).flat() as [UnitEnum]),
z.literal("number"),
]);
export type Unit = z.infer<typeof Unit>;
export const UnitValue = z.object({
type: z.literal("unit"),
unit: Unit,
value: z.number(),
});
export type UnitValue = z.infer<typeof UnitValue>;
export const KeywordValue = z.object({
type: z.literal("keyword"),
// @todo use exact type
value: z.string(),
});
export type KeywordValue = z.infer<typeof KeywordValue>;
/**
* Valid unparsed css value
**/
export const UnparsedValue = z.object({
type: z.literal("unparsed"),
value: z.string(),
// For the builder we want to be able to hide background-image
hidden: z.boolean().optional(),
});
export type UnparsedValue = z.infer<typeof UnparsedValue>;
const FontFamilyValue = z.object({
type: z.literal("fontFamily"),
value: z.array(z.string()),
});
export type FontFamilyValue = z.infer<typeof FontFamilyValue>;
const RgbValue = z.object({
type: z.literal("rgb"),
r: z.number(),
g: z.number(),
b: z.number(),
alpha: z.number(),
});
export type RgbValue = z.infer<typeof RgbValue>;
export const ImageValue = z.object({
type: z.literal("image"),
value: z.union([
z.object({ type: z.literal("asset"), value: z.string() }),
// url is not stored in db and only used by css-engine transformValue
// to prepare image value for rendering
z.object({ type: z.literal("url"), url: z.string() }),
]),
// For the builder we want to be able to hide images
hidden: z.boolean().optional(),
});
export type ImageValue = z.infer<typeof ImageValue>;
// We want to be able to render the invalid value
// and show it is invalid visually, without saving it to the db
export const InvalidValue = z.object({
type: z.literal("invalid"),
value: z.string(),
});
export type InvalidValue = z.infer<typeof InvalidValue>;
const UnsetValue = z.object({
type: z.literal("unset"),
value: z.literal(""),
});
export type UnsetValue = z.infer<typeof UnsetValue>;
export const TupleValueItem = z.union([
UnitValue,
KeywordValue,
UnparsedValue,
RgbValue,
]);
export type TupleValueItem = z.infer<typeof TupleValueItem>;
export const TupleValue = z.object({
type: z.literal("tuple"),
value: z.array(TupleValueItem),
hidden: z.boolean().optional(),
});
export type TupleValue = z.infer<typeof TupleValue>;
const LayerValueItem = z.union([
UnitValue,
KeywordValue,
UnparsedValue,
ImageValue,
TupleValue,
InvalidValue,
]);
export type LayerValueItem = z.infer<typeof LayerValueItem>;
// To support background layers https://developer.mozilla.org/en-US/docs/Web/CSS/background
// and similar comma separated css properties
// InvalidValue used in case of asset not found
export const LayersValue = z.object({
type: z.literal("layers"),
value: z.array(LayerValueItem),
});
export type LayersValue = z.infer<typeof LayersValue>;
const ValidStaticStyleValue = z.union([
ImageValue,
LayersValue,
UnitValue,
KeywordValue,
FontFamilyValue,
RgbValue,
UnparsedValue,
TupleValue,
]);
export type ValidStaticStyleValue = z.infer<typeof ValidStaticStyleValue>;
/**
* All StyleValue types that going to need wrapping into a CSS variable when rendered
* on canvas inside builder.
* Values like InvalidValue, UnsetValue, VarValue don't need to be wrapped
*/
export const isValidStaticStyleValue = (
styleValue: StyleValue
): styleValue is ValidStaticStyleValue => {
// guard against invalid checks
const staticStyleValue = styleValue as ValidStaticStyleValue;
return (
staticStyleValue.type === "image" ||
staticStyleValue.type === "layers" ||
staticStyleValue.type === "unit" ||
staticStyleValue.type === "keyword" ||
staticStyleValue.type === "fontFamily" ||
staticStyleValue.type === "rgb" ||
staticStyleValue.type === "unparsed" ||
staticStyleValue.type === "tuple"
);
};
const VarValue = z.object({
type: z.literal("var"),
value: z.string(),
fallbacks: z.array(ValidStaticStyleValue),
});
export type VarValue = z.infer<typeof VarValue>;
export const StyleValue = z.union([
ValidStaticStyleValue,
InvalidValue,
UnsetValue,
VarValue,
]);
export type StyleValue = z.infer<typeof StyleValue>;
const Style = z.record(z.string(), StyleValue);
export type Style = {
[property in StyleProperty]?: StyleValue;
} & { [property: CustomProperty]: StyleValue };