htmlstreamofficial / preline

Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
https://preline.co
Other
4.9k stars 309 forks source link

File Upload for Nextjs React Project #446

Closed ryanpantk closed 3 months ago

ryanpantk commented 3 months ago

I am looking to add the Preline File Upload to my Nextjs React project with reference to https://preline.co/plugins/html/file-upload.html https://preline.co/docs/file-upload.html

I make some changes from the html provided to convert to jsx and make use of dangerouslySetInnerHTML to resolve the hydration issue of https://github.com/facebook/react/issues/19932

However, while the component renders, the file select window does not pop up when it is being clicked. Has anyone successfuly integrated the File Upload functionality into their Nextjs project and is willing to share how they did it?

This is my Preline Script which is loaded through the layout.tsx of my app directory `"use client";

import { usePathname } from "next/navigation"; import { useEffect } from "react";

import { IStaticMethods } from "preline/preline";

declare global { interface Window { HSStaticMethods: IStaticMethods; } }

export default function PrelineScript() { const path = usePathname();

useEffect(() => { const loadPreline = async () => { const preline = await import("preline/preline"); const fileUpload = await import("@preline/file-upload"); const dropzone = await import("dropzone"); const lodash = await import("lodash"); // Import Preline Components here const { HSDropdown } = preline; const HSFileUpload = fileUpload.default; if ( typeof window !== "undefined" && typeof document .getElementById("data-hs-tab") ?.getAttribute("data-hs-tab") !== "undefined" ) { window.HSStaticMethods.autoInit(); // Initialise Preline Components here HSDropdown.autoInit(); HSFileUpload.autoInit(); } };

loadPreline();

}, [path]);

return null; } `

This is my FileUpload Component

`const FileUploadComponent = () => { return ( <div id="hs-file-upload-with-limited-file-size" className="space-y-2" data-hs-file-upload='{ "url": "/upload", "maxFilesize": 1, "extensions": { "default": { "class": "shrink-0 size-5" }, "xls": { "class": "shrink-0 size-5" }, "zip": { "class": "shrink-0 size-5" }, "csv": { "icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\"/><path d=\"M14 2v4a2 2 0 0 0 2 2h4\"/><path d=\"m5 12-3 3 3 3\"/><path d=\"m9 18 3-3-3-3\"/>", "class": "shrink-0 size-5" } } }'

<div className="flex cursor-pointer justify-center rounded-xl border border-dashed border-gray-300 bg-white p-12" data-hs-file-upload-trigger=""

      <div className="mt-4 flex flex-wrap justify-center text-sm leading-6 text-gray-600">
        <span className="pe-1 font-medium text-gray-800">
          Drop your file here or
        </span>
        <span className="rounded-lg bg-white font-semibold text-blue-600 decoration-2 focus-within:outline-none focus-within:ring-2 focus-within:ring-blue-600 focus-within:ring-offset-2 hover:text-blue-700 hover:underline">
          browse
        </span>
      </div>

      <p className="mt-1 text-xs text-gray-400">Pick a file up to 2MB.</p>
    </div>
  </div>

  <div className="mt-2 hidden" data-hs-file-upload-file-info="">
    <p className="text-sm text-gray-800">
      <span className="font-semibold">File:</span>
      <span data-hs-file-upload-selected-file=""></span>
    </p>
  </div>

  <template
    data-hs-file-upload-preview=""
    dangerouslySetInnerHTML={{
      __html: `
        <div className="rounded-xl border border-solid border-gray-300 bg-white p-3">
          <div className="mb-1 flex items-center justify-between">
            <div className="flex items-center gap-x-3">
              <span
                className="flex size-10 items-center justify-center rounded-lg border border-gray-200 text-gray-500"
                data-hs-file-upload-file-icon=""
              >
                <img
                  className="hidden rounded-lg"
                  data-dz-thumbnail=""
                  alt=""
                />
              </span>
              <div>
                <p className="text-sm font-medium text-gray-800">
                  <span
                    className="inline-block max-w-[300px] truncate align-bottom"
                    data-hs-file-upload-file-name=""
                  ></span>
                  .<span data-hs-file-upload-file-ext=""></span>
                </p>
                <p
                  className="text-xs text-gray-500"
                  data-hs-file-upload-file-size=""
                  data-hs-file-upload-file-success=""
                ></p>
                <p
                  className="text-xs text-red-500"
                  style={{ display: "none" }}
                  data-hs-file-upload-file-error=""
                >
                  File exceeds size limit.
                </p>
              </div>
            </div>
            <div className="flex items-center gap-x-2">
              <span
                className="hs-tooltip inline-block [--placement:top]"
                style={{ display: "none" }}
                data-hs-file-upload-file-error=""
              >
                <span className="hs-tooltip-toggle text-red-500 hover:text-red-800 focus:text-red-800 focus:outline-none">
                  <svg
                    className="size-4 shrink-0"
                    xmlns="http://www.w3.org/2000/svg"
                    width="24"
                    height="24"
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                  >
                    <circle cx="12" cy="12" r="10"></circle>
                    <line x1="12" x2="12" y1="8" y2="12"></line>
                    <line x1="12" x2="12.01" y1="16" y2="16"></line>
                  </svg>
                  <span
                    className="hs-tooltip-content invisible absolute z-10 inline-block max-w-[100px] rounded bg-gray-900 px-2 py-1 text-xs font-medium text-white opacity-0 shadow-sm transition-opacity hs-tooltip-shown:visible hs-tooltip-shown:opacity-100"
                    role="tooltip"
                  >
                    Please try to upload a file smaller than 1MB.
                  </span>
                </span>
              </span>
              <button
                type="button"
                className="text-gray-500 hover:text-gray-800 focus:text-gray-800 focus:outline-none"
                data-hs-file-upload-reload=""
              >
                <svg
                  className="size-4 shrink-0"
                  xmlns="http://www.w3.org/2000/svg"
                  width="24"
                  height="24"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                >
                  <path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"></path>
                  <path d="M21 3v5h-5"></path>
                </svg>
              </button>
              <button
                type="button"
                className="text-gray-500 hover:text-gray-800 focus:text-gray-800 focus:outline-none"
                data-hs-file-upload-remove=""
              >
                <svg
                  className="size-4 shrink-0"
                  xmlns="http://www.w3.org/2000/svg"
                  width="24"
                  height="24"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                >
                  <path d="M3 6h18"></path>
                  <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
                  <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
                  <line x1="10" x2="10" y1="11" y2="17"></line>
                  <line x1="14" x2="14" y1="11" y2="17"></line>
                </svg>
              </button>
            </div>
          </div>

          <div className="flex items-center gap-x-3 whitespace-nowrap">
            <div
              className="flex h-2 w-full overflow-hidden rounded-full bg-gray-200"
              role="progressbar"
              aria-valuenow={0}
              aria-valuemin={0}
              aria-valuemax={100}
              data-hs-file-upload-progress-bar=""
            >
              <div
                className="hs-file-upload-complete:bg-green-500 flex flex-col justify-center overflow-hidden whitespace-nowrap rounded-full bg-blue-600 text-center text-xs text-white transition-all duration-500"
                style={{ width: "0" }}
                data-hs-file-upload-progress-bar-pane=""
              ></div>
            </div>
            <div className="w-10 text-end">
              <span className="text-sm text-gray-800">
                <span data-hs-file-upload-progress-bar-value="">0</span>%
              </span>
            </div>
          </div>
        </div>
      `,
    }}
  ></template>
</div>

); };

export default FileUploadComponent; `

ryanpantk commented 3 months ago

Resolved for anyone interested in the solution:

`"use client";

import { usePathname } from "next/navigation"; import { useEffect } from "react";

import { IStaticMethods } from "preline/preline"; import Dropzone from "dropzone";

declare global { interface Window { HSStaticMethods: IStaticMethods; Dropzone: typeof Dropzone; } }

export default function PrelineScript() { const path = usePathname();

useEffect(() => { const loadPreline = async () => { const preline = await import("preline/preline"); await import("lodash"); // Import Preline Components here const { HSDropdown } = preline;

  const Dropzone = (await import("dropzone")).default;
  window.Dropzone = Dropzone;
  window.HSStaticMethods.autoInit();
  HSDropdown.autoInit();
};
loadPreline();

}, [path]);

return null; } `