Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,5 +171,7 @@ jobs:
working-directory: ${{ github.event.inputs.branch }}
- name: Version and publish "latest" tag to NPM
working-directory: ${{ github.event.inputs.branch }}
run: yarn release --type=latest --sourceTag=${{ github.event.inputs.tag }}
run: >-
yarn release --type=latest --sourceTag=${{ github.event.inputs.tag }}
--createGithubRelease=true
runs-on: ubuntu-latest
2 changes: 1 addition & 1 deletion .github/workflows/wac/release.wac.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export const release = createWorkflow({
{
name: 'Version and publish "latest" tag to NPM',
"working-directory": BRANCH_NAME,
run: `yarn release --type=latest --sourceTag=${DIST_TAG}`
run: `yarn release --type=latest --sourceTag=${DIST_TAG} --createGithubRelease=true`
}
],
{ "working-directory": BRANCH_NAME }
Expand Down
11 changes: 11 additions & 0 deletions extensions/funnelBuilder/FunnelBuilder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";
import { Admin } from "webiny/extensions";

export const FunnelBuilder = () => {
return (
<>
<Admin.Extension src={import.meta.dirname + "/pageType/index.tsx"} />
<Admin.Extension src={import.meta.dirname + "/pageEditor/index.tsx"} />
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react";
import { Button, useDialogs } from "webiny/admin/ui";
import { ElementInputs } from "webiny/admin/website-builder/page/editor";
import { useElementInputs } from "webiny/admin/website-builder/page/editor";
import { useComponent } from "webiny/admin/website-builder/page/editor";

export const ElementInputsDecorator = ElementInputs.createDecorator(Original => {
return function FunnelElementSettings(props) {
const { element } = props;
const { inputs, updateInputs } = useElementInputs(element.id);
const component = useComponent(element.component.name);
const dialogs = useDialogs();

const handleClick = () => {
dialogs.showDialog({
formData: inputs.registry,
title: `Edit ${component.label} Settings`,
content: <pre>{JSON.stringify(inputs, null, 2)}</pre>,
acceptLabel: "Save Field Settings",
cancelLabel: "Cancel",
onAccept: (data: any) => {
console.log(data);
updateInputs(inputs => {
Object.assign(inputs.registry, data);
});
}
});
};

if (props.element.component.name.startsWith("FunnelBuilder/")) {
return (
<>
<Button
variant={"primary"}
text={`Edit ${component.label} Settings`}
className={"w-full"}
onClick={handleClick}
/>
<pre>{JSON.stringify(element, null, 2)}</pre>
</>
);
}
return <Original {...props} />;
};
});
6 changes: 6 additions & 0 deletions extensions/funnelBuilder/pageEditor/elementInputs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from "react";
import { ElementInputsDecorator } from "./ElementInputs.js";

export const FubElementInputs = () => {
return <ElementInputsDecorator />;
};
12 changes: 12 additions & 0 deletions extensions/funnelBuilder/pageEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";
import { StepsNavigator } from "./stepsNavigator/index.js";
import { FubElementInputs } from "./elementInputs/index.js";

export default () => {
return (
<>
<StepsNavigator />
<FubElementInputs />
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React from "react";
import { Button, Icon } from "webiny/admin/ui";
import { ReactComponent as DeleteIcon } from "webiny/admin/icons/close.svg";
import {
useCreateElement,
useDeleteElement,
useUpdateElement
} from "webiny/admin/website-builder/page/editor";
import { type FunnelInputs, useFunnel } from "./useFunnel.js";

const iconClasses =
"absolute z-10 rounded-full bg-neutral-dimmed border-solid border-sm border-neutral-muted cursor-pointer fill-neutral-strong";

const iconPosition = {
top: -8,
right: -8
};

export const StepsNavigator = () => {
const funnel = useFunnel();
const { createElement } = useCreateElement();
const { updateElement } = useUpdateElement();
const { deleteElement } = useDeleteElement();

if (!funnel) {
return null;
}

const { activeStep, steps = [] } = funnel.inputs;

const activateStep = (index: number) => {
funnel.updateInputs(inputs => {
inputs.activeStep = index;
});
};

const deleteStep = (stepElementId: string) => {
deleteElement(stepElementId);
};

const addStep = () => {
const steps = funnel.inputs.steps ?? [];
const insertIndex = Math.max(steps.length - 1, 0);

createElement({
componentName: "FunnelBuilder/Step",
parentId: funnel.id,
slot: "steps",
index: insertIndex,
bindings: {
inputs: {
label: `Step ${steps.length}`
}
}
});

updateElement<FunnelInputs>(funnel.id, inputs => {
inputs.activeStep = insertIndex;
});
};

return (
<div
className={"flex flex-row p-sm bg-neutral-light justify-between"}
data-affects-preview={"height"}
>
<div className={"flex gap-md"}>
{steps.map((step, index) => {
const isFirstStep = index === 0;
const isLastStep = index === steps.length - 1;
const canDelete = !isFirstStep && !isLastStep;
const activeVariant = activeStep === index ? "primary" : "secondary";

return (
<div className={"relative"} key={index}>
<Button
variant={activeVariant}
text={step.label}
className={"border-solid border-sm border-neutral-muted"}
onClick={() => activateStep(index)}
/>
{canDelete ? (
<Icon
icon={<DeleteIcon />}
label={"Delete step"}
style={iconPosition}
onClick={() => deleteStep(step.elementId)}
className={iconClasses}
/>
) : null}
</div>
);
})}
</div>
<Button variant={"ghost"} text={"+ Add step"} onClick={addStep} />
</div>
);
};
21 changes: 21 additions & 0 deletions extensions/funnelBuilder/pageEditor/stepsNavigator/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";
import { PageEditorConfig } from "webiny/admin/website-builder/page/editor";
import { StepsNavigator as Component } from "./StepsNavigator.js";

const { Ui } = PageEditorConfig;

export const StepsNavigator = () => {
return (
<PageEditorConfig>
<Ui.Content.Element
name={"stepsNavigator"}
before={"iframe"}
element={
<Ui.IsNotReadOnly>
<Component />
</Ui.IsNotReadOnly>
}
/>
</PageEditorConfig>
);
};
26 changes: 26 additions & 0 deletions extensions/funnelBuilder/pageEditor/stepsNavigator/useFunnel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
useSelectFromDocument,
$getFirstElementOfType,
useElementInputs
} from "webiny/admin/website-builder/page/editor";

export type FunnelInputs = {
activeStep: number;
registry: Record<string, any>;
steps: Array<{ elementId: string; label: string; children: string[] }>;
};

export const useFunnel = () => {
const elementId = useSelectFromDocument(state => {
const funnel = $getFirstElementOfType(state, "FunnelBuilder/Funnel");
return funnel ? funnel.id : null;
});

const { inputs, updateInputs } = useElementInputs<FunnelInputs>(elementId, 1);

if (!elementId) {
return null;
}

return { id: elementId, inputs, updateInputs };
};
36 changes: 36 additions & 0 deletions extensions/funnelBuilder/pageType/FunnelPageForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react";
import { Grid, Input } from "webiny/admin/ui";
import { pagePathFromTitle } from "webiny/admin/website-builder";
import type { FormApi } from "webiny/admin/form";
import { Bind, UnsetOnUnmount, useForm, validation } from "webiny/admin/form";

const generatePath = (form: FormApi) => () => {
const title = form.getValue("properties.title");

const titlePath = pagePathFromTitle(title ?? "");

form.setValue("properties.path", `/funnel/${titlePath}`);
};

export const FunnelPageForm = () => {
const form = useForm();

return (
<>
<Grid.Column span={12}>
<UnsetOnUnmount name={"properties.title"}>
<Bind name={"properties.title"} validators={[validation.create("required")]}>
<Input label={"Title"} onBlur={generatePath(form)} />
</Bind>
</UnsetOnUnmount>
</Grid.Column>
<Grid.Column span={12}>
<UnsetOnUnmount name={"properties.path"}>
<Bind name={"properties.path"} validators={[validation.create("required")]}>
<Input label={"Path"} />
</Bind>
</UnsetOnUnmount>
</Grid.Column>
</>
);
};
15 changes: 15 additions & 0 deletions extensions/funnelBuilder/pageType/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import { PageListConfig } from "webiny/admin/website-builder/page/list";
import { FunnelPageForm } from "./FunnelPageForm.js";

export default () => {
return (
<PageListConfig>
<PageListConfig.PageType
name={"funnel"}
label={"Funnel"}
element={<FunnelPageForm />}
/>
</PageListConfig>
);
};
Loading
Loading