dudleycu 3 years ago
commit 85c06c089b
  1. 91
      src/App.js
  2. 12
      src/TestPanel.js
  3. 4
      src/skilltree/skillTree.js
  4. 26
      src/skilltree/skillTreeEditor.js
  5. 4
      src/skilltree/skillTreeSelector.js
  6. 9
      src/style.css

@ -4,7 +4,7 @@ import React, {useState,useEffect,useReducer} from 'react';
import Toggle from 'react-toggle' //Tooltip props: http://aaronshaf.github.io/react-toggle/
import Helmet from 'react-helmet'
import {XSquareFill, PlusCircle, LifePreserver, Server, CloudUploadFill} from 'react-bootstrap-icons'
import {TrashFill, PlusCircle, LifePreserver, Server, CloudUploadFill} from 'react-bootstrap-icons'
import { SkillTreeEditor } from './skilltree/skillTreeEditor'
@ -57,11 +57,11 @@ function Box(p) {
}
function Table(p) {
return <p className={p.classes}>
return <span className={p.classes}>
{p.children}
</p>
</span>
}
function InputBox(p) {
@ -93,20 +93,22 @@ function InputBox(p) {
return p.data?<select disabled={p.lockSubmission} className={failed?"failedInput":sending?"submitting":""} value={value} onKeyDown={(f)=>{keydownFunc(f)}} onChange={(f)=>{changeFunc(f)}} onBlur={(f)=>{blurFunc(f)}}>
{p.includeBlankValue&&<option/>}
{p.data.map((item)=><option value={item.id}>{item.id} - {item.name||item.username}</option>)}
{p.data.map((item)=><option key={item.id} value={item.id}>{item.id} - {item.name||item.username}</option>)}
</select>:<input disabled={p.lockSubmission} className={failed?"failedInput":sending?"submitting":""} value={value} onKeyDown={(f)=>{keydownFunc(f)}} onChange={(f)=>{changeFunc(f)}} onBlur={(f)=>{blurFunc(f)}}/>
}
function TableEditor(p) {
const initialVals={}
const { TESTMODE } = p
function updateVals(state,update) {
if (update==='Clear') {
return initialVals
}
state[update.field]=update.value
return state
return state
}
const [fields,setFields] = useState([])
@ -116,7 +118,6 @@ function TableEditor(p) {
const [loading,setLoading] = useState(false)
const [dependencies,setDependencies] = useState([])
const [importAllowed,setImportAllowed] = useState(false)
const [fileData,setFileData] = useState(undefined)
const [lockSubmission,setLockSubmission] = useState(false)
function patchValue(value,p,col,dat) {
@ -143,26 +144,30 @@ function TableEditor(p) {
})
}
}
useEffect(()=>{
setUpdate(true)
},[p.path])
useEffect(()=>{
var promises=[]
parse(fileData,{columns:true,skip_empty_lines:true}).forEach((entry)=>{
for (var col of fields) {
if ((col.dataTypeID===23||col.dataTypeID===701||col.dataTypeID===16)&&entry[col.name]==="") {
entry[col.name]=0
function SubmitDeletion() {
if (!lockSubmission) {
setLockSubmission(true)
var promises = []
for (var dat of data) {
if (document.getElementById("delete_"+dat.id).checked) {
promises.push(axios.delete(p.BACKENDURL+p.path,{data:{pass:p.password,id:dat.id}}))
}
}
promises.push(axios.post(p.BACKENDURL+p.path,{...entry,pass:p.password}))
})
Promise.allSettled(promises)
.then(()=>{
setUpdate(true)
})
},[fileData,p.path,p.BACKENDURL,p.password])
Promise.allSettled(promises)
.catch((err)=>{
alert(err.message)
})
.then((data)=>{
setLockSubmission(false)
setUpdate(true)
})
}
}
useEffect(()=>{
setUpdate(true)
},[p.path,TESTMODE])
useEffect(()=>{
for (var col of fields) {
@ -207,24 +212,36 @@ function TableEditor(p) {
{!loading?
<div>
<table>
{importAllowed&&<caption><label className="buttonLabel" for="uploads">Import CSV</label><input onChange={(f)=>{
{importAllowed&&<caption><label className="buttonLabel" htmlFor="uploads">Import CSV</label><input onChange={(f)=>{
const reader = new FileReader()
reader.onload=(ev)=>{
setFileData(ev.target.result)
var promises=[]
parse(ev.target.result,{columns:true,skip_empty_lines:true}).forEach((entry)=>{
for (var col of fields) {
if ((col.dataTypeID===23||col.dataTypeID===701||col.dataTypeID===16)&&entry[col.name]==="") {
entry[col.name]=0
}
}
promises.push(axios.post(p.BACKENDURL+p.path,{...entry,pass:p.password}))
})
Promise.allSettled(promises)
.then(()=>{
setUpdate(true)
})
}
reader.readAsText(f.target.files[0])
}} style={{opacity:0}} id="uploads" type="file" accept=".txt,.csv"/></caption>}
<thead>
<tr>
<th className="table-padding"></th>
<th className="table-padding"><TrashFill onClick={()=>{SubmitDeletion()}} className="trashButton"/></th>
{fields.map((field,i)=><React.Fragment key={i}><th scope="col" className="table-padding">{field.name}</th></React.Fragment>)}
</tr>
</thead>
<tbody>
{<tr><td></td>{fields.map((col,i)=><td key={i}>{<InputBox includeBlankValue={true} data={dependencies[col.name]} callback4={
(f)=>{setSubmitVal({field:col.name,value:f});}}/>}</td>)}<input style={{display:"none"}}/><PlusCircle onClick={()=>{SubmitBoxes()}} className="submitbutton"/></tr>}
(f)=>{setSubmitVal({field:col.name,value:f});}}/>}</td>)}<td><input style={{display:"none"}}/><PlusCircle onClick={()=>{SubmitBoxes()}} className="submitbutton"/></td></tr>}
{data.map((dat)=><tr key={dat.id}>
<td><XSquareFill className="webicon" onClick={()=>{axios.delete(p.BACKENDURL+p.path,{data:{id:dat.id,pass:p.password}}).then(()=>{setUpdate(true)}).catch((err)=>{alert(err.response.data)})}}/></td>{fields.map((col,i)=><td key={dat.id+"_"+i} className="table-padding table">
<td><input id={"delete_"+dat.id} type="checkbox"/></td>{fields.map((col,i)=><td key={dat.id+"_"+i} className="table-padding table">
<InputBox lockSubmission={lockSubmission} data={dependencies[col.name]} callback={(value)=>patchValue(value,p,col,dat)} callback2={(f,value)=>{if (f.key==='Enter') {f.currentTarget.blur()} else {return 'Chill'}}} value={String(dat[col.name])}/></td>)}</tr>)}
</tbody>
</table>
@ -306,13 +323,13 @@ function DatabaseEditor(p) {
<br/><br/>
<span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}><LifePreserver className="databaseIcon" style={{color:"green"}}/>Live Database</span>
&nbsp;&nbsp;&nbsp;<span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}><LifePreserver className="databaseIcon" style={{color:"red"}}/>Test Database</span><br/>
{databases.map((db)=>{
{databases.map((db,i)=>{
var label = ""
if (db.datname!=="ngsplanner"&&db.datname!=="ngsplanner2") {
var dateStr = db.datname.replace("ngsplanner","")
var date = new Date(dateStr.slice(0,4),dateStr.slice(4,6),dateStr.slice(6,8),dateStr.slice(8,10),dateStr.slice(10,12),dateStr.slice(12,14))
label=<><Server className="databaseIcon" style={{color:"blue"}}/>{"Backup from "+date}</>
return <><span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}>{label}<button style={{background:"blue"}}
return <React.Fragment key={i}><span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}>{label}<button style={{background:"blue"}}
onClick={()=>{
setLoading(true)
axios.post(p.BACKENDURL+"/databases/restorefrombackup",{
@ -328,9 +345,9 @@ function DatabaseEditor(p) {
.then(()=>{
setLoading(false)
})
}}><CloudUploadFill/> Restore</button></span><br/></>
}}><CloudUploadFill/> Restore</button></span><br/></React.Fragment>
} else {
return <></>
return <React.Fragment key={i}/>
}
})}
</>
@ -413,10 +430,10 @@ function AdminPanel(p) {
<div className="boxTitleBar">
<h1>Navigation</h1>
</div>
<p>Testing Mode <Toggle checked={p.TESTMODE} onChange={(f)=>{p.setTESTMODE(f.target.checked)}}/> {p.TESTMODE?<b>ON</b>:<b>OFF</b>}</p>
<span>Testing Mode <Toggle checked={p.TESTMODE} onChange={(f)=>{p.setTESTMODE(f.target.checked)}}/> {p.TESTMODE?<b>ON</b>:<b>OFF</b>}</span>
<div className="adminNavContainer customScrollbar">
<Table classes="adminNav">
{navigationData.map((nav)=>(nav.hr)?<hr/>:<><Link to={process.env.PUBLIC_URL+nav.url}>{nav.page}</Link><br/></>)}
{navigationData.map((nav,i)=>(nav.hr)?<hr key={i}/>:<React.Fragment key={i}><Link to={process.env.PUBLIC_URL+nav.url}>{nav.page}</Link><br/></React.Fragment>)}
<Link to={process.env.PUBLIC_URL+"/admin/database_manager"}>Database Manager</Link><br/>
</Table>
</div>
@ -425,7 +442,7 @@ function AdminPanel(p) {
{navigationData.map((nav)=>(nav.duplicate===undefined&&nav.hr===undefined)&&<Route path={process.env.PUBLIC_URL+nav.url}>
{navigationData.map((nav,i)=>(nav.duplicate===undefined&&nav.hr===undefined)&&<Route key={i} path={process.env.PUBLIC_URL+nav.url}>
<div className="box boxAdminContent">
<div className="boxTitleBar">
<h1>{nav.page}</h1></div>
@ -433,7 +450,7 @@ function AdminPanel(p) {
<Helmet>
<title>{APP_TITLE+" - Admin Panel: "+nav.page}</title>
</Helmet>
{nav.render??<TableEditor password={password} BACKENDURL={GetBackendURL(p)} path={nav.table}/>}
{nav.render??<TableEditor TESTMODE={p.TESTMODE} password={password} BACKENDURL={GetBackendURL(p)} path={nav.table}/>}
</div></div></Route>)}
<Route path={process.env.PUBLIC_URL+"/admin/database_manager"}>
@ -790,8 +807,6 @@ function App() {
const [DATAID,setDATAID] = useState({GetData:()=>{}})
const [update,setUpdate] = useState(false)
const [dataLoaded,setDataLoaded] = useState(false)
const [LOGGEDINUSER,setLOGGEDINUSER] = useState("")
const [LOGGEDINHASH,setLOGGEDINHASH] = useState("")

@ -74,7 +74,7 @@ function EditableClass(p){
function PopupWindow(p) {
return <Modal isOpen={p.modalOpen} onRequestClose={()=>{p.setModalOpen(false)}} shouldFocusAfterRender={true} shouldCloseOnOverlayClick={true} shouldCloseOnEsc={true} className="modal" overlayClassName="modalOverlay">
return <Modal ariaHideApp={false} isOpen={p.modalOpen} onRequestClose={()=>{p.setModalOpen(false)}} shouldFocusAfterRender={true} shouldCloseOnOverlayClick={true} shouldCloseOnEsc={true} className="modal" overlayClassName="modalOverlay">
<div className="box boxModal">
<div className="boxTitleBar">
<h1>{p.title}</h1>
@ -117,7 +117,7 @@ function SelectorWindow(p) {
{(p.sortItems||p.filter)&&<div className="itemBar">
<div className="itemBarSort">
{p.sortItems&&<select className="itemBarForm" value={sortSelector} onChange={(f)=>{setSortSelector(f.currentTarget.value)}}>
{p.sortItems.map((item)=><option value={item}>{item}</option>)}
{p.sortItems.map((item)=><option key={item} value={item}>{item}</option>)}
</select>}
</div>
<div className="itemBarFilter">
@ -127,7 +127,7 @@ function SelectorWindow(p) {
}
<div className="modalItemListContainer customScrollbar">
<ul className="itemlist">
{p.filter?itemList.filter((item)=>p.filterFunction(tabPage,item)).filter((item)=>p.searchFieldFunction(filter,item)).sort((a,b)=>p.sortOrderFunction(sortSelector,a,b)).map((item)=>p.displayFunction(item)):itemList.map((item)=>p.displayFunction(item))}
{p.filter?itemList.filter((item)=>p.filterFunction(tabPage,item)).filter((item)=>p.searchFieldFunction(filter,item)).sort((a,b)=>p.sortOrderFunction(sortSelector,a,b)).map((item,i)=><React.Fragment key={i}>{p.displayFunction(item)}</React.Fragment>):itemList.map((item,i)=><React.Fragment key={i}>{p.displayFunction(item)}</React.Fragment>)}
{p.children}
</ul>
</div>
@ -230,7 +230,7 @@ function SkillTreeBoxes(p) {
return <>
{p.skillTreeSkillData&&p.skillTreeSkillData.map((skill,i)=>{
var splitter = skill.split(",")
return splitter[0]!==""&&splitter[1]!==""&&splitter[2]!==""&&<SkillBox className={isLocked(splitter[2])?"skillLocked":p.skillPointData[p.page-1][i]===GetHighestLevel(splitter[2])?"skillMaxed":p.skillPointData[p.page-1][i]>0?"skillActive":""} boxId={i} skillPointData={p.skillPointData} setSkillPointData={p.setSkillPointData} page={p.page} cl={p.cl} maxPoints={GetHighestLevel(splitter[2])} points={p.points} setPoints={p.setPoints} GetData={p.GetData} skill={splitter.map((numb)=>Number(numb))}/>
return splitter[0]!==""&&splitter[1]!==""&&splitter[2]!==""&&<SkillBox key={i} className={isLocked(splitter[2])?"skillLocked":p.skillPointData[p.page-1][i]===GetHighestLevel(splitter[2])?"skillMaxed":p.skillPointData[p.page-1][i]>0?"skillActive":""} boxId={i} skillPointData={p.skillPointData} setSkillPointData={p.setSkillPointData} page={p.page} cl={p.cl} maxPoints={GetHighestLevel(splitter[2])} points={p.points} setPoints={p.setPoints} GetData={p.GetData} skill={splitter.map((numb)=>Number(numb))}/>
})}
</>
}
@ -593,7 +593,7 @@ AUGMENT
</div>
<ClassSelectorWindow class={className} subClass={subclassName} setClassName={setClassName} setEditClass={setClassNameSetter} editClass={classNameSetter} setSubClassName={setSubClassName} modalOpen={classSelectWindowOpen} setModalOpen={setClassSelectWindowOpen} GetData={p.GetData}/>
<Modal isOpen={classSkillTreeWindowOpen} onRequestClose={() => { setClassSkillTreeWindowOpen(false) }} shouldFocusAfterRender={true} shouldCloseOnOverlayClick={true} shouldCloseOnEsc={true} className="modal" overlayClassName="modalOverlay">
<Modal ariaHideApp={false} isOpen={classSkillTreeWindowOpen} onRequestClose={() => { setClassSkillTreeWindowOpen(false) }} shouldFocusAfterRender={true} shouldCloseOnOverlayClick={true} shouldCloseOnEsc={true} className="modal" overlayClassName="modalOverlay">
<div className="box skillTreeBox">
<div className="boxTitleBar">
<h1>Class Skill Tree</h1>
@ -647,7 +647,7 @@ AUGMENT
}
}}
displayFunction={(item)=>{
return <li className={"itemwep r"+item[WEAPON_WEAPON].rarity} onClick={()=>{setSelectedWeapon(item);setWeaponSelectWindowOpen(false)}}><div className="itemWeaponWrapper"><img className="itemimg" alt="" src={DisplayIcon(item[WEAPON_EXISTENCE_DATA]?.icon)} /><em className="rifle">{GetSpecialWeaponName(item)}</em></div><br /><span className="atk">{item[WEAPON_WEAPON].atk}</span> <ExpandTooltip id={"mouseover-tooltip"+item[WEAPON_WEAPONTYPE].id+"_"+item[WEAPON_WEAPON].id+"_"+item[WEAPON_POTENTIAL].id+"_"+item[WEAPON_POTENTIAL_TOOLTIP].id} tooltip={<>{item[WEAPON_POTENTIAL_TOOLTIP].map((pot,i)=><>{(i!==0)&&<br/>}{pot.name}: {pot.description?pot.description.split("\\n").map((it)=><>{it}<br/> </>):<></>}</>)}</>}>
return <li className={"itemwep r"+item[WEAPON_WEAPON].rarity} onClick={()=>{setSelectedWeapon(item);setWeaponSelectWindowOpen(false)}}><div className="itemWeaponWrapper"><img className="itemimg" alt="" src={DisplayIcon(item[WEAPON_EXISTENCE_DATA]?.icon)} /><em className="rifle">{GetSpecialWeaponName(item)}</em></div><br /><span className="atk">{item[WEAPON_WEAPON].atk}</span> <ExpandTooltip id={"mouseover-tooltip"+item[WEAPON_WEAPONTYPE].id+"_"+item[WEAPON_WEAPON].id+"_"+item[WEAPON_POTENTIAL].id+"_"+item[WEAPON_POTENTIAL_TOOLTIP].id} tooltip={<>{item[WEAPON_POTENTIAL_TOOLTIP].map((pot,i)=><React.Fragment key={i}>{(i!==0)&&<br/>}{pot.name}: {pot.description?pot.description.split("\\n").map((it,ii)=><React.Fragment key={ii}>{it}<br/> </React.Fragment>):<React.Fragment key={i}/>}</React.Fragment>)}</>}>
<span className="pot">{item[WEAPON_POTENTIAL].name}</span>
</ExpandTooltip></li>}}
/>

@ -36,7 +36,7 @@ function SkillTree(p) {
case "┬":context.beginPath();context.moveTo(x*p.gridSizeX+(padX)-p.gridPaddingX,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+p.gridSizeY+p.gridPaddingY);context.stroke();context.beginPath();context.moveTo(x*p.gridSizeX+(padX)+p.gridSizeX+p.gridPaddingX,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+p.gridSizeY+p.gridPaddingY);context.stroke();break;
case "┴":context.beginPath();context.moveTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)-p.gridPaddingY);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX+p.gridPaddingX,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.stroke();context.beginPath();context.moveTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)-p.gridPaddingY);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.lineTo(x*p.gridSizeX+(padX)-p.gridPaddingX,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.stroke();break;
case "┼":context.beginPath();context.moveTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)-p.gridPaddingY);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX/2,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+p.gridSizeY+p.gridPaddingY);context.moveTo(x*p.gridSizeX+(padX)-p.gridPaddingX,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.lineTo(x*p.gridSizeX+(padX)+p.gridSizeX+p.gridPaddingX,Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY)+(y%2===1?p.halflineheight:p.gridSizeY)/2);context.stroke();break;
case "□":context.fillRect(x*p.gridSizeX+(padX), Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY), p.gridSizeX, y%2===1?p.halflineheight:p.gridSizeY);break;
case "□":context.clearRect(x*p.gridSizeX+(padX), Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY), p.gridSizeX, y%2===1?p.halflineheight:p.gridSizeY);context.fillRect(x*p.gridSizeX+(padX), Math.ceil(y/2)*p.gridSizeY+Math.ceil((y-1)/2)*p.halflineheight+(padY), p.gridSizeX, y%2===1?p.halflineheight:p.gridSizeY);break;
default:
}
x++
@ -47,7 +47,7 @@ function SkillTree(p) {
return <canvas
width={width}
height={height} ref={canvasRef} {...p}>{p.children}</canvas>
height={height} ref={canvasRef} style={p.style} className={p.className}>{p.children}</canvas>
}
export {SkillTree}

@ -1,5 +1,5 @@
import { SkillTree } from "./skillTree";
import { useEffect,useState,useMemo,useCallback } from "react";
import React, { useEffect,useState,useMemo,useCallback } from "react";
import { SkillTreeSelector } from "./skillTreeSelector";
import axios from "axios";
@ -174,9 +174,9 @@ function SkillTreeEditor(p) {
return <>
{loading?<img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/>:<>
<h2>{message}</h2>
<label for="classSelect">Class Select:</label><select id="classSelect" value={cl} onChange={(f)=>{setCl(Number(f.currentTarget.value))}}>
<label htmlFor="classSelect">Class Select:</label><select id="classSelect" value={Number.isNaN(Number(cl))?"?":cl} onChange={(f)=>{setCl(Number(f.currentTarget.value))}}>
<option value=""></option>
{Object.keys(classList).map((c)=><option value={c}>{c+" - "+classList[c].name}</option>)}
{Object.keys(classList).map((c)=><option key={classList[c].name} value={c}>{c+" - "+classList[c].name}</option>)}
</select>
<br/>
<br/>
@ -189,19 +189,19 @@ function SkillTreeEditor(p) {
gridDimensionsX={dimensionX} gridDimensionsY={dimensionY} gridSizeX={gridSizeX} gridSizeY={gridSizeY} gridPaddingX={gridPaddingX} gridPaddingY={gridPaddingY}
skillLines={skillLines} halflineheight={halflineheight}
/>
{renderedInputs.map((control)=>control)}
{renderedInputs.map((control,i)=><React.Fragment key={i}>{control}</React.Fragment>)}
<br/>
<hr/>
<br/>
<label for="lineColor">Line Color:</label><input type="color" id="lineColor" value={lineColor} onChange={(f)=>{setLineColor(f.currentTarget.value)}}/><br/>
<label for="lineWidth">Line Width:</label><input type="number" id="lineWidth" value={lineWidth} onChange={(f)=>{setLineWidth(f.currentTarget.value)}}/><br/>
<label for="gridSizeX">Grid Size X:</label><input type="number" id="gridSizeX" value={dimensionX} onChange={(f)=>{setDimensionX(f.currentTarget.value)}}/><br/>
<label for="gridSizeY">Grid Size Y:</label><input type="number" id="gridSizeY" value={dimensionY} onChange={(f)=>{setDimensionY(f.currentTarget.value)}}/><br/>
<label for="subrowHeight">Sub-row Height:</label><input type="number" id="subrowHeight" value={halflineheight} onChange={(f)=>{setHalfLineHeight(f.currentTarget.value)}}/><br/>
<label for="boxSizeX">Box Size X:</label><input type="number" id="boxSizeX" value={gridSizeX} onChange={(f)=>{setGridSizeX(f.currentTarget.value)}}/><br/>
<label for="boxSizeY">Box Size Y:</label><input type="number" id="boxSizeY" value={gridSizeY} onChange={(f)=>{setGridSizeY(f.currentTarget.value)}}/><br/>
<label for="gridPaddingX">Grid Padding X:</label><input type="number" id="gridPaddingX" value={gridPaddingX} onChange={(f)=>{setGridPaddingX(f.currentTarget.value)}}/><br/>
<label for="gridPaddingY">Grid Padding Y:</label><input type="number" id="gridPaddingY" value={gridPaddingY} onChange={(f)=>{setGridPaddingY(f.currentTarget.value)}}/><br/>
<label htmlFor="lineColor">Line Color:</label><input type="color" id="lineColor" value={lineColor} onChange={(f)=>{setLineColor(f.currentTarget.value)}}/><br/>
<label htmlFor="lineWidth">Line Width:</label><input type="number" id="lineWidth" value={lineWidth} onChange={(f)=>{setLineWidth(f.currentTarget.value)}}/><br/>
<label htmlFor="gridSizeX">Grid Size X:</label><input type="number" id="gridSizeX" value={dimensionX} onChange={(f)=>{setDimensionX(f.currentTarget.value)}}/><br/>
<label htmlFor="gridSizeY">Grid Size Y:</label><input type="number" id="gridSizeY" value={dimensionY} onChange={(f)=>{setDimensionY(f.currentTarget.value)}}/><br/>
<label htmlFor="subrowHeight">Sub-row Height:</label><input type="number" id="subrowHeight" value={halflineheight} onChange={(f)=>{setHalfLineHeight(f.currentTarget.value)}}/><br/>
<label htmlFor="boxSizeX">Box Size X:</label><input type="number" id="boxSizeX" value={gridSizeX} onChange={(f)=>{setGridSizeX(f.currentTarget.value)}}/><br/>
<label htmlFor="boxSizeY">Box Size Y:</label><input type="number" id="boxSizeY" value={gridSizeY} onChange={(f)=>{setGridSizeY(f.currentTarget.value)}}/><br/>
<label htmlFor="gridPaddingX">Grid Padding X:</label><input type="number" id="gridPaddingX" value={gridPaddingX} onChange={(f)=>{setGridPaddingX(f.currentTarget.value)}}/><br/>
<label htmlFor="gridPaddingY">Grid Padding Y:</label><input type="number" id="gridPaddingY" value={gridPaddingY} onChange={(f)=>{setGridPaddingY(f.currentTarget.value)}}/><br/>
</div></>}
</>

@ -13,11 +13,11 @@ function SkillTreeSelector(p) {
return <>
<select onChange={(f)=>{p.callback(f.currentTarget.value,Number(p.x),Number(p.y))}} style={{position:"absolute",left:p.ADJUSTMENT[0]+(p.x*p.gridSizeX+p.padX+p.gridSizeX/2),top:p.ADJUSTMENT[1]+((p.y/2)*p.gridSizeY+(p.y/2-1)*p.halflineheight+p.padY+(p.y===0?p.halflineheight:p.gridSizeY)/2)}} value={p.defaultValue}>
{[' ','─','│','□','┌','└','┐','┘','┬','┴','├','┤','┼'].map((ch)=>
<option value={ch}>{ch}</option>)
<option value={ch} key={ch}>{ch}</option>)
}
</select>
{p.defaultValue==='□'&&<select style={{width:"64px",position:"absolute",left:p.ADJUSTMENT[0]+(p.x*p.gridSizeX+p.padX+p.gridSizeX/2),top:p.ADJUSTMENT[1]+((p.y/2)*p.gridSizeY+(p.y/2-1)*p.halflineheight+p.padY+(p.y===0?p.halflineheight:p.gridSizeY)/2)+28}} onChange={(f)=>{p.skillCallback(p.x,p.y,f.currentTarget.value)}} value={p.skill.split(",")[2]}>
{["",...Object.keys((skillList)).filter((skill)=>skillList[skill].class_id===p.cl)].map((skill)=><option value={(skillList[skill])?skillList[skill].id:""}>{(skillList[skill])?skillList[skill].name:""}</option>)}
{["",...Object.keys((skillList)).filter((skill)=>skillList[skill].class_id===p.cl)].map((skill)=><option key={skill} value={(skillList[skill])?skillList[skill].id:""}>{(skillList[skill])?skillList[skill].name:""}</option>)}
</select>}
</>

@ -1408,3 +1408,12 @@ dd:before {
font-family: "Segoe UI Symbol";
content: "\2B1B" !important;
}
.trashButton{
color:rgba(100,50,50,1);
width:24px;
height:24px;
}
.trashButton:hover {
color:rgba(200,50,50,1);
border: 2px solid red;
}
Loading…
Cancel
Save