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 = "Start editing your document here...";
    }
    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}>NewButton>
            <Button icon="folder-open" onClick={handleOpen}>OpenButton>
            <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')}>BButton>
            Tooltip>
            <Tooltip position="top" anchorElement="target" content="Italic (Ctrl+I)">
              <Button icon="italic" onClick={() => handleFormatting('italic')}>IButton>
            Tooltip>
            <Tooltip position="top" anchorElement="target" content="Underline (Ctrl+U)">
              <Button icon="underline" onClick={() => handleFormatting('underline')}>UButton>
            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>Helpstrong>
              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}>OKButton>
            DialogActionsBar>
          Dialog>
        )}
      div>
    AdaptiveModeContext.Provider>
  );
};

export default App;

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