<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nouran Samy</title>
    <description>The latest articles on Forem by Nouran Samy (@nouran96).</description>
    <link>https://forem.com/nouran96</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F578261%2Fad66afde-4eee-49be-b17f-e0430c4e7744.png</url>
      <title>Forem: Nouran Samy</title>
      <link>https://forem.com/nouran96</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/nouran96"/>
    <language>en</language>
    <item>
      <title>Render Tree View Recursively in React &amp; GraphQL</title>
      <dc:creator>Nouran Samy</dc:creator>
      <pubDate>Wed, 01 Dec 2021 19:09:12 +0000</pubDate>
      <link>https://forem.com/nouran96/render-tree-view-recursively-in-react-graphql-41gd</link>
      <guid>https://forem.com/nouran96/render-tree-view-recursively-in-react-graphql-41gd</guid>
      <description>&lt;p&gt;If you want to render a multilevel Tree View Component in React dynamically not caring about how many levels there will be, so you will need to use &lt;strong&gt;Recursion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you need to know what recursion is, you should check out &lt;a href="https://www.geeksforgeeks.org/recursion/"&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article will be using the following packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://mui.com/components/tree-view/#basic-tree-view"&gt;Material UI&lt;/a&gt; =&amp;gt; Tree View Component UI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt; and &lt;a href="https://www.apollographql.com/docs/react/"&gt;Apollo Client&lt;/a&gt; =&amp;gt; Fetch data from &lt;a href="https://www.back4app.com/database/back4app/list-of-all-continents-countries-cities/get-started/javascript/rest-api/fetch"&gt;back4app&lt;/a&gt; database&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install Packages
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npm install @mui/lab @mui/material @mui/icons-material @apollo/client graphql&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Apollo Client Setup
&lt;/h2&gt;

&lt;p&gt;In your index.js, you need to wrap your Components with &lt;code&gt;ApolloProvider&lt;/code&gt; to be available in all you app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  createHttpLink,
} from "@apollo/client";
import { setContext } from "@apollo/client/link/context";

// URI for graphql API on back4app
const httpLink = createHttpLink({
  uri: "https://parseapi.back4app.com/graphql",
});

const headersLink = setContext((_, { headers }) =&amp;gt; {
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      // These keys are found when you create app on back4app
      "X-Parse-Application-Id": "&amp;lt;YOUR_APPLICATION_ID&amp;gt;",
      "X-Parse-Master-Key": "&amp;lt;YOUR_MASTER_KEY&amp;gt;",
      "X-Parse-REST-API-Key": "&amp;lt;YOUR_REST_API_KEY&amp;gt;",
    },
  };
});

const client = new ApolloClient({
  link: headersLink.concat(httpLink),
  cache: new InMemoryCache(),
});

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;ApolloProvider client={client}&amp;gt;
        &amp;lt;App /&amp;gt;
    &amp;lt;/ApolloProvider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById("root")
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prepare you GraphQL Queries
&lt;/h2&gt;

&lt;p&gt;You now need to prepare the queries for the api you're using. I will be using the ContinentsCountriesCities Database on back4app which will provide proper nesting for this tutorial.&lt;/p&gt;

&lt;p&gt;So the queries for the continents, countries and cities will be as follows (You can check the Graphql API playground in your app with docs about queries details)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { gql } from "@apollo/client";

export const GET_CONTINENTS = gql`
  query allContinents {
    data: continentscountriescities_Continents {
      count
      results: edges {
        node {
          objectId
          name
          children: countries {
            count
          }
        }
      }
    }
  }
`;

export const GET_COUNTRIES = gql`
  query allCountries($continentId: ID) {
    data: continentscountriescities_Countries(
      where: { continent: { have: { objectId: { equalTo: $continentId } } } }
    ) {
      count
      results: edges {
        node {
          objectId
          name
          children: cities {
            count
          }
        }
      }
    }
  }
`;

export const GET_CITIES = gql`
  query allCities($countryId: ID) {
    data: continentscountriescities_Cities(
      where: { country: { have: { objectId: { equalTo: $countryId } } } }
    ) {
      count
      results: edges {
        node {
          objectId
          name
        }
      }
    }
  }
`;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;gql&lt;/code&gt; String literal provided by apollo client will help in the validation of your query against the main schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tree View UI
&lt;/h2&gt;

&lt;p&gt;We can use the basic tree view in material ui, but we need to give it custom content to handle the fetching of data on TreeItem click.&lt;/p&gt;

&lt;p&gt;So our &lt;code&gt;CustomTreeItem&lt;/code&gt; would look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect } from "react";
import clsx from "clsx";
import { CircularProgress, Typography } from "@mui/material";
import TreeItem, { useTreeItem } from "@mui/lab/TreeItem";
import { useLazyQuery } from "@apollo/client";
import { GET_COUNTRIES, GET_CITIES } from "../../utils/Queries";

const CustomContent = React.forwardRef(function CustomContent(
  props,
  ref
) {
  // TreeItemContentProps + typename + appendNewData props
  const {
    classes,
    className,
    label,
    nodeId,
    icon: iconProp,
    expansionIcon,
    displayIcon,
    typename,
    appendNewData,
  } = props;

   // Extract last part from Typename key of node from graphql
  // Ex: Continentscountriescities_Country =&amp;gt; Country
  const type: string = typename?.split("_")[1] || "";

  let lazyQueryParams = {};

  // Add lazyQueryParams according to type of node
  switch (type) {
    case "Continent":
      lazyQueryParams = {
        query: GET_COUNTRIES,
        variableName: "continentId",
      };
      break;
    case "Country":
      lazyQueryParams = {
        query: GET_CITIES,
        variableName: "countryId",
      };
      break;
    default:
      lazyQueryParams = {
        query: GET_COUNTRIES,
        variableName: "continentId",
      };
      break;
  }

  // Lazy query for getting children of this node
  const [getChildren, { loading, data }] = useLazyQuery(
    lazyQueryParams?.query,
    {
      variables: { [lazyQueryParams?.variableName]: nodeId },
    }
  );

  const { disabled, expanded, selected, focused, handleExpansion } =
    useTreeItem(nodeId);

  const icon = iconProp || expansionIcon || displayIcon;

  // Append new children to node
  useEffect(() =&amp;gt; {
    if (data?.data?.results &amp;amp;&amp;amp; appendNewData) {
      appendNewData(nodeId, data.data?.results || []);
    }
  }, [data]);

  const handleExpansionClick = (event) =&amp;gt; {
    // Fetch data only once
    if (!data) {
      getChildren();
    }

    handleExpansion(event);
  };

  return (
    &amp;lt;div
      className={clsx(className, classes.root, {
        [classes.expanded]: expanded,
        [classes.selected]: selected,
        [classes.focused]: focused,
        [classes.disabled]: disabled,
      })}
      onClick={handleExpansionClick}
      ref={ref}
    &amp;gt;
      &amp;lt;div className={classes.iconContainer}&amp;gt;{icon}&amp;lt;/div&amp;gt;
      &amp;lt;Typography component="div" className={classes.label}&amp;gt;
        {label}
      &amp;lt;/Typography&amp;gt;

    &amp;lt;/div&amp;gt;
  );
});

const CustomTreeItem = (props) =&amp;gt; {
  return (
    &amp;lt;TreeItem
      ContentComponent={CustomContent}
      // These props will be sent from the parent
      ContentProps={
        { typename: props.typename, appendNewData: props.appendNewData } as any
      }
      {...props}
    /&amp;gt;
  );
};

export default CustomTreeItem;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses the queries we made above and with the &lt;code&gt;useLazyQuery&lt;/code&gt; hook from apollo client, we have a method &lt;code&gt;getChildren()&lt;/code&gt; (or any other name) to be called wherever and whenever we need in the component. So we are calling this method on the &lt;code&gt;handleExpansionClick&lt;/code&gt; method and checking if the data is not already fetched.&lt;/p&gt;

&lt;p&gt;And we are switching the type of the node we are rendering to decide which query to call in the hierarchy.&lt;/p&gt;

&lt;p&gt;Now for the parent component of rendering the tree, It will render the continents data by default on first render and it will have the recursive function that will append the new children data fetched into the main array. For this to happen, all of our queries should have a fixed structure as above.&lt;/p&gt;

&lt;p&gt;The parent component will look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect, useState } from "react";
import { useQuery } from "@apollo/client";
import TreeView from "@mui/lab/TreeView";
import { CircularProgress } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import { GET_CONTINENTS } from "../../utils/Queries";
import CustomTreeItem from "../CustomTreeItem";
import { getModifiedData } from "../../utils/Shared";

const Tree = () =&amp;gt; {
  // Get all continents on first render
  const { loading, data: allContinents } = useQuery(GET_CONTINENTS);
  // Data to render all tree items from
  const [treeItemsData, setTreeItemsData] = useState([]);

  // Set treeItemsData with continents recieved
  useEffect(() =&amp;gt; {
    if (allContinents?.data?.results) {
      setTreeItemsData(allContinents?.data?.results);
    }
  }, [allContinents]);

  // Add new data in its correct place in treeItemsData array
  const appendNewData = (nodeId, data) =&amp;gt; {
    const treeItemsDataClone = JSON.parse(JSON.stringify(treeItemsData)); // Deep Copy

    // getModifiedData is the recursive function (will be shown below alone)
    const newData = getModifiedData(treeItemsDataClone, nodeId, data);

    setTreeItemsData(newData); // set the rendered array with the modified array
  };

  // Render children items recursively
  const renderChild = (node) =&amp;gt; {
    return (
      &amp;lt;CustomTreeItem
        key={node.objectId}
        classes={{ content: styles.treeItemContent }}
        typename={node.__typename}
        appendNewData={appendNewData}
        nodeId={node.objectId}
        label={node.name}
      &amp;gt;
        {/* If children is an object with a count key &amp;gt; 0, render a dummy treeItem to show expand icon on parent node */}
        {node.children &amp;amp;&amp;amp;
          (node.children.count &amp;gt; 0 ? (
            &amp;lt;CustomTreeItem nodeId="1" /&amp;gt;
          ) : (
            node.children.length &amp;amp;&amp;amp;
            node.children.map((child: any) =&amp;gt; renderChild(child.node)) // Recursively rendering children if array is found
          ))}
      &amp;lt;/CustomTreeItem&amp;gt;
    );
  };

  // Show a loader until query resolve
  if (loading) return &amp;lt;CircularProgress /&amp;gt;;
  else if (allContinents)
    return (
      &amp;lt;TreeView
        defaultCollapseIcon={&amp;lt;ExpandMoreIcon /&amp;gt;}
        defaultExpandIcon={&amp;lt;ChevronRightIcon /&amp;gt;}
        sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
      &amp;gt;
        {treeItemsData.map((continent: any) =&amp;gt; {
          return renderChild(continent.node);
        })}
      &amp;lt;/TreeView&amp;gt;
    );
  else return &amp;lt;&amp;gt;&amp;lt;/&amp;gt;;
};

export default Tree;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for the recursive function, It takes the following parameters: the original array, the node id to find and insert new data in it and the new data to insert.&lt;/p&gt;

&lt;p&gt;This function was found &lt;a href="https://stackoverflow.com/a/15524326"&gt;here&lt;/a&gt; but was customized for our specific requirements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
    Original Answer: https://stackoverflow.com/a/15524326
    @Description: Searches for a specific object in nested objects or arrays according to "objectId" key
    @Params: originalData =&amp;gt; The original array or object to search in
             nodeId =&amp;gt; the id to compare to objectId field
             dataToBeAdded =&amp;gt; new data to be added ad children to found node
    @Returns: Modified original data
  */
export const getModifiedData = (
  originalData: any,
  nodeId: string,
  dataToBeAdded: any
) =&amp;gt; {
  let result = null;
  const originalDataCopy = JSON.parse(JSON.stringify(originalData)); // Deep copy

  if (originalData instanceof Array) {
    for (let i = 0; i &amp;lt; originalDataCopy.length; i++) {
      result = getModifiedData(originalDataCopy[i], nodeId, dataToBeAdded);

      if (result) {
        originalDataCopy[i] = result;
      }
    }
  } else {
    for (let prop in originalDataCopy) {
      if (prop === "objectId") {
        if (originalDataCopy[prop] === nodeId) {
          originalDataCopy.children = dataToBeAdded;
          return originalDataCopy;
        }
      }

      if (
        originalDataCopy[prop] instanceof Object ||
        originalDataCopy[prop] instanceof Array
      ) {
        result = getModifiedData(originalDataCopy[prop], nodeId, dataToBeAdded);
        if (result) {
          originalDataCopy[prop] = result;
          break;
        }
      }
    }
  }

  return originalDataCopy;
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will return the modified array to be set in the state easily.&lt;/p&gt;

&lt;p&gt;Sorry for the long code snippets but it is somewhat complex and I wanted to expose all of the code. Working with back4app database and graphql in react wasn't that clear in the docs so I wanted to provide these steps as well.&lt;/p&gt;

&lt;p&gt;Hope this article will help someone implementing a similar feature.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
    </item>
    <item>
      <title>React TinyMCE Editor with noneditable plugin</title>
      <dc:creator>Nouran Samy</dc:creator>
      <pubDate>Wed, 25 Aug 2021 13:39:18 +0000</pubDate>
      <link>https://forem.com/nouran96/react-tinymce-editor-with-noneditable-plugin-27j3</link>
      <guid>https://forem.com/nouran96/react-tinymce-editor-with-noneditable-plugin-27j3</guid>
      <description>&lt;p&gt;Trying to find a nice editor to integrate with in your application is not an easy task. You need a highly customizable package regarding both toolbar and main editor design.&lt;/p&gt;

&lt;p&gt;I also had a feature to implement which needed some non editable (non deletable) text inside the editor wrapping the main content.&lt;/p&gt;

&lt;p&gt;For this case, i decided to use TinyMCE editor which have many plugins including noneditable plugin(but still deletable). We will see the workaround for this issue in this tutorial.&lt;/p&gt;

&lt;p&gt;TinyMCE is a free editor with some amazing opensource plugins but also contains some premium plugins that are not free.&lt;/p&gt;

&lt;p&gt;To start with, we need to install TinyMCE react package.&lt;br&gt;
&lt;code&gt;npm install --save @tinymce/tinymce-react&lt;/code&gt;&lt;br&gt;
You can find the Quick start guide in &lt;a href="https://www.tiny.cloud/docs/integrations/react/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we will add the Editor Component with its main configurations.&lt;/p&gt;

&lt;p&gt;The editor component looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useRef } from "react";
import { Editor } from "@tinymce/tinymce-react";

const editorRef = useRef(null);
const handleEditorChange = (value) =&amp;gt; {
    // value is in HTML format not text
    console.log(value);
};

&amp;lt;Editor
    apiKey="&amp;lt;API_KEY&amp;gt;"
    onInit={(evt, editor) =&amp;gt; (editorRef.current = editor)}
    onEditorChange={handleEditorChange}
/&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get the api key with a free account. It is not mandatory but it will hide an annoying warning above the editor.&lt;/p&gt;

&lt;p&gt;Let's start adding our configurations&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Editor
    ...
    init={{
          menubar: false, // remove menubar if not needed
          toolbar:
            "styleselect| bold italic underline | alignleft aligncenter alignright alignjustify", // add custom buttons for your toolbar
          style_formats: [
            { title: "H1", block: "h1" },
            { title: "H2", block: "h2" },
            { title: "H3", block: "h3" },
            { title: "H4", block: "h4" },
            { title: "H5", block: "h5" },
            { title: "H6", block: "h6" },
            { title: "Paragraph", block: "p" },
          ], // customize the styleselect dropdown in toolbar with only these
          branding: false, // remove tinymce branding
          resize: false, // disallow editor resize
          statusbar: false, // remove bottom status bar
          plugins: "noneditable", // add the noneditable plugin
          content_css: "material-outline",
          skin: "material-outline", // use the material ui theme
          height: 450, // Editor height
        }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We some what customized our editor. But we need to add the non deletable functionality for noneditable plugin.&lt;/p&gt;

&lt;p&gt;For this we need to add initial value for the editor with our non editable content as html. The default css class for noneditable plugin is &lt;code&gt;mceNonEditable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So we're adding this class to our content and applying a function called on pressing backspace or delete button to check on these content and not delete them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Editor 
   ...
   initialValue=`&amp;lt;p 
       class="mceNonEditable"&amp;gt;Non editable content 
      &amp;lt;/p&amp;gt;
       &amp;lt;p&amp;gt;
       &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;/p&amp;gt;
     &amp;lt;p class="mceNonEditable"&amp;gt;Non editable content&amp;lt;/p&amp;gt;`
   init={{
     ...,
     setup: function (editor) {
            editor.on("keydown", function (event) {
              if (event.keyCode === 8 || event.keyCode === 46) {
                const currentLine = editor.selection.getNode();

                // Prevent deletion of non editable text
                if (currentLine.hasAttribute("data-mce-bogus") || currentLine.className.includes("mceNonEditable")) {
                  event.preventDefault();
                  event.stopPropagation();
                  return false;
                }
              }
            });
          },
   }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The initial value attribute takes content as html string with mceNonEditable class. To add multiple non editable contents with space between them, you can use &lt;br&gt; tags as shown above. The setup handler checks for backspace and delete keys and prevent default behavior if cursor is on the line of the non editable content.&lt;/p&gt;

&lt;p&gt;That's it. Now you have an editor with non editable content. You can change styles and add other features and many more. You can find more details from the &lt;a href="https://www.tiny.cloud/docs/"&gt;docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope this quick tutorial is helpful for anyone stuck with an editor and wants to get going fast.&lt;/p&gt;

</description>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Google Calendar Events with React</title>
      <dc:creator>Nouran Samy</dc:creator>
      <pubDate>Thu, 17 Jun 2021 13:36:40 +0000</pubDate>
      <link>https://forem.com/nouran96/google-calendar-events-with-react-544g</link>
      <guid>https://forem.com/nouran96/google-calendar-events-with-react-544g</guid>
      <description>&lt;p&gt;This article will cover the topic of viewing a full calendar in your &lt;strong&gt;React&lt;/strong&gt; application and integrate with &lt;strong&gt;Google Calendar API&lt;/strong&gt; to show events from your account.&lt;/p&gt;

&lt;p&gt;First of all, we need a package that provides a full calendar UI. So we will &lt;a href="https://fullcalendar.io/docs"&gt;Full Calendar Package&lt;/a&gt;. This package is very simple and has many features as localization, different views, theme customizations and more.&lt;/p&gt;

&lt;p&gt;First steps would be to install the package in your react app and initialize an &lt;strong&gt;API_KEY&lt;/strong&gt; and &lt;strong&gt;CLIENT_ID&lt;/strong&gt; in your Google Console to use in Google Calendar APIs.&lt;/p&gt;

&lt;p&gt;In your react component, Add the scopes needed by google calendar for user authorization and showing calendar events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const SCOPES =
  "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple scopes can be added separated by spaces.&lt;/p&gt;

&lt;p&gt;Second, you must add google Api script in your application on component mount like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const [events, setEvents] = useState(null);

  useEffect(() =&amp;gt; {
    const script = document.createElement("script");
    script.async = true;
    script.defer = true;
    script.src = "https://apis.google.com/js/api.js";

    document.body.appendChild(script);

    script.addEventListener("load", () =&amp;gt; {
      if (window.gapi) handleClientLoad();
    });
  }, []);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This last part ensures that the authorization popup will show only when script is fully loaded and google api is ready.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;handleClientLoad()&lt;/code&gt; function loads the Auth2 library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const handleClientLoad = () =&amp;gt; {
    window.gapi.load("client:auth2", initClient);
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Th &lt;code&gt;initClient()&lt;/code&gt; function will initialize the API client library and set up sign in state listeners.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const openSignInPopup = () =&amp;gt; {
   window.gapi.auth2.authorize(
              { client_id: CLIENT_ID, scope: SCOPES },
              (res) =&amp;gt; {
                if (res) {
                  if (res.access_token)
                    localStorage.setItem("access_token", res.access_token);

                  // Load calendar events after authentication
                  window.gapi.client.load("calendar", "v3", listUpcomingEvents);
                }
              }
            );
}  

const initClient = () =&amp;gt; {
    if (!localStorage.getItem("access_token")) {
      openSignInPopup();
    } else {
      // Get events if access token is found without sign in popup
      fetch(
     `https://www.googleapis.com/calendar/v3/calendars/primary/events?key=${API_KEY}&amp;amp;orderBy=startTime&amp;amp;singleEvents=true`,
        {
          headers: {
            Authorization: `Bearer ${localStorage.getItem("access_token")}`,
          },
        }
      )
        .then((res) =&amp;gt; {
          // Check if unauthorized status code is return open sign in popup
          if (res.status !== 401) {
            return res.json();
          } else {
            localStorage.removeItem("access_token");

            openSignInPopup();
          }
        })
        .then((data) =&amp;gt; {
          if (data?.items) {
            setEvents(formatEvents(data.items));
          }
        });
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are using two types of methods for fetching events, The first using the logged in &lt;code&gt;gapi.client&lt;/code&gt; instance and the other using the access_token stored.&lt;/p&gt;

&lt;p&gt;When using the &lt;code&gt;gapi.client&lt;/code&gt;, we are calling a callback function &lt;code&gt;listUpcomingEvents()&lt;/code&gt; to get the events of the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const listUpcomingEvents = () =&amp;gt; {
    window.gapi.client.calendar.events
      .list({
        // Fetch events from user's primary calendar
        calendarId: "primary",
        showDeleted: true,
        singleEvents: true,
      })
      .then(function (response) {
        let events = response.result.items;

        if (events.length &amp;gt; 0) {
          setEvents(formatEvents(events));
        }
      });
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the events, we need to format them for Full calendar package event object structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const formatEvents = (list) =&amp;gt; {
    return list.map((item) =&amp;gt; ({
      title: item.summary,
      start: item.start.dateTime || item.start.date,
      end: item.end.dateTime || item.end.date,
    }));
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are only showing title, start date and end dates but there are more options in full calendar docs.&lt;/p&gt;

&lt;p&gt;At last, we need to render the Full Calendar component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";

return (
      &amp;lt;FullCalendar
        plugins={[dayGridPlugin]}
        initialView="dayGridMonth"
        events={events}
      /&amp;gt;
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a full calendar rendering google calendar events.&lt;/p&gt;

&lt;p&gt;Hope this was a clear tutorial. Thank you and have a great day.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Safari/iPhone compatibility issues</title>
      <dc:creator>Nouran Samy</dc:creator>
      <pubDate>Tue, 13 Apr 2021 14:39:18 +0000</pubDate>
      <link>https://forem.com/nouran96/safari-iphone-compatibility-issues-1mbl</link>
      <guid>https://forem.com/nouran96/safari-iphone-compatibility-issues-1mbl</guid>
      <description>&lt;p&gt;I've been facing a lot of compatibility issues in safari that worked fine in other browsers. So i decided to put all of them in one place for reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Animations
&lt;/h2&gt;

&lt;p&gt;I wanted to add a pulse effect to a rounded div so i animated box-shadow property like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.pulse {
  animation: pulse-animation 1s infinite;
  border-radius: 50%;
}

@keyframes pulse-animation {
  0% {
     box-shadow: 0 0 0 0px #00b2ba;
  }
  100% {
    box-shadow: 0 0 5px 12px rgba(0, 0, 0, 0);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked fine but the box shadow animation was behaving unexpectedly in safari browser. I found out that the safe properties to be used in animations are &lt;strong&gt;opacity and transform&lt;/strong&gt;. So i found a solution to use before and after pseudo elements and apply this effect using opacity and transform like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.pulse {
  position: relative;

  &amp;amp;::after {
    content: "";
    position: absolute;
    left: 0;
    width: 100%;
    height: 100%;
    border: 5px solid rgba(0, 178, 186, 0.7);
    border-radius: 50%;
    animation-name: pulse;
    animation-duration: 1s;
    animation-iteration-count: infinite;
  }
}

@keyframes pulse {
  0% {
    opacity: 1;
    transform: scale(1);
  }

  100% {
    opacity: 0;
    transform: scale(2.5);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the live example in &lt;a href="https://codepen.io/NouranS96/pen/GRrqKow"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Scroll bouncing behavior
&lt;/h2&gt;

&lt;p&gt;Scroll bouncing (also sometimes referred to as scroll &lt;strong&gt;‘rubber-banding’&lt;/strong&gt;, or &lt;strong&gt;‘elastic scrolling’&lt;/strong&gt;) is often used to refer to the effect you see when you scroll to the very top of a page or HTML element, or to the bottom of a page or element, on a device using a touchscreen or a trackpad, and empty space can be seen for a moment before the element or page springs back and aligns itself back to its top/bottom (when you release your touch/fingers).&lt;/p&gt;

&lt;p&gt;You can fix this behavior by giving body and html elements &lt;code&gt;overflow: hidden&lt;/code&gt; and add a body wrapper element that has the following styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;head&amp;gt;
   &amp;lt;style&amp;gt;
        .body-wrapper {
            width: 100vw;
            height: 100vh;
            overflow-y: auto;
            overflow-x: hidden;
            position: relative;
            -webkit-overflow-scrolling: touch; // This 
            property could lead to bouncing of element 
            positioned as fixed
         }
&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div class="body-wrapper"&amp;gt;
        // Your elements goes here
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could refer to this &lt;a href="https://www.smashingmagazine.com/2018/08/scroll-bouncing-websites/"&gt;link&lt;/a&gt; for other ways&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Notch of new iPhones
&lt;/h2&gt;

&lt;p&gt;The upper notch of new iphone devices could prevent your website from taking full width in landscape orientation if not handled properly.&lt;/p&gt;

&lt;p&gt;You could prevent this scenario by adding this viewport meta tag to your head element&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;meta name='viewport' content='initial-scale=1, viewport-fit=cover'&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add these styles&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@supports (padding: max(0px)) {
  body,
  header,
  footer {
    padding-left: min(0vmin, env(safe-area-inset-left));
    padding-right: min(0vmin, env(safe-area-inset-right));
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These safe areas will guarantee you content to be shown no matter what the orientation is.&lt;/p&gt;

&lt;p&gt;You could also refer to this &lt;a href="https://bubblin.io/blog/notch"&gt;link&lt;/a&gt; for more details&lt;/p&gt;

&lt;p&gt;I tried to sum up all the issues i faced with safari or iphones while coding but this article will be updated with any other cases i will face.&lt;/p&gt;

&lt;p&gt;Hope it helps someone.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>React 3D Carousel with Swipe effect</title>
      <dc:creator>Nouran Samy</dc:creator>
      <pubDate>Fri, 12 Mar 2021 18:40:51 +0000</pubDate>
      <link>https://forem.com/nouran96/react-3d-carousel-with-swipe-effect-3cmn</link>
      <guid>https://forem.com/nouran96/react-3d-carousel-with-swipe-effect-3cmn</guid>
      <description>&lt;p&gt;Hello everyone. I've been working on a react project that needed to add a feature of a 3D carousel.&lt;/p&gt;

&lt;p&gt;The package that i used was &lt;a href="https://www.npmjs.com/package/react-spring-3d-carousel"&gt;react-spring-3d-carousel&lt;/a&gt;. Everything was going great with the minimum requirements except for the drag or swipe effect of any slider or a normal carousel.&lt;/p&gt;

&lt;p&gt;P.S, If you're using NextJS you will need to import the library as follows as it uses window object which is not defined in SSR&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import dynamic from "next/dynamic";

const Carousel = dynamic(() =&amp;gt; import("react-spring-3d-carousel"), {
  ssr: false,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But fortunately, I found a way to add this feature using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Touch_events"&gt;Touch Events&lt;/a&gt;. And it is compatible with both android and ios.&lt;/p&gt;

&lt;p&gt;First of all, &lt;a href="https://codesandbox.io/s/1v96j74484?view=preview"&gt;this&lt;/a&gt; is the demo for react 3d carousel package that i used.&lt;/p&gt;

&lt;p&gt;For the swipe effect, i used the answer i found on this stackoverflow question &lt;a href="https://stackoverflow.com/questions/2264072/detect-a-finger-swipe-through-javascript-on-the-iphone-and-android"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All you have to do is add onTouchStart and onTouchMove events listeners to the div surrounding the carousel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div
   style={{ width: "80%", height: "500px", margin: "0 auto" }}
   onTouchStart={handleTouchStart}
   onTouchMove={handleTouchMove}
 &amp;gt;
      &amp;lt;Carousel
        slides={slides}
        goToSlide={state.goToSlide}
        offsetRadius={state.offsetRadius}
        showNavigation={state.showNavigation}
        animationConfig={state.config}
      /&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the functions used to detect the swipe direction are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  let xDown = null;
  let yDown = null;

  const getTouches = (evt) =&amp;gt; {
    return (
      evt.touches || evt.originalEvent.touches // browser API
    ); // jQuery
  };

  const handleTouchStart = (evt) =&amp;gt; {
    const firstTouch = getTouches(evt)[0];
    xDown = firstTouch.clientX;
    yDown = firstTouch.clientY;
  };

  const handleTouchMove = (evt) =&amp;gt; {
    if (!xDown || !yDown) {
      return;
    }

    let xUp = evt.touches[0].clientX;
    let yUp = evt.touches[0].clientY;

    let xDiff = xDown - xUp;
    let yDiff = yDown - yUp;

    if (Math.abs(xDiff) &amp;gt; Math.abs(yDiff)) {
      /*most significant*/
      if (xDiff &amp;gt; 0) {
        /* left swipe */
        setState({ goToSlide: state.goToSlide + 1 });
      } else {
        /* right swipe */
        setState({ goToSlide: state.goToSlide - 1 });
      }
    } else {
      if (yDiff &amp;gt; 0) {
        /* up swipe */
      } else {
        /* down swipe */
      }
    }
    /* reset values */
    xDown = null;
    yDown = null;
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simply, handleTouchStart just captures the first touch x and y point and handleTouchMove detects other movements and calculate the direction from the difference between the start and new point.&lt;/p&gt;

&lt;p&gt;So to sum up, you can find the new 3d carousel demo code with the swipe effect added to it &lt;a href="https://codesandbox.io/s/react-3d-spring-carousel-with-swipe-yvj51?file=/src/example.js"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And of course you can use the touch events on any other element or feature other than a carousel.&lt;/p&gt;

&lt;p&gt;Hope it helps someone out. Thanks for your time.&lt;/p&gt;

</description>
      <category>carousel</category>
      <category>touch</category>
      <category>swipe</category>
      <category>react</category>
    </item>
    <item>
      <title>OOCSS Methodology</title>
      <dc:creator>Nouran Samy</dc:creator>
      <pubDate>Fri, 19 Feb 2021 11:56:24 +0000</pubDate>
      <link>https://forem.com/nouran96/oocss-methodology-92d</link>
      <guid>https://forem.com/nouran96/oocss-methodology-92d</guid>
      <description>&lt;p&gt;&lt;strong&gt;OOCSS&lt;/strong&gt; (Object Oriented Cascading Style Sheets) is concerned with converting your regular CSS styles to reusable classes. A CSS “object” is &lt;strong&gt;a repeating visual pattern, that can be abstracted into an independent snippet of code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It has two major principles: &lt;strong&gt;Separate structure and skin&lt;/strong&gt; and &lt;strong&gt;Separate container and content&lt;/strong&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  1. Separate structure and skin
&lt;/h1&gt;

&lt;p&gt;The structure refers to invisible styles applied to elements (width, height, margin, padding) while the skin is the visible styles (colors, fonts, shadows).&lt;/p&gt;

&lt;p&gt;OOCSS defines these two separately. For example, this snippet of CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.button {
   width: 100px;
   height: 50px;
   background: #000;
   color: #fff;
}

.button-2 {
   width: 100px;
   height: 50px;
   background: #fff;
   color: #333;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that both classes have the same structure but differs in the skin properties. OOCSS deals with this situation and separate them as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.button {
   background: #000;
   color: #fff;
}

.button-2 {
   background: #fff;
   color: #333;
}

.btn-structure {
   width: 100px;
   height: 50px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we don't have redundant code and the &lt;code&gt;.btn-structure&lt;/code&gt; class can be used on any button having the same structure.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Separate container and content
&lt;/h1&gt;

&lt;p&gt;Content refers to the elements as images, paragraphs, divs which are nested inside other elements that serve as Containers.&lt;/p&gt;

&lt;p&gt;Styles used for Content elements should be independent of the container class so that it can be used without restrictions on their parent element. For example, this is a regular styling for a sidebar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#sidebar {
    padding: 2px;
    left: 0;
    margin: 3px;
    position: absolute;
    width: 140px;
}


#sidebar .list {
    margin: 3px;
}


#sidebar .list .list-header {
    font-size: 16px;
    color: red;
}


#sidebar .list .list-body {
    font-size: 12px;
    color: #FFF;
    background-color: red;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if we separate content from container, it will something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.sidebar {
    padding: 2px;
    left: 0;
    margin: 3px;
    position: absolute;
    width: 140px;
}

.list {
    margin: 3px;
}

.list-header {
    font-size: 16px;
    color: red
}

.list-body {
    font-size: 12px;
    color: #FFF;
    background-color: red;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now these classes can be used in many situations without the restriction of having a parent with an id of &lt;code&gt;#sidebar&lt;/code&gt; or &lt;code&gt;.list&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This methodology has some benefits and some disadvantages. The &lt;strong&gt;benefits&lt;/strong&gt; could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scalability and Reusability&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The classes are in a global state that can be used in any project or application with minimum changes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Readability&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other programmers can read your styles easily as there is no nesting or complications.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;disadvantages&lt;/strong&gt; on the other hand could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Not suitable for small projects&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This could be a headache to apply on a small project that has minimum styles and no complications in HTML structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Increases number of classes&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see above, to apply these principles we had to increase our classes to apply separation concept.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;There are many methodologies for organizing your styles, but this one is very simple and can be used instantly as long as you keep these two rules in mind.&lt;/p&gt;

</description>
      <category>css</category>
      <category>oocss</category>
      <category>styles</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
