Skip to content

Commit

Permalink
feat: Add install all button for Plexus
Browse files Browse the repository at this point in the history
Change-Id: I24dd7245a63983f0c496034a374a0956ad6a2362
GitOrigin-RevId: 387ae4b31d7e1f3803e1cb778748588f9c098103
  • Loading branch information
jaslong authored and actions-user committed Dec 18, 2024
1 parent a1de7be commit a4c6279
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 116 deletions.
258 changes: 147 additions & 111 deletions platform/wab/src/wab/client/components/insert-panel/InsertPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ import {
DraggableInsertableProps,
} from "@/wab/client/components/studio/add-drawer/DraggableInsertable";
import { Matcher } from "@/wab/client/components/view-common";
import Button from "@/wab/client/components/widgets/Button";
import { TextboxRef } from "@/wab/client/components/widgets/Textbox";
import {
AddInstallableItem,
AddItem,
AddItemType,
AddTplItem,
Expand Down Expand Up @@ -124,7 +126,6 @@ import { naturalSort } from "@/wab/shared/sort";
import {
canInsertAlias,
canInsertHostlessPackage,
InsertPanelConfig,
} from "@/wab/shared/ui-config-utils";
import { placeholderImgUrl } from "@/wab/shared/urls";
import { Menu } from "antd";
Expand Down Expand Up @@ -337,7 +338,7 @@ const AddDrawerContent = observer(function AddDrawerContent(props: {
),

...(index < groupedItems.length - 1
? [{ type: "separator" } as const]
? [{ type: "separator", group } as const]
: []),
]);

Expand Down Expand Up @@ -404,7 +405,7 @@ const AddDrawerContent = observer(function AddDrawerContent(props: {
return false;
};

const onInsert = async (item: AddItem) => {
const onInsert = spawnWrapper(async (item: AddItem) => {
if (shouldInterceptOnInsert(item)) {
return;
}
Expand Down Expand Up @@ -466,7 +467,7 @@ const AddDrawerContent = observer(function AddDrawerContent(props: {
break;
}
}
};
});

function cycleSection(step: 1 | -1) {
const index = allSectionKeysFlattened.findIndex((sec) => sec === section);
Expand All @@ -488,6 +489,7 @@ const AddDrawerContent = observer(function AddDrawerContent(props: {
onDragStart,
onDragEnd,
matcher,
onInsert,
onInserted,
getItemProps,
highlightedItemIndex,
Expand Down Expand Up @@ -680,6 +682,7 @@ interface AddDrawerContextValue {
onDragStart: DraggableInsertableProps["onDragStart"];
onDragEnd: DraggableInsertableProps["onDragEnd"];
matcher: Matcher;
onInsert: (item: AddItem) => void;
onInserted: (item: AddItem, tplNode: TplNode | null) => void;
getItemProps: (options: UseComboboxGetItemPropsOptions<AddItem>) => any;
highlightedItemIndex: number;
Expand All @@ -705,6 +708,7 @@ type VirtualItem =
}
| {
type: "separator";
group: AddItemGroup;
item?: never;
};

Expand Down Expand Up @@ -738,14 +742,27 @@ const Row = React.memo(function Row(props: {
>
{virtualRow.map((virtualItem) => {
if (virtualItem.type === "header") {
const installableItem = virtualItem.group.sectionInstallableItem;
return (
<ListSectionHeader
key={`"header-${virtualItem.group.key}`}
style={{
paddingTop: 8,
paddingLeft: 8,
paddingRight: 8,
paddingBottom: 0,
}}
showActions={!!installableItem}
actions={
installableItem && (
<Button
size={"small"}
onClick={() => context.onInsert(installableItem)}
>
Install all
</Button>
)
}
>
<span
style={{
Expand Down Expand Up @@ -873,6 +890,7 @@ const Row = React.memo(function Row(props: {
} else if (virtualItem.type === "separator") {
return (
<ListSectionSeparator
key={`separator-${virtualItem.group.key}`}
style={{
paddingTop: 4,
padding: 0,
Expand Down Expand Up @@ -1030,6 +1048,7 @@ interface AddItemGroup {
familyKey?: keyof typeof familyKeyToLabel;
sectionKey?: string;
sectionLabel?: string;
sectionInstallableItem?: AddInstallableItem;
label: string;
items: AddItem[];
codeName?: string;
Expand Down Expand Up @@ -1088,14 +1107,10 @@ export function buildAddItemGroups({
DEVFLAGS.insertPanelContent.builtinSections,
DEVFLAGS.insertPanelContent.overrideSections[isApp ? "app" : "website"]
);
const insertPanelConfig: InsertPanelConfig = {
...studioCtx.appCtx.appConfig.insertPanelContent,
builtinSections,
};
const builtinSectionsInstallables =
DEVFLAGS.insertPanelContent.builtinSectionsInstallables;

const canInsertContext = {
insertPanel: insertPanelConfig,
hostlessPackages: studioCtx.appCtx.appConfig.hostLessComponents ?? [],
isContentCreator: contentEditorMode,
};

Expand Down Expand Up @@ -1134,131 +1149,152 @@ export function buildAddItemGroups({
// We don't want to just call it "built-in" since you may swap in your own custom components using the default-components system (for a few specific kinds).
// But generally later we may allow users to more fully customize this menu? TBD.
...Object.entries(builtinSections).flatMap(([section, groups]) =>
Object.entries(groups).map(
([group, aliases]) =>
!group.startsWith("__") && {
sectionKey: section,
sectionLabel: section,
key: group,
label: group,
items: filterFalsy(
aliases.map((alias) => {
if (!canInsertAlias(uiConfig, alias, canInsertContext)) {
Object.entries(groups).map<AddItemGroup>(([group, aliases]) => {
// Maybe show the install all button
const installableProjectId = builtinSectionsInstallables[group];
let sectionInstallableItem: AddInstallableItem | undefined;
if (installableProjectId) {
// Don't want to download the project, so we use a heuristic
// to guess whether the project is fully installed or not.
// installedCount is not perfect since the project may have
// more components than shown in the section as aliases.
const installedCount = studioCtx.site.components.filter(
(c) => c.templateInfo?.projectId === installableProjectId
).length;
if (installedCount < aliases.length) {
const installable = installableProjectId
? studioCtx.appCtx.appConfig.installables.find(
(meta) =>
meta.type === "ui-kit" &&
meta.projectId === installableProjectId
)
: undefined;
if (installable) {
sectionInstallableItem = createAddInstallable(installable);
}
}
}

return {
sectionKey: section,
sectionLabel: section,
sectionInstallableItem,
key: group,
label: group,
items: filterFalsy(
aliases.map((alias) => {
if (!canInsertAlias(uiConfig, alias, canInsertContext)) {
return undefined;
}
const resolved = insertPanelAliases.get(alias as any);
const aliasImageUrl = `https://static1.plasmic.app/insertables/${alias}.svg`;

// Is this a built-in insertable?
if (!resolved) {
const insertable = INSERTABLES_MAP[alias];
if (!insertable) {
return undefined;
}
const resolved = insertPanelAliases.get(alias as any);
const aliasImageUrl = `https://static1.plasmic.app/insertables/${alias}.svg`;
return { ...insertable, isCompact: true };
}

// Is this a built-in insertable?
if (!resolved) {
const insertable = INSERTABLES_MAP[alias];
if (!insertable) {
return undefined;
}
return { ...insertable, isCompact: true };
// Is this a built-in code component?
if (resolved.startsWith("builtincc:")) {
const componentName = resolved.split(":")[1];
const component = studioCtx.site.components
.filter((c) => isBuiltinCodeComponent(c))
.find((c) => c.name === componentName);
if (!component) {
return undefined;
}
return {
...createAddTplComponent(component),
previewImageUrl: aliasImageUrl,
isCompact: true,
};
}

// Is this a built-in code component?
if (resolved.startsWith("builtincc:")) {
const componentName = resolved.split(":")[1];
const component = studioCtx.site.components
.filter((c) => isBuiltinCodeComponent(c))
.find((c) => c.name === componentName);
if (!component) {
return undefined;
}
// Is this a default component entry?
if (resolved.startsWith("default:")) {
const kind = resolved.split(":")[1];
if (!isDefaultComponentKind(kind)) {
return undefined;
}
const existingComponent = tryGetDefaultComponent(
studioCtx.site,
kind
);
if (existingComponent) {
return {
...createAddTplComponent(component),
previewImageUrl: aliasImageUrl,
...createAddTplComponent(existingComponent),
previewImageUrl: getPlumeImage(kind),
isCompact: true,
};
}

// Is this a default component entry?
if (resolved.startsWith("default:")) {
const kind = resolved.split(":")[1];
if (!isDefaultComponentKind(kind)) {
return undefined;
}
const existingComponent = tryGetDefaultComponent(
studioCtx.site,
kind
);
if (existingComponent) {
return {
...createAddTplComponent(existingComponent),
previewImageUrl: getPlumeImage(kind),
isCompact: true,
};
} else if (
canInsertHostlessPackage(
uiConfig,
"plume",
canInsertContext
)
) {
if (!hasPlexus || DEVFLAGS.runningInCypress) {
const plumeItem = makePlumeInsertables(studioCtx, kind);
if (plumeItem.length > 0) {
return {
...only(plumeItem),
isCompact: true,
};
} else {
return undefined;
}
}

// The template name needs to be of format "<PLEXUS_INSERTABLE_ID>/<kind>". E.g. For Plexus button, it will be "plexus/button".
// The template name will be fetched from devflags.insertableTemplates.
const plexusItem = handleTemplateAlias(
`${PLEXUS_INSERTABLE_ID}/${kind}`,
kind
);
if (plexusItem) {
} else if (
canInsertHostlessPackage(uiConfig, "plume", canInsertContext)
) {
if (!hasPlexus || DEVFLAGS.runningInCypress) {
const plumeItem = makePlumeInsertables(studioCtx, kind);
if (plumeItem.length > 0) {
return {
previewImageUrl: aliasImageUrl,
...plexusItem,
...only(plumeItem),
isCompact: true,
};
} else {
return undefined;
}
}

// The template name needs to be of format "<PLEXUS_INSERTABLE_ID>/<kind>". E.g. For Plexus button, it will be "plexus/button".
// The template name will be fetched from devflags.insertableTemplates.
const plexusItem = handleTemplateAlias(
`${PLEXUS_INSERTABLE_ID}/${kind}`,
kind
);
if (plexusItem) {
return {
previewImageUrl: aliasImageUrl,
...plexusItem,
};
} else {
return undefined;
}
} else {
return undefined;
}
}

if (resolved.startsWith("template:")) {
const templateName = resolved.split(":")[1];
// ASK: Previously, it only returned if a template was found. Is it OK to return undefined if the template isn't found?
return handleTemplateAlias(templateName);
}
if (resolved.startsWith("template:")) {
const templateName = resolved.split(":")[1];
// ASK: Previously, it only returned if a template was found. Is it OK to return undefined if the template isn't found?
return handleTemplateAlias(templateName);
}

// Is this a hostless component entry?
for (const hostlessGroup of getHostLess(studioCtx)) {
if (
canInsertHostlessPackage(
uiConfig,
hostlessGroup.codeName ?? "",
canInsertContext
)
) {
for (const item of hostlessGroup.items) {
if (item.key === "hostless-component-" + resolved) {
if (isTplAddItem(item) && item.systemName) {
installedHostlessComponents.add(item.systemName);
}
return { ...item, isCompact: true };
// Is this a hostless component entry?
for (const hostlessGroup of getHostLess(studioCtx)) {
if (
canInsertHostlessPackage(
uiConfig,
hostlessGroup.codeName ?? "",
canInsertContext
)
) {
for (const item of hostlessGroup.items) {
if (item.key === "hostless-component-" + resolved) {
if (isTplAddItem(item) && item.systemName) {
installedHostlessComponents.add(item.systemName);
}
return { ...item, isCompact: true };
}
}
}
}

return undefined;
})
),
}
)
return undefined;
})
),
};
})
),

// Custom components includes all the components from the project
Expand Down
1 change: 1 addition & 0 deletions platform/wab/src/wab/shared/devflags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ const DEFAULT_DEVFLAGS = {
componentsLabel: "Custom components",
aliases: {},
builtinSections: {},
builtinSectionsInstallables: {},
overrideSections: {},
}),
insertableTemplates: ensureType<InsertableTemplatesGroup | undefined>(
Expand Down
Loading

0 comments on commit a4c6279

Please sign in to comment.