mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-09 05:26:24 +09:00
feat: insert tag commands (#5248)
Ref https://github.com/webstudio-is/webstudio/issues/3632 Added commands for all tags satisfying current tree https://github.com/user-attachments/assets/6e7e29c4-1a9a-452d-a967-448e216e4703
This commit is contained in:
1 parent
1e1edf7a65
commit
aea46aba6c
1 file changed
+128
-4
@@ -25,13 +25,16 @@ import {
|
||||
collectionComponent,
|
||||
parseComponentName,
|
||||
elementComponent,
|
||||
tags,
|
||||
} from "@webstudio-is/sdk";
|
||||
import type { Breakpoint, Page } from "@webstudio-is/sdk";
|
||||
import type { Breakpoint, Instance, Page } from "@webstudio-is/sdk";
|
||||
import type { TemplateMeta } from "@webstudio-is/template";
|
||||
import {
|
||||
$breakpoints,
|
||||
$editingPageId,
|
||||
$instances,
|
||||
$pages,
|
||||
$props,
|
||||
$registeredComponentMetas,
|
||||
$registeredTemplates,
|
||||
$selectedBreakpoint,
|
||||
@@ -44,7 +47,11 @@ import {
|
||||
} from "~/shared/instance-utils";
|
||||
import { humanizeString } from "~/shared/string-utils";
|
||||
import { setCanvasWidth } from "~/builder/features/breakpoints";
|
||||
import { $selectedPage, selectPage } from "~/shared/awareness";
|
||||
import {
|
||||
$selectedInstancePath,
|
||||
$selectedPage,
|
||||
selectPage,
|
||||
} from "~/shared/awareness";
|
||||
import { mapGroupBy } from "~/shared/shim";
|
||||
import { setActiveSidebarPanel } from "~/builder/shared/nano-states";
|
||||
import { $commandMetas } from "~/shared/commands-emitter";
|
||||
@@ -54,6 +61,7 @@ import {
|
||||
getInstanceLabel,
|
||||
InstanceIcon,
|
||||
} from "~/builder/shared/instance-label";
|
||||
import { isTreeSatisfyingContentModel } from "~/shared/content-model";
|
||||
|
||||
const $commandPanel = atom<
|
||||
| undefined
|
||||
@@ -233,6 +241,101 @@ const ComponentOptionsGroup = ({ options }: { options: ComponentOption[] }) => {
|
||||
);
|
||||
};
|
||||
|
||||
type TagOption = {
|
||||
tokens: string[];
|
||||
type: "tag";
|
||||
tag: string;
|
||||
};
|
||||
|
||||
const $tagOptions = computed(
|
||||
[$selectedInstancePath, $instances, $props, $registeredComponentMetas],
|
||||
(instancePath, instances, props, metas) => {
|
||||
const tagOptions: TagOption[] = [];
|
||||
if (instancePath === undefined) {
|
||||
return tagOptions;
|
||||
}
|
||||
const [{ instance, instanceSelector }] = instancePath;
|
||||
const childInstance: Instance = {
|
||||
type: "instance",
|
||||
id: "new_instance",
|
||||
component: elementComponent,
|
||||
children: [],
|
||||
};
|
||||
const newInstances = new Map(instances);
|
||||
newInstances.set(childInstance.id, childInstance);
|
||||
newInstances.set(instance.id, {
|
||||
...instance,
|
||||
children: [...instance.children, { type: "id", value: childInstance.id }],
|
||||
});
|
||||
for (const tag of tags) {
|
||||
childInstance.tag = tag;
|
||||
const isSatisfying = isTreeSatisfyingContentModel({
|
||||
instances: newInstances,
|
||||
props,
|
||||
metas,
|
||||
instanceSelector,
|
||||
});
|
||||
if (isSatisfying) {
|
||||
tagOptions.push({
|
||||
tokens: ["tags", tag, `<${tag}>`],
|
||||
type: "tag",
|
||||
tag,
|
||||
});
|
||||
}
|
||||
}
|
||||
return tagOptions;
|
||||
}
|
||||
);
|
||||
|
||||
const TagOptionsGroup = ({ options }: { options: TagOption[] }) => {
|
||||
return (
|
||||
<CommandGroup
|
||||
name="tag"
|
||||
heading={<CommandGroupHeading>Tags</CommandGroupHeading>}
|
||||
actions={["add"]}
|
||||
>
|
||||
{options.map(({ tag }) => {
|
||||
return (
|
||||
<CommandItem
|
||||
key={tag}
|
||||
// preserve selected state when rerender
|
||||
value={tag}
|
||||
onSelect={() => {
|
||||
closeCommandPanel();
|
||||
const newInstance: Instance = {
|
||||
type: "instance",
|
||||
id: "new_instance",
|
||||
component: elementComponent,
|
||||
tag,
|
||||
children: [],
|
||||
};
|
||||
insertWebstudioFragmentAt({
|
||||
children: [{ type: "id", value: newInstance.id }],
|
||||
instances: [newInstance],
|
||||
props: [],
|
||||
dataSources: [],
|
||||
styleSourceSelections: [],
|
||||
styleSources: [],
|
||||
styles: [],
|
||||
breakpoints: [],
|
||||
assets: [],
|
||||
resources: [],
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Flex gap={2}>
|
||||
<CommandIcon>
|
||||
<InstanceIcon instance={{ component: elementComponent, tag }} />
|
||||
</CommandIcon>
|
||||
<Text variant="labelsSentenceCase">{`<${tag}>`}</Text>
|
||||
</Flex>
|
||||
</CommandItem>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
);
|
||||
};
|
||||
|
||||
type BreakpointOption = {
|
||||
tokens: string[];
|
||||
type: "breakpoint";
|
||||
@@ -423,12 +526,25 @@ const ShortcutOptionsGroup = ({ options }: { options: ShortcutOption[] }) => {
|
||||
};
|
||||
|
||||
const $options = computed(
|
||||
[$componentOptions, $breakpointOptions, $pageOptions, $shortcutOptions],
|
||||
(componentOptions, breakpointOptions, pageOptions, commandOptions) => [
|
||||
[
|
||||
$componentOptions,
|
||||
$breakpointOptions,
|
||||
$pageOptions,
|
||||
$shortcutOptions,
|
||||
$tagOptions,
|
||||
],
|
||||
(
|
||||
componentOptions,
|
||||
breakpointOptions,
|
||||
pageOptions,
|
||||
commandOptions,
|
||||
tagOptions
|
||||
) => [
|
||||
...componentOptions,
|
||||
...breakpointOptions,
|
||||
...pageOptions,
|
||||
...commandOptions,
|
||||
...tagOptions,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -461,6 +577,14 @@ const CommandDialogContent = () => {
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (group === "tag") {
|
||||
return (
|
||||
<TagOptionsGroup
|
||||
key={group}
|
||||
options={matches as TagOption[]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (group === "breakpoint") {
|
||||
return (
|
||||
<BreakpointOptionsGroup
|
||||
|
||||
Reference in new issue
Block a user