import Handsontable from "handsontable" import numbro from 'numbro'; import { textRenderer } from "handsontable/renderers" import { TricksterInventory, TricksterItem } from "./trickster" import Core from "handsontable/core"; import { RefStore } from "../state/state"; export const BasicColumns = [ "Image","Name","Count", ] export const DetailsColumns = [ "Desc","Use", ] export const MoveColumns = [ "MoveCount","Move", ] export const TagColumns = [ "All","Equip","Drill","Card","Quest","Consume", "Compound" ] export const HackColumns = [ ] export const EquipmentColumns = [ "MinLvl","Slots","RefineNumber","RefineState", ] export const StatsColumns = [ "AP","GunAP","AC","DX","MP","MA","MD","WT","DA","LK","HP","DP","HV", ] export const ColumnNames = [ ...BasicColumns, ...MoveColumns, ...DetailsColumns, ...EquipmentColumns, ...StatsColumns, ...TagColumns, ] export type ColumnName = typeof ColumnNames[number] const c = (a:ColumnName | ColumnInfo):ColumnName => { switch(typeof a) { case "string": return a case "object": return a.name } } export const LazyColumn = c; export const ColumnSorter = (a:ColumnName | ColumnInfo, b: ColumnName | ColumnInfo):number => { let n1 = ColumnNames.indexOf(c(a)) let n2 = ColumnNames.indexOf(c(b)) if(n1 == n2) { return 0 } return n1 > n2 ? 1 : -1 } export interface ColumnInfo { name: ColumnName displayName:string options?:(item:TricksterItem, r:RefStore)=>string[] renderer?:any filtering?:boolean writable?:boolean getter(item:TricksterItem):(string | number) } class Image implements ColumnInfo { name:ColumnName = 'Image' displayName = " " renderer = coverRenderer getter(item:TricksterItem):(string|number) { return item.image ? item.image : "" } } function coverRenderer(instance:any, td:any, row:any, col:any, prop:any, value:any, cellProperties:any) { const stringifiedValue = Handsontable.helper.stringify(value); if (stringifiedValue.startsWith('http')) { const img:any = document.createElement('IMG'); img.src = value; Handsontable.dom.addEvent(img, 'mousedown', event =>{ event!.preventDefault(); }); Handsontable.dom.empty(td); td.appendChild(img); } else { } } class Name implements ColumnInfo { name:ColumnName = "Name" displayName = "Name" filtering = true renderer = nameRenderer getter(item:TricksterItem):(string|number){ return item.item_name } } function nameRenderer(instance:any, td:any, row:any, col:any, prop:any, value:any, cellProperties:any) { const stringifiedValue = Handsontable.helper.stringify(value); let showText = stringifiedValue; const div= document.createElement('div'); div.innerHTML = showText div.title = showText div.style.maxWidth = "20ch" div.style.textOverflow = "ellipsis" div.style.overflow= "hidden" div.style.whiteSpace= "nowrap" Handsontable.dom.addEvent(div, 'mousedown', event =>{ event!.preventDefault(); }); Handsontable.dom.empty(td); td.appendChild(div); td.classList.add("htLeft") } class Count implements ColumnInfo { name:ColumnName = "Count" displayName = "Count" renderer = "numeric" filtering = true getter(item:TricksterItem):(string|number){ return item.item_count } } class Move implements ColumnInfo { name:ColumnName = "Move" displayName = "Target" writable = true options = getMoveTargets getter(item:TricksterItem):(string|number){ return "-" } } const getMoveTargets = (item:TricksterItem, r:RefStore):string[] => { let out:string[] = []; for(const k of r.invs.value.keys()) { out.push(k) } out.push("") out.push("") out.push("!TRASH") return out } class MoveCount implements ColumnInfo { name:ColumnName = "MoveCount" displayName = "Move #" renderer = moveCountRenderer writable = true getter(item:TricksterItem):(string|number){ return "" } } function moveCountRenderer(instance:Core, td:any, row:number, col:number, prop:any, value:any, cellProperties:any) { let newValue = value; if (Handsontable.helper.isNumeric(newValue)) { const numericFormat = cellProperties.numericFormat; const cellCulture = numericFormat && numericFormat.culture || '-'; const cellFormatPattern = numericFormat && numericFormat.pattern; const className = cellProperties.className || ''; const classArr = className.length ? className.split(' ') : []; if (typeof cellCulture !== 'undefined' && !numbro.languages()[cellCulture]) { const shortTag:any = cellCulture.replace('-', ''); const langData = (numbro as any)[shortTag]; if (langData) { numbro.registerLanguage(langData); } } const totalCount = Number(instance.getCell(row,col-1)?.innerHTML) numbro.setLanguage(cellCulture); const num = numbro(newValue) if(totalCount < num.value()) { const newNum = numbro(totalCount) newValue = newNum.format(cellFormatPattern || '0'); }else { newValue = num.format(cellFormatPattern || '0'); } if (classArr.indexOf('htLeft') < 0 && classArr.indexOf('htCenter') < 0 && classArr.indexOf('htRight') < 0 && classArr.indexOf('htJustify') < 0) { classArr.push('htRight'); } if (classArr.indexOf('htNumeric') < 0) { classArr.push('htNumeric'); } cellProperties.className = classArr.join(' '); td.dir = 'ltr'; newValue = newValue + "x" }else { newValue = "" } textRenderer(instance, td, row, col, prop, newValue, cellProperties); } class Equip implements ColumnInfo { name:ColumnName = "Equip" displayName = "equip" getter(item:TricksterItem):(string|number){ return item.is_equip ? 1 : 0 } } class Drill implements ColumnInfo { name:ColumnName = "Drill" displayName = "drill" getter(item:TricksterItem):(string|number){ return item.is_drill ? 1 : 0 } } class All implements ColumnInfo { name:ColumnName = "All" displayName = "swap" getter(_:TricksterItem):(string|number){ return -10000 } } class Card implements ColumnInfo { name:ColumnName = "Card" displayName = "card" getter(item:TricksterItem):(string|number){ return cardFilter(item) ? 1 : 0 } } const cardFilter= (item:TricksterItem): boolean => { return (item.item_name.endsWith(" Card") || item.item_name.startsWith("Star Card")) } class Compound implements ColumnInfo { name:ColumnName = "Compound" displayName = "comp" getter(item:TricksterItem):(string|number){ return compFilter(item) ? 1 : 0 } } const compFilter= (item:TricksterItem): boolean => { return (item.item_desc.toLowerCase().includes("compound item")) } class Quest implements ColumnInfo { name:ColumnName = "Quest" displayName = "quest" getter(item:TricksterItem):(string|number){ return questFilter(item) ? 1 : 0 } } const questFilter= (item:TricksterItem): boolean => { return false } class Consume implements ColumnInfo { name:ColumnName = "Consume" displayName = "eat" getter(item:TricksterItem):(string|number){ return consumeFilter(item) ? 1 : 0 } } const consumeFilter= (item:TricksterItem): boolean => { const tl = item.item_use.toLowerCase() return tl.includes("recover") || tl.includes("restores") } class AP implements ColumnInfo { name:ColumnName = "AP" displayName = "AP" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["AP"] : "" } } class GunAP implements ColumnInfo { name:ColumnName = "GunAP" displayName = "Gun AP" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["Gun AP"] : "" } } class AC implements ColumnInfo { name:ColumnName = "AC" displayName = "AC" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["AC"] : "" } } class DX implements ColumnInfo { name:ColumnName = "DX" displayName = "DX" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["DX"] : "" } } class MP implements ColumnInfo { name:ColumnName = "MP" displayName = "MP" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["MP"] : "" } } class MA implements ColumnInfo { name:ColumnName = "MA" displayName = "MA" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["MA"] : "" } } class MD implements ColumnInfo { name:ColumnName = "MD" displayName = "MD" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["MD"] : "" } } class WT implements ColumnInfo { name:ColumnName = "WT" displayName = "WT" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["WT"] : "" } } class DA implements ColumnInfo { name:ColumnName = "DA" displayName = "DA" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["DA"] : "" } } class LK implements ColumnInfo { name:ColumnName = "LK" displayName = "LK" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["LK"] : "" } } class HP implements ColumnInfo { name:ColumnName = "HP" displayName = "HP" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["HP"] : "" } } class DP implements ColumnInfo { name:ColumnName = "DP" displayName = "DP" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["DP"] : "" } } class HV implements ColumnInfo { name:ColumnName = "HV" displayName = "HV" getter(item:TricksterItem):(string|number){ return item.stats ? item.stats["HV"] : "" } } class MinLvl implements ColumnInfo { name:ColumnName = "MinLvl" displayName = "lvl" getter(item:TricksterItem):(string|number){ //TODO: return item.item_min_level? item.item_min_level:"" } } class Slots implements ColumnInfo { name:ColumnName = "Slots" displayName = "slots" getter(item:TricksterItem):(string|number){ //TODO: return item.item_slots ? item.item_slots : "" } } class RefineNumber implements ColumnInfo { name:ColumnName = "RefineNumber" displayName = "refine" getter(item:TricksterItem):(string|number){ return item.refine_level ? item.refine_level : 0 } } class RefineState implements ColumnInfo { name:ColumnName = "RefineState" displayName = "bork" getter(item:TricksterItem):(string|number){ return item.refine_state ? item.refine_state : 0 } } class Desc implements ColumnInfo { name:ColumnName = "Desc" displayName = "desc" renderer = descRenderer getter(item:TricksterItem):(string|number){ return item.item_desc } } function descRenderer(instance:any, td:any, row:any, col:any, prop:any, value:any, cellProperties:any) { const stringifiedValue = Handsontable.helper.stringify(value); let showText = stringifiedValue; const div= document.createElement('div'); div.innerHTML = showText div.title = showText div.style.maxWidth = "30ch" div.style.textOverflow = "ellipsis" div.style.overflow= "hidden" div.style.whiteSpace= "nowrap" Handsontable.dom.addEvent(div, 'mousedown', event =>{ event!.preventDefault(); }); Handsontable.dom.empty(td); td.appendChild(div); td.classList.add("htLeft") } class Use implements ColumnInfo { name:ColumnName = "Use" displayName = "use" renderer= useRenderer; getter(item:TricksterItem):(string|number){ return item.item_use } } function useRenderer(instance:any, td:any, row:any, col:any, prop:any, value:any, cellProperties:any) { const stringifiedValue = Handsontable.helper.stringify(value); let showText = stringifiedValue; const div= document.createElement('div'); div.title = showText div.innerHTML = showText div.style.maxWidth = "30ch" div.style.textOverflow = "ellipsis" div.style.overflow= "hidden" div.style.whiteSpace= "nowrap" Handsontable.dom.addEvent(div, 'mousedown', event =>{ event!.preventDefault(); }); Handsontable.dom.empty(td); td.appendChild(div); td.classList.add("htLeft") } export const ColumnByNames = (...n:ColumnName[]) => { return n.map(ColumnByName) } export const ColumnByName = (n:ColumnName) => { return Columns[n] } export const Columns:{[Property in ColumnName]:ColumnInfo}= { Use: new Use(), Desc: new Desc(), Image: new Image(), Name: new Name(), Count: new Count(), Move: new Move(), MoveCount: new MoveCount(), Equip: new Equip(), Drill: new Drill(), Card: new Card(), Quest: new Quest(), Consume: new Consume(), AP: new AP(), GunAP: new GunAP(), AC: new AC(), DX: new DX(), MP: new MP(), MA: new MA(), MD: new MD(), WT: new WT(), DA: new DA(), LK: new LK(), HP: new HP(), DP: new DP(), HV: new HV(), MinLvl: new MinLvl(), Slots: new Slots(), RefineNumber: new RefineNumber(), RefineState: new RefineState(), All: new All(), Compound: new Compound(), }