Skip to content

[WEB-8] [EDITOR] Basic Delete Block Functionality #341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
30 changes: 30 additions & 0 deletions frontend/src/cse-ui-kit/DeleteBlock_button/DeleteBlock-Styled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import styled from "styled-components";

export type buttonProps = {
background?: string;
isFocused?: boolean
};
export const StyledButton = styled.div<buttonProps>`
width: 45px;
height: 45px;
margin: 5px;
background: ${(props) => props.background};

display: ${(props) => props.isFocused ? "flex" : "none"};
justify-content: center;
align-items: center;
border-radius: 30px;
user-select: none;

&:hover {
background: #efeef3;
color: black;
transform: scale(1.04);
}
&:active {
background: #c8d1fa;
transform: scale(0.96);
}

cursor: pointer;
`;
31 changes: 31 additions & 0 deletions frontend/src/cse-ui-kit/DeleteBlock_button/DeleteBlock.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import DeleteBlock from './DeleteBlock';

export default {
title: 'CSE-UIKIT/DeleteBlockButton',
component: DeleteBlock,

argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof DeleteBlock>;

const Template: ComponentStory<typeof DeleteBlock> = (args) =>
(
<div
style={{
margin: "30px"
}}
>
Delete Button
<DeleteBlock {...args}></DeleteBlock>
</div>
)

export const Primary = Template.bind({});
Primary.args = {
background: "#FFF",
isFocused: true
}
19 changes: 19 additions & 0 deletions frontend/src/cse-ui-kit/DeleteBlock_button/DeleteBlock.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can move this entire component into frontend/src/cse-ui-kit/DeleteBlock_button/index.ts That way there isn't a need for an empty file with a singular import :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this doesn't work since index.ts is a ts file instead of a tsx file :/

All of the existing Button components in cse-ui-kit (eg CreateContentBlock, CreateHeadingBlock) have an similar index.ts file with a single import.

Should I change the file to index.tsx? Or keep it consistent with the other button components?

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { MouseEventHandler } from "react";
import { StyledButton, buttonProps } from "./DeleteBlock-Styled";
import { AiOutlineClose } from "react-icons/ai";

type Props = {
onClick?: MouseEventHandler<HTMLDivElement>;
} & buttonProps;

export default function DeleteBlock({ onClick, ...styleProps }: Props) {
return (
<StyledButton
data-anchor="DeleteBlockButton"
onClick={onClick}
{...styleProps}
>
<AiOutlineClose />
</StyledButton>
);
}
3 changes: 3 additions & 0 deletions frontend/src/cse-ui-kit/DeleteBlock_button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import DeleteBlock from './DeleteBlock';

export default DeleteBlock;
2 changes: 1 addition & 1 deletion frontend/src/packages/editor/componentFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const constructors: Record<string, (props: CMSBlockProps) => JSX.Element> = {
export const buildComponentFactory = (opManager: OperationManager, onClick: (id: number) => void, onUpdate: UpdateCallback) => (block: BlockData, blockId: number, isFocused: boolean) : JSX.Element => {
const componentProps = {
id: blockId,
key: blockId,
key: block[0].key,
showToolBar: isFocused,
initialValue: block,
update: buildUpdateHandler(blockId, opManager, onUpdate),
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/packages/editor/components/EditorBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const EditorBlock: FC<CMSBlockProps> = ({
textSize: leaf.textSize ?? defaultTextSize,
...attributes
}

return leaf.align == null
? <Text {...props}>{children}</Text>
: <AlignedText {...props}>{children}</AlignedText>;
Expand Down
50 changes: 47 additions & 3 deletions frontend/src/packages/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BlockData, UpdateCallback } from "./types";
import CreateContentBlock from "src/cse-ui-kit/CreateContentBlock_button";
import CreateHeadingBlock from "src/cse-ui-kit/CreateHeadingBlock_button";
import SyncDocument from "src/cse-ui-kit/SyncDocument_button";
import DeleteBlock from "src/cse-ui-kit/DeleteBlock_button";
import PublishDocument from "src/cse-ui-kit/PublishDocument_button";
import EditorHeader from "src/deprecated/components/Editor/EditorHeader";
import { useParams } from "react-router-dom";
Expand All @@ -16,6 +17,7 @@ import { OperationManager } from "./operationManager";
import { publishDocument } from "./api/cmsFS/volumes";
import { CMSOperation } from "./api/OTClient/operation";


const Container = styled.div`
display: flex;
flex-direction: column;
Expand All @@ -26,6 +28,16 @@ const InsertContentWrapper = styled.div`
display: flex;
`;

const BlockWrapper = styled.div`
display: flex;
width: fit-content;
align-items: center;
`;

const BlockContainer = styled.div`
min-width: 640px;
`;

const EditorPage: FC = () => {
const { id } = useParams();
const wsClient = useRef<Client | null>(null);
Expand Down Expand Up @@ -72,13 +84,25 @@ const EditorPage: FC = () => {

// buildClickHandler builds handlers for events where new blocks are created and propagates them to the OT manager
const buildButtonClickHandler = (type: "heading" | "paragraph") => () => {
const newElement = { type: type, children: [{ text: "" }] };
// TODO: More robust key creation; Math.random is unique enough for its purpose, but it still feels a little clunky
const newElement = { type: type, key: Math.random().toString(), children: [{ text: "" }] };
console.log(newElement.key);

// push and update this creation operation to the operation manager
setBlocks((prev) => [...prev, [newElement]]);
setFocusedId(blocks.length);
opManager.current?.pushToServer(newCreationOperation(newElement, blocks.length));
}
}


const deleteButtonClickHandler = (idx : number) => () => {
setFocusedId(-1);
const newBlocks = [...blocks];
newBlocks.splice(idx, 1);
setBlocks(newBlocks);

// TODO OT Client integration
}

return (
<div style={{ height: "100%" }}>
Expand All @@ -87,7 +111,15 @@ const EditorPage: FC = () => {
<PublishDocument onClick={() => publishDocument(id ?? "")} />
</EditorHeader>
<Container>
{blocks.map((block, idx) => createBlock(block, idx, focusedId === idx))}
{blocks.map((block, idx) =>
<BlockWrapper key={idx}>
<DeleteBlock onClick={deleteButtonClickHandler(idx)} isFocused={focusedId == idx}/>
<BlockContainer>
{createBlock(block, idx, focusedId === idx)}
</BlockContainer>
</BlockWrapper>
)
}
<InsertContentWrapper>
<CreateHeadingBlock onClick={buildButtonClickHandler("heading")} />
<CreateContentBlock onClick={buildButtonClickHandler("paragraph")} />
Expand All @@ -108,4 +140,16 @@ const newCreationOperation = (newValue: any, index: number): CMSOperation => ({
}
});

// constructs a new deletion operation in response to the deletion of an existing block
// TODO: implement me :D
const deletionOperation = (index: number): CMSOperation => ({
Path: [index],
OperationType: "delete",
IsNoOp: false,
Operation: {
"$type": "noop",
noop: {},
}
});

export default EditorPage;
4 changes: 3 additions & 1 deletion frontend/src/packages/editor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ export type BlockData = Descendant[];
export type OpPropagator = (id: number, update: BlockData, operation: BaseOperation[]) => void;
export type UpdateCallback = (id: number, update: BlockData) => void;

type CustomElement = { type: "paragraph" | "heading"; children: CustomText[] };
type CustomElement = { type: "paragraph" | "heading"; key?: string; children: CustomText[] };
export type CustomText = {
key?: string;
textSize?: number;
text: string;
bold?: boolean;
Expand All @@ -23,6 +24,7 @@ export interface CMSBlockProps {
id: number;
showToolBar: boolean;
onEditorClick: () => void;
key?: string;
}


Expand Down
Loading