Skip to content

Commit

Permalink
Merge pull request #14 from broadlume/feat/color-picker_dnd-file-input
Browse files Browse the repository at this point in the history
  • Loading branch information
isrodela authored Jun 27, 2024
2 parents 71e865d + 4bdeb01 commit 985a11b
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/components/color-picker/color-picker-input.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useState } from "react";
import { Meta, StoryObj } from "@storybook/react";

import { ColorPickerInput } from "./color-picker-input";

const meta: Meta = {
component: ColorPickerInput,
title: 'Components/Color Picker Input',
};

export default meta;
type Story = StoryObj<typeof ColorPickerInput>;

export const Demo: Story = {
render: _ => {
const [color, setColor] = useState('#d2e8ba')
return (<ColorPickerInput color={color} name="color-picker-demo" setColor={setColor} key='color-picker' />)
}
}
40 changes: 40 additions & 0 deletions src/components/color-picker/color-picker-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Input } from "@components/input/input";

interface ColorPickerInputProps {
color: string;
setColor: (value: string) => void;
name: string;
className?: string;
colorPickerInputClasses?: string;
textInputClasses?: string;
}

const ColorPickerInput: React.FC<ColorPickerInputProps> = ({
name,
color,
setColor,
className = '',
colorPickerInputClasses = '',
textInputClasses = '',
}) => {
return (
<div className={`~flex ~gap-2 ${className}`}>
<Input
className={`~w-14 ~p-1 ~rounded ${colorPickerInputClasses}`}
type="color"
value={color}
name={name}
onChange={(e) => setColor(e.target.value)}
/>
<Input
className={`~w-24 ~rounded ~border ${textInputClasses}`}
type="text"
value={color}
name={name}
onChange={(e) => setColor(e.target.value)}
/>
</div>
);
};

export { ColorPickerInput };
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useState } from "react";
import { Meta, StoryObj } from "@storybook/react";

import { DragNDropFileInput } from "./drag-n-drop-file-input";

const meta: Meta = {
component: DragNDropFileInput,
title: 'Components/DragNDrop File Input',
};

export default meta;
type Story = StoryObj<typeof DragNDropFileInput>;

export const Demo: Story = {
render: _ => {
const [file, setFile] = useState(new File([''], ''))
return (<DragNDropFileInput file={file} setFile={setFile} key='dnd-file-input' />)
}
}
105 changes: 105 additions & 0 deletions src/components/drag-n-drop-file-input/drag-n-drop-file-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useRef, useState } from "react";

import { Button } from "@components/button";

interface DragNDropFileInputProps {
file: File;
setFile: (updater: File) => void;
}

const DragNDropFileInput: React.FC<DragNDropFileInputProps> = ({
file,
setFile,
}) => {
const fileInput = useRef<HTMLInputElement>(null);
const [onDrag, setOnDrag] = useState(false);

const dropHandler = (ev: React.DragEvent<HTMLDivElement>) => {
setOnDrag(false);
console.log("File(s) dropped");

// Prevent default behavior (Prevent file from being opened)
ev.preventDefault();

if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
[...ev.dataTransfer.items].forEach((item) => {
// If dropped items aren't files, reject them
if (item.kind === "file") {
setDroppedFile(item.getAsFile());
}
});
} else {
// Use DataTransfer interface to access the file(s)
[...ev.dataTransfer.files].forEach((file) => {
setDroppedFile(file);
});
}
};

const setDroppedFile = (file: File | undefined | null) => {
if (file && ["image/png", "image/jpeg"].includes(file?.type as string)) {
setFile(file);
}
};

const dragOverHandler = (ev: React.DragEvent<HTMLDivElement>) => {
if (!onDrag) {
setOnDrag(true);
}
console.log("File(s) in drop zone");

// Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
};

const dragLeaveHandler = (ev: React.DragEvent<HTMLDivElement>) => {
if (onDrag) {
setOnDrag(false);
}
console.log("File(s) left drop zone");

// Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
};

const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
setFile(e.target.files[0]);
}
};

return (
<div
onDrop={(event) => dropHandler(event)}
onDragOver={(event) => dragOverHandler(event)}
onDragLeave={(event) => dragLeaveHandler(event)}
className={`~flex ~flex-col ~justify-center ~items-center ~w-3/4 ~mx-auto ~p-8 ~bg-[#FAFAFA] ~rounded ~border-dotted ~border-2 ${onDrag ? "~border-[#1FA384]" : "~border-[#E8E8E8]"}`}
>
<div className="~flex ~justify-center ~items-center">
<p className="~text-sm ~text-[#A6A6A6] ~mr-2">Drag and Drop or</p>
<Button
className="~primary-type-button ~text-[0.7rem]"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
fileInput.current?.click();
}}
>
Browse
</Button>
<input
type="file"
accept="image/png, image/jpeg"
name="logo"
hidden={true}
onChange={(e) => onFileChange(e)}
ref={fileInput}
/>
</div>
{file?.name ? <p className="~mt-2 ~text-center">{file?.name}</p> : ''}
</div>
);
};

export { DragNDropFileInput };

0 comments on commit 985a11b

Please sign in to comment.