562 lines
13 KiB
TypeScript
562 lines
13 KiB
TypeScript
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 = <T extends ColumnInfo>(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(),
|
|
}
|