generated from Klectr/KTemplate
Compare commits
3 Commits
86d4f94f56
...
95924ba49f
Author | SHA1 | Date | |
---|---|---|---|
|
95924ba49f | ||
0866f0b470 | |||
|
39b13b6dcb |
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="color-scheme" content="light dark" />
|
||||||
<title>KlectrTemplate</title>
|
<title>KlectrTemplate</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import { useDebounce } from "../utils/useDebounce"
|
|||||||
import { LayerEnum } from "../utils/enums"
|
import { LayerEnum } from "../utils/enums"
|
||||||
import images, { ImageCardType } from "../signals/images"
|
import images, { ImageCardType } from "../signals/images"
|
||||||
import { updateLocalStorage } from "../utils/localStorage"
|
import { updateLocalStorage } from "../utils/localStorage"
|
||||||
|
import { isTheme } from "../utils/isTheme"
|
||||||
|
|
||||||
namespace ImageCard {
|
namespace ImageCard {
|
||||||
export interface ImageCardProps {
|
export interface ImageCardProps {
|
||||||
@ -90,7 +91,7 @@ export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onmousedown={_handleMouseDown}
|
onmousedown={_handleMouseDown}
|
||||||
className="select-none transition flex flex-col justify-stretch shadow-lg rounded border border-[#3c3c3c] absolute"
|
className="select-none transition flex flex-col justify-stretch shadow-md rounded border border-[#1c1c1c] absolute"
|
||||||
style={{
|
style={{
|
||||||
zIndex: `${focusedItem.value == itemKey ? LayerEnum.CARD_ELEVATED : LayerEnum.CARD}`,
|
zIndex: `${focusedItem.value == itemKey ? LayerEnum.CARD_ELEVATED : LayerEnum.CARD}`,
|
||||||
top: `${item.position.y}px`,
|
top: `${item.position.y}px`,
|
||||||
@ -104,7 +105,7 @@ export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps
|
|||||||
backgroundPosition: 'center'
|
backgroundPosition: 'center'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button className="flex justify-center items-center hover:bg-blue-500 w-5 h-5 text-white text-md absolute right-0 top-0" onclick={(_e: Event) => {
|
<button className="flex justify-center items-center hover:bg-blue-500 rounded w-5 h-5 dark:text-[#777] dark:hover:text-white text-white text-md absolute right-0 top-0" onclick={(_e: Event) => {
|
||||||
ImagesSignal.default.removeImage(item.id)
|
ImagesSignal.default.removeImage(item.id)
|
||||||
ImagesSignal.default.images.notify()
|
ImagesSignal.default.images.notify()
|
||||||
debounceLSUpdate()
|
debounceLSUpdate()
|
||||||
@ -128,7 +129,7 @@ function ExpandIcon({ cb }: {
|
|||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="#333"
|
stroke={isTheme('dark') ? "#777" : "#999"}
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { useRef, useEffect } from "kaioken"
|
import { useRef, useEffect } from "kaioken"
|
||||||
import { ImagesSignal, NotesSigal, canvasDimentsion } from "../signals"
|
import { ImagesSignal, NotesSigal, TextSignal, canvasDimentsion } from "../signals"
|
||||||
import { NoteCard } from "./NoteCard"
|
import { NoteCard } from "./NoteCard"
|
||||||
import notes from "../signals/notes"
|
import notes from "../signals/notes"
|
||||||
import { MiniMap } from "./MiniMap"
|
import { MiniMap } from "./MiniMap"
|
||||||
import { ImageCard } from "./ImageCard"
|
import { ImageCard } from "./ImageCard"
|
||||||
import images from "../signals/images"
|
import images from "../signals/images"
|
||||||
import { CardSelector } from "./cardSelector/CardSelector"
|
import { CardSelector } from "./cardSelector/CardSelector"
|
||||||
|
import { isTheme } from "../utils/isTheme"
|
||||||
|
import { TextItem } from "./TextItem"
|
||||||
|
|
||||||
export default function InfiniteCanvas() {
|
export default function InfiniteCanvas() {
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
@ -42,13 +44,13 @@ export default function InfiniteCanvas() {
|
|||||||
className="h-screen w-full absolute top-0 left-0"
|
className="h-screen w-full absolute top-0 left-0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="absolute top-0 left-0"
|
className="dark:bg-black absolute top-0 left-0"
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
style={{
|
style={{
|
||||||
width: `${canvasDimentsion.value.width}px`,
|
width: `${canvasDimentsion.value.width}px`,
|
||||||
height: `${canvasDimentsion.value.width}px`,
|
height: `${canvasDimentsion.value.width}px`,
|
||||||
backgroundSize: "30px 30px",
|
backgroundSize: "30px 30px",
|
||||||
backgroundImage: "radial-gradient(circle, rgba(255, 255, 255, 0.2) 1px, transparent 1px)",
|
backgroundImage: `radial-gradient(circle, rgba(${isTheme('dark') ? '255, 255, 255, 0.2' : '0, 0, 0, 0.2'}) 1px, transparent 1px)`,
|
||||||
}}>
|
}}>
|
||||||
{Object.keys(NotesSigal.default.notes.value).map((itemKey: string) => {
|
{Object.keys(NotesSigal.default.notes.value).map((itemKey: string) => {
|
||||||
const item = NotesSigal.default.notes.value[itemKey]
|
const item = NotesSigal.default.notes.value[itemKey]
|
||||||
@ -63,6 +65,14 @@ export default function InfiniteCanvas() {
|
|||||||
<ImageCard key={itemKey} data={item} />
|
<ImageCard key={itemKey} data={item} />
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{Object.keys(TextSignal.default.texts.value).map((itemKey: string) => {
|
||||||
|
const item = TextSignal.default.texts.value[itemKey]
|
||||||
|
return (
|
||||||
|
<TextItem key={itemKey} data={item} />
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -3,6 +3,7 @@ import notes, { NoteCardType } from "../signals/notes"
|
|||||||
import { canvasDimentsion } from "../signals"
|
import { canvasDimentsion } from "../signals"
|
||||||
import { LayerEnum } from "../utils/enums"
|
import { LayerEnum } from "../utils/enums"
|
||||||
import images, { ImageCardType } from "../signals/images"
|
import images, { ImageCardType } from "../signals/images"
|
||||||
|
import texts from "../signals/texts"
|
||||||
|
|
||||||
const _MAP_OFFSET = 20
|
const _MAP_OFFSET = 20
|
||||||
const _MAP_SCALE_FACTOR = 10
|
const _MAP_SCALE_FACTOR = 10
|
||||||
@ -34,15 +35,14 @@ export function MiniMap() {
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={el} style={{
|
<div
|
||||||
position: 'fixed',
|
className="dark:bg-[#ffffff11] bg-[#0001] fixed rounded"
|
||||||
backgroundColor: '#ffffff11',
|
ref={el} style={{
|
||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
translate: `${xPos}px ${yPos}px`,
|
translate: `${xPos}px ${yPos}px`,
|
||||||
zIndex: `${LayerEnum.MINIMAP}`,
|
zIndex: `${LayerEnum.MINIMAP}`,
|
||||||
borderRadius: '4px'
|
}}>
|
||||||
}}>
|
|
||||||
|
|
||||||
{Object.keys(images.images.value).map((imageKey: ImageCardType['id']) => {
|
{Object.keys(images.images.value).map((imageKey: ImageCardType['id']) => {
|
||||||
const image = images.images.value[imageKey]
|
const image = images.images.value[imageKey]
|
||||||
@ -57,16 +57,13 @@ export function MiniMap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={el} className={"bg-green-500 hover:bg-blue-500 cursor-pointer"}
|
<div ref={el} className={"absolute dark:bg-green-500 bg-green-300 dark:hover:bg-blue-500 hover:bg-blue-300 cursor-pointer border dark:border-[#222] border-green-500 rounded"}
|
||||||
onclick={_handleItemClick}
|
onclick={_handleItemClick}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
|
||||||
width: `${image.dimensions.w / _MAP_SCALE_FACTOR}px`,
|
width: `${image.dimensions.w / _MAP_SCALE_FACTOR}px`,
|
||||||
height: `${image.dimensions.h / _MAP_SCALE_FACTOR}px`,
|
height: `${image.dimensions.h / _MAP_SCALE_FACTOR}px`,
|
||||||
top: `${(image.position.y / _MAP_SCALE_FACTOR)}px`,
|
top: `${(image.position.y / _MAP_SCALE_FACTOR)}px`,
|
||||||
left: `${(image.position.x / _MAP_SCALE_FACTOR)}px`,
|
left: `${(image.position.x / _MAP_SCALE_FACTOR)}px`,
|
||||||
border: '1px solid #222',
|
|
||||||
borderRadius: '2px',
|
|
||||||
zIndex: `${LayerEnum.MINIMAP + 1}`
|
zIndex: `${LayerEnum.MINIMAP + 1}`
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
@ -86,14 +83,40 @@ export function MiniMap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"bg-gray-500 hover:bg-blue-500 cursor-pointer"}
|
<div className={"absolute dark:bg-gray-500 bg-gray-300 hover:bg-blue-500 cursor-pointer border dark:border-[#222] border-gray-500 rounded"}
|
||||||
onclick={_handleItemClick}
|
onclick={_handleItemClick}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
|
||||||
width: `${note.dimensions.w / _MAP_SCALE_FACTOR}px`,
|
width: `${note.dimensions.w / _MAP_SCALE_FACTOR}px`,
|
||||||
height: `${note.dimensions.h / _MAP_SCALE_FACTOR}px`,
|
height: `${note.dimensions.h / _MAP_SCALE_FACTOR}px`,
|
||||||
top: `${(note.position.y / _MAP_SCALE_FACTOR)}px`,
|
top: `${(note.position.y / _MAP_SCALE_FACTOR)}px`,
|
||||||
left: `${(note.position.x / _MAP_SCALE_FACTOR)}px`,
|
left: `${(note.position.x / _MAP_SCALE_FACTOR)}px`,
|
||||||
|
zIndex: `${LayerEnum.MINIMAP + 1}`
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
{Object.keys(texts.texts.value).map((textKey: textCardType['id']) => {
|
||||||
|
const text = texts.texts.value[textKey]
|
||||||
|
const el = useRef(null)
|
||||||
|
|
||||||
|
function _handleItemClick(_e: MouseEvent) {
|
||||||
|
window.scrollTo({
|
||||||
|
left: text.position.x - ((viewportWidth / 2) - (text.dimensions.w / 2)),
|
||||||
|
top: text.position.y - ((viewportHeight / 2) - (text.dimensions.h / 2)),
|
||||||
|
behavior: 'smooth'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={el} className={"bg-indigo-500 hover:bg-blue-500 cursor-pointer"}
|
||||||
|
onclick={_handleItemClick}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: `${text.dimensions.w / _MAP_SCALE_FACTOR}px`,
|
||||||
|
height: `${text.dimensions.h / _MAP_SCALE_FACTOR}px`,
|
||||||
|
top: `${(text.position.y / _MAP_SCALE_FACTOR)}px`,
|
||||||
|
left: `${(text.position.x / _MAP_SCALE_FACTOR)}px`,
|
||||||
border: '1px solid #222',
|
border: '1px solid #222',
|
||||||
borderRadius: '2px',
|
borderRadius: '2px',
|
||||||
zIndex: `${LayerEnum.MINIMAP + 1}`
|
zIndex: `${LayerEnum.MINIMAP + 1}`
|
||||||
@ -103,16 +126,13 @@ export function MiniMap() {
|
|||||||
})}
|
})}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={'bg-blue-200 bg-opacity-10'}
|
className={'absolute bg-blue-200 bg-opacity-10 border dark:border-blue-800 border-blue-500 bg-blue-500 rounded'}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
|
||||||
width: `${viewportWidth / _MAP_SCALE_FACTOR}px`,
|
width: `${viewportWidth / _MAP_SCALE_FACTOR}px`,
|
||||||
height: `${viewportHeight / _MAP_SCALE_FACTOR}px`,
|
height: `${viewportHeight / _MAP_SCALE_FACTOR}px`,
|
||||||
top: `${scrollY.value / _MAP_SCALE_FACTOR}px`,
|
top: `${scrollY.value / _MAP_SCALE_FACTOR}px`,
|
||||||
left: `${scrollX.value / _MAP_SCALE_FACTOR}px`,
|
left: `${scrollX.value / _MAP_SCALE_FACTOR}px`,
|
||||||
border: '1px solid #777',
|
zIndex: `${LayerEnum.MINIMAP * 1000}`,
|
||||||
zIndex: `${LayerEnum.MINIMAP}`,
|
|
||||||
borderRadius: '2px'
|
|
||||||
}}></div>
|
}}></div>
|
||||||
</div >
|
</div >
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ import { NotesSigal, focusedItem } from "../signals"
|
|||||||
import { useDebounce } from "../utils/useDebounce"
|
import { useDebounce } from "../utils/useDebounce"
|
||||||
import notes, { NoteCardType } from "../signals/notes"
|
import notes, { NoteCardType } from "../signals/notes"
|
||||||
import { LayerEnum } from "../utils/enums"
|
import { LayerEnum } from "../utils/enums"
|
||||||
|
import { isTheme } from "../utils/isTheme"
|
||||||
|
|
||||||
namespace NoteCard {
|
namespace NoteCard {
|
||||||
export interface NoteCardProps {
|
export interface NoteCardProps {
|
||||||
@ -88,31 +89,30 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onmousedown={() => focusedItem.value = itemKey}
|
onmousedown={() => focusedItem.value = itemKey}
|
||||||
className="select-none transition flex flex-col justify-stretch shadow-lg rounded border border-[#3c3c3c] absolute"
|
className="text-[#333] dark:bg-[#111] dark:border-[#1c1c1c] bg-[#eee] select-none transition flex flex-col justify-stretch shadow-md rounded border border-[#ddd] absolute"
|
||||||
style={{
|
style={{
|
||||||
zIndex: `${focusedItem.value == itemKey ? LayerEnum.CARD_ELEVATED : LayerEnum.CARD}`,
|
zIndex: `${focusedItem.value == itemKey ? LayerEnum.CARD_ELEVATED : LayerEnum.CARD}`,
|
||||||
width: `${item.dimensions.w}px`,
|
width: `${item.dimensions.w}px`,
|
||||||
height: `${item.dimensions.h}px`,
|
height: `${item.dimensions.h}px`,
|
||||||
top: `${item.position.y}px`,
|
top: `${item.position.y}px`,
|
||||||
left: `${item.position.x}px`,
|
left: `${item.position.x}px`,
|
||||||
backgroundColor: '#181818',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex-1 flex flex-col gap-1">
|
<div className="flex-1 flex flex-col gap-1">
|
||||||
<div className="px-2 flex justify-between items-center cursor-move" onmousedown={_handleMouseDown}>
|
<div className="px-2 flex justify-between items-center cursor-move" onmousedown={_handleMouseDown}>
|
||||||
<div style={{
|
<div style={{
|
||||||
opacity: saved.value ? '0' : '100'
|
opacity: saved.value ? '0' : '100'
|
||||||
}} className={`rounded-full w-1 h-1 bg-white`}></div>
|
}} className={`rounded-full w-1 h-1 dark:bg-white bg-green-500`}></div>
|
||||||
<button className="text-md" onclick={(_e: Event) => {
|
<button className="text-md dark:text-[#777] text-black" onclick={(_e: Event) => {
|
||||||
NotesSigal.default.removeNote(item.id)
|
NotesSigal.default.removeNote(item.id)
|
||||||
NotesSigal.default.notes.notify()
|
NotesSigal.default.notes.notify()
|
||||||
updateLocalStorage()
|
updateLocalStorage()
|
||||||
}}>x</button>
|
}}>x</button>
|
||||||
</div>
|
</div>
|
||||||
<hr className="border border-[#3c3c3c]" />
|
<hr className="border dark:border-[#1c1c1c] border-[#ddd]" />
|
||||||
<textarea
|
<textarea
|
||||||
placeholder={"Todo: put some note here"}
|
placeholder={"Todo: put some note here"}
|
||||||
className="flex resize-none px-2 w-full h-full bg-transparent resize-none focus:outline-none text-gray-300"
|
className="flex resize-none px-2 w-full h-full bg-transparent resize-none focus:outline-none dark:text-gray-300"
|
||||||
value={item.contents}
|
value={item.contents}
|
||||||
onkeypress={() => { saved.value = false }}
|
onkeypress={() => { saved.value = false }}
|
||||||
onchange={(e) => {
|
onchange={(e) => {
|
||||||
@ -142,7 +142,7 @@ function ExpandIcon({ cb }: {
|
|||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="#333"
|
stroke={isTheme('dark') ? "#777" : "#999"}
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
|
108
src/components/TextItem.tsx
Normal file
108
src/components/TextItem.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { signal, useRef } from "kaioken"
|
||||||
|
import { TextSignal, focusedItem } from "../signals"
|
||||||
|
import { useDebounce } from "../utils/useDebounce"
|
||||||
|
import texts, { TextCardType } from "../signals/texts"
|
||||||
|
import { LayerEnum } from "../utils/enums"
|
||||||
|
|
||||||
|
namespace TextItem {
|
||||||
|
export interface TextCardProps {
|
||||||
|
key: TextCardType['id']
|
||||||
|
data: TextCardType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
|
||||||
|
const saved = signal(true)
|
||||||
|
const pressed = signal(false)
|
||||||
|
const newX = useRef(0)
|
||||||
|
const newY = useRef(0)
|
||||||
|
const offsetX = useRef(0)
|
||||||
|
const offsetY = useRef(0)
|
||||||
|
const initialResizeX = useRef(0)
|
||||||
|
const initialResizeY = useRef(0)
|
||||||
|
|
||||||
|
const { debounce } = useDebounce()
|
||||||
|
|
||||||
|
function updateLocalStorage(time?: number) {
|
||||||
|
debounce(() => {
|
||||||
|
localStorage.setItem("texts", JSON.stringify(texts.texts.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 }
|
||||||
|
|
||||||
|
TextSignal.default.updateTextProperty(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()
|
||||||
|
offsetX.current = e.offsetX
|
||||||
|
offsetY.current = e.offsetY
|
||||||
|
pressed.value = true
|
||||||
|
window.addEventListener('mousemove', _handleMouseMove)
|
||||||
|
window.addEventListener('mouseup', _handleMouseUp)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _handleResizeMove(e: MouseEvent) {
|
||||||
|
const { pageX, pageY } = e
|
||||||
|
const [newX, newY] = [initialResizeX.current - pageX, initialResizeY.current - pageY]
|
||||||
|
|
||||||
|
const newW = -newX + item.dimensions.w
|
||||||
|
const newH = -newY + item.dimensions.h
|
||||||
|
const newDim = { w: newW, h: newH }
|
||||||
|
|
||||||
|
TextSignal.default.updateTextProperty(itemKey, 'dimensions', newDim)
|
||||||
|
TextSignal.default.texts.notify()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _handleResizeMouseDown(e: MouseEvent) {
|
||||||
|
initialResizeX.current = e.pageX
|
||||||
|
initialResizeY.current = e.pageY
|
||||||
|
pressed.value = true
|
||||||
|
window.addEventListener('mousemove', _handleResizeMove)
|
||||||
|
window.addEventListener('mouseup', _handleResizeMouseUp)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _handleResizeMouseUp() {
|
||||||
|
pressed.value = false
|
||||||
|
updateLocalStorage()
|
||||||
|
window.removeEventListener('mousemove', _handleResizeMove)
|
||||||
|
window.removeEventListener('mouseup', _handleResizeMouseUp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onmousedown={() => focusedItem.value = itemKey}
|
||||||
|
className="select-none transition flex flex-col justify-stretch shadow-lg rounded border border-[#3c3c3c] absolute border-dashed"
|
||||||
|
style={{
|
||||||
|
zIndex: `${focusedItem.value == itemKey ? LayerEnum.CARD_ELEVATED : LayerEnum.CARD}`,
|
||||||
|
width: `${item.dimensions.w}px`,
|
||||||
|
height: `${item.dimensions.h}px`,
|
||||||
|
top: `${item.position.y}px`,
|
||||||
|
left: `${item.position.x}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 56 18">
|
||||||
|
<text x="0" y="15" fill={'white'}>{item.contents}</text>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div >
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ export function CardSelector() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className="z-50 flex gap-1 border border-[#9c9c9c] rounded-full fixed px-4 bg-[#181818] top-2 py-1 shadow-xl"
|
className="z-50 flex gap-1 border dark:border-[#3c3c3c] border-[#ddd] rounded-full fixed px-4 dark:bg-[#222] bg-[#eee] top-2 py-1 shadow-md"
|
||||||
style={{
|
style={{
|
||||||
left: `${window.innerWidth / 2 - (containerRef.current?.getBoundingClientRect().width ?? 1) / 2}px`
|
left: `${window.innerWidth / 2 - (containerRef.current?.getBoundingClientRect().width ?? 1) / 2}px`
|
||||||
}}>
|
}}>
|
||||||
|
@ -1,9 +1,30 @@
|
|||||||
|
import { TextSignal } from "../../signals";
|
||||||
|
import texts from "../../signals/texts";
|
||||||
|
import { updateLocalStorage } from "../../utils/localStorage";
|
||||||
import { defaultClassName } from "./utils";
|
import { defaultClassName } from "./utils";
|
||||||
|
|
||||||
export function TextButton() {
|
export function TextButton() {
|
||||||
|
|
||||||
|
function _handleClick(e: MouseEvent) {
|
||||||
|
TextSignal.default.addText({
|
||||||
|
type: "text",
|
||||||
|
title: "New Note",
|
||||||
|
contents: "todo: fill me",
|
||||||
|
position: {
|
||||||
|
x: e.pageX - 100,
|
||||||
|
y: e.pageY + (window.innerHeight / 2) - 100
|
||||||
|
},
|
||||||
|
dimensions: {
|
||||||
|
w: 200,
|
||||||
|
h: 100
|
||||||
|
}
|
||||||
|
})
|
||||||
|
updateLocalStorage("text", texts.texts.value)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
onclick={_handleClick}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
|
@ -6,3 +6,4 @@ export const canvasDimentsion = signal({ width: 3000, height: 3000 })
|
|||||||
|
|
||||||
export * as NotesSigal from "./notes"
|
export * as NotesSigal from "./notes"
|
||||||
export * as ImagesSignal from "./images"
|
export * as ImagesSignal from "./images"
|
||||||
|
export * as TextSignal from "./texts"
|
||||||
|
46
src/signals/texts.ts
Normal file
46
src/signals/texts.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { signal } from "kaioken"
|
||||||
|
import { Card } from "../types"
|
||||||
|
import { focusedItem } from "."
|
||||||
|
|
||||||
|
export type TextCardType = Card<"text">
|
||||||
|
|
||||||
|
const texts = signal<Record<TextCardType["id"], TextCardType>>({})
|
||||||
|
|
||||||
|
function loadLocalStorage() {
|
||||||
|
texts.value = JSON.parse(localStorage.getItem("texts") ?? "{}")
|
||||||
|
}
|
||||||
|
|
||||||
|
function addText(data: Omit<TextCardType, "id">) {
|
||||||
|
const newCard = {
|
||||||
|
...data,
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
}
|
||||||
|
texts.value[newCard.id] = newCard
|
||||||
|
texts.notify()
|
||||||
|
focusedItem.value = newCard.id
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeText(id: TextCardType["id"]) {
|
||||||
|
delete texts.value[id]
|
||||||
|
texts.notify()
|
||||||
|
}
|
||||||
|
function updateTextProperty<K extends keyof TextCardType>(
|
||||||
|
id: TextCardType["id"],
|
||||||
|
property: K,
|
||||||
|
data: TextCardType[K]
|
||||||
|
) {
|
||||||
|
const newData = {
|
||||||
|
...texts.value[id],
|
||||||
|
[property]: data,
|
||||||
|
}
|
||||||
|
texts.value[id] = newData
|
||||||
|
texts.notify()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
texts,
|
||||||
|
addText,
|
||||||
|
removeText,
|
||||||
|
updateTextProperty,
|
||||||
|
loadLocalStorage,
|
||||||
|
}
|
@ -23,11 +23,9 @@ html {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-color: #111;
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export type CardTypes = "note" | "image"
|
export type CardTypes = "note" | "image" | "text"
|
||||||
export type positionCoords = { x: number; y: number }
|
export type positionCoords = { x: number; y: number }
|
||||||
export type dimensionCoords = { w: number; h: number }
|
export type dimensionCoords = { w: number; h: number }
|
||||||
|
|
||||||
|
3
src/utils/isTheme.ts
Normal file
3
src/utils/isTheme.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function isTheme(value: "light" | "dark") {
|
||||||
|
return window.matchMedia(`(prefers-color-scheme: ${value})`).matches
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
|
import { CardTypes } from "../types"
|
||||||
|
|
||||||
export function updateLocalStorage(
|
export function updateLocalStorage(
|
||||||
location: "notes" | "images",
|
location: CardTypes,
|
||||||
collection: unknown[] | Record<string, unknown>
|
collection: unknown[] | Record<string, unknown>
|
||||||
) {
|
) {
|
||||||
localStorage.setItem(location, JSON.stringify(collection))
|
localStorage.setItem(location, JSON.stringify(collection))
|
||||||
|
Loading…
Reference in New Issue
Block a user