Simple Options Module

About


This module will help with stuff like settings where users can select from different options on a setting, such as Low, Medium, or High graphics.


Module

--!strict

--> Class
local options = {}
options.__index = options

--> Types
export type Options = {
	Options : {[number] : any},
	current : number,
	onChanges : {[number] : (new : any, old : any) -> ()},
	
	set : (Options, index : number) -> (),
	next : (Options) -> (),
	previous : (Options) -> (),
	get : (Options) -> any,
	onChange : (Options, fnc : (new : any, old : any) -> ()) -> () -> (),
	insert : (Options, value : any) -> (),
	remove : (Options, index : number) -> (),
}

--> Constructor
function options.new(opt : {[number] : any}, default : number?) : Options
	local self = setmetatable({}, options)
	
	self.Options = opt
	self.current = default or 1
	self.onChanges = {}
	
	return self :: any
end

--> methods
function options:set(index : number)
	local old = self.current
	self.current = math.clamp(index, 1, #self.Options)
	
	for i, v in pairs(self.onChanges) do
		task.spawn(v, self.Options[self.current], self.Options[old])
	end
end

function options:next()
	self = self :: Options
	local old = self.current
	local new = self.current + 1
	if new > #self.Options then
		new = 1
	end

	self.current = new

	for i, v in pairs(self.onChanges) do
		task.spawn(v, self.Options[self.current], self.Options[old])
	end
end

function options:previous()
	self = self :: Options
	local old = self.current
	local new = self.current - 1
	if new < 1 then
		new = #self.Options
	end

	self.current = new

	for i, v in pairs(self.onChanges) do
		task.spawn(v, self.Options[self.current], self.Options[old])
	end
end

function options:insert(value : any)
	table.insert(self.Options, value)
end

function options:remove(index : number)
	if self.Options[index] ~= nil then
		local old = self.Options[index]
		table.remove(self.Options, index)
		
		if self.current == index then
			self.current = math.clamp(index, 1, #self.Options)
			for i, v in pairs(self.onChanges) do
				task.spawn(v, self.Options[self.current], old)
			end
		end
	end
end

function options:get()
	return self.Options[self.current]
end

function options:onChange(fnc : (new : any, old : any) -> ())
	table.insert(self.onChanges, fnc)
	
	return function()
		local index = table.find(self.onChanges, fnc)
		if index ~=  nil then
			table.remove(self.onChanges, index)
		end
	end
end

--> return
return options

Example


local Options = require(game:GetService('ReplicatedStorage').Options)

local opt = {
	'Hello',
	'Hey',
	'Hay',
	'Goodbye',
	'Bye',
	'Bay'
}

local selection = Options.new(opt, 3)
print('Initial Selection: ', selection:get()) --> Hay

selection:previous()
print('Previous Selection: ', selection:get()) --> Hey

selection:next()
print('Next Selection: ', selection:get()) --> Hay

selection:set(1)
print('Set Selection: ', selection:get()) --> Hello

selection:insert('Test')
selection:set(7)
print('Inserted Selection: ', selection:get()) --> Test

selection:remove(7)
print('Removed Selection: ', selection:get()) --> Bay

local listener = selection:onChange(function(new : any, old : any) -- Goodbye
	print('New: ', new, ' Old: ', old)
end)

selection:set(4)
listener()

selection:set(1) -- Hello

Documentation


.new(opt : {[number] : any}, default : number?)

this will create a new options class, opt will be a array of options, you cannot use string keys with this.

:set(index : number)

this will set the current option to the index given. If a index greater than the amount of options is passed it will default to the last option, if one smaller than 1 is passed, it will default to 1

:next()

this will add 1 to the current index option.

:previous()

this will minus 1 to the current index option

:get()

this will return the value of the current index

:onChange(fnc : (old : any, new : any) → ())

this will add a function to the onChanges table inside the class, which will get called every time the index is changed. It will return a function to remove the passed function from the callbacks.

:insert(value : any)

this will insert the value into the options table.

:remove(index : number)

this will remove the passed index from the options table. If the current index is the given index it will stay the same unless it is not replaced by a new value. If the value is nil (from it being at the last index and the last index gets removed) it will go to the last index available.

1 Like

Quite useful for a voting system, nice :+1:
Although I didn’t quite like how things were formatted and whatnot, so I cleaned up the code a little (for those who want to use it):

--!strict

local Options = {}

export type Options<T=any> = {
	Options : {[number] : T},
	_onChanges : {[(new: T, old: T) -> ()] : true?},
	_current : number,

	set : (Options<T>, index : number) -> (),
	next : (Options<T>) -> (),
	previous : (Options<T>) -> (),
	get : (Options<T>) -> T,
	onChange : (Options<T>, fn : (new : T, old : T) -> ()) -> (() -> ()),
	insert : (Options<T>, value : T) -> (),
	remove : (Options<T>, index : number) -> (),
}

function Options.set(self: Options, index : number)
	local old = self._current
	self._current = math.clamp(index, 1, #self.Options)

	for fn in self._onChanges do
		task.defer(fn, self.Options[self._current], self.Options[old])
	end
end

function Options.next(self: Options)
	local old = self._current
	local new = self._current + 1
	if new > #self.Options then
		new = 1
	end

	self._current = new

	for fn in self._onChanges do
		task.defer(fn, self.Options[self._current], self.Options[old])
	end
end

function Options.previous(self: Options)
	local old = self._current
	local new = self._current - 1
	if new < 1 then
		new = #self.Options
	end

	self._current = new

	for fn in self._onChanges do
		task.defer(fn, self.Options[self._current], self.Options[old])
	end
end

function Options.insert(self: Options, value : any)
	table.insert(self.Options, value)
end

function Options.remove(self: Options, index : number)
	if self.Options[index] == nil then
		return
	end
	local old = self.Options[index]
	table.remove(self.Options, index)
	
	if self._current ~= index then
		return
	end

	self._current = math.clamp(index, 1, #self.Options)

	for fn in self._onChanges do
		task.defer(fn, self.Options[self._current], old)
	end
end

function Options.get(self: Options)
	return self.Options[self._current]
end

function Options.onChange(self: Options, fn : (new : any, old : any) -> ())
	self._onChanges[fn] = true
	
	return function()
		self._onChanges[fn] = nil
	end
end

-- Create a new <code>Options</code> object.
local function new<T>(opt : {[number] : T}, default : number?) : Options<T>
	return setmetatable({
		Options = opt,

		_current = default or 1,
		_onChanges = {}
	}, {
		__index = Options
	}) :: any
end

return {
	new = new
}