import Handsontable from 'handsontable' import Core from 'handsontable/core' import { textRenderer } from 'handsontable/renderers' import numbro from 'numbro' import { TricksterItem } from './trickster' export const BasicColumns = ['Image', 'Name', 'Count'] as const export const DetailsColumns = ['Desc', 'Use'] as const export const MoveColumns = ['MoveCount', 'Move'] as const export const TagColumns = ['All', 'Equip', 'Drill', 'Card', 'Quest', 'Consume', 'Compound'] as const export const EquipmentColumns = ['MinLvl', 'Slots', 'RefineNumber', 'RefineState'] as const export const StatsColumns = [ 'AP', 'GunAP', 'AC', 'DX', 'MP', 'MA', 'MD', 'WT', 'DA', 'LK', 'HP', 'DP', 'HV', ] as const export const HackColumns = [] as const export const ColumnNames = [ ...BasicColumns, ...MoveColumns, ...DetailsColumns, ...EquipmentColumns, ...StatsColumns, ...TagColumns, ...HackColumns, ] as const 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 => { const n1 = ColumnNames.indexOf(c(a)) const n2 = ColumnNames.indexOf(c(b)) if (n1 === n2) { return 0 } return n1 > n2 ? 1 : -1 } export interface ColumnInfo { name: ColumnName displayName: string options?: (s: string[]) => 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.item_image ? item.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) const 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 = (invs: string[]): string[] => { const out: string[] = [] for (const k of invs) { 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?.culture || '-' const cellFormatPattern = 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_comment.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_comment } } function descRenderer( _instance: any, td: any, _row: any, _col: any, _prop: any, value: any, _cellProperties: any, ) { const stringifiedValue = Handsontable.helper.stringify(value) const 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) const 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 test = (n: new () => T): [string, T] => { const nn = new n() return [nn.name, nn] } 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(), }