mirror of
https://github.com/Steffo99/todocolors.git
synced 2024-11-22 08:14:18 +00:00
Various fixes
This commit is contained in:
parent
0c3b44a505
commit
ced9053789
10 changed files with 91 additions and 43 deletions
|
@ -1,8 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "Todocolors",
|
"name": "Todocolors",
|
||||||
"short_name": "Todocolors",
|
"short_name": "Todo",
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
|
"scope": "/",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
|
"theme_color": "#0d193b",
|
||||||
"background_color": "#0c193b",
|
"background_color": "#0c193b",
|
||||||
"description": "Self-hostable multiplayer todo app",
|
"description": "Self-hostable multiplayer todo app",
|
||||||
"categories": ["productivity"],
|
"categories": ["productivity"],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
.taskEditorContainer {
|
.taskEditorContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +9,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
max-width: 480px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
.titleArea {
|
.titleArea {
|
||||||
grid-area: title;
|
grid-area: title;
|
||||||
|
height: 72px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titleDisplay {
|
.titleDisplay {
|
||||||
|
|
|
@ -22,12 +22,12 @@ interface BoardHeaderProps {
|
||||||
export function BoardHeader({lang, className, metadataHook, layoutHook: {columningHook, groupingHook, sortingHook}}: BoardHeaderProps) {
|
export function BoardHeader({lang, className, metadataHook, layoutHook: {columningHook, groupingHook, sortingHook}}: BoardHeaderProps) {
|
||||||
return (
|
return (
|
||||||
<header className={cn(style.boardHeader, className)}>
|
<header className={cn(style.boardHeader, className)}>
|
||||||
<BoardHeaderTitle className={style.titleArea} editorHook={metadataHook}/>
|
|
||||||
<div className={cn(style.buttonsArea, style.leftButtonsArea)}>
|
<div className={cn(style.buttonsArea, style.leftButtonsArea)}>
|
||||||
<NavigateHomeButton lang={lang}/>
|
<NavigateHomeButton lang={lang}/>
|
||||||
<ToggleStarredButton lang={lang}/>
|
<ToggleStarredButton lang={lang}/>
|
||||||
<ToggleEditingButton lang={lang} metadataHook={metadataHook}/>
|
<ToggleEditingButton lang={lang} metadataHook={metadataHook}/>
|
||||||
</div>
|
</div>
|
||||||
|
<BoardHeaderTitle className={style.titleArea} editorHook={metadataHook}/>
|
||||||
<div className={cn(style.buttonsArea, style.rightButtonsArea)}>
|
<div className={cn(style.buttonsArea, style.rightButtonsArea)}>
|
||||||
<CycleColumningButton lang={lang} value={columningHook.value} next={columningHook.next}/>
|
<CycleColumningButton lang={lang} value={columningHook.value} next={columningHook.next}/>
|
||||||
<CycleGroupingButton lang={lang} next={groupingHook.next}/>
|
<CycleGroupingButton lang={lang} next={groupingHook.next}/>
|
||||||
|
|
|
@ -18,5 +18,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.taskViewerBack {
|
.taskViewerBack {
|
||||||
flex-direction: row-reverse;
|
justify-content: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskViewerDebug {
|
||||||
|
font-size: xx-small;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,23 @@ import {TaskWithId} from "@/app/[lang]/board/[board]/(page)/(view)/(task)/TaskWi
|
||||||
import {faTrashArrowUp} from "@fortawesome/free-solid-svg-icons"
|
import {faTrashArrowUp} from "@fortawesome/free-solid-svg-icons"
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||||
import cn from "classnames"
|
import cn from "classnames"
|
||||||
import {Dispatch, MouseEvent, SetStateAction, useCallback, useState} from "react"
|
import {Dispatch, SyntheticEvent, SetStateAction, useCallback, useState} from "react"
|
||||||
|
|
||||||
|
|
||||||
export function TaskViewer({lang, task, setEditorInput}: {lang: string, task: TaskWithId, setEditorInput: Dispatch<SetStateAction<string>>}) {
|
export function TaskViewer({lang, task, setEditorInput}: {lang: string, task: TaskWithId, setEditorInput: Dispatch<SetStateAction<string>>}) {
|
||||||
const [isFlipped, setFlipped] = useState<boolean>(false)
|
const [isFlipped, setFlipped] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const toggleFlipped = useCallback((e: SyntheticEvent<HTMLElement>) => {
|
||||||
|
if("key" in e && typeof e["key"] === "string") {
|
||||||
|
if(!["Enter", " "].includes(e.key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setFlipped(prev => !prev)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn({
|
className={cn({
|
||||||
|
@ -23,17 +34,23 @@ export function TaskViewer({lang, task, setEditorInput}: {lang: string, task: Ta
|
||||||
[style.taskViewerFront]: !isFlipped,
|
[style.taskViewerFront]: !isFlipped,
|
||||||
[style.taskViewerBack]: isFlipped,
|
[style.taskViewerBack]: isFlipped,
|
||||||
}, taskClassNames(task[1]))}
|
}, taskClassNames(task[1]))}
|
||||||
onClick={() => setFlipped(prev => !prev)}
|
onClick={toggleFlipped}
|
||||||
|
onKeyDownCapture={toggleFlipped}
|
||||||
>
|
>
|
||||||
{isFlipped ? <TaskViewerBack lang={lang} task={task} setEditorInput={setEditorInput}/> : <TaskViewerFront lang={lang} task={task}/>}
|
{isFlipped ? <TaskViewerBack lang={lang} task={task} setEditorInput={setEditorInput}/> : <TaskViewerFront lang={lang} task={task}/>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function TaskViewerFront({lang, task}: {lang: string, task: TaskWithId}) {
|
function TaskViewerFront({task}: {lang: string, task: TaskWithId}) {
|
||||||
const {sendRequest} = useBoardConsumer()
|
const {sendRequest} = useBoardConsumer()
|
||||||
|
|
||||||
const toggleStatus = useCallback((e: MouseEvent<HTMLDivElement>) => {
|
const toggleStatus = useCallback((e: SyntheticEvent<HTMLDivElement>) => {
|
||||||
|
if("key" in e && typeof e["key"] === "string") {
|
||||||
|
if(!["Enter", " "].includes(e.key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
let request: ModifyTaskBoardRequest
|
let request: ModifyTaskBoardRequest
|
||||||
|
@ -52,7 +69,7 @@ function TaskViewerFront({lang, task}: {lang: string, task: TaskWithId}) {
|
||||||
}, [task, sendRequest])
|
}, [task, sendRequest])
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className={style.taskIcon} onClick={toggleStatus} tabIndex={0}>
|
<div className={style.taskIcon} onClick={toggleStatus} onKeyDown={toggleStatus} tabIndex={0}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
size={"lg"}
|
size={"lg"}
|
||||||
icon={task[1].status === TaskStatus.Complete ? TASK_ICON_TO_FONTAWESOME_SOLID[task[1].icon] : TASK_ICON_TO_FONTAWESOME_REGULAR[task[1].icon]}
|
icon={task[1].status === TaskStatus.Complete ? TASK_ICON_TO_FONTAWESOME_SOLID[task[1].icon] : TASK_ICON_TO_FONTAWESOME_REGULAR[task[1].icon]}
|
||||||
|
@ -69,7 +86,12 @@ function TaskViewerBack({lang, task, setEditorInput}: {lang: string, task: TaskW
|
||||||
const {t} = useClientTranslation(lang, "board")
|
const {t} = useClientTranslation(lang, "board")
|
||||||
const {sendRequest} = useBoardConsumer()
|
const {sendRequest} = useBoardConsumer()
|
||||||
|
|
||||||
const recreateTask = useCallback((e: MouseEvent<HTMLButtonElement>) => {
|
const recreateTask = useCallback((e: SyntheticEvent<HTMLButtonElement>) => {
|
||||||
|
if("key" in e && typeof e["key"] === "string") {
|
||||||
|
if(!["Enter", " "].includes(e.key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
setEditorInput(taskToString(task[1]))
|
setEditorInput(taskToString(task[1]))
|
||||||
|
@ -78,8 +100,11 @@ function TaskViewerBack({lang, task, setEditorInput}: {lang: string, task: TaskW
|
||||||
}, [task, setEditorInput, sendRequest])
|
}, [task, setEditorInput, sendRequest])
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className={style.taskButtons}>
|
<div className={style.taskViewerDebug}>
|
||||||
<button title={t("taskButtonRecreate")} onClick={recreateTask}>
|
{task[0]}
|
||||||
|
</div>
|
||||||
|
<div className={style.taskViewerButtons}>
|
||||||
|
<button title={t("taskButtonRecreate")} onClick={recreateTask} onKeyDown={recreateTask} tabIndex={0}>
|
||||||
<FontAwesomeIcon size={"sm"} icon={faTrashArrowUp}/>
|
<FontAwesomeIcon size={"sm"} icon={faTrashArrowUp}/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
40
todoblue/src/app/[lang]/board/[board]/(page)/BoardPage.tsx
Normal file
40
todoblue/src/app/[lang]/board/[board]/(page)/BoardPage.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import {TaskEditor} from "@/app/[lang]/board/[board]/(page)/(edit)/TaskEditor"
|
||||||
|
import {BoardHeader} from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeader"
|
||||||
|
import {BoardMain} from "@/app/[lang]/board/[board]/(page)/(view)/BoardMain"
|
||||||
|
import {useBoardLayoutEditor} from "@/app/[lang]/board/[board]/(page)/useBoardLayoutEditor"
|
||||||
|
import {useBoardMetadataEditor} from "@/app/[lang]/board/[board]/(page)/useBoardMetadataEditor"
|
||||||
|
import {useTaskEditor} from "@/app/[lang]/board/[board]/(page)/useTaskEditor"
|
||||||
|
import style from "@/app/[lang]/board/[board]/page.module.css"
|
||||||
|
|
||||||
|
|
||||||
|
export function BoardPage({lang}: {lang: string}) {
|
||||||
|
const metadataHook = useBoardMetadataEditor()
|
||||||
|
const layoutHook = useBoardLayoutEditor()
|
||||||
|
const editorHook = useTaskEditor()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style.pageRoot}>
|
||||||
|
<BoardHeader
|
||||||
|
lang={lang}
|
||||||
|
className={style.pageHeader}
|
||||||
|
metadataHook={metadataHook}
|
||||||
|
layoutHook={layoutHook}
|
||||||
|
/>
|
||||||
|
<BoardMain
|
||||||
|
lang={lang}
|
||||||
|
className={style.pageMain}
|
||||||
|
columning={layoutHook.columningHook.value}
|
||||||
|
grouping={layoutHook.groupingHook.value}
|
||||||
|
sorting={layoutHook.sortingHook.value}
|
||||||
|
setEditorInput={editorHook.setInput}
|
||||||
|
/>
|
||||||
|
<TaskEditor
|
||||||
|
lang={lang}
|
||||||
|
className={style.pageEditor}
|
||||||
|
editorHook={editorHook}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -6,7 +6,8 @@
|
||||||
"main"
|
"main"
|
||||||
"editor"
|
"editor"
|
||||||
;
|
;
|
||||||
grid-template-rows: 72px 1fr 48px;
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: auto 1fr 48px;
|
||||||
grid-row-gap: 8px;
|
grid-row-gap: 8px;
|
||||||
|
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
|
@ -30,6 +31,4 @@
|
||||||
|
|
||||||
.pageEditor {
|
.pageEditor {
|
||||||
grid-area: editor;
|
grid-area: editor;
|
||||||
|
|
||||||
min-height: 44px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,16 @@
|
||||||
"use client";
|
|
||||||
import {TaskEditor} from "@/app/[lang]/board/[board]/(page)/(edit)/TaskEditor"
|
import {TaskEditor} from "@/app/[lang]/board/[board]/(page)/(edit)/TaskEditor"
|
||||||
|
import {BoardPage} from "@/app/[lang]/board/[board]/(page)/BoardPage"
|
||||||
import {useBoardLayoutEditor} from "@/app/[lang]/board/[board]/(page)/useBoardLayoutEditor"
|
import {useBoardLayoutEditor} from "@/app/[lang]/board/[board]/(page)/useBoardLayoutEditor"
|
||||||
import {useBoardMetadataEditor} from "@/app/[lang]/board/[board]/(page)/useBoardMetadataEditor"
|
import {useBoardMetadataEditor} from "@/app/[lang]/board/[board]/(page)/useBoardMetadataEditor"
|
||||||
import {BoardMain} from "@/app/[lang]/board/[board]/(page)/(view)/BoardMain"
|
import {BoardMain} from "@/app/[lang]/board/[board]/(page)/(view)/BoardMain"
|
||||||
import {BoardHeader} from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeader"
|
import {BoardHeader} from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeader"
|
||||||
import {useTaskEditor} from "@/app/[lang]/board/[board]/(page)/useTaskEditor"
|
import {useTaskEditor} from "@/app/[lang]/board/[board]/(page)/useTaskEditor"
|
||||||
|
import {Metadata} from "next"
|
||||||
import style from "./page.module.css"
|
import style from "./page.module.css"
|
||||||
|
|
||||||
|
|
||||||
export default function page({params: {lang}}: {params: {lang: string}}) {
|
export default function page({params: {lang}}: {params: {lang: string}}) {
|
||||||
const metadataHook = useBoardMetadataEditor()
|
|
||||||
const layoutHook = useBoardLayoutEditor()
|
|
||||||
const editorHook = useTaskEditor()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.pageRoot}>
|
<BoardPage lang={lang}/>
|
||||||
<BoardHeader
|
|
||||||
lang={lang}
|
|
||||||
className={style.pageHeader}
|
|
||||||
metadataHook={metadataHook}
|
|
||||||
layoutHook={layoutHook}
|
|
||||||
/>
|
|
||||||
<BoardMain
|
|
||||||
lang={lang}
|
|
||||||
className={style.pageMain}
|
|
||||||
columning={layoutHook.columningHook.value}
|
|
||||||
grouping={layoutHook.groupingHook.value}
|
|
||||||
sorting={layoutHook.sortingHook.value}
|
|
||||||
setEditorInput={editorHook.setInput}
|
|
||||||
/>
|
|
||||||
<TaskEditor
|
|
||||||
lang={lang}
|
|
||||||
className={style.pageEditor}
|
|
||||||
editorHook={editorHook}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ export const metadata: NextMetadata = {
|
||||||
creator: "Steffo",
|
creator: "Steffo",
|
||||||
robots: "noindex, nofollow",
|
robots: "noindex, nofollow",
|
||||||
manifest: "manifest.json",
|
manifest: "manifest.json",
|
||||||
icons: "favicon-wbg.ico"
|
icons: "favicon-wbg.ico",
|
||||||
|
themeColor: "#0d193b"
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function layout({children}: { children: ReactNode }) {
|
export default function layout({children}: { children: ReactNode }) {
|
||||||
|
|
Loading…
Reference in a new issue