react-email-builder
Version:
A simple React drag and drop email builder.
233 lines (232 loc) • 9.07 kB
JavaScript
import { useCallback, useContext, useMemo } from 'react';
import { EmailBuilderConfigContext, EmailBuilderDefaultFontStyleContext, EmailBuilderSelectedBlockInfoContext, EmailBuilderStateContext, SetEmailBuilderStateContext } from '../context';
import { copyBlock, createPlaceholder } from '../utils';
export function useEmailBuilderConfig() {
return useContext(EmailBuilderConfigContext);
}
export function useEmailBuilderState() {
return useContext(EmailBuilderStateContext);
}
export function useSetEmailBuilderState() {
return useContext(SetEmailBuilderStateContext);
}
export function useSelectedBlock() {
return useContext(EmailBuilderSelectedBlockInfoContext);
}
export function useBlockStyle(block) {
const { style } = block;
const fontStyle = useContext(EmailBuilderDefaultFontStyleContext);
return useMemo(() => {
const p = style.padding || [];
const u = undefined;
return {
...fontStyle,
backgroundColor: style.bgColor,
paddingTop: p[0] ?? u,
paddingRight: p[1] ?? u,
paddingBottom: p[2] ?? u,
paddingLeft: p[3] ?? u
};
}, [fontStyle, style]);
}
export function useBlockEditor(blockId) {
const setState = useSetEmailBuilderState();
return useCallback((mutate) => {
setState((prev) => ({
...prev,
blocks: prev.blocks.map((block) => {
if (block.id === blockId) {
return mutate(block);
}
if (block.type === 'columns') {
let columnsTouched = false;
const cols = block;
const nextColumns = {
...cols,
attrs: {
...cols.attrs,
columns: cols.attrs.columns.map((column) => {
let columnTouched = false;
const nextColumn = {
...column,
blocks: column.blocks.map((columnBlock) => {
if (columnBlock.id === blockId) {
columnTouched = true;
return mutate(columnBlock);
}
return columnBlock;
})
};
if (columnTouched) {
columnsTouched = true;
}
return columnTouched ? nextColumn : column;
})
}
};
return columnsTouched ? nextColumns : block;
}
return block;
})
}));
}, [setState, blockId]);
}
export function useBlockAttrsEditor(block) {
const setBlock = useBlockEditor(block.id);
const setAttrs = useCallback((attrs) => {
setBlock((prev) => ({
...prev,
attrs: {
...prev.attrs,
...attrs
}
}));
}, [setBlock]);
return setAttrs;
}
export function useDeleteBlock() {
const setState = useSetEmailBuilderState();
return useCallback((blockId) => {
setState((prev) => {
const filterBlocks = (blocks) => {
const newBlocks = [];
let touched = false;
blocks.forEach((block) => {
if (block.id === blockId) {
touched = true;
}
else {
let newBlock = block;
if (newBlock.type === 'columns') {
const cols = newBlock;
let columnsTouched = false;
newBlock = {
...cols,
attrs: {
...cols.attrs,
columns: cols.attrs.columns.map((column) => {
const newColumnBlocks = filterBlocks(column.blocks);
if (newColumnBlocks !== column.blocks) {
columnsTouched = true;
return {
...column,
blocks: newColumnBlocks
};
}
return column;
})
}
};
if (columnsTouched) {
touched = true;
}
else {
newBlock = block;
}
}
newBlocks.push(newBlock);
}
});
if (touched && !newBlocks.length) {
newBlocks.push(createPlaceholder());
}
return touched ? newBlocks : blocks;
};
const prevSelected = prev.selectedId === blockId;
return {
...prev,
blocks: filterBlocks(prev.blocks),
selectedId: prevSelected ? undefined : prev.selectedId,
dragoverId: prev.dragoverId === blockId ? undefined : prev.dragoverId,
tab: prevSelected && prev.tab === 'settings' ? 'blocks' : prev.tab
};
});
}, [setState]);
}
export function useMoveBlock() {
const setState = useSetEmailBuilderState();
return useCallback((blockId, direction) => {
setState((prev) => {
const move = (blocks) => {
const newBlocks = [...blocks];
const index = newBlocks.findIndex((block) => block.id === blockId);
if (index > -1) {
const max = newBlocks.length - 1;
let target = index + direction;
if (target < 0) {
target = 0;
}
if (target > max) {
target = max;
}
const old = newBlocks[target];
newBlocks[target] = newBlocks[index];
newBlocks[index] = old;
return newBlocks;
}
return blocks;
};
return {
...prev,
blocks: move(prev.blocks).map((block) => {
if (block.type === 'columns') {
const cols = block;
return {
...block,
attrs: {
...cols.attrs,
columns: cols.attrs.columns.map((column) => ({
...column,
blocks: move(column.blocks)
}))
}
};
}
return block;
})
};
});
}, [setState]);
}
export function useCopyBlock() {
const setState = useSetEmailBuilderState();
const config = useEmailBuilderConfig();
return useCallback((blockId) => {
setState((prev) => {
let copiedBlockId;
const copy = (blocks) => {
const newBlocks = [];
blocks.forEach((block) => {
newBlocks.push(block);
if (block.id === blockId) {
const copied = copyBlock(block, config);
copiedBlockId = copied.id;
newBlocks.push(copied);
}
});
return newBlocks;
};
const blocks = copy(prev.blocks).map((block) => {
if (block.type === 'columns') {
const cols = block;
return {
...cols,
attrs: {
...cols.attrs,
columns: cols.attrs.columns.map((col) => ({
...col,
blocks: copy(col.blocks)
}))
}
};
}
return block;
});
return {
...prev,
blocks,
selectedId: copiedBlockId
};
});
}, [setState, config]);
}