Merge branch 'master' of https://github.com/sigonasr2/ngsplanner
This commit is contained in:
commit
85c06c089b
85
src/App.js
85
src/App.js
@ -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 Toggle from 'react-toggle' //Tooltip props: http://aaronshaf.github.io/react-toggle/
|
||||||
import Helmet from 'react-helmet'
|
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'
|
import { SkillTreeEditor } from './skilltree/skillTreeEditor'
|
||||||
|
|
||||||
@ -57,11 +57,11 @@ function Box(p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Table(p) {
|
function Table(p) {
|
||||||
return <p className={p.classes}>
|
return <span className={p.classes}>
|
||||||
|
|
||||||
{p.children}
|
{p.children}
|
||||||
|
|
||||||
</p>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
function InputBox(p) {
|
function InputBox(p) {
|
||||||
@ -93,7 +93,7 @@ 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)}}>
|
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.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)}}/>
|
</select>:<input disabled={p.lockSubmission} className={failed?"failedInput":sending?"submitting":""} value={value} onKeyDown={(f)=>{keydownFunc(f)}} onChange={(f)=>{changeFunc(f)}} onBlur={(f)=>{blurFunc(f)}}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +101,8 @@ function TableEditor(p) {
|
|||||||
|
|
||||||
const initialVals={}
|
const initialVals={}
|
||||||
|
|
||||||
|
const { TESTMODE } = p
|
||||||
|
|
||||||
function updateVals(state,update) {
|
function updateVals(state,update) {
|
||||||
if (update==='Clear') {
|
if (update==='Clear') {
|
||||||
return initialVals
|
return initialVals
|
||||||
@ -116,7 +118,6 @@ function TableEditor(p) {
|
|||||||
const [loading,setLoading] = useState(false)
|
const [loading,setLoading] = useState(false)
|
||||||
const [dependencies,setDependencies] = useState([])
|
const [dependencies,setDependencies] = useState([])
|
||||||
const [importAllowed,setImportAllowed] = useState(false)
|
const [importAllowed,setImportAllowed] = useState(false)
|
||||||
const [fileData,setFileData] = useState(undefined)
|
|
||||||
const [lockSubmission,setLockSubmission] = useState(false)
|
const [lockSubmission,setLockSubmission] = useState(false)
|
||||||
|
|
||||||
function patchValue(value,p,col,dat) {
|
function patchValue(value,p,col,dat) {
|
||||||
@ -144,25 +145,29 @@ function TableEditor(p) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(()=>{
|
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}}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Promise.allSettled(promises)
|
||||||
|
.catch((err)=>{
|
||||||
|
alert(err.message)
|
||||||
|
})
|
||||||
|
.then((data)=>{
|
||||||
|
setLockSubmission(false)
|
||||||
setUpdate(true)
|
setUpdate(true)
|
||||||
},[p.path])
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(()=>{
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
promises.push(axios.post(p.BACKENDURL+p.path,{...entry,pass:p.password}))
|
|
||||||
})
|
|
||||||
Promise.allSettled(promises)
|
|
||||||
.then(()=>{
|
|
||||||
setUpdate(true)
|
setUpdate(true)
|
||||||
})
|
},[p.path,TESTMODE])
|
||||||
},[fileData,p.path,p.BACKENDURL,p.password])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
for (var col of fields) {
|
for (var col of fields) {
|
||||||
@ -207,24 +212,36 @@ function TableEditor(p) {
|
|||||||
{!loading?
|
{!loading?
|
||||||
<div>
|
<div>
|
||||||
<table>
|
<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()
|
const reader = new FileReader()
|
||||||
reader.onload=(ev)=>{
|
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])
|
reader.readAsText(f.target.files[0])
|
||||||
}} style={{opacity:0}} id="uploads" type="file" accept=".txt,.csv"/></caption>}
|
}} style={{opacity:0}} id="uploads" type="file" accept=".txt,.csv"/></caption>}
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<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>)}
|
{fields.map((field,i)=><React.Fragment key={i}><th scope="col" className="table-padding">{field.name}</th></React.Fragment>)}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{<tr><td></td>{fields.map((col,i)=><td key={i}>{<InputBox includeBlankValue={true} data={dependencies[col.name]} callback4={
|
{<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}>
|
{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>)}
|
<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>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -306,13 +323,13 @@ function DatabaseEditor(p) {
|
|||||||
<br/><br/>
|
<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>
|
<span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}><LifePreserver className="databaseIcon" style={{color:"green"}}/>Live Database</span>
|
||||||
<span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}><LifePreserver className="databaseIcon" style={{color:"red"}}/>Test Database</span><br/>
|
<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 = ""
|
var label = ""
|
||||||
if (db.datname!=="ngsplanner"&&db.datname!=="ngsplanner2") {
|
if (db.datname!=="ngsplanner"&&db.datname!=="ngsplanner2") {
|
||||||
var dateStr = db.datname.replace("ngsplanner","")
|
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))
|
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}</>
|
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={()=>{
|
onClick={()=>{
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
axios.post(p.BACKENDURL+"/databases/restorefrombackup",{
|
axios.post(p.BACKENDURL+"/databases/restorefrombackup",{
|
||||||
@ -328,9 +345,9 @@ function DatabaseEditor(p) {
|
|||||||
.then(()=>{
|
.then(()=>{
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
})
|
})
|
||||||
}}><CloudUploadFill/> Restore</button></span><br/></>
|
}}><CloudUploadFill/> Restore</button></span><br/></React.Fragment>
|
||||||
} else {
|
} else {
|
||||||
return <></>
|
return <React.Fragment key={i}/>
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
@ -413,10 +430,10 @@ function AdminPanel(p) {
|
|||||||
<div className="boxTitleBar">
|
<div className="boxTitleBar">
|
||||||
<h1>Navigation</h1>
|
<h1>Navigation</h1>
|
||||||
</div>
|
</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">
|
<div className="adminNavContainer customScrollbar">
|
||||||
<Table classes="adminNav">
|
<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/>
|
<Link to={process.env.PUBLIC_URL+"/admin/database_manager"}>Database Manager</Link><br/>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</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="box boxAdminContent">
|
||||||
<div className="boxTitleBar">
|
<div className="boxTitleBar">
|
||||||
<h1>{nav.page}</h1></div>
|
<h1>{nav.page}</h1></div>
|
||||||
@ -433,7 +450,7 @@ function AdminPanel(p) {
|
|||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{APP_TITLE+" - Admin Panel: "+nav.page}</title>
|
<title>{APP_TITLE+" - Admin Panel: "+nav.page}</title>
|
||||||
</Helmet>
|
</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>)}
|
</div></div></Route>)}
|
||||||
|
|
||||||
<Route path={process.env.PUBLIC_URL+"/admin/database_manager"}>
|
<Route path={process.env.PUBLIC_URL+"/admin/database_manager"}>
|
||||||
@ -790,8 +807,6 @@ function App() {
|
|||||||
const [DATAID,setDATAID] = useState({GetData:()=>{}})
|
const [DATAID,setDATAID] = useState({GetData:()=>{}})
|
||||||
const [update,setUpdate] = useState(false)
|
const [update,setUpdate] = useState(false)
|
||||||
|
|
||||||
const [dataLoaded,setDataLoaded] = useState(false)
|
|
||||||
|
|
||||||
const [LOGGEDINUSER,setLOGGEDINUSER] = useState("")
|
const [LOGGEDINUSER,setLOGGEDINUSER] = useState("")
|
||||||
const [LOGGEDINHASH,setLOGGEDINHASH] = useState("")
|
const [LOGGEDINHASH,setLOGGEDINHASH] = useState("")
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ function EditableClass(p){
|
|||||||
|
|
||||||
function PopupWindow(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="box boxModal">
|
||||||
<div className="boxTitleBar">
|
<div className="boxTitleBar">
|
||||||
<h1>{p.title}</h1>
|
<h1>{p.title}</h1>
|
||||||
@ -117,7 +117,7 @@ function SelectorWindow(p) {
|
|||||||
{(p.sortItems||p.filter)&&<div className="itemBar">
|
{(p.sortItems||p.filter)&&<div className="itemBar">
|
||||||
<div className="itemBarSort">
|
<div className="itemBarSort">
|
||||||
{p.sortItems&&<select className="itemBarForm" value={sortSelector} onChange={(f)=>{setSortSelector(f.currentTarget.value)}}>
|
{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>}
|
</select>}
|
||||||
</div>
|
</div>
|
||||||
<div className="itemBarFilter">
|
<div className="itemBarFilter">
|
||||||
@ -127,7 +127,7 @@ function SelectorWindow(p) {
|
|||||||
}
|
}
|
||||||
<div className="modalItemListContainer customScrollbar">
|
<div className="modalItemListContainer customScrollbar">
|
||||||
<ul className="itemlist">
|
<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}
|
{p.children}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -230,7 +230,7 @@ function SkillTreeBoxes(p) {
|
|||||||
return <>
|
return <>
|
||||||
{p.skillTreeSkillData&&p.skillTreeSkillData.map((skill,i)=>{
|
{p.skillTreeSkillData&&p.skillTreeSkillData.map((skill,i)=>{
|
||||||
var splitter = skill.split(",")
|
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>
|
</div>
|
||||||
|
|
||||||
<ClassSelectorWindow class={className} subClass={subclassName} setClassName={setClassName} setEditClass={setClassNameSetter} editClass={classNameSetter} setSubClassName={setSubClassName} modalOpen={classSelectWindowOpen} setModalOpen={setClassSelectWindowOpen} GetData={p.GetData}/>
|
<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="box skillTreeBox">
|
||||||
<div className="boxTitleBar">
|
<div className="boxTitleBar">
|
||||||
<h1>Class Skill Tree</h1>
|
<h1>Class Skill Tree</h1>
|
||||||
@ -647,7 +647,7 @@ AUGMENT
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
displayFunction={(item)=>{
|
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>
|
<span className="pot">{item[WEAPON_POTENTIAL].name}</span>
|
||||||
</ExpandTooltip></li>}}
|
</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.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)+(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.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:
|
default:
|
||||||
}
|
}
|
||||||
x++
|
x++
|
||||||
@ -47,7 +47,7 @@ function SkillTree(p) {
|
|||||||
|
|
||||||
return <canvas
|
return <canvas
|
||||||
width={width}
|
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}
|
export {SkillTree}
|
@ -1,5 +1,5 @@
|
|||||||
import { SkillTree } from "./skillTree";
|
import { SkillTree } from "./skillTree";
|
||||||
import { useEffect,useState,useMemo,useCallback } from "react";
|
import React, { useEffect,useState,useMemo,useCallback } from "react";
|
||||||
import { SkillTreeSelector } from "./skillTreeSelector";
|
import { SkillTreeSelector } from "./skillTreeSelector";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
@ -174,9 +174,9 @@ function SkillTreeEditor(p) {
|
|||||||
return <>
|
return <>
|
||||||
{loading?<img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/>:<>
|
{loading?<img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/>:<>
|
||||||
<h2>{message}</h2>
|
<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>
|
<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>
|
</select>
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
@ -189,19 +189,19 @@ function SkillTreeEditor(p) {
|
|||||||
gridDimensionsX={dimensionX} gridDimensionsY={dimensionY} gridSizeX={gridSizeX} gridSizeY={gridSizeY} gridPaddingX={gridPaddingX} gridPaddingY={gridPaddingY}
|
gridDimensionsX={dimensionX} gridDimensionsY={dimensionY} gridSizeX={gridSizeX} gridSizeY={gridSizeY} gridPaddingX={gridPaddingX} gridPaddingY={gridPaddingY}
|
||||||
skillLines={skillLines} halflineheight={halflineheight}
|
skillLines={skillLines} halflineheight={halflineheight}
|
||||||
/>
|
/>
|
||||||
{renderedInputs.map((control)=>control)}
|
{renderedInputs.map((control,i)=><React.Fragment key={i}>{control}</React.Fragment>)}
|
||||||
<br/>
|
<br/>
|
||||||
<hr/>
|
<hr/>
|
||||||
<br/>
|
<br/>
|
||||||
<label for="lineColor">Line Color:</label><input type="color" id="lineColor" value={lineColor} onChange={(f)=>{setLineColor(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 for="lineWidth">Line Width:</label><input type="number" id="lineWidth" value={lineWidth} onChange={(f)=>{setLineWidth(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 for="gridSizeX">Grid Size X:</label><input type="number" id="gridSizeX" value={dimensionX} onChange={(f)=>{setDimensionX(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 for="gridSizeY">Grid Size Y:</label><input type="number" id="gridSizeY" value={dimensionY} onChange={(f)=>{setDimensionY(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 for="subrowHeight">Sub-row Height:</label><input type="number" id="subrowHeight" value={halflineheight} onChange={(f)=>{setHalfLineHeight(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 for="boxSizeX">Box Size X:</label><input type="number" id="boxSizeX" value={gridSizeX} onChange={(f)=>{setGridSizeX(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 for="boxSizeY">Box Size Y:</label><input type="number" id="boxSizeY" value={gridSizeY} onChange={(f)=>{setGridSizeY(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 for="gridPaddingX">Grid Padding X:</label><input type="number" id="gridPaddingX" value={gridPaddingX} onChange={(f)=>{setGridPaddingX(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 for="gridPaddingY">Grid Padding Y:</label><input type="number" id="gridPaddingY" value={gridPaddingY} onChange={(f)=>{setGridPaddingY(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></>}
|
</div></>}
|
||||||
</>
|
</>
|
||||||
|
@ -13,11 +13,11 @@ function SkillTreeSelector(p) {
|
|||||||
return <>
|
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}>
|
<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)=>
|
{[' ','─','│','□','┌','└','┐','┘','┬','┴','├','┤','┼'].map((ch)=>
|
||||||
<option value={ch}>{ch}</option>)
|
<option value={ch} key={ch}>{ch}</option>)
|
||||||
}
|
}
|
||||||
</select>
|
</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]}>
|
{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>}
|
</select>}
|
||||||
</>
|
</>
|
||||||
|
|
||||||
|
@ -1408,3 +1408,12 @@ dd:before {
|
|||||||
font-family: "Segoe UI Symbol";
|
font-family: "Segoe UI Symbol";
|
||||||
content: "\2B1B" !important;
|
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…
x
Reference in New Issue
Block a user