1.0.6 • Published 7 months ago

tunangn-react-modal v1.0.6

Weekly downloads
-
License
MIT
Repository
-
Last release
7 months ago

tunangn-react-modal

Use to show message by using Dialog, snackbar or to show side in React. You can use this package easily to manage your dialog, snackbar or side. Ensure for privacy!! Dialog, side and snackbar only show (in screen or in DOM Tree) if they are opened.

Live demo

See more examples in live demo.

Note

  • This will not cause unexpected re-renders to other components when you open modal item(s). (Only the modal react component re-render)
  • This package contains default items that you just use them to solve many cases.
  • I use typescript in all examples.

Install

You can install it by:

npm install tunangn-react-modal

Import to App.jsx or App.tsx and open default dialog:

import { TunangnModal, dialog } "tunangn-react-modal";

export default function App() {
  return (
    <div>
      <button onClick={() => dialog({ title: "My first dialog" })}>Open my dialog</button>
      <TunangnModal />
    </div>
  )
}

How to use?

I will show you how to use TunangnModal in this article.

An default modal item always has 4 components:

  • Header has title and x button (optional).
  • Body has content.
  • Footer: depend on type of Modal Item, the Footer will has difference children. Firstly, I want to talk about title and content.
  • Container: wrap all components above.

Table of Contents

TunangnModal

This is the Modal React Component that you have to place it in App Component. There are 3 default modal items, you can use all of them immediately with dialog, side and snackbar from tunangn-react-modal.

TunangnModal has 3 properties:

  • canUseWhiteBG: Will white background or black background be used?.
  • className: (don't recommend) replace the default class name. If you replace the default class name, make sure you have suitable style because the default inline style will be unapply.
  • items: if you want to custom you own modal items, you can use this properties.

items is an object contains options to assign modal item to list. Its properties:

NameTypeDescription
typeMITypesUse to modify the title of modal item.
placeOn (Only for Side)SidePlaces \| undefinedWhere is side placed?
position (Only for Snackbar)SnackbarPositions \| undefinedWhat is position of snackbar?
duration (Only for Snackbar)number \| null \| undefinedHow long does snackbar last? If you want to disable the auto-close behaviour, you can assign null to this option.
clearDefaultInlineStyleboolean \| undefinedClear default inline stlyes. You can use the default class name to style the UI component for Modal Item. Note: this option will not work if you use element as Function Component or using this option if you want to write css to the default class name.
classNamestring \| undefinedClass name of item's container. This will be based class name for other ui element components. You don't need clearDefaultInlineStyle to clear the default inline style, because the default inline style will be cleared if className option is assigned. Note: this option will not work if you use element as Function Component or using this option if you just want to modify UI Element with your own css
element((props: CustomizedModalItemProps) => JSX.Element) \| undefinedUse this option if you want to create you own modal item.

Modal Item Data

Note: You cannot use these properties in your own customized modal item.

When you open an dialog with dialog function, an side with side function or an snackbar with snackbar function. You can pass an object to this function, an this object is data of Modal Item, the data that you want to modify the content of modal item. Each modal item always has title and content, so you can pass an object with title, content properties to modify the default modal item.

{
  title?: string | JSX.Element,
  content?: string | JSX.Element
}

All default modal items have same data: | Name | Type | Description | | :------------ | :------------ | :------------ | | title | string \| JSX.Element \| undefined | Use to modify the title of modal item. | | content | string \| JSX.Element \| undefined | Use to modify the content of modal item. |

Modal Item Properties (Default and Customized)

Modal Item Data is a part of Modal Item Properties. Including:

  • close: a function that you can use to close the modal item. Sometime, you will need to pass a result to this function. It returns a result object. The result object:
{
  isAgree: boolean,
  data?: any,
  message?: string
}
  • item: an object contains properties and methods of modal item:
NameTypeDescription
namestringName of Modal Item.
typestring \| JSX.Element \| undefinedUse to modify the content of modal item.
placeOn (Only for Side)SidePlaces \| undefinedWhere is side placed?
position (Only for Snackbar)SnackbarPositions \| undefinedWhat is position of snackbar?
duration (Only for Snackbar)number \| null \| undefinedHow long does snackbar last? If you want to disable the auto-close behaviour, you can assign null to this option.
getDatagetData<T>(): TUse to get data from open function.
  • utils: an object contains helper functions to support the modal item has behaviour almost like default one.
NameTypeDescription
getContainerStyle(style?: React.CSSProperties \| undefined) => React.CSSPropertiesRecommend Use to get container style. You can pass your custom style to this function, your style will override the default one.
runAnimationMITypes \| undefinedType of Modal Item.

Dialog

Note: You cannot use these properties in your own customized modal item.

I will show a dialog and open another dialog depend on result.isAgree. Firstly, import dialog from tunangn-react-modal, dialog receive a data object contains title, content and its own properties:

NameTypeDescription
cancelBtnLabelstring \| JSX.Element \| null \| undefinedUse to set label for cancel button of dialog. You can hide this button by assign null.
agreeBtnLabelstring \| JSX.Element \| null \| undefinedUse to set label for agree button of dialog. You can hide this button by assign null.

Let's show a dialog

import { TunangnModal, dialog } "tunangn-react-modal";

let dialogTitle = <p>Terms and Conditions <span style={{color: "red"}}>*</span></p>;

export default function App() {
  return (
    <>
      <TunangnModal />
      <button onClick={() => {
        dialog({
          title: dialogTitle,
          content: (
            <div>
              <p>Please read carefully:</p>
              <h4>Item 1</h4>
              <h4>Item 2</h4>
              <h4>Item 3</h4>
              <h4>Item 4</h4>
              <h4>Item 5</h4>
            </div>
          )
        })
        .then(result => {
          if(result.isAgree) {
            dialog({ title: dialogTitle, content: "You accepted our terms and conditions.", cancelBtnLabel: null })
          } else {
            dialog({ title: dialogTitle, content: "You unaccepted :(", agreeBtnLabel: null })
          }
        })
      }}>Open default dialog</button>
    </>
  )
}

You can see in the example above, there are 2 dialog with difference content will be showed depend on result.isAgree. And I will hide cancel button with agreed dialog, hide agree button with canceled dialog.

Result

Open dialog

image

Trying agree

image

Trying cancel

image

Side

Note: You cannot use these properties in your own customized modal item.

Side doesn't have its own properties.

I wil show left-side (default) with title and content has 10 images.

Let's show a side

import { TunangnModal, side } "tunangn-react-modal";

let sideTitle = <p style={{display: "flex", alignItems: "center"}}>
  <span className="material-symbols-outlined" style={{marginRight: ".75rem"}}>photo_library</span>Images
</p>;

export default function App() {
  const [imageUrls, setImageUrls] = React.useState<Array<string>>([]);

  React.useEffect(() => {
    let promises = Array(10).fill(0).map(i => fetch("https://picsum.photos/200/300"));
    Promise.all(promises).then(responses => {
      let urls: string[] = [];
      responses.forEach(response => {
        urls.push(response.url);
      });
      setImageUrls(urls);
    });
  }, []);

  return (
    <>
      <TunangnModal />
      <button onClick={() => {
        side({
          title: sideTitle,
          content: (
            <div style={{ overflowY: "scroll", maxHeight: "calc(100vh - 48px)" }}>
              {
                imageUrls.length > 0 ?
                imageUrls.map((url, index) => {
                  return <img src={url} key={index} />
                }) :
                <p>There aren't images.</p>
              }
            </div>
          )
        })
      }}>Open images side</button>
    </>
  )
}

10 images are response, but the side will not be re-render. I think you have the answer, so you need to customize you own side. I will show you in the last example!!!

Result

image

I re-open the side to see 10 images.

Snackbar

Note: You cannot use these properties in your own customized modal item.

Snackbar has 1 property: | Name | Type | Description | | :------------ | :------------ | :------------ | | color | string \| undefined | Use to modify the background color of header. |

I will show 4 various snackbar with various title and content.

import { TunangnModal, snackbar } "tunangn-react-modal";

let successSnackbar = {
  title: <span className="material-symbols-outlined">check_circle</span>,
  content: "You action is performed successfully.",
  color: "success"
};

let errorSnackbar = {
  title: <span className="material-symbols-outlined">error</span>,
  content: "There is an error.",
  color: "error"
};

let warningSnackbar = {
  title: <span className="material-symbols-outlined">warning</span>,
  content: "Your requesting data will cause an unexpected side effect!!! ",
  color: "warning"
};

let otherSnackbar = {
  title: <span className="material-symbols-outlined">attach_money</span>,
  content: "You payment is processed.",
  color: "#a8329b"
};

export default function App() {
  return (
    <>
      <TunangnModal />
      <button onClick={() => {
        snackbar(successSnackbar);
      }}>Open snackbar</button>
    </>
  )
}

Result

The success snackbar

image

The error snackbar

image

The waring snackbar

image

The other snackbar

image

Customize your own Tunangn Modal Item

I will build a small profile right-side with:

  • Username, user's avatar.
  • Use css.
  • Remove almost inline styles.
  • Fetch data (images).

I will create a file in src/components/profile/Profile.tsx:

export default function Profile(props: CustomizedModalItemProps) {
  // Ref of container
  const profileRef = React.useRef<HTMLDivElement>(null);
  const data = props.item.getData() as any;

  props.item.getData

  const [imageUrls, setImageUrls] = React.useState<Array<string>>([]);
  const [user, setUser] = React.useState<UserSideData>();

  React.useEffect(() => {
    let promises = Array(10).fill(0).map(i => fetch("https://picsum.photos/300"));

    // Get user
    getUser(data.userId).then(user => setUser(user));

    // Resolve images
    Promise.all(promises).then(responses => {
      let urls: string[] = [];
      responses.forEach(response => {
        urls.push(response.url);
      });
      setImageUrls(urls);
    });

    // Use container's ref to perform animation when profile is showed.
    props.utils.runAnimation!(profileRef.current!);
  }, []);

  return (
    <div
      ref={profileRef}
      style={props.utils.getContainerStyle({
        minWidth: "420px",
        padding: ".75rem",
        borderTopLeftRadius: "16px",
        borderBottomLeftRadius: "16px",
        overflowY: "scroll"
      })}
    >
      <div className="profile-header">
        <div className="user-info">
          {
            user
            ? (
              <>
                <img className="user-info-image" src={user.url} style={{marginRight: "0.75rem"}} />
                <strong>{user.name}</strong>
              </>
            )
            : <strong>There isn't user.</strong>
          }
        </div>
        <span className="material-symbols-outlined btn-close" onClick={() => props.close({ isAgree: false })}>close</span>
      </div>
      <div className="profile-body">
        <p className="user-info-bio">
          { user?.bio }
        </p>
        <h3 style={{marginBottom: ".75rem"}}>Shortcuts</h3>
        {
          sideBodyContent.shortcuts.map((shortcut, index) => (
            <div className="profile-shortcut border-top" key={index} style={{padding: ".75rem 0"}}>
              {
                shortcut.map(article => (
                  <button className="btn-article" key={article.id}>
                    <span className="material-symbols-outlined" style={{marginRight: ".75rem"}}>{article.icon}</span>
                    <span>{upperCaseFirstChar(article.name)}</span>
                  </button>
                ))
              }
            </div>
          ))
        }
        <h3 className="border-top" style={{padding: ".75rem 0"}}>Images</h3>
        <div className="profile-images">
          {
            imageUrls.length > 0 ?
            imageUrls.map((url, index) => {
              return <img src={url} key={index} />
            }) :
            <p>There aren't images.</p>
          }
        </div>
      </div>
    </div>
  )
}

Then import Profile.tsx to App.tsx and use it:

import { TunangnModal, openTMI } "tunangn-react-modal";

import Profile, { openSideProfile, profileSideName } from "./components/profile/Profile";

// Assign user id. There are 2 userids: user-01 and user-02
let userId = "user-02";

export default function App() {
  return (
    <>
      <header className="app-header">
        <p></p>
        <span className="material-symbols-outlined btn-profile" onClick={() => openSideProfile(userId)}>account_circle</span>
      </header>
      <TunangnModal
        items={{
          myProfileSide: {
            type: "side",
            placeOn: "right",
            element: Profile
          }
        }}
      />
    </>
  )
}

Result

image

You can see the source in the live demo above

1.0.6

7 months ago

1.0.5

7 months ago

1.0.4

7 months ago

1.0.3

10 months ago

1.0.2

10 months ago

1.0.1

10 months ago

1.0.0

10 months ago