mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-23 14:42:28 +01:00
fix more anys
This commit is contained in:
parent
ee105329d8
commit
2442402af5
@ -2,17 +2,17 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { GangMemberTask } from "../GangMemberTask";
|
||||
|
||||
interface Gang {
|
||||
export interface FormulaGang {
|
||||
respect: number;
|
||||
territory: number;
|
||||
wantedLevel: number;
|
||||
}
|
||||
|
||||
export function calculateWantedPenalty(gang: Gang): number {
|
||||
export function calculateWantedPenalty(gang: FormulaGang): number {
|
||||
return gang.respect / (gang.respect + gang.wantedLevel);
|
||||
}
|
||||
|
||||
export function calculateRespectGain(gang: Gang, member: GangMember, task: GangMemberTask): number {
|
||||
export function calculateRespectGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number {
|
||||
if (task.baseRespect === 0) return 0;
|
||||
let statWeight =
|
||||
(task.hackWeight / 100) * member.hack +
|
||||
@ -30,7 +30,7 @@ export function calculateRespectGain(gang: Gang, member: GangMember, task: GangM
|
||||
return Math.pow(11 * task.baseRespect * statWeight * territoryMult * respectMult, territoryPenalty);
|
||||
}
|
||||
|
||||
export function calculateWantedLevelGain(gang: Gang, member: GangMember, task: GangMemberTask): number {
|
||||
export function calculateWantedLevelGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number {
|
||||
if (task.baseWanted === 0) return 0;
|
||||
let statWeight =
|
||||
(task.hackWeight / 100) * member.hack +
|
||||
@ -53,7 +53,7 @@ export function calculateWantedLevelGain(gang: Gang, member: GangMember, task: G
|
||||
return Math.min(100, calc);
|
||||
}
|
||||
|
||||
export function calculateMoneyGain(gang: Gang, member: GangMember, task: GangMemberTask): number {
|
||||
export function calculateMoneyGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number {
|
||||
if (task.baseMoney === 0) return 0;
|
||||
let statWeight =
|
||||
(task.hackWeight / 100) * member.hack +
|
||||
|
@ -6,6 +6,12 @@ import { makeRuntimeRejectMsg } from "../NetscriptEvaluator";
|
||||
import { Player } from "../Player";
|
||||
import { CityName } from "../Locations/data/CityNames";
|
||||
import { BasicHGWOptions } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Server } from "../Server/Server";
|
||||
import { FormulaGang } from "../Gang/formulas/formulas";
|
||||
import { INetscriptHelper } from "../NetscriptFunctions/INetscriptHelper";
|
||||
import { GangMember } from "../Gang/GangMember";
|
||||
import { GangMemberTask } from "../Gang/GangMemberTask";
|
||||
|
||||
type ExternalFunction = (...args: unknown[]) => unknown;
|
||||
export type ExternalAPI = {
|
||||
@ -35,24 +41,6 @@ export type NetscriptContext = {
|
||||
helper: WrappedNetscriptHelpers;
|
||||
};
|
||||
|
||||
type NetscriptHelpers = {
|
||||
updateDynamicRam: (fnName: string, ramCost: number) => void;
|
||||
makeRuntimeErrorMsg: (caller: string, msg: string) => string;
|
||||
string: (funcName: string, argName: string, v: unknown) => string;
|
||||
number: (funcName: string, argName: string, v: unknown) => number;
|
||||
city: (funcName: string, argName: string, v: unknown) => CityName;
|
||||
boolean: (v: unknown) => boolean;
|
||||
getServer: (hostname: string, ctx: NetscriptContext) => BaseServer;
|
||||
checkSingularityAccess: (func: string) => void;
|
||||
hack: (
|
||||
ctx: NetscriptContext,
|
||||
hostname: string,
|
||||
manual: boolean,
|
||||
{ threads: requestedThreads, stock }?: BasicHGWOptions,
|
||||
) => Promise<number>;
|
||||
getValidPort: (funcName: string, port: number) => IPort;
|
||||
};
|
||||
|
||||
type WrappedNetscriptHelpers = {
|
||||
makeRuntimeErrorMsg: (msg: string) => string;
|
||||
string: (argName: string, v: unknown) => string;
|
||||
@ -63,10 +51,15 @@ type WrappedNetscriptHelpers = {
|
||||
checkSingularityAccess: () => void;
|
||||
hack: (hostname: string, manual: boolean, { threads: requestedThreads, stock }?: BasicHGWOptions) => Promise<number>;
|
||||
getValidPort: (port: number) => IPort;
|
||||
player(p: unknown): IPlayer;
|
||||
server(s: unknown): Server;
|
||||
gang(g: unknown): FormulaGang;
|
||||
gangMember(m: unknown): GangMember;
|
||||
gangTask(t: unknown): GangMemberTask;
|
||||
};
|
||||
|
||||
function wrapFunction(
|
||||
helpers: NetscriptHelpers,
|
||||
helpers: INetscriptHelper,
|
||||
wrappedAPI: ExternalAPI,
|
||||
workerScript: WorkerScript,
|
||||
func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown,
|
||||
@ -96,6 +89,11 @@ function wrapFunction(
|
||||
checkSingularityAccess: () => helpers.checkSingularityAccess(functionName),
|
||||
hack: (hostname: string, manual: boolean, extra?: BasicHGWOptions) => helpers.hack(ctx, hostname, manual, extra),
|
||||
getValidPort: (port: number) => helpers.getValidPort(functionPath, port),
|
||||
player: (p: unknown) => helpers.player(functionPath, p),
|
||||
server: (s: unknown) => helpers.server(functionPath, s),
|
||||
gang: (g: unknown) => helpers.gang(functionPath, g),
|
||||
gangMember: (m: unknown) => helpers.gangMember(functionPath, m),
|
||||
gangTask: (t: unknown) => helpers.gangTask(functionPath, t),
|
||||
},
|
||||
};
|
||||
function wrappedFunction(...args: unknown[]): unknown {
|
||||
@ -111,7 +109,7 @@ function wrapFunction(
|
||||
}
|
||||
|
||||
export function wrapAPI(
|
||||
helpers: NetscriptHelpers,
|
||||
helpers: INetscriptHelper,
|
||||
wrappedAPI: ExternalAPI,
|
||||
workerScript: WorkerScript,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
|
@ -57,6 +57,7 @@ import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions"
|
||||
import { LogBoxEvents, LogBoxCloserEvents } from "./ui/React/LogBoxManager";
|
||||
import { arrayToString } from "./utils/helpers/arrayToString";
|
||||
import { isString } from "./utils/helpers/isString";
|
||||
import { FormulaGang as FormulaGang } from "./Gang/formulas/formulas";
|
||||
|
||||
import { BaseServer } from "./Server/BaseServer";
|
||||
import { NetscriptGang } from "./NetscriptFunctions/Gang";
|
||||
@ -108,6 +109,10 @@ import { recentScripts } from "./Netscript/RecentScripts";
|
||||
import { CityName } from "./Locations/data/CityNames";
|
||||
import { InternalAPI, NetscriptContext, wrapAPI } from "./Netscript/APIWrapper";
|
||||
import { INetscriptHelper } from "./NetscriptFunctions/INetscriptHelper";
|
||||
import { IPlayer } from "./PersonObjects/IPlayer";
|
||||
import { PlayerObject } from "./PersonObjects/Player/PlayerObject";
|
||||
import { GangMember } from "./Gang/GangMember";
|
||||
import { GangMemberTask } from "./Gang/GangMemberTask";
|
||||
|
||||
interface NS extends INS {
|
||||
[key: string]: any;
|
||||
@ -463,6 +468,14 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
return out;
|
||||
};
|
||||
|
||||
const roughlyIs = (expect: object, actual: unknown): boolean => {
|
||||
if (typeof actual !== "object" || actual == null) return false;
|
||||
const expects = Object.keys(expect);
|
||||
const actuals = Object.keys(actual);
|
||||
for (const expect of expects) if (!actuals.includes(expect)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const helper: INetscriptHelper = {
|
||||
updateDynamicRam: updateDynamicRam,
|
||||
makeRuntimeErrorMsg: makeRuntimeErrorMsg,
|
||||
@ -514,6 +527,28 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
}
|
||||
return iport;
|
||||
},
|
||||
player(funcName: string, p: unknown): IPlayer {
|
||||
if (!roughlyIs(new PlayerObject(), p)) throw makeRuntimeErrorMsg(funcName, `player should be a Player.`);
|
||||
return p as IPlayer;
|
||||
},
|
||||
server(funcName: string, s: unknown): Server {
|
||||
if (!roughlyIs(new Server(), s)) throw makeRuntimeErrorMsg(funcName, `server should be a Server.`);
|
||||
return s as Server;
|
||||
},
|
||||
gang(funcName: string, g: unknown): FormulaGang {
|
||||
if (!roughlyIs({ respect: 0, territory: 0, wantedLevel: 0 }, g))
|
||||
throw makeRuntimeErrorMsg(funcName, `gang should be a Gang.`);
|
||||
return g as FormulaGang;
|
||||
},
|
||||
gangMember(funcName: string, m: unknown): GangMember {
|
||||
if (!roughlyIs(new GangMember(), m)) throw makeRuntimeErrorMsg(funcName, `member should be a GangMember.`);
|
||||
return m as GangMember;
|
||||
},
|
||||
gangTask(funcName: string, t: unknown): GangMemberTask {
|
||||
if (!roughlyIs(new GangMemberTask("", "", false, false, {}), t))
|
||||
throw makeRuntimeErrorMsg(funcName, `task should be a GangMemberTask.`);
|
||||
return t as GangMemberTask;
|
||||
},
|
||||
};
|
||||
|
||||
const singularity = NetscriptSingularity(Player, workerScript);
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
WarehouseAPI,
|
||||
OfficeAPI,
|
||||
InvestmentOffer,
|
||||
Office as NSOffice,
|
||||
} from "../ScriptEditor/NetscriptDefinitions";
|
||||
|
||||
import {
|
||||
@ -733,7 +734,7 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript
|
||||
},
|
||||
hireEmployee:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_divisionName: unknown, _cityName: unknown): any => {
|
||||
(_divisionName: unknown, _cityName: unknown): NSEmployee | undefined => {
|
||||
checkAccess(ctx, 8);
|
||||
const divisionName = ctx.helper.string("divisionName", _divisionName);
|
||||
const cityName = ctx.helper.city("cityName", _cityName);
|
||||
@ -817,7 +818,7 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript
|
||||
},
|
||||
getOffice:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_divisionName: unknown, _cityName: unknown): any => {
|
||||
(_divisionName: unknown, _cityName: unknown): NSOffice => {
|
||||
checkAccess(ctx, 8);
|
||||
const divisionName = ctx.helper.string("divisionName", _divisionName);
|
||||
const cityName = ctx.helper.city("cityName", _cityName);
|
||||
@ -838,6 +839,7 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript
|
||||
Management: office.employeeProd[EmployeePositions.Management],
|
||||
"Research & Development": office.employeeProd[EmployeePositions.RandD],
|
||||
Training: office.employeeProd[EmployeePositions.Training],
|
||||
Unassigned: 0,
|
||||
},
|
||||
employeeJobs: {
|
||||
Operations: office.employeeJobs[EmployeePositions.Operations],
|
||||
|
@ -27,7 +27,11 @@ import {
|
||||
calculateWeakenTime,
|
||||
} from "../Hacking";
|
||||
import { Programs } from "../Programs/Programs";
|
||||
import { Formulas as IFormulas } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import {
|
||||
Formulas as IFormulas,
|
||||
HacknetNodeConstants as DefHacknetNodeConstants,
|
||||
HacknetServerConstants as DefHacknetServerConstants,
|
||||
} from "../ScriptEditor/NetscriptDefinitions";
|
||||
import {
|
||||
calculateRespectGain,
|
||||
calculateWantedLevelGain,
|
||||
@ -64,8 +68,9 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In
|
||||
},
|
||||
repFromDonation:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_amount: unknown, player: any): number => {
|
||||
(_amount: unknown, _player: unknown): number => {
|
||||
const amount = ctx.helper.number("amount", _amount);
|
||||
const player = ctx.helper.player(_player);
|
||||
checkFormulasAccess(ctx);
|
||||
return repFromDonation(amount, player);
|
||||
},
|
||||
@ -91,25 +96,33 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In
|
||||
hacking: {
|
||||
hackChance:
|
||||
(ctx: NetscriptContext) =>
|
||||
(server: any, player: any): number => {
|
||||
(_server: unknown, _player: unknown): number => {
|
||||
const server = ctx.helper.server(_server);
|
||||
const player = ctx.helper.player(_player);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateHackingChance(server, player);
|
||||
},
|
||||
hackExp:
|
||||
(ctx: NetscriptContext) =>
|
||||
(server: any, player: any): number => {
|
||||
(_server: unknown, _player: unknown): number => {
|
||||
const server = ctx.helper.server(_server);
|
||||
const player = ctx.helper.player(_player);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateHackingExpGain(server, player);
|
||||
},
|
||||
hackPercent:
|
||||
(ctx: NetscriptContext) =>
|
||||
(server: any, player: any): number => {
|
||||
(_server: unknown, _player: unknown): number => {
|
||||
const server = ctx.helper.server(_server);
|
||||
const player = ctx.helper.player(_player);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculatePercentMoneyHacked(server, player);
|
||||
},
|
||||
growPercent:
|
||||
(ctx: NetscriptContext) =>
|
||||
(server: any, _threads: unknown, player: any, _cores: unknown = 1): number => {
|
||||
(_server: unknown, _threads: unknown, _player: unknown, _cores: unknown = 1): number => {
|
||||
const server = ctx.helper.server(_server);
|
||||
const player = ctx.helper.player(_player);
|
||||
const threads = ctx.helper.number("threads", _threads);
|
||||
const cores = ctx.helper.number("cores", _cores);
|
||||
checkFormulasAccess(ctx);
|
||||
@ -117,19 +130,25 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In
|
||||
},
|
||||
hackTime:
|
||||
(ctx: NetscriptContext) =>
|
||||
(server: any, player: any): number => {
|
||||
(_server: unknown, _player: unknown): number => {
|
||||
const server = ctx.helper.server(_server);
|
||||
const player = ctx.helper.player(_player);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateHackingTime(server, player) * 1000;
|
||||
},
|
||||
growTime:
|
||||
(ctx: NetscriptContext) =>
|
||||
(server: any, player: any): number => {
|
||||
(_server: unknown, _player: unknown): number => {
|
||||
const server = ctx.helper.server(_server);
|
||||
const player = ctx.helper.player(_player);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateGrowTime(server, player) * 1000;
|
||||
},
|
||||
weakenTime:
|
||||
(ctx: NetscriptContext) =>
|
||||
(server: any, player: any): number => {
|
||||
(_server: unknown, _player: unknown): number => {
|
||||
const server = ctx.helper.server(_server);
|
||||
const player = ctx.helper.player(_player);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateWeakenTime(server, player) * 1000;
|
||||
},
|
||||
@ -180,7 +199,7 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateNodeCost(n, mult);
|
||||
},
|
||||
constants: (ctx: NetscriptContext) => (): any => {
|
||||
constants: (ctx: NetscriptContext) => (): DefHacknetNodeConstants => {
|
||||
checkFormulasAccess(ctx);
|
||||
return Object.assign({}, HacknetNodeConstants);
|
||||
},
|
||||
@ -255,7 +274,7 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In
|
||||
checkFormulasAccess(ctx);
|
||||
return HScalculateServerCost(n, mult);
|
||||
},
|
||||
constants: (ctx: NetscriptContext) => (): any => {
|
||||
constants: (ctx: NetscriptContext) => (): DefHacknetServerConstants => {
|
||||
checkFormulasAccess(ctx);
|
||||
return Object.assign({}, HacknetServerConstants);
|
||||
},
|
||||
@ -263,25 +282,35 @@ export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): In
|
||||
gang: {
|
||||
wantedPenalty:
|
||||
(ctx: NetscriptContext) =>
|
||||
(gang: any): number => {
|
||||
(_gang: unknown): number => {
|
||||
const gang = ctx.helper.gang(_gang);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateWantedPenalty(gang);
|
||||
},
|
||||
respectGain:
|
||||
(ctx: NetscriptContext) =>
|
||||
(gang: any, member: any, task: any): number => {
|
||||
(_gang: unknown, _member: unknown, _task: unknown): number => {
|
||||
const gang = ctx.helper.gang(_gang);
|
||||
const member = ctx.helper.gangMember(_member);
|
||||
const task = ctx.helper.gangTask(_task);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateRespectGain(gang, member, task);
|
||||
},
|
||||
wantedLevelGain:
|
||||
(ctx: NetscriptContext) =>
|
||||
(gang: any, member: any, task: any): number => {
|
||||
(_gang: unknown, _member: unknown, _task: unknown): number => {
|
||||
const gang = ctx.helper.gang(_gang);
|
||||
const member = ctx.helper.gangMember(_member);
|
||||
const task = ctx.helper.gangTask(_task);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateWantedLevelGain(gang, member, task);
|
||||
},
|
||||
moneyGain:
|
||||
(ctx: NetscriptContext) =>
|
||||
(gang: any, member: any, task: any): number => {
|
||||
(_gang: unknown, _member: unknown, _task: unknown): number => {
|
||||
const gang = ctx.helper.gang(_gang);
|
||||
const member = ctx.helper.gangMember(_member);
|
||||
const task = ctx.helper.gangTask(_task);
|
||||
checkFormulasAccess(ctx);
|
||||
return calculateMoneyGain(gang, member, task);
|
||||
},
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { CityName } from "src/Locations/data/CityNames";
|
||||
import { NetscriptContext } from "src/Netscript/APIWrapper";
|
||||
import { IPort } from "src/NetscriptPort";
|
||||
import { CityName } from "../Locations/data/CityNames";
|
||||
import { NetscriptContext } from "../Netscript/APIWrapper";
|
||||
import { IPort } from "../NetscriptPort";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Server } from "../Server/Server";
|
||||
import { BaseServer } from "../Server/BaseServer";
|
||||
import { FormulaGang } from "../Gang/formulas/formulas";
|
||||
import { GangMember } from "../Gang/GangMember";
|
||||
import { GangMemberTask } from "../Gang/GangMemberTask";
|
||||
import { BasicHGWOptions } from "../ScriptEditor/NetscriptDefinitions";
|
||||
|
||||
export interface INetscriptHelper {
|
||||
updateDynamicRam(functionName: string, ram: number): void;
|
||||
@ -12,6 +18,11 @@ export interface INetscriptHelper {
|
||||
boolean(v: unknown): boolean;
|
||||
getServer(ip: string, ctx: NetscriptContext): BaseServer;
|
||||
checkSingularityAccess(func: string): void;
|
||||
hack(ctx: NetscriptContext, hostname: string, manual: boolean): Promise<number>;
|
||||
hack(ctx: NetscriptContext, hostname: string, manual: boolean, extra?: BasicHGWOptions): Promise<number>;
|
||||
getValidPort(funcName: string, port: number): IPort;
|
||||
player(funcName: string, p: unknown): IPlayer;
|
||||
server(funcName: string, s: unknown): Server;
|
||||
gang(funcName: string, g: unknown): FormulaGang;
|
||||
gangMember(funcName: string, m: unknown): GangMember;
|
||||
gangTask(funcName: string, m: unknown): GangMemberTask;
|
||||
}
|
||||
|
4
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
4
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -7070,7 +7070,7 @@ interface CorporationInfo {
|
||||
* Employee in an office
|
||||
* @public
|
||||
*/
|
||||
interface Employee {
|
||||
export interface Employee {
|
||||
/** Name of the employee */
|
||||
name: string;
|
||||
/** Morale of the employee */
|
||||
@ -7170,7 +7170,7 @@ interface Warehouse {
|
||||
* Office for a division in a city.
|
||||
* @public
|
||||
*/
|
||||
interface Office {
|
||||
export interface Office {
|
||||
/** City of the office */
|
||||
loc: string;
|
||||
/** Maximum number of employee */
|
||||
|
@ -193,9 +193,9 @@ export function Root(props: IProps): React.ReactElement {
|
||||
// setup monaco-vim
|
||||
if (options.vim && editor && !vimEditor) {
|
||||
try {
|
||||
// This library is not typed
|
||||
// @ts-expect-error
|
||||
window.require(["monaco-vim"], function (MonacoVim: any) {
|
||||
console.log(MonacoVim);
|
||||
setVimEditor(MonacoVim.initVimMode(editor, vimStatusRef.current));
|
||||
MonacoVim.VimMode.Vim.defineEx("write", "w", function () {
|
||||
// your own implementation on what you want to do when :w is pressed
|
||||
@ -212,7 +212,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
|
||||
// Setup "go to next tab" and "go to previous tab". This is a little more involved
|
||||
// since these aren't Ex commands (they run in normal mode, not after typing `:`)
|
||||
MonacoVim.VimMode.Vim.defineAction("nextTabs", function (_cm: any, args: { repeat?: number }) {
|
||||
MonacoVim.VimMode.Vim.defineAction("nextTabs", function (_cm: unknown, args: { repeat?: number }) {
|
||||
const nTabs = args.repeat ?? 1;
|
||||
// Go to the next tab (to the right). Wraps around when at the rightmost tab
|
||||
const currIndex = currentTabIndex();
|
||||
@ -221,7 +221,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
onTabClick(nextIndex);
|
||||
}
|
||||
});
|
||||
MonacoVim.VimMode.Vim.defineAction("prevTabs", function (_cm: any, args: { repeat?: number }) {
|
||||
MonacoVim.VimMode.Vim.defineAction("prevTabs", function (_cm: unknown, args: { repeat?: number }) {
|
||||
const nTabs = args.repeat ?? 1;
|
||||
// Go to the previous tab (to the left). Wraps around when at the leftmost tab
|
||||
const currIndex = currentTabIndex();
|
||||
|
Loading…
Reference in New Issue
Block a user