Skip to content

Commit

Permalink
Merge pull request #4 from Gyabi/feature-dev
Browse files Browse the repository at this point in the history
v0.1.0
  • Loading branch information
Gyabi committed Jan 28, 2024
2 parents 36cea39 + 94c3ba5 commit 36a1db7
Show file tree
Hide file tree
Showing 31 changed files with 1,479 additions and 951 deletions.
2 changes: 1 addition & 1 deletion app/components/Footer.tsx
Expand Up @@ -4,7 +4,7 @@ export const Footer = () => {
<footer className='h-5 w-full flex items-center justify-end px-4 py-2 bg-purple-700'>
<div className="text-black text-xs">
<div>
v0.0.2
v0.1.0
</div>
</div>
</footer>
Expand Down
406 changes: 69 additions & 337 deletions app/projects/add.tsx

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions app/projects/component/buttons/add-button.tsx
@@ -0,0 +1,21 @@
"use client";
import { IoMdAddCircle } from "react-icons/io";

/**
* 追加ボタンプロパティ
*/

interface AddButtonProps {
onClick: () => void;
}

export const AddButton = ({onClick}: AddButtonProps) => {
return (
<div className="flex justify-center items-center bg-gray-200 dark:bg-gray-700 hover:bg-purple-500 rounded-md px-2 py-2 mr-2"
onClick={onClick}>
<div className="m-1">
<IoMdAddCircle />
</div>
</div>
);
};
20 changes: 20 additions & 0 deletions app/projects/component/buttons/cancel-button.tsx
@@ -0,0 +1,20 @@
"use client";
import { MdCancel } from "react-icons/md";

/**
* キャンセルボタンプロパティ
*/
interface CancelButtonProps {
onClick: () => void;
}

export const CancelButton = ({onClick}: CancelButtonProps) => {
return (
<div className="flex justify-center items-center bg-red-500 dark:bg-red-700 rounded-md px-2 py-2 mr-2 h-10 w-10"
onClick={onClick}>
<div className="m-1">
<MdCancel />
</div>
</div>
);
};
20 changes: 20 additions & 0 deletions app/projects/component/buttons/done-button.tsx
@@ -0,0 +1,20 @@
"use client";
import { MdDone } from "react-icons/md";

/**
* 完了ボタンプロパティ
*/
interface DoneButtonProps {
onClick: () => void;
}

export const DoneButton = ({onClick}: DoneButtonProps) => {
return (
<div className="flex justify-center items-center bg-green-500 dark:bg-green-700 rounded-md px-2 py-2 mr-2 h-10 w-10"
onClick={onClick}>
<div className="m-1">
<MdDone />
</div>
</div>
);
};
19 changes: 19 additions & 0 deletions app/projects/component/buttons/edit-button.tsx
@@ -0,0 +1,19 @@
"use client";
import { MdEdit } from "react-icons/md";

/**
* 編集ボタンプロパティ
*/

interface EditButtonProps {
onClick: () => void;
}

export const EditButton = ({onClick}: EditButtonProps) => {
return (
<div className="flex justify-center items-center bg-gray-200 dark:bg-gray-700 hover:bg-purple-500 rounded-full px-2 py-2 mr-2"
onClick={onClick}>
<MdEdit />
</div>
);
};
@@ -1,12 +1,12 @@
"use client";
import { openExplorer, copyToClipboard, openTerminal, openVsCode, openBrowser } from "../logic/url_path_utils";
import { openExplorer, copyToClipboard, openTerminal, openVsCode, openBrowser } from "../../logic/url_path_utils";
import { FaRegFolderOpen } from "react-icons/fa6";
import { BsBrowserChrome } from "react-icons/bs";
import { TbBrandVscode } from "react-icons/tb";
import { FaRegClipboard } from "react-icons/fa";
import { BsTerminal } from "react-icons/bs";
import React from "react";
import { TimerModal } from "./timer-modal";
import { TimerModal } from "../modal/timer-modal";

import { message } from "@tauri-apps/api/dialog";

Expand Down
Expand Up @@ -3,19 +3,20 @@ import React from 'react';

export interface CustomCardProps {
id: string; // UUID as string
containerId: string; // Container UUID as string
projectName: string; // Project name
description: string; // Description
showProject: (id: string) => void; // Function to show project
openSelect: (containerId:string, projectId:string) => void;
children?: React.ReactNode;
}

const CustomCard: React.FC<CustomCardProps> = ({id, projectName, description, showProject, children}:CustomCardProps) => {
const CustomCard: React.FC<CustomCardProps> = ({id, containerId, projectName, description, openSelect, children}:CustomCardProps) => {
return (
<div className="w-full px-8 py-4 m-2 flex flex-col bg-white hover:bg-purple-500 border border-2 border-gray-200 hover:border-purple-800 rounded-xl shadow-lg dark:bg-gray-800 dark:border-gray-700"
onClick={() => showProject(id)}
onClick={() => openSelect(containerId, id)}
>
{children}
<h5 className="mb-2 text-2xl font-extrabold tracking-tight text-gray-900 dark:text-white" style={{ wordWrap: 'break-word' }}>{projectName}</h5>
<h5 className="mb-2 text-2xl font-extrabold tracking-tight decoration-purple-700 underline text-gray-900 dark:text-white" style={{ wordWrap: 'break-word' }}>{projectName}</h5>
<p className="mb-3 font-normal text-gray-700 dark:text-gray-400" style={{ wordWrap: 'break-word' }}>
{description.split('\n').map((line, i) => (
<span key={i}>
Expand Down
100 changes: 100 additions & 0 deletions app/projects/component/dnd/droppable-container.tsx
@@ -0,0 +1,100 @@
"use client";
import { useSortable, AnimateLayoutChanges, defaultAnimateLayoutChanges } from "@dnd-kit/sortable";
import { Project } from "../../data/project";
import { CSS } from "@dnd-kit/utilities";
import { RxDragHandleDots2 } from "react-icons/rx";
import { useEffect, useState } from "react";
import { CancelButton } from "../buttons/cancel-button";
import { EditButton } from "../buttons/edit-button";
import { ask } from "@tauri-apps/api/dialog";

const animateLayoutChanges: AnimateLayoutChanges = (args) =>
defaultAnimateLayoutChanges({...args, wasDragging: true});


interface DroppableContainerProps {
id: string;
children?: React.ReactNode;
title: string;
items: Project[];
updateContainer: (containerId:string, name:string) => void;
deleteContainer: (containerId:string) => void;
}

export const DroppableContainer: React.FC<DroppableContainerProps> = ({ id, children, title, items, updateContainer, deleteContainer }: DroppableContainerProps) => {
const {
active,
attributes,
isDragging,
listeners,
over,
setNodeRef,
transition,
transform,
} = useSortable({
id,
data: {
type: 'container',
children: items,
},
animateLayoutChanges,
});
const style={
transition,
transform: CSS.Transform.toString(transform),
opacity: isDragging ? 0.5 : undefined,
}

const [isEditing, setIsEditing] = useState(false);
const [editingText, setEditingText] = useState(title);

return (
<div ref={setNodeRef} style={style} className="relative w-full p-2 mb-2 flex flex-col bg-white border border-2 border-gray-200 hover:border-purple-800 rounded-xl shadow-lg dark:bg-gray-800 dark:border-gray-700">
<div className="flex justify-between w-full">
<div className="flex justify-start w-full">
{!isEditing ?
<h5 className="mb-2 text-2xl font-bold tracking-tight decoration-amber-700 underline text-gray-900 dark:text-white" style={{ wordWrap: 'break-word' }}>{title}</h5>
:
<input type="text" className="mb-2 text-2xl font-bold tracking-tight decoration-amber-700 underline text-gray-900 dark:text-white bg-amber-200" style={{ wordWrap: 'break-word' }} value={editingText}
onChange={(e)=> setEditingText(e.target.value)}
/>
}
<div className="mx-2">
<EditButton onClick={()=> setIsEditing((state)=>{
if(state && editingText !== title){
// コンテナ名の更新
updateContainer(title, editingText);
}
return !state;
})} />
</div>
</div>
{isEditing &&
<div className="mr-10">
<CancelButton onClick={async ()=>{
await ask("本当に削除しますか?",{
title: "",
type: "warning",
okLabel: "OK",
cancelLabel: "Cancel",
}).then(async (result) => {
if(result){
setIsEditing(false);
// データの削除と保存を実行
deleteContainer(title);
};
});
}}
/>
</div>
}
</div>
{children}
<div className="absolute top-4 right-4">
<button {...listeners} {...attributes}>
<RxDragHandleDots2 size={24} />
</button>
</div>
</div>
);
}
60 changes: 60 additions & 0 deletions app/projects/component/dnd/sortable-item.tsx
@@ -0,0 +1,60 @@
"use client";
import React from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { RxDragHandleDots2 } from "react-icons/rx";

export enum handlePositionType {
topRight = "topRight",
middleRight = "middleRight",
}
interface SortableItemProps {
id: string;
children?: React.ReactNode;
handlePosition?: handlePositionType;
}

export const SortableItem: React.FC<SortableItemProps> = ({ id, children, handlePosition }: SortableItemProps) => {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging
} = useSortable({id: id});

const style = {
transform: CSS.Transform.toString(transform),
transition,
zIndex: isDragging ? "100" : "auto",
opacity: isDragging ? 0.3 : 1
};

return (
<div ref={setNodeRef} style={style} className="relative">
{children}
{handlePosition === undefined && (
<div className="absolute top-4 right-4">
<button {...listeners} {...attributes}>
<RxDragHandleDots2 size={24} />
</button>
</div>
)}
{handlePosition === handlePositionType.topRight && (
<div className="absolute top-4 right-4">
<button {...listeners} {...attributes}>
<RxDragHandleDots2 size={24} />
</button>
</div>
)}
{handlePosition === handlePositionType.middleRight && (
<div className="absolute bottom-2 right-2">
<button {...listeners} {...attributes}>
<RxDragHandleDots2 size={24} />
</button>
</div>
)}
</div>
)
}
39 changes: 39 additions & 0 deletions app/projects/component/inputs/multi-row-input.tsx
@@ -0,0 +1,39 @@
"use client";

/**
* 複数行入力プロパティ
*/
interface MultiRowInputProps {
title?: string;
subtitle?: string;
placeholder: string;
value: string;
defaultHeight?: number;
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
}

/**
* 複数行入力コンポーネント
* @param param0
* @returns
*/
export const MultiRowInput = ({title, subtitle, placeholder, value, defaultHeight, onChange}: MultiRowInputProps) => {
return (
<div>
{/* titleが存在するなら表示 */}
{title && <label className="block decoration-purple-700 underline text-gray-700 dark:text-white text-sm font-bold mb-2">
{title}
</label>}
{/* subtitleが存在するなら表示 */}
{subtitle && <p className="block text-gray-700 dark:text-white text-xs font-bold my-2">
{subtitle}
</p>}
<textarea
className={`shadow appearance-none border rounded h-${defaultHeight} w-full py-2 px-3 text-gray-700 dark:text-white leading-tight focus:outline-none focus:shadow-outline`}
placeholder={placeholder}
value={value}
onChange={onChange}
/>
</div>
);
};
34 changes: 34 additions & 0 deletions app/projects/component/inputs/single-row-input.tsx
@@ -0,0 +1,34 @@
"use client";

/**
* 単一行入力プロパティ
*/
interface SingleRowInputProps {
title?: string;
subtitle?: string;
placeholder: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

export const SingleRowInput = ({title, subtitle, placeholder, value, onChange}: SingleRowInputProps) => {
return (
<div>
{/* titleが存在するなら表示 */}
{title && <label className="block decoration-purple-700 underline text-gray-700 dark:text-white text-sm font-bold mb-2">
{title}
</label>}
{/* subtitleが存在するなら表示 */}
{subtitle && <p className="block text-gray-700 dark:text-white text-xs font-bold mb-2">
{subtitle}
</p>}
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 dark:text-white leading-tight focus:outline-none focus:shadow-outline mb-2"
type="text"
placeholder={placeholder}
value={value}
onChange={onChange}
/>
</div>
);
};

0 comments on commit 36a1db7

Please sign in to comment.