From ae58276f92f0fddd451c8eac38f9cd683071991a Mon Sep 17 00:00:00 2001 From: Triston Armstrong Date: Sat, 5 Oct 2024 12:49:22 -0400 Subject: [PATCH] add image support --- src/components/CardSelector.tsx | 33 +++++++++++- src/components/ImageCard.tsx | 86 +++++++++++++++++++++++++++++++ src/components/InfinateCanvas.tsx | 60 ++++++++++++--------- src/components/MiniMap.tsx | 34 ++++++++++-- src/components/NoteCard.tsx | 9 ++-- src/signals/images.ts | 44 ++++++++++++++++ src/signals/index.ts | 1 + src/signals/notes.ts | 18 +++---- src/types/Card.ts | 8 +-- src/utils/useDebounce.ts | 1 + 10 files changed, 248 insertions(+), 46 deletions(-) create mode 100644 src/components/ImageCard.tsx diff --git a/src/components/CardSelector.tsx b/src/components/CardSelector.tsx index 7259753..b71a4b6 100644 --- a/src/components/CardSelector.tsx +++ b/src/components/CardSelector.tsx @@ -1,5 +1,5 @@ import { useRef } from "kaioken" -import { NotesSigal } from "../signals" +import { ImagesSignal, NotesSigal } from "../signals" export function CardSelector() { const containerRef = useRef(null) @@ -60,7 +60,34 @@ function StickyNote() { function Image() { function _handleClick() { - alert("created image") + const input = document.createElement('input') + input.type = 'file' + input.onchange = (e: any) => { + const file = e.target.files[0] + const reader = new FileReader() + reader.readAsDataURL(file) + reader.onload = readerEvent => { + const content = readerEvent.target?.result; + let img: string = ''; + if (typeof content == 'string') img = content?.split(':')[1] + if (!img) return + + ImagesSignal.default.addImage({ + type: "image", + title: "New Image", + contents: content as string, + position: { + x: e.pageX - 100, + y: e.pageY + (window.innerHeight / 2) - 100 + }, + dimensions: { + w: 200, + h: 200 + } + }) + } + } + input.click() } return ( @@ -93,3 +120,5 @@ function Image() { ) } + + diff --git a/src/components/ImageCard.tsx b/src/components/ImageCard.tsx new file mode 100644 index 0000000..6a8e578 --- /dev/null +++ b/src/components/ImageCard.tsx @@ -0,0 +1,86 @@ +import { signal, useRef } from "kaioken" +import { ImagesSignal, focusedItem } from "../signals" +import { useDebounce } from "../utils/useDebounce" +import { LayerEnum } from "../utils/enums" +import images, { ImageCardType } from "../signals/images" + +namespace ImageCard { + export interface ImageCardProps { + key: ImageCardType['id'] + data: ImageCardType + } +} + +export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps) { + const pressed = signal(false) + const newX = useRef(0) + const newY = useRef(0) + const offsetX = useRef(0) + const offsetY = useRef(0) + const { debounce } = useDebounce() + + function updateLocalStorage(time?: number) { + debounce(() => { + console.log(itemKey, "updated storage") + localStorage.setItem("images", JSON.stringify(images.images.value)) + }, time) + } + + function _handleMouseMove(e: MouseEvent) { + e.preventDefault() + if (!pressed.value) return + + newX.current = e.pageX - offsetX.current + newY.current = e.pageY - offsetY.current + const newPos = { x: newX.current, y: newY.current } + + ImagesSignal.default.updateImageProperty(itemKey, 'position', newPos) + updateLocalStorage() + } + + function _handleMouseUp(e: MouseEvent) { + e.preventDefault() + pressed.value = false + window.removeEventListener('mousemove', _handleMouseMove) + window.removeEventListener('mouseup', _handleMouseUp) + } + + function _handleMouseDown(e: MouseEvent) { + e.preventDefault() + focusedItem.value = itemKey + offsetX.current = e.offsetX + offsetY.current = e.offsetY + pressed.value = true + window.addEventListener('mousemove', _handleMouseMove) + window.addEventListener('mouseup', _handleMouseUp) + } + + return ( +
+ + + {item.title} +
+ + ) +} diff --git a/src/components/InfinateCanvas.tsx b/src/components/InfinateCanvas.tsx index f3dbecd..0b8914f 100644 --- a/src/components/InfinateCanvas.tsx +++ b/src/components/InfinateCanvas.tsx @@ -1,9 +1,11 @@ import { useRef, useEffect } from "kaioken" import { CardSelector } from "./CardSelector" -import { NotesSigal, canvasDimentsion } from "../signals" +import { ImagesSignal, NotesSigal, canvasDimentsion } from "../signals" import { NoteCard } from "./NoteCard" import notes from "../signals/notes" import { MiniMap } from "./MiniMap" +import { ImageCard } from "./ImageCard" +import images from "../signals/images" export default function InfiniteCanvas() { const containerRef = useRef(null) @@ -25,6 +27,7 @@ export default function InfiniteCanvas() { updateDimensions() window.addEventListener("resize", updateDimensions) notes.loadLocalStorage() + images.loadLocalStorage() return () => { window.removeEventListener("resize", updateDimensions) @@ -33,29 +36,40 @@ export default function InfiniteCanvas() { return ( <> - - + <> + <> + + -
-
- {Object.keys(NotesSigal.default.notes.value).map((itemKey: string) => { - const item = NotesSigal.default.notes.value[itemKey] - return ( - - ) - })} -
-
+
+
+ {Object.keys(NotesSigal.default.notes.value).map((itemKey: string) => { + const item = NotesSigal.default.notes.value[itemKey] + return ( + + ) + })} + + {Object.keys(ImagesSignal.default.images.value).map((itemKey: string) => { + const item = ImagesSignal.default.images.value[itemKey] + return ( + + ) + })} +
+
+ + ) } diff --git a/src/components/MiniMap.tsx b/src/components/MiniMap.tsx index 423d265..f782e45 100644 --- a/src/components/MiniMap.tsx +++ b/src/components/MiniMap.tsx @@ -1,8 +1,8 @@ import { signal, useEffect, useRef } from "kaioken" -import notes from "../signals/notes" -import { Card } from "../types/Card" +import notes, { NoteCardType } from "../signals/notes" import { canvasDimentsion } from "../signals" import { LayerEnum } from "../utils/enums" +import images, { ImageCardType } from "../signals/images" const _MAP_OFFSET = 20 const _MAP_SCALE_FACTOR = 10 @@ -44,8 +44,36 @@ export function MiniMap() { borderRadius: '4px' }}> + {Object.keys(images.images.value).map((imageKey: ImageCardType['id']) => { + const image = images.images.value[imageKey] - {Object.keys(notes.notes.value).map((noteKey: Card['id']) => { + function _handleItemClick(_e: MouseEvent) { + window.scrollTo({ + left: image.position.x - ((viewportWidth / 2) - (image.dimensions.w / 2)), + top: image.position.y - ((viewportHeight / 2) - (image.dimensions.h / 2)), + behavior: 'smooth' + }) + } + + return ( +
+ ) + })} + + + {Object.keys(notes.notes.value).map((noteKey: NoteCardType['id']) => { const note = notes.notes.value[noteKey] function _handleItemClick(_e: MouseEvent) { diff --git a/src/components/NoteCard.tsx b/src/components/NoteCard.tsx index e5ec7c4..3d6c168 100644 --- a/src/components/NoteCard.tsx +++ b/src/components/NoteCard.tsx @@ -1,14 +1,13 @@ import { signal, useRef } from "kaioken" import { NotesSigal, focusedItem } from "../signals" -import { Card } from "../types" import { useDebounce } from "../utils/useDebounce" -import notes from "../signals/notes" +import notes, { NoteCardType } from "../signals/notes" import { LayerEnum } from "../utils/enums" namespace NoteCard { export interface NoteCardProps { - key: Card['id'] - data: Card + key: NoteCardType['id'] + data: NoteCardType } } @@ -23,7 +22,6 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) { function updateLocalStorage(time?: number) { debounce(() => { - console.log(itemKey, "updated storage") localStorage.setItem("notes", JSON.stringify(notes.notes.value)) }, time) } @@ -77,6 +75,7 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
diff --git a/src/signals/images.ts b/src/signals/images.ts index e69de29..6a35a8d 100644 --- a/src/signals/images.ts +++ b/src/signals/images.ts @@ -0,0 +1,44 @@ +import { signal } from "kaioken" +import { Card } from "../types" + +export type ImageCardType = Card<"image"> + +const images = signal>({}) + +function loadLocalStorage() { + images.value = JSON.parse(localStorage.getItem("images") ?? "{}") +} + +function addImage(data: Omit) { + const newCard = { + ...data, + id: crypto.randomUUID(), + } + images.value[newCard.id] = newCard + images.notify() +} + +function removeImage(id: ImageCardType["id"]) { + delete images.value[id] + images.notify() +} +function updateImageProperty( + id: ImageCardType["id"], + property: K, + data: ImageCardType[K] +) { + const newData = { + ...images.value[id], + [property]: data, + } + images.value[id] = newData + images.notify() +} + +export default { + images, + addImage, + removeImage, + updateImageProperty, + loadLocalStorage, +} diff --git a/src/signals/index.ts b/src/signals/index.ts index 294444c..21dfeb8 100644 --- a/src/signals/index.ts +++ b/src/signals/index.ts @@ -5,3 +5,4 @@ export const focusedItem = signal(null) export const canvasDimentsion = signal({ width: 3000, height: 3000 }) export * as NotesSigal from "./notes" +export * as ImagesSignal from "./images" diff --git a/src/signals/notes.ts b/src/signals/notes.ts index a3ee6e4..e117ba3 100644 --- a/src/signals/notes.ts +++ b/src/signals/notes.ts @@ -1,31 +1,31 @@ import { signal } from "kaioken" import { Card } from "../types" -const notes = signal>({}) +export type NoteCardType = Card<"note"> + +const notes = signal>({}) function loadLocalStorage() { notes.value = JSON.parse(localStorage.getItem("notes") ?? "{}") } -function addNote(data: Omit) { +function addNote(data: Omit) { const newCard = { ...data, id: crypto.randomUUID(), } notes.value[newCard.id] = newCard notes.notify() - //updateLocalStorage() } -function removeNote(id: Card["id"]) { +function removeNote(id: NoteCardType["id"]) { delete notes.value[id] notes.notify() - //updateLocalStorage() } -function updateNoteProperty( - id: Card["id"], +function updateNoteProperty( + id: NoteCardType["id"], property: K, - data: Card[K] + data: NoteCardType[K] ) { const newData = { ...notes.value[id], @@ -33,7 +33,6 @@ function updateNoteProperty( } notes.value[id] = newData notes.notify() - //updateLocalStorage() } export default { @@ -41,6 +40,5 @@ export default { addNote, removeNote, updateNoteProperty, - //updateLocalStorage, loadLocalStorage, } diff --git a/src/types/Card.ts b/src/types/Card.ts index 7893bb9..cd5ae2c 100644 --- a/src/types/Card.ts +++ b/src/types/Card.ts @@ -2,11 +2,13 @@ export type CardTypes = "note" | "image" export type positionCoords = { x: number; y: number } export type dimensionCoords = { w: number; h: number } -export interface Card { +type Base64 = string + +export interface Card { id: string - type: CardTypes + type: Ttype title: string - contents: string + contents: Ttype extends "image" ? Base64 : string position: positionCoords dimensions: dimensionCoords } diff --git a/src/utils/useDebounce.ts b/src/utils/useDebounce.ts index 003f9b9..35b6ec6 100644 --- a/src/utils/useDebounce.ts +++ b/src/utils/useDebounce.ts @@ -12,6 +12,7 @@ function createState(): UseDebounceState { export function useDebounce() { if (!sideEffectsEnabled()) return createState() + return useHook("useDebounce", createState, ({ hook, update, isInit }) => { if (!isInit) return { timer: hook.timer, debounce: hook.debounce }