diff --git a/src/common/graph/node.ts b/src/common/graph/node.ts index 2a1126ab43d5a6ff52fac534b57433ac2a10e93b..cdfd492c3ecd87735a3bb17e41622e2fb5794dba 100644 --- a/src/common/graph/node.ts +++ b/src/common/graph/node.ts @@ -14,7 +14,7 @@ export interface NodeProperties { banner?: string; video?: string; references?: DescriptiveReference[]; - visibleAfter?: Date; + visibleAfter?: string; // YYYY-MM-DD } export interface NodeData extends NodeProperties { @@ -76,7 +76,7 @@ export class Node public banner: string; public video: string; public references: DescriptiveReference[]; - public visibleAfter: Date; + public visibleAfter?: string; public neighbors: Node[]; public links: Link[]; diff --git a/src/editor/components/datevisibilityinput.css b/src/editor/components/datevisibilityinput.css new file mode 100644 index 0000000000000000000000000000000000000000..903a032218598311812c939b908c1c291b4f8d26 --- /dev/null +++ b/src/editor/components/datevisibilityinput.css @@ -0,0 +1,8 @@ +div#ks-editor #date-checkbox { + width: initial; +} + +div#ks-editor label { + vertical-align: text-top; + line-height: 0.6; +} diff --git a/src/editor/components/datevisibilityinput.tsx b/src/editor/components/datevisibilityinput.tsx new file mode 100644 index 0000000000000000000000000000000000000000..623ac5ba2af94b4bf909fe25e3555ab8ae49d149 --- /dev/null +++ b/src/editor/components/datevisibilityinput.tsx @@ -0,0 +1,95 @@ +import React, { useEffect, useState } from "react"; +import "./datevisibilityinput.css"; + +type DateVisibilityInputProps = { + date?: string; // YYYY-MM-DD + onDateChange: (date?: string) => void; +}; + +function DateVisibilityInput({ date, onDateChange }: DateVisibilityInputProps) { + const isDate = (date?: string): boolean => { + if (!date) { + return false; + } + + // Format: YYYY-MM-DD + // Try to convert to date and trigger onChange + const splits: string[] = date.split("-", 3); + if (splits.length != 3) { + return false; + } + + const year = parseInt(splits[0]); + const month = parseInt(splits[1]); + const day = parseInt(splits[2]); + + if (isNaN(day) || isNaN(month) || isNaN(year)) { + return false; + } + + return true; + }; + + const dateToString = (date: Date): string => { + return ( + date.getFullYear() + + "-" + + date.getMonth().toString().padStart(2, "0") + + "-" + + date.getDate().toString().padStart(2, "0") + ); + }; + + const initialEditedDate = () => { + return isDate(date) ? date : dateToString(new Date()); + }; + + const [editedDate, setEditedDate] = useState<string>(initialEditedDate()); + const [alwaysVisible, setAlwaysVisible] = useState<boolean>(!isDate(date)); + + useEffect(() => { + setEditedDate(initialEditedDate()); + setAlwaysVisible(!isDate(date)); + }, [date]); + + const updateVisibilityState = (visible: boolean) => { + setAlwaysVisible(visible); + + if (alwaysVisible) { + onDateChange(undefined); + } else { + onDateChange(editedDate); + } + }; + + const handleDateChange = (date?: string) => { + if (isDate(date)) { + setEditedDate(date); + updateVisibilityState(false); + } else { + updateVisibilityState(true); + } + }; + + return ( + <div className="date-input"> + <input + id="date-checkbox" + type={"checkbox"} + checked={alwaysVisible} + onChange={(e) => updateVisibilityState(e.target.checked)} + /> + <label htmlFor="date-checkbox">Always visible</label> + {editedDate} + {!alwaysVisible && ( + <input + type={"date"} + value={editedDate} + onChange={(e) => handleDateChange(e.target.value)} + /> + )} + </div> + ); +} + +export default DateVisibilityInput; diff --git a/src/editor/components/nodedetails.css b/src/editor/components/nodedetails.css index 4211813387c9ff1e02816867fe515f50703366bf..9357fb857827daa57104b4aed4b783c350966266 100644 --- a/src/editor/components/nodedetails.css +++ b/src/editor/components/nodedetails.css @@ -16,3 +16,8 @@ div#ks-editor #nodedetails #node-name { div#ks-editor #nodedetails .empty-select-option { display: none; } + +div#ks-editor #nodedetails h4 { + margin-bottom: 6px; + margin-top: 10px; +} diff --git a/src/editor/components/nodedetails.tsx b/src/editor/components/nodedetails.tsx index 8f7981c2ea99605c7f7f3b0cef8bd38520d3076f..57185977fd30da9d0f7365bc76b0ad3934d676a3 100644 --- a/src/editor/components/nodedetails.tsx +++ b/src/editor/components/nodedetails.tsx @@ -4,6 +4,7 @@ import { NodeType } from "../../common/graph/nodetype"; import "./nodedetails.css"; import { NodeDataChangeRequest } from "../editor"; import ReferencesEditor from "./referenceseditor"; +import DateVisibilityInput from "./datevisibilityinput"; type NodeDetailsProps = { selectedNodes: Node[]; @@ -47,6 +48,7 @@ function NodeDetails({ banner: getCollectiveValue((n) => n.banner, undefined), references: getCollectiveValue((n) => n.references, undefined), type: getCollectiveValue((n) => n.type, undefined), + visibleAfter: getCollectiveValue((n) => n.visibleAfter, undefined), }; const handleDataChange = function <ValueType>( @@ -96,9 +98,6 @@ function NodeDetails({ <div id="nodedetails" onBlur={handleBlur}> {selectedNodes.length === 1 ? ( <div> - <label htmlFor="node-name" hidden> - Name - </label> <input type="text" id="node-name" @@ -117,8 +116,7 @@ function NodeDetails({ {selectedNodes.length === 1 && ( <div> - <label htmlFor="node-description">Description</label> - <br /> + <h4>Description</h4> <textarea id="node-description" name="node-description" @@ -132,8 +130,7 @@ function NodeDetails({ </div> )} <div> - <label htmlFor="node-image">Icon Image</label> - <br /> + <h4>Icon Image</h4> {referenceData.icon && ( <div> <img @@ -157,8 +154,7 @@ function NodeDetails({ /> </div> <div> - <label htmlFor="node-detail-image">Banner Image</label> - <br /> + <h4>Banner Image</h4> {referenceData.banner && ( <div> <img @@ -182,8 +178,7 @@ function NodeDetails({ /> </div> <div> - <label htmlFor="node-type">Type</label> - <br /> + <h4>Type</h4> <select id="node-type" name="node-type" @@ -205,8 +200,7 @@ function NodeDetails({ </select> </div> <div> - <label htmlFor="node-video">Video</label> - <br /> + <h4>Video</h4> <input type="text" placeholder="Video URL" @@ -218,13 +212,25 @@ function NodeDetails({ } ></input> </div> - {selectedNodes.length === 1 && ( - <ReferencesEditor - references={referenceData.references ?? []} - onReferencesChange={(references) => - handleDataChange("references", references) + <div> + <h4>Visible after date</h4> + <DateVisibilityInput + date={referenceData.visibleAfter} + onDateChange={(date) => + handleDataChange("visibleAfter", date) } /> + </div> + {selectedNodes.length === 1 && ( + <div> + <h4>References</h4> + <ReferencesEditor + references={referenceData.references ?? []} + onReferencesChange={(references) => + handleDataChange("references", references) + } + /> + </div> )} </div> ); diff --git a/src/editor/components/nodetypeentry.css b/src/editor/components/nodetypeentry.css index 36fbe895154014404616a848d44a4cfebe91de63..ea3719dce3345504d94f92ca799e6bf5614aa2f0 100644 --- a/src/editor/components/nodetypeentry.css +++ b/src/editor/components/nodetypeentry.css @@ -17,3 +17,8 @@ div#ks-editor .color-circle { margin: 10px; border-radius: 50%; } + +div#ks-editor h4 { + margin-bottom: 6px; + margin-top: 10px; +} diff --git a/src/editor/components/nodetypeentry.tsx b/src/editor/components/nodetypeentry.tsx index c3f84a998e67d48b88c7b005baaf939de9c6fd49..6550a14da9a70c26179e866a45213a7c115a2954 100644 --- a/src/editor/components/nodetypeentry.tsx +++ b/src/editor/components/nodetypeentry.tsx @@ -59,8 +59,7 @@ function NodeTypeEntry({ return ( <div className="node-type" onBlur={handleBlur}> - <label htmlFor="node-type-name">Name</label> - <br /> + <h4>Name</h4> <input className="node-type-name" type={"text"} @@ -70,13 +69,14 @@ function NodeTypeEntry({ } /> <br /> - <label>Color</label> - <br /> + <h4>Color</h4> <label htmlFor="node-type-color-r">R</label> <input id="node-type-color-r" className="node-type-color" type={"number"} + min={"0"} + max={"255"} value={nodeType.color.r.toString()} onChange={(event) => handleColorChange("r", event.target.value)} /> @@ -86,6 +86,8 @@ function NodeTypeEntry({ id="node-type-color-g" className="node-type-color" type={"number"} + min={"0"} + max={"255"} value={nodeType.color.g.toString()} onChange={(event) => handleColorChange("g", event.target.value)} /> @@ -95,6 +97,8 @@ function NodeTypeEntry({ id="node-type-color-b" className="node-type-color" type={"number"} + min={"0"} + max={"255"} value={nodeType.color.b.toString()} onChange={(event) => handleColorChange("b", event.target.value)} /> diff --git a/src/editor/components/referenceseditor.tsx b/src/editor/components/referenceseditor.tsx index 00dfedf844be253f3c7bc1fbc9ce6772d5f534e8..f8c6a8a62745768d3180d7b72e2812076882c931 100644 --- a/src/editor/components/referenceseditor.tsx +++ b/src/editor/components/referenceseditor.tsx @@ -35,7 +35,6 @@ function ReferencesEditor({ return ( <div id="references-editor"> - <label htmlFor="references-editor">References</label> {references.map( (reference: DescriptiveReference, index: number) => { return (