import { reverse, head } from "lodash"; import { CURSOR_ID } from "./types"; import { EditorObjectState } from "./components/EditorObject/types"; import { AddTypes } from "./components/EditorObject/EditorObject"; import { ComponentTypes } from "@sc/plugins/webcomponents/v2/types"; export const defaultCursorContent = { type: ComponentTypes.CURSOR, id: CURSOR_ID, parent: false, style: false, }; /** * Takes an object representing a new component and adds it immediately after an existing item in the content array * @params */ export const addThisAfterThat = ( incomingContent = [], item, id: string | number | boolean ) => { if (!id) return; let addedContent = []; // console.log({ item, label: item.label }); const recursivelyAddThisAfterThat = ( newItem?: any, afterId?: string | boolean, content?: any[] ) => { // 1. Get the position of the 'afterId' item const key = content.findIndex((itm) => itm.id === afterId); // 2. Remove any children from the new Item const { children, ...newItemWithoutChildren } = newItem; // 3. Generate a new Id for the new item const id = Math.random() .toString(36) .slice(2); // 4. Add the childless item to the content array that you will return for db updating addedContent = [ ...content.slice(0, key + 1), { ...newItemWithoutChildren, id, }, ...content.slice(key + 1), ]; // 5. Add any child items if there is any if (newItem.hasOwnProperty("children")) { // Loop through each child object const children = [...newItem.children].reverse(); // reverse(newItem.children); // const children = newItem.children; children.forEach((itm) => { // Set the previously generated id as the child object's parent const updatedItm = { ...itm, parent: id }; // Call the recursive function to update the added content array with our child object // ...or handle any children that my new item has addedContent = recursivelyAddThisAfterThat( updatedItm, id, addedContent ); }); } // console.log("after children update", id, this.addedContent); // this.changeState(id, "active"); // 6. Return the updated content array return addedContent; }; // Get content item const prevContent = incomingContent; // Establish parent for new item const object = prevContent.filter((itm) => itm.id === id); if (!object.length) return; const itemWithParent = { ...item, parent: object[0].parent, // state: "active", }; // console.log({ object, itemWithParent }); // Call recursive function to build a chain of items to add // as decendents of this object const theContent = recursivelyAddThisAfterThat( itemWithParent, id, prevContent ); // console.log({ addedContent, theContent }); // Now update the document with all the new items return theContent.filter((itm) => itm.id !== CURSOR_ID); }; let removedContent = []; export const recursivelyRemoveItem = (content, id) => { removedContent = content.filter((itm) => itm.id !== id); // everything without the item removedContent .filter((itm) => itm.parent === id) .forEach((itm) => { removedContent = recursivelyRemoveItem(removedContent, itm.id); }); return removedContent; }; /** * Removes an item from the content array * @params */ export const removeItem = (content, id) => { // const key = content.findIndex((itm) => itm.id === id); // if (content[key].parent) { // console.log(`Recursively removing ${id} from ${content[key].parent}`); // return recursivelyRemoveItem([], content, id); // } }; /** * Duplicates a specific item in the content array * @params */ export const duplicateItem = () => null; /** * Moves an item in the content array to another location in the content array * @param id1 The id of the content object (the source) that you want to move * @param id2 The id of the content object (the destination) that you want to move id1 under (or next to) * @param content The array of all the content in the page */ export const moveThisByThat = (content, id1, id2) => { // Cancel the move if the ids are the same if (id1 === id2) return; // Find where in the content array those items are const keyOfId1 = content.findIndex((itm) => itm.id === id1); const keyOfId2 = content.findIndex((itm) => itm.id === id2); const id1content = content[keyOfId1]; // Cancel if any of the id's do not exist if (keyOfId1 === -1 || keyOfId2 === -2) return; // Remove the first id const contentAfterId1IsRemoved = [ ...content.slice(0, keyOfId1), ...content.slice(keyOfId1 + 1), ]; // Add the first id after the second id // const parent = keyOfId2 > -2 ? content[keyOfId2].parent : false; const parent = false; const contentAfterId2IsAdded = [ ...contentAfterId1IsRemoved.slice(0, keyOfId2), { ...id1content, parent }, ...contentAfterId1IsRemoved.slice(keyOfId2), ]; // Remove any cursors and return the updated content return contentAfterId2IsAdded.filter((itm) => itm.id !== CURSOR_ID); }; export const addHoverCursor = ( content, id: string | boolean = false, addType ) => { if (!id) return; // console.log(content, id, addType); // 1. Remove all existing curosrs const contentWithoutCursors = content.filter((itm) => itm.id !== CURSOR_ID); // 2. Find the location in the content array where the item is const key = contentWithoutCursors.findIndex((itm) => itm.id === id); if (key === -1) return; const i = addType === AddTypes.AFTER ? 1 : 0; // 3. Add the cursor in that position const theCursorsParentId = addType === AddTypes.INSIDE ? id : contentWithoutCursors[key].parent; // const cursorContent = { ...defaultCursorContent, parent: theCursorsParentId }; // console.log(id, addType, i); // 4. Change the state of the parent container to be in a hover state // 5. Return the updated content // return contentWithoutCursors; return [ ...contentWithoutCursors.slice(0, key + i), defaultCursorContent, ...contentWithoutCursors.slice(key + i), ]; }; const bodyContent = { parent: false, state: "normal", id: "2540", properties: { height: "100%", }, type: "Body", canHaveChildren: true, }; export const generateContent = (content) => { const cursorContent = { ...defaultCursorContent, parent: bodyContent.id, }; return addThisAfterThat([bodyContent, cursorContent], content, CURSOR_ID); }; export const generateSections = (sections: any[] = []) => { const cursorContent = { ...defaultCursorContent, parent: bodyContent.id, }; const content = { ...bodyContent, children: sections }; let res = addThisAfterThat([bodyContent, cursorContent], content, CURSOR_ID); res[res.findIndex((itm) => itm.parent === bodyContent.id)].parent = false; const toRet = res.filter((itm) => itm.id !== bodyContent.id); // console.log("Returning", toRet); return toRet; }; /** * Updates the entire content array * @params */ export const updateContent = () => null; /** * Updates the properties object of an item in the content array * @params */ export const updateComponentStyle = () => null; /** * Updates an item in the content array * @params */ export const updateComponentSettings = () => null; /** * Sets the provided object to be the active object * @params */ export const setActiveObject = () => null; /** * Triggers an undo or a redo command * @params */ export const doUndoRedo = () => null; /** * Changes the state of the provided item and returns a modified content array * @params */ export const changeState = ( content: any[] = [], id: string | Boolean = false, state: EditorObjectState ) => { // if (!id) return; // return content.map((itm) => { // if ( // itm.state !== EditorObjectState.ACTIVE || // state === EditorObjectState.ACTIVE // ) // delete itm.state; // return itm.id === id ? { ...itm, state } : itm; // }); }; /** * Updates the width and height of the provided object * @params */ export const resizeObject = () => null; /** * A global way to update the editor's canDrag property * @params */ export const setCanDrag = () => null; /** * Provides a list of all the direct ancestors to a provided editor item * @params */ export const listAncestors = (content, id) => { if (!id) return; let ancestors = []; const generateAncestors = (providedContent, id) => { const i = providedContent.findIndex((c) => c.id === id); if (i > -1) { const parent = providedContent[i].parent; ancestors = [...ancestors, providedContent[i]]; if (parent) generateAncestors(providedContent, parent); return ancestors; } }; return generateAncestors(content, id); }; /** * Provides a list of all the direct children of a provided editor item * @params */ export const listChildren = (content: object[], id: string) => { if (!id) return; const generateChildren = (providedContent: object[], id: string) => { // get obj's children const children = providedContent.filter((itm) => itm.parent === id); return { ...head(providedContent.filter((itm) => itm.id === id)), children: [ ...children.map((itm) => generateChildren(providedContent, itm.id)), ], }; }; return generateChildren(content, id); }; /** * Generates a list of the current items in the editor. Including an icon to use when display. Also will return a filtered list of components if you provide the filter in [key, value] format (e.g. ['type', 'Body'] for filtering out the main body or ['canHaveChildren', true] for filtering only containers) * @params */ export const listComponents = () => null;