Skip to content
Open
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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ FROM_EMAIL="hello@usesend.com"
API_RATE_LIMIT=2
AUTH_EMAIL_RATE_LIMIT=5

NEXT_PUBLIC_IS_CLOUD=true
NEXT_PUBLIC_IS_CLOUD=true
68 changes: 33 additions & 35 deletions packages/email-editor/src/menus/TextMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { BubbleMenu, BubbleMenuProps, isTextSelection } from "@tiptap/react";
import {
BubbleMenu,
BubbleMenuProps,
Editor,
isTextSelection,
} from "@tiptap/react";
import {
AlignCenterIcon,
AlignLeftIcon,
Expand All @@ -20,16 +25,13 @@ import {
TextQuoteIcon,
UnderlineIcon,
} from "lucide-react";
import { TextMenuButton } from "./TextMenuButton";
import { Button } from "@usesend/ui/src/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@usesend/ui/src/popover";
import { Separator } from "@usesend/ui/src/separator";
import { useMemo, useState } from "react";
import { LinkEditorPanel } from "../components/panels/LinkEditorPanel";
import {TextMenuButton} from "./TextMenuButton";
import {Button} from "@usesend/ui/src/button";
import {Popover, PopoverContent, PopoverTrigger} from "@usesend/ui/src/popover";
import {Separator} from "@usesend/ui/src/separator";
import {useMemo, useState} from "react";
import {LinkEditorPanel} from "../components/panels/LinkEditorPanel";
import {EditorState} from "@tiptap/pm/state";
// import { allowedLogoAlignment } from "../nodes/logo";

export interface TextMenuItem {
Expand Down Expand Up @@ -92,15 +94,15 @@ const textColors = [
];

export function TextMenu(props: TextMenuProps) {
const { editor } = props;
const {editor} = props;

const icons = [AlignLeftIcon, AlignCenterIcon, AlignRightIcon];
const alignmentItems: TextMenuItem[] = ["left", "center", "right"].map(
(alignment, index) => ({
name: alignment,
isActive: () => editor?.isActive({ textAlign: alignment })!,
isActive: () => editor?.isActive({textAlign: alignment})!,
command: () => {
if (props?.editor?.isActive({ textAlign: alignment })) {
if (props?.editor?.isActive({textAlign: alignment})) {
props?.editor?.chain()?.focus().unsetTextAlign().run();
} else {
props?.editor?.chain().focus().setTextAlign(alignment).run()!;
Expand Down Expand Up @@ -185,11 +187,11 @@ export function TextMenu(props: TextMenuProps) {
.focus()
.lift("taskItem")
.liftListItem("listItem")
.setHeading({ level: 1 })
.setHeading({level: 1})
.run(),
id: "heading1",
disabled: () => !editor?.can().setHeading({ level: 1 }),
isActive: () => editor?.isActive("heading", { level: 1 }),
disabled: () => !editor?.can().setHeading({level: 1}),
isActive: () => editor?.isActive("heading", {level: 1}),
label: "Heading 1",
type: "option",
},
Expand All @@ -201,11 +203,11 @@ export function TextMenu(props: TextMenuProps) {
?.focus()
?.lift("taskItem")
.liftListItem("listItem")
.setHeading({ level: 2 })
.setHeading({level: 2})
.run(),
id: "heading2",
disabled: () => !editor?.can().setHeading({ level: 2 }),
isActive: () => editor?.isActive("heading", { level: 2 }),
disabled: () => !editor?.can().setHeading({level: 2}),
isActive: () => editor?.isActive("heading", {level: 2}),
label: "Heading 2",
type: "option",
},
Expand All @@ -217,11 +219,11 @@ export function TextMenu(props: TextMenuProps) {
?.focus()
?.lift("taskItem")
.liftListItem("listItem")
.setHeading({ level: 3 })
.setHeading({level: 3})
.run(),
id: "heading3",
disabled: () => !editor?.can().setHeading({ level: 3 }),
isActive: () => editor?.isActive("heading", { level: 3 }),
disabled: () => !editor?.can().setHeading({level: 3}),
isActive: () => editor?.isActive("heading", {level: 3}),
label: "Heading 3",
type: "option",
},
Expand Down Expand Up @@ -249,9 +251,9 @@ export function TextMenu(props: TextMenuProps) {

const bubbleMenuProps: TextMenuProps = {
...props,
shouldShow: ({ editor, state, from, to }) => {
const { doc, selection } = state;
const { empty } = selection;
shouldShow: ({editor, state, from, to}) => {
const {doc, selection} = state;
const {empty} = selection;

// Sometime check for `empty` is not enough.
// Doubleclick an empty paragraph returns a node size of 2.
Expand Down Expand Up @@ -300,11 +302,7 @@ export function TextMenu(props: TextMenuProps) {
<ContentTypePicker options={contentTypePickerOptions} />
<EditLinkPopover
onSetLink={(url) => {
editor
?.chain()
.focus()
.setLink({ href: url, target: "_blank" })
.run();
editor?.chain().focus().setLink({href: url, target: "_blank"}).run();

// editor?.commands.blur();
}}
Expand All @@ -320,7 +318,7 @@ export function TextMenu(props: TextMenuProps) {
variant="ghost"
className="hover:bg-slate-100 hover:text-slate-900"
>
<span style={{ color: selectedColor }}>A</span>
<span style={{color: selectedColor}}>A</span>
<ChevronDown className="h-4 w-4 ml-1.5 text-gray-800" />
</Button>
</PopoverTrigger>
Expand All @@ -341,7 +339,7 @@ export function TextMenu(props: TextMenuProps) {
: ""
}`}
>
<span style={{ color: color.value }}>A</span>
<span style={{color: color.value}}>A</span>
<span className=" capitalize">{color.name}</span>
</button>
))}
Expand All @@ -355,7 +353,7 @@ type ContentTypePickerProps = {
options: ContentTypePickerOption[];
};

function ContentTypePicker({ options }: ContentTypePickerProps) {
function ContentTypePicker({options}: ContentTypePickerProps) {
const activeOption = useMemo(
() => options.find((option) => option.isActive()),
[options]
Expand Down Expand Up @@ -401,7 +399,7 @@ type EditLinkPopoverType = {
onSetLink: (url: string) => void;
};

function EditLinkPopover({ onSetLink }: EditLinkPopoverType) {
function EditLinkPopover({onSetLink}: EditLinkPopoverType) {
return (
<Popover>
<PopoverTrigger asChild>
Expand Down
48 changes: 28 additions & 20 deletions packages/email-editor/src/nodes/variable.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { NodeViewProps, NodeViewWrapper, ReactRenderer } from "@tiptap/react";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@usesend/ui/src/popover";
import { cn } from "@usesend/ui/lib/utils";
import { Input } from "@usesend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { SuggestionOptions } from "@tiptap/suggestion";
import tippy, { GetReferenceClientRect } from "tippy.js";
import { CheckIcon, TriangleAlert } from "lucide-react";
import {NodeViewProps, NodeViewWrapper, ReactRenderer} from "@tiptap/react";
import {Popover, PopoverContent, PopoverTrigger} from "@usesend/ui/src/popover";
import {cn} from "@usesend/ui/lib/utils";
import {Input} from "@usesend/ui/src/input";
import {Button} from "@usesend/ui/src/button";
import {forwardRef, useEffect, useImperativeHandle, useState} from "react";
import {SuggestionOptions} from "@tiptap/suggestion";
import tippy, {GetReferenceClientRect} from "tippy.js";
import {CheckIcon, TriangleAlert} from "lucide-react";

export interface VariableOptions {
name: string;
Expand All @@ -26,14 +22,14 @@ export const VariableList = forwardRef((props: any, ref) => {
console.log("item: ", item);

if (item) {
props.command({ id: item, name: item, fallback: "" });
props.command({id: item, name: item, fallback: ""});
}
};

useEffect(() => setSelectedIndex(0), [props.items]);

useImperativeHandle(ref, () => ({
onKeyDown: ({ event }: { event: KeyboardEvent }) => {
onKeyDown: ({event}: {event: KeyboardEvent}) => {
if (event.key === "ArrowUp") {
setSelectedIndex(
(selectedIndex + props.items.length - 1) % props.items.length
Expand Down Expand Up @@ -85,7 +81,7 @@ export function getVariableSuggestions(
variables: Array<string> = []
): Omit<SuggestionOptions, "editor"> {
return {
items: ({ query }) => {
items: ({query}) => {
return variables
.concat(query.length > 0 ? [query] : [])
.filter((item) => item.toLowerCase().startsWith(query.toLowerCase()))
Expand Down Expand Up @@ -154,9 +150,10 @@ export function getVariableSuggestions(
}

export function VariableComponent(props: NodeViewProps) {
const { name, fallback } = props.node.attrs as VariableOptions;
const [isEditing, setIsEditing] = useState(false);
const {name, fallback} = props.node.attrs as VariableOptions;
const [fallbackValue, setFallbackValue] = useState(fallback);
const { getPos, editor } = props;
const {getPos, editor} = props;

console.log(props.selected);

Expand All @@ -165,6 +162,8 @@ export function VariableComponent(props: NodeViewProps) {
props.updateAttributes({
fallback: fallbackValue,
});

setIsEditing(false);
};

return (
Expand All @@ -175,17 +174,26 @@ export function VariableComponent(props: NodeViewProps) {
draggable="false"
data-drag-handle=""
>
<Popover open={props.selected}>
<Popover
open={isEditing}
onOpenChange={(open) => !open && setIsEditing(false)}
>
<PopoverTrigger asChild>
<button
className={cn(
"inline-flex items-center justify-center rounded-md text-sm gap-1 ring-offset-white transition-colors",
"px-2 border border-gray-300 shadow-sm cursor-pointer text-foreground/80"
)}
onClick={(e) => {
// onClick={(e) => {
// e.preventDefault();
// const pos = getPos();
// editor.commands.setNodeSelection(pos);
// }}
onDoubleClick={(e) => {
e.preventDefault();
const pos = getPos();
editor.commands.setNodeSelection(pos);
setIsEditing(true);
}}
>
<span className="">{`{{${name}}}`}</span>
Expand Down