generated from Klectr/KTemplate
Compare commits
5 Commits
32b05d76f0
...
ad8ed2f596
Author | SHA1 | Date | |
---|---|---|---|
ad8ed2f596 | |||
2398d10c9f | |||
f7b392f909 | |||
ff7043a35d | |||
a662b5abc0 |
@ -1,4 +1,4 @@
|
|||||||
import { signal, useRef } from "kaioken"
|
import { signal, useCallback, useEffect, useRef } from "kaioken"
|
||||||
import { NotesSigal, focusedItem } from "../signals"
|
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"
|
||||||
@ -10,6 +10,7 @@ import { Divider } from "./Divider"
|
|||||||
import { ExportIcon } from "./icons/ExportIcon"
|
import { ExportIcon } from "./icons/ExportIcon"
|
||||||
import { createFileAndExport } from "../utils/createFileAndExport"
|
import { createFileAndExport } from "../utils/createFileAndExport"
|
||||||
import { ContextMenuPortal } from "./ContextMenuPortal"
|
import { ContextMenuPortal } from "./ContextMenuPortal"
|
||||||
|
import { HelpIcon } from "./icons/HelpIcon"
|
||||||
|
|
||||||
namespace NoteCard {
|
namespace NoteCard {
|
||||||
export interface NoteCardProps {
|
export interface NoteCardProps {
|
||||||
@ -28,6 +29,7 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
const initialResizeX = useRef(0)
|
const initialResizeX = useRef(0)
|
||||||
const initialResizeY = useRef(0)
|
const initialResizeY = useRef(0)
|
||||||
const openContextMenu = signal(false)
|
const openContextMenu = signal(false)
|
||||||
|
const showHelp = signal(false)
|
||||||
|
|
||||||
const { debounce } = useDebounce()
|
const { debounce } = useDebounce()
|
||||||
|
|
||||||
@ -111,10 +113,14 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
focusedItem.value = itemKey
|
focusedItem.value = itemKey
|
||||||
}
|
}
|
||||||
|
|
||||||
function _handleExportClick(_e: MouseEvent) {
|
function _exportFile() {
|
||||||
createFileAndExport("Note", item.contents, "text/markdown")
|
createFileAndExport("Note", item.contents, "text/markdown")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _handleExportClick(_e: MouseEvent) {
|
||||||
|
_exportFile()
|
||||||
|
}
|
||||||
|
|
||||||
function _handleMouseClick(e: MouseEvent) {
|
function _handleMouseClick(e: MouseEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
openContextMenu.value = !openContextMenu.value
|
openContextMenu.value = !openContextMenu.value
|
||||||
@ -124,6 +130,49 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
openContextMenu.value = false
|
openContextMenu.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _handleHelpHover() {
|
||||||
|
if (showHelp.value) return
|
||||||
|
showHelp.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function _handleHelpOut() {
|
||||||
|
if (!showHelp.value) return
|
||||||
|
showHelp.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const _handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||||
|
if (!e.ctrlKey) return
|
||||||
|
|
||||||
|
// TODO: add support for other os
|
||||||
|
// TODO: add modal popup
|
||||||
|
|
||||||
|
switch (e.key) {
|
||||||
|
case 'Delete':
|
||||||
|
e.preventDefault()
|
||||||
|
_handleClose(e)
|
||||||
|
break
|
||||||
|
case 'Backspace':
|
||||||
|
e.preventDefault()
|
||||||
|
_handleClose(e)
|
||||||
|
break
|
||||||
|
case 'e':
|
||||||
|
e.preventDefault()
|
||||||
|
_exportFile()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}, [itemKey, item.position, NotesSigal.default])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (focusedItem.value !== itemKey) return
|
||||||
|
window.addEventListener('keydown', _handleKeyDown)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('keydown', _handleKeyDown)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [focusedItem.value, itemKey])
|
||||||
|
|
||||||
const cardPositionStyle = {
|
const cardPositionStyle = {
|
||||||
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`,
|
||||||
@ -137,68 +186,89 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
oncontextmenu={_handleMouseClick}
|
<div
|
||||||
onmousedown={_handleFocusCard}
|
oncontextmenu={_handleMouseClick}
|
||||||
style={cardPositionStyle}
|
onmousedown={_handleFocusCard}
|
||||||
className="overflow-hidden text-[#333] dark:bg-[#1a1a1a] dark:border-[#1c1c1c] bg-[#efeff0] select-none transition flex flex-col justify-stretch shadow-md rounded border border-[#ddd] absolute"
|
style={cardPositionStyle}
|
||||||
>
|
className="overflow-hidden text-[#333] dark:bg-[#1a1a1a] dark:border-[#1c1c1c] bg-[#efeff0] select-none transition flex flex-col justify-stretch shadow-md rounded border border-[#ddd] absolute"
|
||||||
<div className="overflow-hidden flex-1 flex flex-col gap-1">
|
>
|
||||||
{/* Header Bar */}
|
<div className="overflow-hidden flex-1 flex flex-col gap-1">
|
||||||
<div className="px-2 flex justify-between items-center cursor-move" onmousedown={_handleMouseDown}>
|
{/* Header Bar */}
|
||||||
<div style={saveIndicatorStyle} className="rounded-full w-1 h-1 dark:bg-white bg-green-500"></div>
|
<div className="px-2 flex justify-between items-center cursor-move" onmousedown={_handleMouseDown}>
|
||||||
|
<div className={"flex gap-1 items-center"}>
|
||||||
<div className="flex gap-2">
|
<HelpIcon onMouseOver={_handleHelpHover} onMouseOut={_handleHelpOut} />
|
||||||
<div
|
{/* Save indicator*/}
|
||||||
onclick={_handleExportClick}
|
<div style={saveIndicatorStyle} className="rounded-full w-1 h-1 dark:bg-white bg-green-500"></div>
|
||||||
className="flex items-center">
|
|
||||||
<ExportIcon
|
|
||||||
className="dark:text-[#5c5c5c] cursor-pointer w-4 h-4 text-[#9c9c9c] hover:text-blue-500 transition-color duration-300"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div
|
||||||
|
onclick={_handleExportClick}
|
||||||
|
className="flex items-center">
|
||||||
|
<ExportIcon
|
||||||
|
className="dark:text-[#5c5c5c] cursor-pointer w-4 h-4 text-[#9c9c9c] hover:text-blue-500 transition-color duration-300"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<button className="text-md dark:text-[#777] text-black" onclick={_handleClose}>x</button>
|
<button className="text-md dark:text-[#777] text-black" onclick={_handleClose}>x</button>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr className="border dark:border-[#2c2c2c] border-[#ddd]" />
|
|
||||||
|
|
||||||
{/* Content Body */}
|
|
||||||
<MarkDownEditor initial={item.contents} onChange={_handleMdChange} />
|
|
||||||
<ExpandIcon cb={_handleResizeMouseDown} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ContextMenuPortal open={openContextMenu.value} closeAction={_handleContextClose}>
|
|
||||||
<div className="bg-[#3c3c3c] flex flex-col rounded">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<div className="text-md dark:text-[#999] text-black px-2 py-1">
|
|
||||||
{item.title}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr className="border dark:border-[#2c2c2c] border-[#ddd] m-0 p-0" />
|
<hr className="border dark:border-[#2c2c2c] border-[#ddd]" />
|
||||||
|
|
||||||
<div>
|
{/* Content Body */}
|
||||||
<ul>
|
<MarkDownEditor initial={item.contents} onChange={_handleMdChange} />
|
||||||
<li className="flex items-center gap-2 hover:bg-[#fff] dark:hover:bg-[#1a1a1a] cursor-pointer px-2 py-1">
|
<ExpandIcon cb={_handleResizeMouseDown} />
|
||||||
<button onclick={_handleClose} className="text-md dark:text-[#999] text-black">
|
</div>
|
||||||
Delete
|
|
||||||
</button>
|
<ContextMenuPortal open={openContextMenu.value} closeAction={_handleContextClose}>
|
||||||
</li>
|
<div className="bg-[#3c3c3c] flex flex-col rounded">
|
||||||
<li className="flex items-center gap-2 hover:bg-[#fff] dark:hover:bg-[#1a1a1a] cursor-pointer px-2 py-1">
|
<div className="flex justify-between items-center">
|
||||||
<button onclick={_handleExportClick} className="text-md dark:text-[#999] text-black">
|
<div className="text-md dark:text-[#999] text-black px-2 py-1">
|
||||||
export
|
{item.title}
|
||||||
</button>
|
</div>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
|
||||||
|
<hr className="border dark:border-[#2c2c2c] border-[#ddd] m-0 p-0" />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li className="flex items-center gap-2 hover:bg-[#fff] dark:hover:bg-[#1a1a1a] cursor-pointer px-2 py-1">
|
||||||
|
<button onclick={_handleClose} className="text-md dark:text-[#999] text-black">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-2 hover:bg-[#fff] dark:hover:bg-[#1a1a1a] cursor-pointer px-2 py-1">
|
||||||
|
<button onclick={_handleExportClick} className="text-md dark:text-[#999] text-black">
|
||||||
|
export
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ContextMenuPortal>
|
||||||
|
</div >
|
||||||
|
|
||||||
|
{/* HOTKEY PAPER */}
|
||||||
|
{showHelp.value &&
|
||||||
|
<div
|
||||||
|
className={"text-white absolute bg-[#1c1c1c] rounded-md p-1 z-[1000] border border-blue-500"}
|
||||||
|
style={{
|
||||||
|
top: `${item.position.y}px`,
|
||||||
|
left: `${item.position.x - 120}px`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col text-sm dark:text-[#999] text-black">
|
||||||
|
<small>ctrl + del = delete</small>
|
||||||
|
<small>ctrl + back = delete</small>
|
||||||
|
<small>ctrl + e = export</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ContextMenuPortal>
|
}
|
||||||
</div >
|
</>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,3 +298,4 @@ function ExpandIcon({ cb }: {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
35
src/components/icons/HelpIcon.tsx
Normal file
35
src/components/icons/HelpIcon.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
namespace HelpIcon {
|
||||||
|
export interface Props {
|
||||||
|
onMouseOver?: () => void
|
||||||
|
onMouseOut?: () => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function HelpIcon({ onMouseOver, onMouseOut }: HelpIcon.Props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
onmouseout={onMouseOut}
|
||||||
|
onmouseover={onMouseOver}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
className="w-4 h-4 hover:text-blue-500 cursor-pointer"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
|
||||||
|
<path
|
||||||
|
d="M12 17h.01"
|
||||||
|
/>
|
||||||
|
</svg>)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user