generated from Klectr/KTemplate
Compare commits
3 Commits
ad8ed2f596
...
a506da11ad
Author | SHA1 | Date | |
---|---|---|---|
a506da11ad | |||
958124cd9e | |||
a7a84a24e1 |
28
eslint.config.js
Normal file
28
eslint.config.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import globals from "globals"
|
||||||
|
import pluginJs from "@eslint/js"
|
||||||
|
import tseslint from "typescript-eslint"
|
||||||
|
import pluginReact from "eslint-plugin-react"
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{ files: ["./src/**/*.{js,mjs,cjs,ts,jsx,tsx}"] },
|
||||||
|
{ plugins: { react: pluginReact } },
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
pluginReact.configs.flat.recommended,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"react/no-unknown-property": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
@ -18,14 +18,21 @@
|
|||||||
"tiny-markdown-editor": "^0.1.26"
|
"tiny-markdown-editor": "^0.1.26"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.13.0",
|
||||||
"@tauri-apps/cli": "^1",
|
"@tauri-apps/cli": "^1",
|
||||||
"autoprefixer": "^10.4.18",
|
"autoprefixer": "^10.4.18",
|
||||||
"commitizen": "^4.3.1",
|
"commitizen": "^4.3.1",
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
|
"eslint": "^9.13.0",
|
||||||
|
"eslint-plugin-react": "^7.37.2",
|
||||||
|
"globals": "^15.11.0",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
|
"typescript-eslint": "^8.11.0",
|
||||||
"vite": "^5.0.0",
|
"vite": "^5.0.0",
|
||||||
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-kaioken": "^0.13.1"
|
"vite-plugin-kaioken": "^0.13.1"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import { useClickOutside, useKeyStroke, useMouse } from "@kaioken-core/hooks";
|
import { useClickOutside, useMouse } from "@kaioken-core/hooks";
|
||||||
import { Portal, signal, useEffect, useRef } from "kaioken";
|
import { Portal, signal, useEffect, useRef } from "kaioken";
|
||||||
|
|
||||||
namespace ContextMenuPortal {
|
interface ContextMenuPortalProps {
|
||||||
export interface Props {
|
|
||||||
children: JSX.Children
|
children: JSX.Children
|
||||||
open: boolean
|
open: boolean
|
||||||
closeAction: (() => void) | null | undefined
|
closeAction: (() => void) | null | undefined
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export function ContextMenuPortal({ children, open, closeAction }: ContextMenuPortal.Props) {
|
export function ContextMenuPortal({ children, open, closeAction }: ContextMenuPortalProps) {
|
||||||
const { mouse } = useMouse()
|
const { mouse } = useMouse()
|
||||||
const pos = signal({ x: 0, y: 0 })
|
const pos = signal({ x: 0, y: 0 })
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
|
@ -6,15 +6,14 @@ import images, { ImageCardType } from "../signals/images"
|
|||||||
import { updateLocalStorage } from "../utils/localStorage"
|
import { updateLocalStorage } from "../utils/localStorage"
|
||||||
import { useThemeDetector } from "../utils/useThemeDetector"
|
import { useThemeDetector } from "../utils/useThemeDetector"
|
||||||
import { ContextMenuPortal } from "./ContextMenuPortal"
|
import { ContextMenuPortal } from "./ContextMenuPortal"
|
||||||
|
import { CardTypes } from "../types"
|
||||||
|
|
||||||
namespace ImageCard {
|
interface ImageCardProps {
|
||||||
export interface ImageCardProps {
|
|
||||||
key: ImageCardType['id']
|
key: ImageCardType['id']
|
||||||
data: ImageCardType
|
data: ImageCardType
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps) {
|
export function ImageCard({ key: itemKey, data: item }: ImageCardProps) {
|
||||||
const { debounce } = useDebounce()
|
const { debounce } = useDebounce()
|
||||||
const pressed = signal(false)
|
const pressed = signal(false)
|
||||||
const newX = useRef(0)
|
const newX = useRef(0)
|
||||||
@ -27,7 +26,7 @@ export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps
|
|||||||
|
|
||||||
function debounceLSUpdate(time?: number) {
|
function debounceLSUpdate(time?: number) {
|
||||||
debounce(() => {
|
debounce(() => {
|
||||||
updateLocalStorage("images", images.images.value)
|
updateLocalStorage(CardTypes.IMAGES, images.images).notify()
|
||||||
}, time)
|
}, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +89,7 @@ export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps
|
|||||||
window.removeEventListener('mouseup', _handleResizeMouseUp)
|
window.removeEventListener('mouseup', _handleResizeMouseUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _handleClose(_e: Event) {
|
function _handleClose() {
|
||||||
ImagesSignal.default.removeImage(item.id)
|
ImagesSignal.default.removeImage(item.id)
|
||||||
ImagesSignal.default.images.notify()
|
ImagesSignal.default.images.notify()
|
||||||
debounceLSUpdate()
|
debounceLSUpdate()
|
||||||
@ -161,7 +160,7 @@ export function ImageCard({ key: itemKey, data: item }: ImageCard.ImageCardProps
|
|||||||
|
|
||||||
|
|
||||||
function ExpandIcon({ cb }: {
|
function ExpandIcon({ cb }: {
|
||||||
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined
|
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => void) | null | undefined
|
||||||
}) {
|
}) {
|
||||||
const isDarkTheme = useThemeDetector()
|
const isDarkTheme = useThemeDetector()
|
||||||
return (
|
return (
|
||||||
|
@ -110,7 +110,7 @@ function getInitialPosition(canvasDimensions: typeof canvasDimentsion): ScrollTo
|
|||||||
try {
|
try {
|
||||||
initPosition = JSON.parse(localStorage.getItem("pos") ?? "")
|
initPosition = JSON.parse(localStorage.getItem("pos") ?? "")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("no local storage for pos")
|
console.error("no local storage for pos " + e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initPosition) return defaultScroll
|
if (!initPosition) return defaultScroll
|
||||||
|
@ -2,14 +2,12 @@ import { Editor, Listener } from 'tiny-markdown-editor'
|
|||||||
import './md.css'
|
import './md.css'
|
||||||
import { useEffect, useRef } from "kaioken"
|
import { useEffect, useRef } from "kaioken"
|
||||||
|
|
||||||
namespace MarkDownEditor {
|
interface MarkDownEditorProps {
|
||||||
export interface Props {
|
|
||||||
initial: string
|
initial: string
|
||||||
onChange: Listener<'change'>
|
onChange: Listener<'change'>
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MarkDownEditor({ initial, onChange }: MarkDownEditor.Props) {
|
export function MarkDownEditor({ initial, onChange }: MarkDownEditorProps) {
|
||||||
const elRef = useRef<HTMLDivElement>(null)
|
const elRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -24,7 +24,7 @@ export function MiniMap() {
|
|||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function _handleScroll(_e: Event) {
|
function _handleScroll() {
|
||||||
scrollX.value = window.scrollX
|
scrollX.value = window.scrollX
|
||||||
scrollY.value = window.scrollY
|
scrollY.value = window.scrollY
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ export function MiniMap() {
|
|||||||
const image = images.images.value[imageKey]
|
const image = images.images.value[imageKey]
|
||||||
const el = useRef(null)
|
const el = useRef(null)
|
||||||
|
|
||||||
function _handleItemClick(_e: MouseEvent) {
|
function _handleItemClick() {
|
||||||
const newLeft = image.position.x - ((viewportWidth / 2) - (image.dimensions.w / 2))
|
const newLeft = image.position.x - ((viewportWidth / 2) - (image.dimensions.w / 2))
|
||||||
const newTop = image.position.y - ((viewportHeight / 2) - (image.dimensions.h / 2))
|
const newTop = image.position.y - ((viewportHeight / 2) - (image.dimensions.h / 2))
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ export function MiniMap() {
|
|||||||
const newZIndex = LayerEnum.MINIMAP + 1
|
const newZIndex = LayerEnum.MINIMAP + 1
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<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"}
|
<div key={imageKey} 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={{
|
||||||
width: `${newWidth}px`,
|
width: `${newWidth}px`,
|
||||||
@ -82,7 +82,7 @@ export function MiniMap() {
|
|||||||
{Object.keys(notes.notes.value).map((noteKey: NoteCardType['id']) => {
|
{Object.keys(notes.notes.value).map((noteKey: NoteCardType['id']) => {
|
||||||
const note = notes.notes.value[noteKey]
|
const note = notes.notes.value[noteKey]
|
||||||
|
|
||||||
function _handleItemClick(_e: MouseEvent) {
|
function _handleItemClick() {
|
||||||
const newLeft = note.position.x - ((viewportWidth / 2) - (note.dimensions.w / 2))
|
const newLeft = note.position.x - ((viewportWidth / 2) - (note.dimensions.w / 2))
|
||||||
const newTop = note.position.y - ((viewportHeight / 2) - (note.dimensions.h / 2))
|
const newTop = note.position.y - ((viewportHeight / 2) - (note.dimensions.h / 2))
|
||||||
window.scrollTo({
|
window.scrollTo({
|
||||||
@ -99,7 +99,7 @@ export function MiniMap() {
|
|||||||
const newZIndex = LayerEnum.MINIMAP + 1
|
const newZIndex = LayerEnum.MINIMAP + 1
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"absolute dark:bg-gray-500 bg-gray-300 hover:bg-blue-500 cursor-pointer border dark:border-[#222] border-gray-400 rounded"}
|
<div key={noteKey} className={"absolute dark:bg-gray-500 bg-gray-300 hover:bg-blue-500 cursor-pointer border dark:border-[#222] border-gray-400 rounded"}
|
||||||
onclick={_handleItemClick}
|
onclick={_handleItemClick}
|
||||||
style={{
|
style={{
|
||||||
width: `${newWidth}px`,
|
width: `${newWidth}px`,
|
||||||
@ -116,7 +116,7 @@ export function MiniMap() {
|
|||||||
const text = texts.texts.value[textKey]
|
const text = texts.texts.value[textKey]
|
||||||
const el = useRef(null)
|
const el = useRef(null)
|
||||||
|
|
||||||
function _handleItemClick(_e: MouseEvent) {
|
function _handleItemClick() {
|
||||||
window.scrollTo({
|
window.scrollTo({
|
||||||
left: text.position.x - ((viewportWidth / 2) - (text.dimensions.w / 2)),
|
left: text.position.x - ((viewportWidth / 2) - (text.dimensions.w / 2)),
|
||||||
top: text.position.y - ((viewportHeight / 2) - (text.dimensions.h / 2)),
|
top: text.position.y - ((viewportHeight / 2) - (text.dimensions.h / 2)),
|
||||||
@ -125,7 +125,7 @@ export function MiniMap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={el} className={"bg-indigo-500 hover:bg-blue-500 cursor-pointer rounded"}
|
<div key={textKey} ref={el} className={"bg-indigo-500 hover:bg-blue-500 cursor-pointer rounded"}
|
||||||
onclick={_handleItemClick}
|
onclick={_handleItemClick}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -12,14 +12,12 @@ import { createFileAndExport } from "../utils/createFileAndExport"
|
|||||||
import { ContextMenuPortal } from "./ContextMenuPortal"
|
import { ContextMenuPortal } from "./ContextMenuPortal"
|
||||||
import { HelpIcon } from "./icons/HelpIcon"
|
import { HelpIcon } from "./icons/HelpIcon"
|
||||||
|
|
||||||
namespace NoteCard {
|
interface NoteCardProps {
|
||||||
export interface NoteCardProps {
|
|
||||||
key: NoteCardType['id']
|
key: NoteCardType['id']
|
||||||
data: NoteCardType
|
data: NoteCardType
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
export function NoteCard({ key: itemKey, data: item }: NoteCardProps) {
|
||||||
const saved = signal(true)
|
const saved = signal(true)
|
||||||
const pressed = signal(false)
|
const pressed = signal(false)
|
||||||
const newX = useRef(0)
|
const newX = useRef(0)
|
||||||
@ -103,7 +101,7 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
saved.value = false
|
saved.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function _handleClose(_e: Event) {
|
function _handleClose() {
|
||||||
NotesSigal.default.removeNote(item.id)
|
NotesSigal.default.removeNote(item.id)
|
||||||
NotesSigal.default.notes.notify()
|
NotesSigal.default.notes.notify()
|
||||||
updateLocalStorage()
|
updateLocalStorage()
|
||||||
@ -117,7 +115,7 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
createFileAndExport("Note", item.contents, "text/markdown")
|
createFileAndExport("Note", item.contents, "text/markdown")
|
||||||
}
|
}
|
||||||
|
|
||||||
function _handleExportClick(_e: MouseEvent) {
|
function _handleExportClick() {
|
||||||
_exportFile()
|
_exportFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,11 +147,11 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'Delete':
|
case 'Delete':
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
_handleClose(e)
|
_handleClose()
|
||||||
break
|
break
|
||||||
case 'Backspace':
|
case 'Backspace':
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
_handleClose(e)
|
_handleClose()
|
||||||
break
|
break
|
||||||
case 'e':
|
case 'e':
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@ -273,7 +271,7 @@ export function NoteCard({ key: itemKey, data: item }: NoteCard.NoteCardProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ExpandIcon({ cb }: {
|
function ExpandIcon({ cb }: {
|
||||||
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined
|
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => void) | null | undefined
|
||||||
}) {
|
}) {
|
||||||
const isDarkTheme = useThemeDetector()
|
const isDarkTheme = useThemeDetector()
|
||||||
|
|
||||||
|
@ -3,17 +3,15 @@ import { TextSignal, focusedItem } from "../signals"
|
|||||||
import { useDebounce } from "../utils/useDebounce"
|
import { useDebounce } from "../utils/useDebounce"
|
||||||
import texts, { TextCardType } from "../signals/texts"
|
import texts, { TextCardType } from "../signals/texts"
|
||||||
import { LayerEnum } from "../utils/enums"
|
import { LayerEnum } from "../utils/enums"
|
||||||
import { Card } from "../types"
|
import { Card, CardTypes } from "../types"
|
||||||
import { useThemeDetector } from "../utils/useThemeDetector"
|
import { useThemeDetector } from "../utils/useThemeDetector"
|
||||||
|
|
||||||
namespace TextItem {
|
interface TextCardProps {
|
||||||
export interface TextCardProps {
|
|
||||||
key: TextCardType['id']
|
key: TextCardType['id']
|
||||||
data: TextCardType
|
data: TextCardType
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
|
export function TextItem({ key: itemKey, data: item }: TextCardProps) {
|
||||||
const { debounce } = useDebounce()
|
const { debounce } = useDebounce()
|
||||||
const pressed = signal(false)
|
const pressed = signal(false)
|
||||||
const newX = useRef(0)
|
const newX = useRef(0)
|
||||||
@ -28,7 +26,7 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
|
|||||||
const elDems = elRef.current?.getBoundingClientRect()
|
const elDems = elRef.current?.getBoundingClientRect()
|
||||||
const elW = elDems?.width ?? 100
|
const elW = elDems?.width ?? 100
|
||||||
const elH = elDems?.height ?? 100
|
const elH = elDems?.height ?? 100
|
||||||
const newDems: Card<'texts'>['dimensions'] = { w: elW, h: elH }
|
const newDems: Card<CardTypes.TEXTS>['dimensions'] = { w: elW, h: elH }
|
||||||
TextSignal.default.updateTextProperty(itemKey, 'dimensions', newDems)
|
TextSignal.default.updateTextProperty(itemKey, 'dimensions', newDems)
|
||||||
TextSignal.default.texts.notify()
|
TextSignal.default.texts.notify()
|
||||||
}, [elRef.current, item.fontSize])
|
}, [elRef.current, item.fontSize])
|
||||||
@ -37,7 +35,7 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
|
|||||||
|
|
||||||
function updateLocalStorage(time?: number) {
|
function updateLocalStorage(time?: number) {
|
||||||
debounce(() => {
|
debounce(() => {
|
||||||
localStorage.setItem("texts", JSON.stringify(texts.texts.value))
|
localStorage.setItem(CardTypes.TEXTS, JSON.stringify(texts.texts.value))
|
||||||
}, time)
|
}, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +85,7 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
|
|||||||
window.addEventListener('mouseup', _handleResizeMouseUp)
|
window.addEventListener('mouseup', _handleResizeMouseUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _handleResizeMouseUp(_e: MouseEvent) {
|
function _handleResizeMouseUp() {
|
||||||
pressed.value = false
|
pressed.value = false
|
||||||
window.removeEventListener('mousemove', _handleResizeMove)
|
window.removeEventListener('mousemove', _handleResizeMove)
|
||||||
window.removeEventListener('mouseup', _handleResizeMouseUp)
|
window.removeEventListener('mouseup', _handleResizeMouseUp)
|
||||||
@ -157,7 +155,7 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
|
|||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
ref={pRef}
|
ref={pRef}
|
||||||
//@ts-expect-error
|
//@ts-expect-error oninput doesnt exist on the props type
|
||||||
oninput={_handleContentInput}
|
oninput={_handleContentInput}
|
||||||
contentEditable
|
contentEditable
|
||||||
className={'text-p inline-block px-2 w-full select-none drop-shadow relative'}>
|
className={'text-p inline-block px-2 w-full select-none drop-shadow relative'}>
|
||||||
@ -171,13 +169,12 @@ export function TextItem({ key: itemKey, data: item }: TextItem.TextCardProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace CloseIcon {
|
interface CloseIconProps {
|
||||||
export interface Props {
|
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => void) | null | undefined,
|
||||||
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined,
|
|
||||||
item: TextSignal.TextCardType
|
item: TextSignal.TextCardType
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function CloseIcon({ item, cb }: CloseIcon.Props) {
|
|
||||||
|
function CloseIcon({ item, cb }: CloseIconProps) {
|
||||||
const isDark = useThemeDetector()
|
const isDark = useThemeDetector()
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
@ -204,13 +201,12 @@ function CloseIcon({ item, cb }: CloseIcon.Props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ExpandIcon {
|
interface ExpandIconProps {
|
||||||
export interface Props {
|
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => void) | null | undefined,
|
||||||
cb: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined,
|
|
||||||
item: TextSignal.TextCardType
|
item: TextSignal.TextCardType
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function ExpandIcon({ cb, item }: ExpandIcon.Props) {
|
|
||||||
|
function ExpandIcon({ cb, item }: ExpandIconProps) {
|
||||||
const isDark = useThemeDetector()
|
const isDark = useThemeDetector()
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
} from "kaioken"
|
} from "kaioken"
|
||||||
|
import { noop } from "kaioken/utils"
|
||||||
|
|
||||||
type Toast = {
|
type Toast = {
|
||||||
ts: number
|
ts: number
|
||||||
@ -21,11 +22,15 @@ const defaultDuration = 3000
|
|||||||
|
|
||||||
const ToastContext = createContext<{
|
const ToastContext = createContext<{
|
||||||
showToast: (type: Toast["type"], message: string) => void
|
showToast: (type: Toast["type"], message: string) => void
|
||||||
}>(null as any)
|
}>({ showToast: noop })
|
||||||
|
|
||||||
export const useToast = () => useContext(ToastContext)
|
export const useToast = () => useContext(ToastContext)
|
||||||
|
|
||||||
export const ToastContextProvider: Kaioken.FC = ({ children }) => {
|
interface ToastProviderProps {
|
||||||
|
children: JSX.Children
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ToastContextProvider({ children }: ToastProviderProps) {
|
||||||
const [toasts, setToasts] = useState<Toast[]>([])
|
const [toasts, setToasts] = useState<Toast[]>([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ImagesSignal } from "../../signals"
|
import { ImagesSignal } from "../../signals"
|
||||||
import images from "../../signals/images"
|
import images from "../../signals/images"
|
||||||
|
import { CardTypes } from "../../types"
|
||||||
import { updateLocalStorage } from "../../utils/localStorage"
|
import { updateLocalStorage } from "../../utils/localStorage"
|
||||||
import { useToast } from "../Toast"
|
import { useToast } from "../Toast"
|
||||||
import { Tooltip } from "./Tooltip"
|
import { Tooltip } from "./Tooltip"
|
||||||
@ -13,12 +14,16 @@ export function ImageCardButton() {
|
|||||||
input.accept = "image/*"
|
input.accept = "image/*"
|
||||||
input.multiple = false
|
input.multiple = false
|
||||||
|
|
||||||
input.onchange = (e: any) => {
|
input.onchange = (e: Event) => {
|
||||||
const file = e.target.files[0]
|
const el = e.target as HTMLInputElement
|
||||||
|
if (!el.files?.length) return
|
||||||
|
if (!el.files.length) return
|
||||||
|
const file = el.files[0]
|
||||||
|
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
reader.readAsDataURL(file)
|
reader.readAsDataURL(file)
|
||||||
reader.onload = function(readerEvent) {
|
reader.onload = function(readerEvent) {
|
||||||
let image = document.createElement('img')
|
const image = document.createElement('img')
|
||||||
image.onload = function() {
|
image.onload = function() {
|
||||||
const { width, height } = image
|
const { width, height } = image
|
||||||
|
|
||||||
@ -33,7 +38,7 @@ export function ImageCardButton() {
|
|||||||
if (!img) return
|
if (!img) return
|
||||||
|
|
||||||
const imgId = ImagesSignal.default.addImage({
|
const imgId = ImagesSignal.default.addImage({
|
||||||
type: "image",
|
type: CardTypes.IMAGES,
|
||||||
title: "New Image",
|
title: "New Image",
|
||||||
contents: content as string,
|
contents: content as string,
|
||||||
position: {
|
position: {
|
||||||
@ -47,7 +52,7 @@ export function ImageCardButton() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateLocalStorage("images", images.images.value)
|
updateLocalStorage(CardTypes.IMAGES, images.images).notify()
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof DOMException) {
|
if (e instanceof DOMException) {
|
||||||
if (e.name !== 'QuotaExceededError') return
|
if (e.name !== 'QuotaExceededError') return
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import images from "../../signals/images"
|
import images, { ImageCardType } from "../../signals/images"
|
||||||
import notes from "../../signals/notes"
|
import notes, { NoteCardType } from "../../signals/notes"
|
||||||
import { Card } from "../../types"
|
import texts, { TextCardType } from "../../signals/texts"
|
||||||
|
import { Card, CardTypes } from "../../types"
|
||||||
import { convertBase64ToJson } from "../../utils/convertBase64ToJson"
|
import { convertBase64ToJson } from "../../utils/convertBase64ToJson"
|
||||||
import { updateLocalStorage } from "../../utils/localStorage"
|
import { updateLocalStorage } from "../../utils/localStorage"
|
||||||
import { Tooltip } from "./Tooltip"
|
import { Tooltip } from "./Tooltip"
|
||||||
@ -9,36 +10,55 @@ import { defaultClassName } from "./utils"
|
|||||||
export function ImportButton() {
|
export function ImportButton() {
|
||||||
|
|
||||||
function _handleImport() {
|
function _handleImport() {
|
||||||
|
// guard clause to prevent overwriting existing cards
|
||||||
|
if (images.images.value || notes.notes.value) {
|
||||||
|
const isConfirmed = confirm("Are you sure you want to overwrite your existing cards?")
|
||||||
|
if (!isConfirmed) return
|
||||||
|
}
|
||||||
|
|
||||||
const input = document.createElement('input')
|
const input = document.createElement('input')
|
||||||
input.type = 'file'
|
input.type = 'file'
|
||||||
input.accept = ".json"
|
input.accept = ".json"
|
||||||
input.multiple = false
|
input.multiple = false
|
||||||
input.onchange = (e: any) => {
|
input.onchange = (e: Event) => {
|
||||||
const file = e.target.files[0]
|
if (e.target === null) return
|
||||||
|
const el = e.target as HTMLInputElement
|
||||||
|
if (!el.files?.length) return
|
||||||
|
const file = el.files[0]
|
||||||
|
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
reader.readAsDataURL(file)
|
reader.readAsDataURL(file)
|
||||||
reader.onload = function(readerEvent) {
|
reader.onload = function(readerEvent) {
|
||||||
let content = readerEvent.target?.result;
|
let content = readerEvent.target?.result;
|
||||||
// get only the base64 parts and not any identifiers
|
// get only the base64 parts and not any identifiers
|
||||||
content = (content as string).split(',')[1]
|
content = (content as string).split(',')[1]
|
||||||
const data: Record<string, Card<'notes'> | Card<'images'>> = convertBase64ToJson(content)
|
const data: Record<string, Card<CardTypes>> = convertBase64ToJson(content)
|
||||||
for (let key in data) {
|
console.log(data)
|
||||||
|
for (const key in data) {
|
||||||
const item = data[key]
|
const item = data[key]
|
||||||
if (item.type == 'images') {
|
|
||||||
const { id, ...rest } = item
|
const { id, ...rest } = item
|
||||||
images.addImage(rest)
|
console.log(id, rest)
|
||||||
}
|
switch (item.type) {
|
||||||
|
case CardTypes.IMAGES:
|
||||||
if (item.type == 'notes') {
|
console.log("adding image: ", rest)
|
||||||
const { id, ...rest } = item
|
images.addImage(rest as ImageCardType)
|
||||||
notes.addNote(rest)
|
break;
|
||||||
|
case CardTypes.NOTES:
|
||||||
|
notes.addNote(rest as NoteCardType)
|
||||||
|
break;
|
||||||
|
case CardTypes.TEXTS:
|
||||||
|
texts.addText(rest as TextCardType)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLocalStorage('notes', notes.notes.value)
|
console.log("images: ", images.images.value)
|
||||||
updateLocalStorage('images', images.images.value)
|
|
||||||
notes.notes.notify()
|
updateLocalStorage(CardTypes.NOTES, notes.notes).notify()
|
||||||
images.images.notify()
|
updateLocalStorage(CardTypes.IMAGES, images.images).notify()
|
||||||
|
updateLocalStorage(CardTypes.TEXTS, texts.texts).notify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input.click()
|
input.click()
|
||||||
|
@ -21,7 +21,7 @@ export function TextButton() {
|
|||||||
h: 100
|
h: 100
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
updateLocalStorage("texts", texts.texts.value)
|
updateLocalStorage("texts", texts.texts).notify()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
namespace ExportIcon {
|
interface ExportIconProps {
|
||||||
export interface Props {
|
|
||||||
className?: string
|
className?: string
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export function ExportIcon({ className }: ExportIcon.Props) {
|
|
||||||
|
export function ExportIcon({ className }: ExportIconProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
namespace HelpIcon {
|
interface HelpIconProps {
|
||||||
export interface Props {
|
|
||||||
onMouseOver?: () => void
|
onMouseOver?: () => void
|
||||||
onMouseOut?: () => void
|
onMouseOut?: () => void
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export function HelpIcon({ onMouseOver, onMouseOut }: HelpIcon.Props) {
|
|
||||||
|
export function HelpIcon({ onMouseOver, onMouseOut }: HelpIconProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { signal } from "kaioken"
|
import { signal } from "kaioken"
|
||||||
import { Card } from "../types"
|
import { Card, CardTypes } from "../types"
|
||||||
import { focusedItem } from "."
|
import { focusedItem } from "."
|
||||||
|
|
||||||
export type ImageCardType = Card<"images">
|
export type ImageCardType = Card<CardTypes.IMAGES>
|
||||||
|
|
||||||
const images = signal<Record<ImageCardType["id"], ImageCardType>>({})
|
const images = signal<Record<ImageCardType["id"], ImageCardType>>({})
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ function addImage(data: Omit<ImageCardType, "id">) {
|
|||||||
...data,
|
...data,
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
}
|
}
|
||||||
|
console.log("adding image: ", newCard)
|
||||||
images.value[newCard.id] = newCard
|
images.value[newCard.id] = newCard
|
||||||
images.notify()
|
images.notify()
|
||||||
focusedItem.value = newCard.id
|
focusedItem.value = newCard.id
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { signal } from "kaioken"
|
import { signal } from "kaioken"
|
||||||
import { Card } from "../types"
|
import { Card, CardTypes } from "../types"
|
||||||
import { focusedItem } from "."
|
import { focusedItem } from "."
|
||||||
|
|
||||||
export type NoteCardType = Card<"notes">
|
export type NoteCardType = Card<CardTypes.NOTES>
|
||||||
|
|
||||||
const notes = signal<Record<NoteCardType["id"], NoteCardType>>({})
|
const notes = signal<Record<NoteCardType["id"], NoteCardType>>({})
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { signal } from "kaioken"
|
import { signal } from "kaioken"
|
||||||
import { Card } from "../types"
|
import { Card, CardTypes } from "../types"
|
||||||
import { focusedItem } from "."
|
import { focusedItem } from "."
|
||||||
|
|
||||||
export type TextCardType = Card<"texts"> & {
|
export type TextCardType = Card<CardTypes.TEXTS> & {
|
||||||
fontSize: number
|
fontSize: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
export type CardTypes = "notes" | "images" | "texts"
|
|
||||||
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 }
|
||||||
|
|
||||||
|
export enum CardTypes {
|
||||||
|
NOTES = "notes",
|
||||||
|
IMAGES = "images",
|
||||||
|
TEXTS = "texts",
|
||||||
|
}
|
||||||
|
|
||||||
type Base64 = string
|
type Base64 = string
|
||||||
|
|
||||||
export interface Card<Ttype extends CardTypes> {
|
export interface Card<TCard extends CardTypes> {
|
||||||
id: string
|
id: string
|
||||||
type: Ttype
|
type: TCard
|
||||||
title: string
|
title: string
|
||||||
contents: Ttype extends "image" ? Base64 : string
|
contents: TCard extends "image" ? Base64 : string
|
||||||
position: positionCoords
|
position: positionCoords
|
||||||
dimensions: dimensionCoords
|
dimensions: dimensionCoords
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
import { CardTypes } from "../types"
|
import { Signal } from "kaioken"
|
||||||
|
import { Card, CardTypes } from "../types"
|
||||||
|
|
||||||
export function updateLocalStorage(
|
export function updateLocalStorage(
|
||||||
location: CardTypes,
|
location: CardTypes,
|
||||||
collection: unknown[] | Record<string, unknown>
|
collection: Signal<Record<string, Card<CardTypes>>>
|
||||||
) {
|
) {
|
||||||
localStorage.setItem(location, JSON.stringify(collection))
|
try {
|
||||||
|
localStorage.setItem(location, JSON.stringify(collection.value))
|
||||||
|
} catch (e) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Could not update local storage " + e,
|
||||||
|
"LocalStorageError"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return collection
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@ import { sideEffectsEnabled, useHook } from "kaioken"
|
|||||||
import { noop } from "kaioken/utils"
|
import { noop } from "kaioken/utils"
|
||||||
|
|
||||||
type UseDebounceState = {
|
type UseDebounceState = {
|
||||||
timer: number
|
timer: Timer | undefined
|
||||||
debounce: (this: any, func: Function, timeout?: number) => void
|
debounce: (func: (...args: unknown[]) => void, timeout?: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function createState(): UseDebounceState {
|
function createState(): UseDebounceState {
|
||||||
return { timer: 0, debounce: noop }
|
return { timer: undefined, debounce: noop }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDebounce() {
|
export function useDebounce() {
|
||||||
@ -17,8 +17,7 @@ export function useDebounce() {
|
|||||||
if (!isInit) return { timer: hook.timer, debounce: hook.debounce }
|
if (!isInit) return { timer: hook.timer, debounce: hook.debounce }
|
||||||
|
|
||||||
hook.debounce = function debounce(
|
hook.debounce = function debounce(
|
||||||
this: any,
|
func: (...args: unknown[]) => void,
|
||||||
func: Function,
|
|
||||||
timeout = 300
|
timeout = 300
|
||||||
) {
|
) {
|
||||||
clearTimeout(hook.timer)
|
clearTimeout(hook.timer)
|
||||||
|
@ -1,9 +1,21 @@
|
|||||||
import { defineConfig } from "vite"
|
import { defineConfig } from "vite"
|
||||||
import kaioken from "vite-plugin-kaioken"
|
import kaioken from "vite-plugin-kaioken"
|
||||||
|
import eslint from "vite-plugin-eslint"
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [kaioken()],
|
plugins: [
|
||||||
|
kaioken(),
|
||||||
|
eslint({
|
||||||
|
cache: true,
|
||||||
|
exclude: ["src-tauri/**/*"],
|
||||||
|
emitError: true,
|
||||||
|
emitWarning: true,
|
||||||
|
failOnError: false,
|
||||||
|
failOnWarning: false,
|
||||||
|
lintOnStart: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||||
//
|
//
|
||||||
// 1. prevent vite from obscuring rust errors
|
// 1. prevent vite from obscuring rust errors
|
||||||
|
Loading…
Reference in New Issue
Block a user