DEV Community

Cover image for Microsoft Word–KendoReact
Pranav jana
Pranav jana

Posted on

Microsoft Word–KendoReact

This is a submission for the KendoReact Free Components Challenge.

This project is a simple Microsoft Word–like clone built with React and KendoReact’s free components.

It demonstrates a basic text editor with functionalities such as:

[1] File Operations: New, Open, and Save (with a simulated save dialog).

[2] Text Formatting: Bold, Italic, and Underline commands.

[3] UI Components: A rich interface including Toolbars, ButtonGroups, Floating Action Button, Popover (for help), Skeleton (for loading state), Badge (to indicate unsaved changes), Chip/ChipList (for page indicator), and a few others.

[4] Animations & Typography: Used for enhanced UI/UX.

Components Used

The following free KendoReact components have been used:

A> Toolbar & ButtonGroup: For file operations and formatting commands.

B) Buttons & FloatingActionButton: To trigger actions.

C) Tooltip & Popover: For providing helpful hints.

D} Animation: To add visual transitions.

E) Skeleton & ProgressBar: To simulate loading and action feedback.

F} Chip & ChipList: To display document metadata (e.g., page indicator).

G] Dialogs: For save confirmation.

H] Typography: For text display.

I) AdaptiveModeContext: To ensure adaptive UI behaviors.

Project Structure

App.tsx: Contains the complete React component that renders the Word clone. It uses a contenteditable div for text editing and integrates various KendoReact components.

☑️ README.md: Provides an overview of the project, installation instructions, and component details.

App.tsx

import React, { useRef, useState, useEffect } from 'react';
import { Button, ButtonGroup, FloatingActionButton, Chip, ChipList } from '@progress/kendo-react-buttons';
import { Toolbar } from '@progress/kendo-react-toolbar';
import { Dialog, DialogActionsBar } from '@progress/kendo-react-dialogs';
import { Tooltip } from '@progress/kendo-react-tooltip';
import { Animation } from '@progress/kendo-react-animation';
import { Typography } from '@progress/kendo-react-common';
import { Skeleton, ProgressBar } from '@progress/kendo-react-indicators';
import { AdaptiveModeContext } from '@progress/kendo-react-layout';
import { Popover } from '@progress/kendo-react-popover';
import '@progress/kendo-theme-default/dist/all.css';

const App = () => {
  const editorRef = useRef<HTMLDivElement>(null);
  const fabRef = useRef<HTMLButtonElement>(null);

  // States for document title, unsaved changes, loading, save dialog, and help popover.
  const [fileName, setFileName] = useState("Untitled Document");
  const [unsavedChanges, setUnsavedChanges] = useState(0);
  const [isLoading, setIsLoading] = useState(true);
  const [showSaveDialog, setShowSaveDialog] = useState(false);
  const [showHelpPopover, setShowHelpPopover] = useState(false);

  // Simulate a loading state on startup.
  useEffect(() => {
    const timer = setTimeout(() => setIsLoading(false), 1000);
    return () => clearTimeout(timer);
  }, []);

  // Execute document command for rich text editing.
  const execCommand = (command: string, value?: any) => {
    document.execCommand(command, false, value);
  };

  // Focus the editor and run the command.
  const handleFormatting = (command: string) => {
    if (editorRef.current) {
      editorRef.current.focus();
      execCommand(command);
      setUnsavedChanges(unsavedChanges + 1);
    }
  };

  // Handlers for file operations.
  const handleNew = () => {
    if (editorRef.current) {
      editorRef.current.innerHTML = "<p>Start editing your document here...</p>";
    }
    setFileName("Untitled Document");
    setUnsavedChanges(0);
  };

  const handleOpen = () => {
    // For demo purposes, we simply alert.
    alert("Open functionality is not implemented in this demo.");
  };

  const handleSave = () => {
    // In a real app you might serialize the document here.
    setShowSaveDialog(true);
    setUnsavedChanges(0);
  };

  const closeSaveDialog = () => {
    setShowSaveDialog(false);
  };

  // Toggle help popover visibility.
  const toggleHelpPopover = () => {
    setShowHelpPopover(!showHelpPopover);
  };

  // Track any text changes in the editor.
  const handleEditorInput = () => {
    setUnsavedChanges(prev => prev + 1);
  };

  return (
    <AdaptiveModeContext.Provider value="desktop">
      <div style={{ margin: '20px' }}>
        {/* App title */}
        <Typography variant="h4" style={{ marginBottom: '10px' }}>
          Word Clone - {fileName}
        </Typography>

        {/* Main toolbar with file and formatting commands */}
        <Toolbar>
          <ButtonGroup>
            <Button icon="file" onClick={handleNew}>New</Button>
            <Button icon="folder-open" onClick={handleOpen}>Open</Button>
            <Button icon="save" onClick={handleSave}>
              {/* Badge to indicate unsaved changes */}
              {unsavedChanges > 0 && (
                <span style={{
                  background: 'red',
                  borderRadius: '50%',
                  color: 'white',
                  fontSize: '10px',
                  padding: '2px 6px',
                  position: 'absolute',
                  top: '5px',
                  right: '5px'
                }}>
                  {unsavedChanges}
                </span>
              )}
              Save
            </Button>
          </ButtonGroup>
          <span style={{ marginLeft: '20px' }}></span>
          <ButtonGroup>
            <Tooltip position="top" anchorElement="target" content="Bold (Ctrl+B)">
              <Button icon="bold" onClick={() => handleFormatting('bold')}>B</Button>
            </Tooltip>
            <Tooltip position="top" anchorElement="target" content="Italic (Ctrl+I)">
              <Button icon="italic" onClick={() => handleFormatting('italic')}>I</Button>
            </Tooltip>
            <Tooltip position="top" anchorElement="target" content="Underline (Ctrl+U)">
              <Button icon="underline" onClick={() => handleFormatting('underline')}>U</Button>
            </Tooltip>
          </ButtonGroup>
        </Toolbar>

        {/* Animated text editor area */}
        <Animation>
          {isLoading ? (
            <Skeleton shape="rectangular" width="100%" height="300px" />
          ) : (
            <div
              ref={editorRef}
              contentEditable
              onInput={handleEditorInput}
              style={{
                border: '1px solid #ccc',
                minHeight: '300px',
                padding: '10px',
                marginTop: '10px'
              }}
            >
              <p>Start editing your document here...</p>
            </div>
          )}
        </Animation>

        {/* ChipList showing a page indicator */}
        <div style={{ marginTop: '10px' }}>
          <ChipList>
            <Chip text="Page 1" />
          </ChipList>
        </div>

        {/* Floating Action Button to toggle help popover */}
        <FloatingActionButton
          ref={fabRef}
          icon="help"
          style={{ position: 'fixed', bottom: '20px', right: '20px' }}
          onClick={toggleHelpPopover}
        />

        {/* Popover for help information */}
        {showHelpPopover && fabRef.current && (
          <Popover
            anchor={fabRef.current}
            align={{ vertical: 'top', horizontal: 'center' }}
            collision={{ horizontal: 'flip', vertical: 'flip' }}
            onClose={() => setShowHelpPopover(false)}
          >
            <div style={{ padding: '10px' }}>
              <Typography variant="subtitle1">
                <strong>Help</strong>
              </Typography>
              <Typography variant="body1">
                Use the toolbar to create a new document, open or save your work.
                Select text and click Bold, Italic, or Underline to format.
              </Typography>
            </div>
          </Popover>
        )}

        {/* Save confirmation dialog */}
        {showSaveDialog && (
          <Dialog title="Save Document" onClose={closeSaveDialog}>
            <div style={{ margin: '10px' }}>
              <Typography variant="body1">
                Your document has been saved successfully.
              </Typography>
              {/* Example of a ProgressBar used inside the dialog */}
              <div style={{ marginTop: '10px' }}>
                <ProgressBar value={100} animation={{ duration: 300 }} />
              </div>
            </div>
            <DialogActionsBar>
              <Button onClick={closeSaveDialog}>OK</Button>
            </DialogActionsBar>
          </Dialog>
        )}
      </div>
    </AdaptiveModeContext.Provider>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Enjoy experimenting with your Word clone, and fork to expand the functionality further!

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay