Skip to content

Commit

Permalink
Merge pull request #48 from low-earth-orbit/38-custom-mouse-pointer
Browse files Browse the repository at this point in the history
#38 Add custom mouse pointer
  • Loading branch information
low-earth-orbit authored Sep 18, 2024
2 parents a0d19eb + 969bff6 commit d3fa74a
Show file tree
Hide file tree
Showing 35 changed files with 365 additions and 67 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: ""
labels: ""
labels: "bug"
assignees: ""
---

Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: Feature request
about: Suggest an idea for this project
title: ""
labels: ""
labels: "feature"
assignees: ""
---

Expand Down
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"trailingComma": "all",
"semi": true,
"singleQuote": false,
"printWidth": 80,
"endOfLine": "lf",
"insertFinalNewline": true
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"prettier.prettierPath": "./node_modules/prettier"
}
65 changes: 65 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Contributing to Konva Whiteboard

Thank you for your interest in contributing to this project! Below are some guidelines to help you get started.

You can find the list of outstanding tasks in the [Issues](https://github.com/low-earth-orbit/konva-whiteboard/issues) tab.

If you're a committed contributor and would like to become a collaborator instead, please let me know in [Discussions](https://github.com/low-earth-orbit/konva-whiteboard/discussions).

## How to Contribute

1. **Before You Start:**

- **Ask the project owner to assign you the issue** you plan to work on.

2. **Fork the Repository:**
- Start by forking this repository to your own GitHub account.
3. **Clone Your Fork:**

- Clone your fork locally to work on your changes.

4. **Create a Branch:**

- Use a feature branch for your changes:
```bash
git branch feature/your-feature-name
```

5. **Make Your Changes:**
- Work on your feature or fix. Make sure your code is clean and follows the project's style guidelines (e.g., Prettier, ESLint, TypeScript checks).
6. **Write Tests:**
- If applicable, write tests to cover your changes to ensure functionality doesn't break.

7. **Commit Your Changes:**

- Write clear, concise commit messages that explain the reasoning for your changes:
```bash
git commit -m "Add feature: [brief description]"
```

8. **Push Your Changes:**

- Push your branch to your fork:
```bash
git push origin feature/your-feature-name
```

9. **Submit a Pull Request:**
- Create a pull request from your feature branch into the `main` branch of this repository. Ensure your PR follows the template provided and includes a description of your changes.

## Code Style

- Follow the coding standards set up by ESLint and Prettier in the project.
- Make sure your TypeScript passes all type checks.

## Pull Request Guidelines

- Reference any related issues in your PR description.
- Ensure your changes have been tested locally.
- Provide a brief description of your changes.

## Communication

- If you're unsure about a feature or would like more information, feel free to comment on the issue thread.
- Any questions or suggestions can be raised via the [Discussions](https://github.com/low-earth-orbit/konva-whiteboard/discussions) board.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Leo Hong
Copyright (c) 2024 Leo Hong & Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
35 changes: 20 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
# Konva Whiteboard

A simple whiteboard app built using Konva library.
This is an work-in-progress hobby project aimed at creating a sketch board app, similar to [Microsoft Whiteboard](https://www.microsoft.com/en-ca/microsoft-365/microsoft-whiteboard/digital-whiteboard-app), or a graphic design tool like [Polotno](https://studio.polotno.com/).

Demo: [Try it here](https://whiteboard.leohong.dev)

## Features
Project management is handled through [Issues](https://github.com/low-earth-orbit/konva-whiteboard/issues), and discussions or suggestions can be made on the [Discussions](https://github.com/low-earth-orbit/konva-whiteboard/discussions) board.

## Tech Stack

- Front-end: `React`, `Redux`, `Next.js`, `Konva.js`,`Material UI`
- Programming language: `TypeScript`

## Planning

Although there isn’t a strict timeline, the project is divided into three phases:

- Phase 1: Front-end only, focusing on basic drawing and canvas functionality.
- Phase 2: Introduce a backend and expand on front-end features.
- Phase 3: Advanced features such as sharing, live edit, and user management.

## Implemented Features

- Canvas objects

Expand All @@ -17,20 +32,10 @@ Demo: [Try it here](https://whiteboard.leohong.dev)

- Canvas
- History (undo & redo)
- Delete item or clear the entire canvas
- Delete an object or clear the entire canvas
- Keyboard shortcuts for delete/undo/redo actions
- Persistent canvas data stored in browser's local storage

## Tech Stack

- React
- Konva.js
- TypeScript
- Redux
- Next.js
- MUI (Material UI)
- Tailwind CSS

## Develop

### Run
Expand All @@ -43,6 +48,6 @@ npm run dev

Then, open [http://localhost:3000](http://localhost:3000) in your browser to access the app.

## Contribute
## Contributing & License

Contributions are welcome! More details will be provided soon.
The project is licensed under [The MIT License](LICENSE). Contributions are welcome! Please read the [Contributing Guidelines](CONTRIBUTING.md) for more information.
97 changes: 68 additions & 29 deletions components/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { v4 as uuid } from "uuid";
import { Stage } from "react-konva";
import Toolbar from "./Toolbar";
import Toolbar from "./toolbar/Toolbar";
import InkLayer from "./ink/InkLayer";
import ShapesLayer from "./shapes/ShapesLayer";
import ConfirmationDialog from "./ConfirmationDialog";
Expand All @@ -18,6 +18,11 @@ import {
redo,
setCanvasObjects,
} from "../redux/canvasSlice";
import { SHAPE_DEFAULT_HEIGHT, SHAPE_DEFAULT_WIDTH } from "./shapes/shapeUtils";
import {
TEXT_DEFAULT_HEIGHT,
TEXT_DEFAULT_WIDTH,
} from "./textFields/textFieldUtils";

export interface StageSizeType {
width: number;
Expand Down Expand Up @@ -60,6 +65,7 @@ export default function Canvas() {
const { canvasObjects, selectedObjectId } = useSelector(
(state: RootState) => state.canvas,
);
const [isDarkMode, setIsDarkMode] = useState(false);

const [selectedTool, setSelectedTool] = useState<ToolType>("pen");
const [strokeColor, setStrokeColor] = useState<string>("#2986cc");
Expand All @@ -71,30 +77,61 @@ export default function Canvas() {

const [open, setOpen] = useState(false); // confirmation modal for delete button - clear canvas

// // update cursor style
// useEffect(() => {
// const getCursorStyle = () => {
// switch (selectedTool) {
// case "pen":
// return `url(/mousePointer/pen.svg) 0 24, default`;
// case "addText":
// return `url(/mousePointer/text.svg) 0 24, text`;
// case "addRectangle":
// return `url(/mousePointer/rectangle.svg) 0 24, pointer`;
// case "addOval":
// return `url(/mousePointer/oval.svg) 0 24, pointer`;
// case "eraser":
// return `url(/mousePointer/erase.svg) 0 24, default`;
// default:
// return "default";
// }
// };

// document.body.style.cursor = getCursorStyle();
// return () => {
// document.body.style.cursor = "default";
// };
// }, [selectedTool]);
// Dark mode listener
useEffect(() => {
if (typeof window !== "undefined") {
const darkModeListener = window.matchMedia(
"(prefers-color-scheme: dark)",
);

setIsDarkMode(darkModeListener.matches);

// Listen for changes in dark mode
const handleThemeChange = (e: MediaQueryListEvent) => {
setIsDarkMode(e.matches);
};

darkModeListener.addEventListener("change", handleThemeChange);

return () => {
darkModeListener.removeEventListener("change", handleThemeChange);
};
}
}, []);

// update cursor style
useEffect(() => {
const getCursorStyle = () => {
const basePath = isDarkMode
? "/mousePointer/dark/" // Path for dark mode SVGs
: "/mousePointer/light/"; // Path for light mode SVGs

switch (selectedTool) {
case "pen":
return `url(${basePath}add.svg) 12 12, crosshair`;
case "addText":
return `url(${basePath}text.svg) 12 12, text`;
case "addRectangle":
return `url(${basePath}rectangle.svg) 12 12, pointer`;
case "addOval":
return `url(${basePath}oval.svg) 12 12, pointer`;
case "eraser":
return `url(${basePath}erase.svg) 12 12, default`;
default:
return "default";
}
};

const resetCursor = () => {
document.body.style.cursor = getCursorStyle();
};

resetCursor();

return () => {
document.body.style.cursor = "default";
};
}, [isDarkMode, selectedTool]);

// load from local storage
useEffect(() => {
Expand Down Expand Up @@ -207,8 +244,8 @@ export default function Canvas() {
type: "text" as const,
x: x,
y: y,
width: 10,
height: 10,
width: TEXT_DEFAULT_WIDTH,
height: TEXT_DEFAULT_HEIGHT,
fill: strokeColor, // use strokeColor for fill for now
// strokeWidth not applied to text field for now
text: "Double click to edit.",
Expand All @@ -230,8 +267,8 @@ export default function Canvas() {
strokeWidth: strokeWidth,
x: x,
y: y,
width: 5,
height: 5,
width: SHAPE_DEFAULT_WIDTH,
height: SHAPE_DEFAULT_HEIGHT,
}; // common shape properties

let newShape: CanvasObjectType;
Expand Down Expand Up @@ -392,13 +429,15 @@ export default function Canvas() {
onDelete={handleDelete}
strokeWidth={strokeWidth}
setStrokeWidth={(newWidth) => updateStyle("strokeWidth", newWidth)}
isDarkMode={isDarkMode}
/>
<ConfirmationDialog
open={open}
onClose={() => setOpen(false)}
onConfirm={resetCanvasState}
title="Clear Canvas"
description="Are you sure you want to clear the canvas? This action cannot be undone."
isDarkMode={isDarkMode}
/>
</>
);
Expand Down
4 changes: 4 additions & 0 deletions components/ConfirmationDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type ConfirmationDialogProps = {
onConfirm: () => void;
title: string;
description: string;
isDarkMode: boolean;
};

const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
Expand All @@ -22,7 +23,10 @@ const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
onConfirm,
title,
description,
isDarkMode,
}) => {
const bgColor = isDarkMode ? "bg-gray-500" : "bg-white";

return (
<Dialog
open={open}
Expand Down
Loading

0 comments on commit d3fa74a

Please sign in to comment.