Support state category in meta (#1584)

Ref https://github.com/webstudio-is/webstudio-builder/issues/1518

Now default states should be spread per component. And states have
category with fallback to "states".
This commit is contained in:
Bogdan Chadkin authored and GitHub committed 2023-05-06 23:53:46 +07:00
1 parent 4f6bad897c
commit 3bec46192b
29 files changed
+226 -156

No files matched your search

@@ -42,7 +42,7 @@ import {
type ReactNode,
} from "react";
import { mergeRefs } from "@react-aria/utils";
import type { ComponentState } from "@webstudio-is/react-sdk";
import { type ComponentState, stateCategories } from "@webstudio-is/react-sdk";
import { type ItemSource, menuCssVars, StyleSource } from "./style-source";
import { useSortable } from "./use-sortable";
import { matchSorter } from "match-sorter";
@@ -263,18 +263,10 @@ const markAddedValues = <Item extends IntermediateItem>(
return items.map((item) => ({ ...item, isAdded: valueIds.has(item.id) }));
};
const userActionStates = [
":hover",
":active",
":focus",
":focus-visible",
":focus-within",
];
const renderMenuItems = (props: {
selectedItemSelector: undefined | ItemSelector;
item: IntermediateItem;
states: { selector: string; label: string }[];
states: ComponentState[];
onSelect?: (itemSelector: ItemSelector) => void;
onEdit?: (itemId: IntermediateItem["id"]) => void;
onDuplicate?: (itemId: IntermediateItem["id"]) => void;
@@ -328,40 +320,55 @@ const renderMenuItems = (props: {
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuLabel>States</DropdownMenuLabel>
{props.states.map(({ label, selector }) => (
<DropdownMenuItem
key={selector}
withIndicator={true}
icon={
props.item.id === props.selectedItemSelector?.styleSourceId &&
selector === props.selectedItemSelector.state ? (
<CheckMarkIcon
color={
props.item.states.includes(selector)
? rawTheme.colors.foregroundPrimary
: rawTheme.colors.foregroundIconMain
{stateCategories.map((currentCategory) => {
const categoryStates = props.states.filter(
({ category }) => (category ?? "states") === currentCategory
);
// prevent rendering empty category
if (categoryStates.length === 0) {
return;
}
return (
<>
<DropdownMenuSeparator />
<DropdownMenuLabel>
{humanizeString(currentCategory)}
</DropdownMenuLabel>
{categoryStates.map(({ label, selector }) => (
<DropdownMenuItem
key={selector}
withIndicator={true}
icon={
props.item.id === props.selectedItemSelector?.styleSourceId &&
selector === props.selectedItemSelector.state ? (
<CheckMarkIcon
color={
props.item.states.includes(selector)
? rawTheme.colors.foregroundPrimary
: rawTheme.colors.foregroundIconMain
}
/>
) : props.item.states.includes(selector) ? (
<DotIcon color={rawTheme.colors.foregroundPrimary} />
) : null
}
/>
) : props.item.states.includes(selector) ? (
<DotIcon color={rawTheme.colors.foregroundPrimary} />
) : null
}
onSelect={() =>
props.onSelect?.({
styleSourceId: props.item.id,
// toggle state selection
state:
props.selectedItemSelector?.state === selector
? undefined
: selector,
})
}
>
{label}
</DropdownMenuItem>
))}
onSelect={() =>
props.onSelect?.({
styleSourceId: props.item.id,
// toggle state selection
state:
props.selectedItemSelector?.state === selector
? undefined
: selector,
})
}
>
{label}
</DropdownMenuItem>
))}
</>
);
})}
</>
);
};
@@ -422,13 +429,7 @@ export const StyleSourceInput = (
let hasNewTokenItem = false;
const states = [
...userActionStates.map((selector) => ({
selector,
label: humanizeString(selector),
})),
...(props.componentStates ?? []),
];
const states = props.componentStates ?? [];
return (
<Combobox>
@@ -1,9 +1,10 @@
import { BlockquoteIcon } from "@webstudio-is/icons";
import type { defaultTag } from "./blockquote";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/blockquote.props";
@@ -63,6 +64,7 @@ export const meta: WsComponentMeta = {
type: "rich-text",
label: "Blockquote",
Icon: BlockquoteIcon,
states: defaultStates,
presetStyle,
children: [{ type: "text", value: "Blockquote you can edit" }],
};
@@ -1,9 +1,10 @@
import { BodyIcon } from "@webstudio-is/icons";
import { body } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/body.props";
import type { defaultTag } from "./body";
@@ -41,6 +42,7 @@ export const meta: WsComponentMeta = {
type: "container",
label: "Body",
Icon: BodyIcon,
states: defaultStates,
presetStyle,
};
@@ -1,8 +1,9 @@
import { BoldIcon } from "@webstudio-is/icons";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/bold.props";
import { b } from "../css/normalize";
@@ -16,6 +17,7 @@ export const meta: WsComponentMeta = {
type: "rich-text-child",
label: "Bold Text",
Icon: BoldIcon,
states: defaultStates,
presetStyle,
};
+6 -4
View File
@@ -1,8 +1,9 @@
import { BoxIcon } from "@webstudio-is/icons";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/box.props";
import type { Box } from "./box";
@@ -40,6 +41,7 @@ export const meta: WsComponentMeta = {
type: "container",
label: "Box",
Icon: BoxIcon,
states: defaultStates,
presetStyle,
};
@@ -1,9 +1,10 @@
import { ButtonElementIcon } from "@webstudio-is/icons";
import { button } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/button.props";
import type { defaultTag } from "./button";
@@ -19,7 +20,7 @@ export const meta: WsComponentMeta = {
Icon: ButtonElementIcon,
presetStyle,
states: [
{ selector: ":active", label: "Active" },
...defaultStates,
{ selector: ":disabled", label: "Disabled" },
{ selector: ":enabled", label: "Enabled" },
],
@@ -1,8 +1,9 @@
import { CheckboxCheckedIcon } from "@webstudio-is/icons";
import type {
WsComponentMeta,
WsComponentPropsMeta,
PresetStyle,
import {
type WsComponentMeta,
type WsComponentPropsMeta,
type PresetStyle,
defaultStates,
} from "./component-meta";
import { props } from "./__generated__/checkbox-field.props";
import type { defaultTag } from "./checkbox-field";
@@ -20,6 +21,7 @@ export const meta: WsComponentMeta = {
type: "container",
label: "Checkbox Field",
Icon: CheckboxCheckedIcon,
states: defaultStates,
presetStyle,
children: [
{ type: "instance", component: "Checkbox", props: [], children: [] },
@@ -1,8 +1,9 @@
import { CheckboxCheckedIcon } from "@webstudio-is/icons";
import type {
WsComponentMeta,
WsComponentPropsMeta,
PresetStyle,
import {
type WsComponentMeta,
type WsComponentPropsMeta,
type PresetStyle,
defaultStates,
} from "./component-meta";
import type { defaultTag } from "./checkbox";
import { input } from "../css/normalize";
@@ -24,6 +25,7 @@ export const meta: WsComponentMeta = {
Icon: CheckboxCheckedIcon,
presetStyle,
states: [
...defaultStates,
{ selector: ":checked", label: "Checked" },
{ selector: ":required", label: "Required" },
{ selector: ":optional", label: "Optional" },
@@ -1,8 +1,9 @@
import { CodeIcon } from "@webstudio-is/icons";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { type defaultTag, displayVarNamespace } from "./code";
import { props } from "./__generated__/code.props";
@@ -39,6 +40,7 @@ export const meta: WsComponentMeta = {
type: "rich-text",
label: "Code",
Icon: CodeIcon,
states: defaultStates,
presetStyle,
children: [{ type: "text", value: "Code you can edit" }],
};
@@ -25,13 +25,24 @@ export const componentCategories = [
"forms",
] as const;
export const stateCategories = ["states", "component-states"] as const;
export const ComponentState = z.object({
category: z.enum(stateCategories).optional(),
selector: z.string(),
label: z.string(),
});
export type ComponentState = z.infer<typeof ComponentState>;
export const defaultStates: ComponentState[] = [
{ selector: ":hover", label: "Hover" },
{ selector: ":active", label: "Active" },
{ selector: ":focus", label: "Focus" },
{ selector: ":focus-visible", label: "Focus Visible" },
{ selector: ":focus-within", label: "Focus Within" },
];
const WsComponentMeta = z.object({
category: z.enum(componentCategories).optional(),
// container - can accept other components with dnd
@@ -1,9 +1,10 @@
import { FormIcon } from "@webstudio-is/icons";
import { form } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./form";
import { props } from "./__generated__/form.props";
@@ -20,6 +21,7 @@ export const meta: WsComponentMeta = {
type: "container",
label: "Form",
Icon: FormIcon,
states: defaultStates,
presetStyle,
children: [
{
@@ -1,10 +1,11 @@
import { HeadingIcon } from "@webstudio-is/icons";
import type { ComponentProps } from "react";
import { h1, h2, h3, h4, h5, h6 } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { Heading } from "./heading";
import { props } from "./__generated__/heading.props";
@@ -26,6 +27,7 @@ export const meta: WsComponentMeta = {
label: "Heading",
Icon: HeadingIcon,
children: [{ type: "text", value: "Heading you can edit" }],
states: defaultStates,
presetStyle,
};
@@ -1,9 +1,10 @@
import { ImageIcon } from "@webstudio-is/icons";
import { img } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./image";
import { props } from "./__generated__/image.props";
@@ -31,6 +32,7 @@ export const meta: WsComponentMeta = {
type: "embed",
label: "Image",
Icon: ImageIcon,
states: defaultStates,
presetStyle,
};
@@ -1,9 +1,10 @@
import { FormTextFieldIcon } from "@webstudio-is/icons";
import { input } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./input";
import { props } from "./__generated__/input.props";
@@ -19,6 +20,7 @@ export const meta: WsComponentMeta = {
Icon: FormTextFieldIcon,
presetStyle,
states: [
...defaultStates,
{ selector: "::placeholder", label: "Placeholder" },
{ selector: ":valid", label: "Valid" },
{ selector: ":invalid", label: "Invalid" },
@@ -1,9 +1,10 @@
import { TextItalicIcon } from "@webstudio-is/icons";
import type { defaultTag } from "./italic";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/italic.props";
import { i } from "../css/normalize";
@@ -22,6 +23,7 @@ export const meta: WsComponentMeta = {
type: "rich-text-child",
label: "Italic Text",
Icon: TextItalicIcon,
states: defaultStates,
presetStyle,
};
@@ -1,8 +1,9 @@
import { TextBlockIcon } from "@webstudio-is/icons";
import type {
WsComponentMeta,
WsComponentPropsMeta,
PresetStyle,
import {
type WsComponentMeta,
type WsComponentPropsMeta,
type PresetStyle,
defaultStates,
} from "./component-meta";
import { props } from "./__generated__/label.props";
import type { defaultTag } from "./label";
@@ -20,6 +21,7 @@ export const meta: WsComponentMeta = {
type: "rich-text",
label: "Input Label",
Icon: TextBlockIcon,
states: defaultStates,
presetStyle,
children: [{ type: "text", value: "Form Label" }],
};
+13 -5
View File
@@ -1,9 +1,10 @@
import { LinkIcon } from "@webstudio-is/icons";
import { a } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./link";
import { props } from "./__generated__/link.props";
@@ -28,7 +29,14 @@ export const meta: WsComponentMeta = {
label: "Link Text",
Icon: LinkIcon,
presetStyle,
states: [{ selector: "[aria-current=page]", label: "Current page" }],
states: [
...defaultStates,
{
category: "component-states",
selector: "[aria-current=page]",
label: "Current page",
},
],
children: [{ type: "text", value: "Link text you can edit" }],
};
@@ -1,9 +1,10 @@
import { ListItemIcon } from "@webstudio-is/icons";
import { li } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./list-item";
import { props } from "./__generated__/list-item.props";
@@ -19,6 +20,7 @@ export const meta: WsComponentMeta = {
label: "List Item",
Icon: ListItemIcon,
children: [{ type: "text", value: "List Item you can edit" }],
states: defaultStates,
presetStyle,
};
@@ -1,8 +1,9 @@
import { ListIcon } from "@webstudio-is/icons";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/list.props";
import type { ListTag } from "./list";
@@ -46,6 +47,7 @@ export const meta: WsComponentMeta = {
type: "container",
label: "List",
Icon: ListIcon,
states: defaultStates,
presetStyle,
children: [],
};
@@ -1,9 +1,10 @@
import { TextAlignLeftIcon } from "@webstudio-is/icons";
import { p } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./paragraph";
import { props } from "./__generated__/paragraph.props";
@@ -18,6 +19,7 @@ export const meta: WsComponentMeta = {
label: "Paragraph",
Icon: TextAlignLeftIcon,
children: [{ type: "text", value: "Pragraph you can edit" }],
states: defaultStates,
presetStyle,
};
@@ -1,8 +1,9 @@
import { RadioCheckedIcon } from "@webstudio-is/icons";
import type {
WsComponentMeta,
WsComponentPropsMeta,
PresetStyle,
import {
type WsComponentMeta,
type WsComponentPropsMeta,
type PresetStyle,
defaultStates,
} from "./component-meta";
import { props } from "./__generated__/radio-button-field.props";
import type { defaultTag } from "./radio-button-field";
@@ -20,6 +21,7 @@ export const meta: WsComponentMeta = {
type: "container",
label: "Radio Button Field",
Icon: RadioCheckedIcon,
states: defaultStates,
presetStyle,
children: [
{ type: "instance", component: "RadioButton", props: [], children: [] },
@@ -1,8 +1,9 @@
import { RadioCheckedIcon } from "@webstudio-is/icons";
import type {
WsComponentMeta,
WsComponentPropsMeta,
PresetStyle,
import {
type WsComponentMeta,
type WsComponentPropsMeta,
type PresetStyle,
defaultStates,
} from "./component-meta";
import type { defaultTag } from "./radio-button";
import { input } from "../css/normalize";
@@ -24,6 +25,7 @@ export const meta: WsComponentMeta = {
Icon: RadioCheckedIcon,
presetStyle,
states: [
...defaultStates,
{ selector: ":checked", label: "Checked" },
{ selector: ":required", label: "Required" },
{ selector: ":optional", label: "Optional" },
@@ -1,8 +1,9 @@
import { DashIcon } from "@webstudio-is/icons";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/separator.props";
import type { defaultTag } from "./separator";
@@ -44,6 +45,7 @@ export const meta: WsComponentMeta = {
type: "embed",
label: "Separator",
Icon: DashIcon,
states: defaultStates,
presetStyle,
children: [],
};
@@ -1,9 +1,10 @@
import { PaintBrushIcon } from "@webstudio-is/icons";
import { span } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./span";
import { props } from "./__generated__/span.props";
@@ -16,6 +17,7 @@ export const meta: WsComponentMeta = {
type: "rich-text-child",
label: "Styled Text",
Icon: PaintBrushIcon,
states: defaultStates,
presetStyle,
};
@@ -1,9 +1,10 @@
import { SubscriptIcon } from "@webstudio-is/icons";
import { sub } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./subscript";
import { props } from "./__generated__/subscript.props";
@@ -16,6 +17,7 @@ export const meta: WsComponentMeta = {
type: "rich-text-child",
label: "Subscript Text",
Icon: SubscriptIcon,
states: defaultStates,
presetStyle,
};
@@ -1,9 +1,10 @@
import { SuperscriptIcon } from "@webstudio-is/icons";
import { sup } from "../css/normalize";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import type { defaultTag } from "./superscript";
import { props } from "./__generated__/superscript.props";
@@ -16,6 +17,7 @@ export const meta: WsComponentMeta = {
type: "rich-text-child",
label: "Superscript Text",
Icon: SuperscriptIcon,
states: defaultStates,
presetStyle,
};
@@ -1,8 +1,9 @@
import { TextBlockIcon } from "@webstudio-is/icons";
import type {
PresetStyle,
WsComponentMeta,
WsComponentPropsMeta,
import {
defaultStates,
type PresetStyle,
type WsComponentMeta,
type WsComponentPropsMeta,
} from "./component-meta";
import { props } from "./__generated__/text-block.props";
import type { defaultTag } from "./text-block";
@@ -23,6 +24,7 @@ export const meta: WsComponentMeta = {
type: "rich-text",
label: "Text Block",
Icon: TextBlockIcon,
states: defaultStates,
presetStyle,
children: [{ type: "text", value: "Block of text you can edit" }],
};
@@ -1,9 +1,10 @@
import { FormTextAreaIcon } from "@webstudio-is/icons";
import { textarea } from "../css/normalize";
import type {
WsComponentMeta,
WsComponentPropsMeta,
PresetStyle,
import {
type WsComponentMeta,
type WsComponentPropsMeta,
type PresetStyle,
defaultStates,
} from "./component-meta";
import type { defaultTag } from "./textarea";
import { props } from "./__generated__/textarea.props";
@@ -23,6 +24,7 @@ export const meta: WsComponentMeta = {
Icon: FormTextAreaIcon,
presetStyle,
states: [
...defaultStates,
{ selector: "::placeholder", label: "Placeholder" },
{ selector: ":valid", label: "Valid" },
{ selector: ":invalid", label: "Invalid" },
+1
View File
@@ -15,5 +15,6 @@ export {
type WsComponentMeta,
type ComponentState,
componentCategories,
stateCategories,
} from "./components/component-meta";
export * from "./embed-template";