Skip to content
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

Next.js - Reusable Confirm Dialog #12

Closed
mkubdev opened this issue Feb 20, 2023 · 6 comments
Closed

Next.js - Reusable Confirm Dialog #12

mkubdev opened this issue Feb 20, 2023 · 6 comments
Assignees
Labels
invalid This doesn't seem right published This label is used to indicate issues and pull requests that have been published on the Metaphor Web under review This label is used to indicate issues and pull requests that are currently under review by the relev

Comments

@mkubdev
Copy link
Member

mkubdev commented Feb 20, 2023

Metaphore Name

Next.js - Reusable Confirm Dialog

Share your metaphore story!

TODO: Add gif here!

TLDR

This is a complete guide on how to create a reusable confirm dialog component for next.js. Here we go!

Prerequisites

Before you begin, ensure that you have node and npm or Yarn installed on your machine. Here is a run-down of the core technologies we will be using.

  • Next.js — A framework for building server-side rendered(SSR) React applications with ease. It handles most of the challenges that come with building SSR React apps.
  • React — A very popular JavaScript DOM rendering framework for building scalable web applications using a component-based architecture.
  • TailwindCSS — A utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup.

Setup

Create a next.js application with tailwind pre-configured:

npx create-next-app@latest --ts -e with-tailwindcss my_app
# or
yarn create next-app --typescript --example with-tailwindcss my_app
npm install @headlessui/react -s
# or
yarn add @headlessui/react

Components

ConfirmDialog

import { Dialog, Transition } from '@headlessui/react';
import { Fragment, useState } from 'react';

import ButtonCancel from '@/components/ButtonCancel';
import ButtonConfirm from '@/components/ButtonConfirm';

interface Props {
  title: string;
  children: React.ReactNode;
  open: boolean;
  onClose: Function;
  onConfirm: Function;
}
export default function ConfirmDialog(props: Props) {
  const { open, onClose, title, children, onConfirm } = props;
  if (!open) {
    return <></>;
  }

  return (
    <Transition appear show={open} as={Fragment}>
      <Dialog as="div" className="relative z-10" onClose={onClose}>
        <Transition.Child
          as={Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-200"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          <div className="fixed inset-0 bg-black bg-opacity-25" />
        </Transition.Child>

        <div className="fixed inset-0 overflow-y-auto">
          <div className="flex items-center justify-center min-h-full p-4 text-center">
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0 scale-95"
              enterTo="opacity-100 scale-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100 scale-100"
              leaveTo="opacity-0 scale-95"
            >
              <Dialog.Panel className="w-full max-w-md p-6 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-2xl">
                <Dialog.Title
                  as="h3"
                  className="text-lg font-medium leading-6 text-gray-900"
                >
                  {title}
                </Dialog.Title>
                <div className="mt-2">
                  <p className="text-sm text-gray-500">{children}</p>
                </div>

                <div className="flex justify-between mt-4">
                  <Button
                    onClick={() => onClose()}
                    className="bg-secondary hover:bg-secondary-light"
                  >
                    No
                  </Button>
                  <ButtonConfirm
                    onClick={() => {
                      onClose();
                      onConfirm();
                    }}
                  >
                    Yes
                  </ButtonConfirm>
                </div>
              </Dialog.Panel>
            </Transition.Child>
          </div>
        </div>
      </Dialog>
    </Transition>
  );
}

ButtonCancel

interface Props {
  children: React.ReactNode;
  type?: 'submit' | 'button' | 'reset';
  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
  className?: string;
}
export default function ButtonCancel(props: Props) {
  const { type = 'button', children, onClick, className = '' } = props;
  return (
    <button
      className={`bg-red-400 hover:bg-red-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline ${className}`}
      type={type}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

ButtonConfirm

interface Props {
  children: React.ReactNode;
  type?: 'submit' | 'button' | 'reset';
  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
  className?: string;
}
export default function ButtonConfirm(props: Props) {
  const { type = 'button', children, onClick, className = '' } = props;
  return (
    <button
      className={`bg-green-400 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline ${className}`}
      type={type}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

How to use the component?

In order to use the <ConfirmDialog /> component, you need to use a boolean state to display it:

import { useState, useCallback } from 'react';

import ConfirmDialog from './ConfirmDialog';

export default function Home() {
  const [confirmOpen, setConfirmOpen] = useState(false);

  // ...

  const handleConfirm = useCallback(() => {
    console.log('confirmed!');
  }, []);

  return (
    <>
      <button
        className="px-4 py-2 font-bold text-white rounded bg-cyan-400 hover:bg-red-700 focus:outline-none focus:shadow-outline"
        onClick={() => setConfirmOpen(true)}
      >
        Open
      </button>
      <ConfirmDialog
        title="Modal title: Put a modal title!"
        open={confirmOpen}
        onClose={() => setConfirmOpen(false)}
        onConfirm={(e) => handleConfirm(e)}
      >
        Are you sure you have completed all answer?
      </ConfirmDialog>
    </>
  );
}

A demo/repos link

No response

@mkubdev mkubdev added the punk::request Punk adding new metaphor label Feb 20, 2023
@github-actions
Copy link
Contributor

Hello Punk! It's great having you contribute to this project

Welcome to the community :neckbeard:

If you would like to continue contributing to open source and would like to do it with an awesome inclusive community, you should join our GitHub Organisation - we help and encourage each other to contribute to open source little and often :neckbeard:. Any questions let us know.

@darkterminal
Copy link
Member

Noice!!! 🔥🔥🔥

Using useCallback to more safety. 😁

const handleConfirm = useCallback( () => { 
       console.log('confirmed!'); 
}, []);

@mkubdev mkubdev added metaphore Signs for issues that contain metaphors from punk members and removed punk::request Punk adding new metaphor labels Feb 24, 2023
@darkterminal darkterminal added the javascript Tag for Javasript story label Feb 25, 2023
@darkterminal darkterminal changed the title WIP: Add [Next.js - Reusable Confirm Dialog] WIP: Next.js - Reusable Confirm Dialog Feb 25, 2023
@mkubdev
Copy link
Member Author

mkubdev commented Mar 15, 2023

Noice!!! 🔥🔥🔥

Using useCallback to more safety. 😁

const handleConfirm = useCallback( () => { 
       console.log('confirmed!'); 
}, []);

I will add this change, can you add a suggestion so that i commit change directly? Will finish it today, hehe i forget this one.

@darkterminal
Copy link
Member

Is this metaphor are ready to close?

@mkubdev
Copy link
Member Author

mkubdev commented Mar 28, 2023

Is this metaphor are ready to close?

No, let me complete it! 😁

@darkterminal
Copy link
Member

Complete and close this metaphor 💯

@darkterminal darkterminal added the under review This label is used to indicate issues and pull requests that are currently under review by the relev label Mar 29, 2023
@mkubdev mkubdev changed the title WIP: Next.js - Reusable Confirm Dialog Next.js - Reusable Confirm Dialog Apr 11, 2023
@mkubdev mkubdev added invalid This doesn't seem right and removed metaphore Signs for issues that contain metaphors from punk members javascript Tag for Javasript story labels Jul 23, 2023
@mkubdev mkubdev closed this as completed Jul 23, 2023
@github-actions github-actions bot added the published This label is used to indicate issues and pull requests that have been published on the Metaphor Web label Jul 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid This doesn't seem right published This label is used to indicate issues and pull requests that have been published on the Metaphor Web under review This label is used to indicate issues and pull requests that are currently under review by the relev
Projects
None yet
Development

No branches or pull requests

2 participants