From 5a21e2e64c2d94ee10c653613990298170baa93f Mon Sep 17 00:00:00 2001
From: Watermelon914 <37270891+Watermelon914@users.noreply.github.com>
Date: Sat, 17 Jul 2021 05:05:57 +0100
Subject: [PATCH] Circuit submodules (#60109)
Adds the module component that is basically a subroutine. Allows you to compact your logic into a bunch of functions.
---
code/__DEFINES/dcs/signals.dm | 9 +
code/__DEFINES/wiremod.dm | 4 +
code/_globalvars/lists/wiremod.dm | 9 +
code/datums/components/shell.dm | 8 +-
.../research/designs/wiremod_designs.dm | 10 +-
code/modules/research/techweb/all_nodes.dm | 1 +
code/modules/wiremod/component.dm | 25 ++
.../wiremod/components/abstract/module.dm | 252 ++++++++++++++++++
code/modules/wiremod/integrated_circuit.dm | 29 +-
code/modules/wiremod/port.dm | 4 -
tgstation.dme | 2 +
.../packages/tgui/interfaces/CircuitModule.js | 145 ++++++++++
12 files changed, 480 insertions(+), 18 deletions(-)
create mode 100644 code/_globalvars/lists/wiremod.dm
create mode 100644 code/modules/wiremod/components/abstract/module.dm
create mode 100644 tgui/packages/tgui/interfaces/CircuitModule.js
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 77a6f7ae808..33ed59e50d2 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -1346,6 +1346,15 @@
/// Sent to [/obj/item/circuit_component] when it is removed from a circuit. (/obj/item/integrated_circuit)
#define COMSIG_CIRCUIT_COMPONENT_REMOVED "circuit_component_removed"
+/// Called when the integrated circuit's cell is set.
+#define COMSIG_CIRCUIT_SET_CELL "circuit_set_cell"
+
+/// Called when the integrated circuit is turned on or off.
+#define COMSIG_CIRCUIT_SET_ON "circuit_set_on"
+
+/// Called when the integrated circuit's shell is set.
+#define COMSIG_CIRCUIT_SET_SHELL "circuit_set_shell"
+
/// Sent to an atom when a [/obj/item/usb_cable] attempts to connect to something. (/obj/item/usb_cable/usb_cable, /mob/user)
#define COMSIG_ATOM_USB_CABLE_TRY_ATTACH "usb_cable_try_attach"
/// Attaches the USB cable to the atom. If the USB cables moves away, it will disconnect.
diff --git a/code/__DEFINES/wiremod.dm b/code/__DEFINES/wiremod.dm
index 784dcaa3e3c..807270544cc 100644
--- a/code/__DEFINES/wiremod.dm
+++ b/code/__DEFINES/wiremod.dm
@@ -4,6 +4,10 @@
/// Define to automatically handle calling the output port. Will not call the output port if the input_received proc returns TRUE.
#define TRIGGER_CIRCUIT_COMPONENT(component, port) if(!component.input_received(port) && (component.circuit_flags & CIRCUIT_FLAG_OUTPUT_SIGNAL)) component.trigger_output.set_output(COMPONENT_SIGNAL)
+// Port defines
+
+#define PORT_MAX_NAME_LENGTH 50
+
// Port types. Determines what the port can connect to
/// Can accept any datatype. Only works for inputs, output types will runtime.
diff --git a/code/_globalvars/lists/wiremod.dm b/code/_globalvars/lists/wiremod.dm
new file mode 100644
index 00000000000..bada761dcd2
--- /dev/null
+++ b/code/_globalvars/lists/wiremod.dm
@@ -0,0 +1,9 @@
+GLOBAL_LIST_INIT(wiremod_types, list(
+ PORT_TYPE_ANY,
+ PORT_TYPE_STRING,
+ PORT_TYPE_NUMBER,
+ PORT_TYPE_SIGNAL,
+ PORT_TYPE_LIST,
+ PORT_TYPE_TABLE,
+ PORT_TYPE_ATOM,
+))
diff --git a/code/datums/components/shell.dm b/code/datums/components/shell.dm
index 487aa60e5c8..9e064ef3852 100644
--- a/code/datums/components/shell.dm
+++ b/code/datums/components/shell.dm
@@ -196,7 +196,8 @@
* Attaches a circuit to the parent. Doesn't do any checks to see for any existing circuits so that should be done beforehand.
*/
/datum/component/shell/proc/attach_circuit(obj/item/integrated_circuit/circuitboard, mob/living/user)
- if(!user.transferItemToLoc(circuitboard, parent))
+ var/atom/movable/parent_atom = parent
+ if(!user.transferItemToLoc(circuitboard, parent_atom))
return
locked = FALSE
attached_circuit = circuitboard
@@ -206,11 +207,12 @@
to_add.forceMove(attached_circuit)
attached_circuit.add_component(to_add)
RegisterSignal(circuitboard, COMSIG_CIRCUIT_ADD_COMPONENT_MANUALLY, .proc/on_circuit_add_component_manually)
- attached_circuit.set_shell(parent)
+ attached_circuit.set_shell(parent_atom)
+ if(attached_circuit.display_name != "")
+ parent_atom.name = "[initial(parent_atom.name)] ([attached_circuit.display_name])"
attached_circuit.locked = FALSE
if(shell_flags & SHELL_FLAG_REQUIRE_ANCHOR)
- var/atom/movable/parent_atom = parent
on_unfasten(parent_atom, parent_atom.anchored)
/**
diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm
index 5dcbd975e27..86ecfdb14a8 100644
--- a/code/modules/research/designs/wiremod_designs.dm
+++ b/code/modules/research/designs/wiremod_designs.dm
@@ -214,15 +214,21 @@
build_path = /obj/item/circuit_component/select
/datum/design/component/tempsensor
- name = "Temperature Sensor"
+ name = "Temperature Sensor Component"
id = "comp_tempsensor"
build_path = /obj/item/circuit_component/tempsensor
/datum/design/component/pressuresensor
- name = "Pressure Sensor"
+ name = "Pressure Sensor Component"
id = "comp_pressuresensor"
build_path = /obj/item/circuit_component/pressuresensor
+/datum/design/component/module
+ name = "Module Component"
+ id = "comp_module"
+ build_path = /obj/item/circuit_component/module
+
+
/datum/design/compact_remote_shell
name = "Compact Remote Shell"
desc = "A handheld shell with one big button."
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 0e8f23e5baa..9785c5259a0 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -213,6 +213,7 @@
"comp_light",
"comp_logic",
"comp_mmi",
+ "comp_module",
"comp_multiplexer",
"comp_not",
"comp_pressuresensor",
diff --git a/code/modules/wiremod/component.dm b/code/modules/wiremod/component.dm
index 82edd1c9876..95e2af3dc15 100644
--- a/code/modules/wiremod/component.dm
+++ b/code/modules/wiremod/component.dm
@@ -142,8 +142,21 @@
/obj/item/circuit_component/proc/add_input_port(name, type, trigger = TRUE, default = null)
var/datum/port/input/input_port = new(src, name, type, trigger, default)
input_ports += input_port
+ if(parent)
+ SStgui.update_uis(parent)
return input_port
+/**
+ * Removes an input port and deletes it. This will not cleanup any references made by derivatives of the circuit component
+ *
+ * Arguments:
+ * * input_port - The input port to remove.
+ */
+/obj/item/circuit_component/proc/remove_input_port(datum/port/input/input_port)
+ input_ports -= input_port
+ qdel(input_port)
+ if(parent)
+ SStgui.update_uis(parent)
/**
* Adds an output port and returns it
@@ -157,6 +170,18 @@
output_ports += output_port
return output_port
+/**
+ * Removes an output port and deletes it. This will not cleanup any references made by derivatives of the circuit component
+ *
+ * Arguments:
+ * * output_port - The output port to remove.
+ */
+/obj/item/circuit_component/proc/remove_output_port(datum/port/output/output_port)
+ output_ports -= output_port
+ qdel(output_port)
+ if(parent)
+ SStgui.update_uis(parent)
+
/**
* Called whenever an input is received from one of the ports.
*
diff --git a/code/modules/wiremod/components/abstract/module.dm b/code/modules/wiremod/components/abstract/module.dm
new file mode 100644
index 00000000000..ba55b12c56b
--- /dev/null
+++ b/code/modules/wiremod/components/abstract/module.dm
@@ -0,0 +1,252 @@
+/**
+ * # Module Component
+ *
+ * A component that has an input, output
+ */
+/obj/item/circuit_component/module
+ display_name = "Module"
+ display_desc = "A component that has other components within it, acting like a function. Use it in your hand to control the amount of input and output ports it has, as well as being able to access the integrated circuit contained inside."
+
+ var/obj/item/integrated_circuit/module/internal_circuit
+
+ var/obj/item/circuit_component/module_input/input_component
+ var/obj/item/circuit_component/module_output/output_component
+
+ /// Linked ports that follow a `first_port = second_port` keyed structure.
+ var/list/linked_ports = list()
+
+ var/port_limit = 10
+
+/obj/item/integrated_circuit/module
+ var/obj/item/circuit_component/attached_module
+
+/obj/item/integrated_circuit/module/ui_host(mob/user)
+ . = ..()
+ if(. == src)
+ return attached_module
+
+/obj/item/integrated_circuit/module/set_display_name(new_name)
+ . = ..()
+ attached_module.display_name = new_name
+
+/obj/item/integrated_circuit/module/Destroy()
+ attached_module = null
+ return ..()
+
+/obj/item/circuit_component/module_input
+ display_name = "Input"
+ display_desc = "A component that receives data from the module it is attached to"
+
+ removable = FALSE
+
+ /// The currently attached module
+ var/obj/item/circuit_component/module/attached_module
+
+/obj/item/circuit_component/module_input/Destroy()
+ attached_module = null
+ return ..()
+
+/obj/item/circuit_component/module_output
+ display_name = "Output"
+ display_desc = "A component that outputs data to the module it is attached to."
+
+ removable = FALSE
+
+ /// The currently attached module
+ var/obj/item/circuit_component/module/attached_module
+
+/obj/item/circuit_component/module_output/input_received(datum/port/input/port)
+ . = ..()
+ if(!port)
+ return
+ // We don't check the parent here because frankly, we don't care. We only sync our input with the module's output
+ var/datum/port/output/port_to_update = attached_module.linked_ports[port]
+ if(!port_to_update)
+ CRASH("[port.type] doesn't have a linked port in [type]!")
+
+ port_to_update.set_output(port.input_value)
+
+/obj/item/circuit_component/module/input_received(datum/port/input/port)
+ . = ..()
+ if(!port)
+ return
+ var/datum/port/output/port_to_update = linked_ports[port]
+ if(!port_to_update)
+ CRASH("[port.type] doesn't have a linked port in [type]!")
+
+ port_to_update.set_output(port.input_value)
+
+/obj/item/circuit_component/module_output/Destroy()
+ attached_module = null
+ return ..()
+
+/obj/item/circuit_component/module/Initialize()
+ . = ..()
+ internal_circuit = new(src)
+ internal_circuit.attached_module = src
+
+ input_component = new(internal_circuit)
+ input_component.attached_module = src
+ internal_circuit.add_component(input_component)
+ input_component.rel_x = 400
+ input_component.rel_y = 200
+
+ output_component = new(internal_circuit)
+ output_component.attached_module = src
+ internal_circuit.add_component(output_component)
+ output_component.rel_x = 400
+ output_component.rel_y = 200
+
+/obj/item/circuit_component/module/add_to(obj/item/integrated_circuit/added_to)
+ . = ..()
+ RegisterSignal(added_to, COMSIG_CIRCUIT_SET_CELL, .proc/handle_set_cell)
+ RegisterSignal(added_to, COMSIG_CIRCUIT_SET_ON, .proc/handle_set_on)
+ RegisterSignal(added_to, COMSIG_CIRCUIT_SET_SHELL, .proc/handle_set_shell)
+ internal_circuit.set_cell(added_to.cell)
+ internal_circuit.set_shell(added_to.shell)
+ internal_circuit.set_on(added_to.on)
+
+
+/obj/item/circuit_component/module/removed_from(obj/item/integrated_circuit/removed_from)
+ internal_circuit.set_cell(null)
+ internal_circuit.set_on(FALSE)
+ internal_circuit.remove_current_shell()
+ UnregisterSignal(removed_from, list(
+ COMSIG_CIRCUIT_SET_CELL,
+ COMSIG_CIRCUIT_SET_ON,
+ COMSIG_CIRCUIT_SET_SHELL,
+ ))
+ return ..()
+
+/obj/item/circuit_component/module/proc/handle_set_cell(datum/source, obj/item/stock_parts/cell/cell)
+ SIGNAL_HANDLER
+ internal_circuit.set_cell(cell)
+
+/obj/item/circuit_component/module/proc/handle_set_on(datum/source, new_value)
+ SIGNAL_HANDLER
+ internal_circuit.set_on(new_value)
+
+/obj/item/circuit_component/module/proc/handle_set_shell(datum/source, atom/movable/new_shell)
+ SIGNAL_HANDLER
+ internal_circuit.set_shell(new_shell)
+
+/obj/item/circuit_component/module/Destroy()
+ QDEL_NULL(input_component)
+ QDEL_NULL(output_component)
+ QDEL_NULL(internal_circuit)
+ linked_ports = null
+ return ..()
+
+/obj/item/circuit_component/module/ui_data(mob/user)
+ . = list()
+ .["input_ports"] = list()
+ for(var/datum/port/input/input_port as anything in input_ports)
+ .["input_ports"] += list(list(
+ "name" = input_port.name,
+ "type" = input_port.datatype,
+ ))
+
+ .["output_ports"] = list()
+ for(var/datum/port/output/output_port as anything in output_ports)
+ .["output_ports"] += list(list(
+ "name" = output_port.name,
+ "type" = output_port.datatype,
+ ))
+
+/obj/item/circuit_component/module/ui_static_data(mob/user)
+ . = list()
+ .["global_port_types"] = GLOB.wiremod_types
+
+/obj/item/circuit_component/module/attackby(obj/item/I, mob/living/user, params)
+ if(istype(I, /obj/item/circuit_component))
+ internal_circuit.attackby(I, user, params)
+ return
+ return ..()
+
+#define WITHIN_RANGE(id, table) (id >= 1 && id <= length(table))
+
+/obj/item/circuit_component/module/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("open_internal_circuit")
+ internal_circuit.interact(usr)
+ . = TRUE
+ if("add_input_port")
+ if(length(input_ports) > port_limit)
+ return
+ var/datum/port/new_port = add_input_port("Input Port", PORT_TYPE_ANY)
+ linked_ports[new_port] = input_component.add_output_port("Input Port", PORT_TYPE_ANY)
+ . = TRUE
+ if("remove_input_port")
+ var/port_id = text2num(params["port_id"])
+ if(!WITHIN_RANGE(port_id, input_ports))
+ return
+ var/datum/port/removed_port = input_ports[port_id]
+ linked_ports -= removed_port
+ remove_input_port(removed_port)
+ input_component.remove_output_port(input_component.output_ports[port_id])
+ . = TRUE
+ if("add_output_port")
+ if(length(output_ports) > port_limit)
+ return
+ var/datum/port/new_port = output_component.add_input_port("Output Port", PORT_TYPE_ANY)
+ linked_ports[new_port] = add_output_port("Output Port", PORT_TYPE_ANY)
+ . = TRUE
+ if("remove_output_port")
+ var/port_id = text2num(params["port_id"])
+ if(!WITHIN_RANGE(port_id, output_ports))
+ return
+
+ var/datum/port/removed_port = output_component.input_ports[port_id]
+ linked_ports -= removed_port
+ remove_output_port(output_ports[port_id])
+ output_component.remove_input_port(removed_port)
+ . = TRUE
+ if("set_port_name", "set_port_type")
+ var/port_id = text2num(params["port_id"])
+ var/is_input = params["is_input"]
+
+ var/list/ports_to_use
+ var/list/internal_ports_to_use
+ if(is_input)
+ ports_to_use = input_ports
+ internal_ports_to_use = input_component.output_ports
+ else
+ ports_to_use = output_ports
+ internal_ports_to_use = output_component.input_ports
+
+ if(!WITHIN_RANGE(port_id, ports_to_use))
+ return
+
+ var/datum/port/component_port = ports_to_use[port_id]
+ var/datum/port/internal_component_port = internal_ports_to_use[port_id]
+
+ if(action == "set_port_type")
+ var/type = params["port_type"]
+ if(!(type in GLOB.wiremod_types))
+ return
+ component_port.set_datatype(type)
+ internal_component_port.set_datatype(type)
+ else
+ var/port_name = params["port_name"]
+ if(!port_name)
+ return
+ port_name = strip_html(port_name, PORT_MAX_NAME_LENGTH)
+ component_port.name = port_name
+ internal_component_port.name = port_name
+ . = TRUE
+
+ if(.)
+ SStgui.update_uis(internal_circuit)
+
+#undef WITHIN_RANGE
+
+/obj/item/circuit_component/module/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CircuitModule", name)
+ ui.open()
+ ui.set_autoupdate(FALSE)
diff --git a/code/modules/wiremod/integrated_circuit.dm b/code/modules/wiremod/integrated_circuit.dm
index fdae1cf903c..b1415f682b7 100644
--- a/code/modules/wiremod/integrated_circuit.dm
+++ b/code/modules/wiremod/integrated_circuit.dm
@@ -53,7 +53,7 @@
/obj/item/integrated_circuit/loaded/Initialize()
. = ..()
- cell = new /obj/item/stock_parts/cell/high(src)
+ set_cell(new /obj/item/stock_parts/cell/high(src))
/obj/item/integrated_circuit/Destroy()
for(var/obj/item/circuit_component/to_delete in attached_components)
@@ -73,6 +73,10 @@
else
. += span_notice("There is no power cell installed.")
+/obj/item/integrated_circuit/proc/set_cell(obj/item/stock_parts/cell_to_set)
+ SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_CELL, cell_to_set)
+ cell = cell_to_set
+
/obj/item/integrated_circuit/attackby(obj/item/I, mob/living/user, params)
. = ..()
if(istype(I, /obj/item/circuit_component))
@@ -85,7 +89,7 @@
return
if(!user.transferItemToLoc(I, src))
return
- cell = I
+ set_cell(I)
I.add_fingerprint(user)
user.visible_message(span_notice("[user] inserts a power cell into [src]."), span_notice("You insert the power cell into [src]."))
return
@@ -101,7 +105,7 @@
I.play_tool_sound(src)
user.visible_message(span_notice("[user] unscrews the power cell from [src]."), span_notice("You unscrew the power cell from [src]."))
cell.forceMove(drop_location())
- cell = null
+ set_cell(null)
return
/**
@@ -114,7 +118,8 @@
*/
/obj/item/integrated_circuit/proc/set_shell(atom/movable/new_shell)
remove_current_shell()
- on = TRUE
+ set_on(TRUE)
+ SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_SHELL, new_shell)
shell = new_shell
RegisterSignal(shell, COMSIG_PARENT_QDELETING, .proc/remove_current_shell)
for(var/obj/item/circuit_component/attached_component as anything in attached_components)
@@ -122,8 +127,6 @@
// Their input ports may be updated with user values, but the outputs haven't updated
// because on is FALSE
TRIGGER_CIRCUIT_COMPONENT(attached_component, null)
- if(display_name != "")
- shell.name = "[initial(shell.name)] ([display_name])"
/**
* Unregisters the current shell attached to this circuit.
@@ -137,9 +140,13 @@
attached_component.unregister_shell(shell)
UnregisterSignal(shell, COMSIG_PARENT_QDELETING)
shell = null
- on = FALSE
+ set_on(FALSE)
SEND_SIGNAL(src, COMSIG_CIRCUIT_SHELL_REMOVED)
+/obj/item/integrated_circuit/proc/set_on(new_value)
+ SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_ON, new_value)
+ on = new_value
+
/**
* Adds a component to the circuitboard
*
@@ -428,9 +435,9 @@
var/new_name = params["display_name"]
if(new_name)
- display_name = strip_html(params["display_name"], label_max_length)
+ set_display_name(strip_html(params["display_name"], label_max_length))
else
- display_name = ""
+ set_display_name("")
if(shell)
if(display_name != "")
@@ -458,6 +465,10 @@
#undef WITHIN_RANGE
+/// Sets the display name that appears on the shell.
+/obj/item/integrated_circuit/proc/set_display_name(new_name)
+ display_name = new_name
+
/**
* Returns the creator of the integrated circuit. Used in admin messages and other related things.
*/
diff --git a/code/modules/wiremod/port.dm b/code/modules/wiremod/port.dm
index 212610ce8e9..a9b2b9b962b 100644
--- a/code/modules/wiremod/port.dm
+++ b/code/modules/wiremod/port.dm
@@ -52,10 +52,6 @@
return "grey"
/datum/port/Destroy(force)
- if(!force && !QDELETED(connected_component))
- // This should never happen. Ports should be deleted with their components
- stack_trace("Attempted to delete a port with a non-destroyed connected_component! (port name: [name], component type: [connected_component.type])")
- return QDEL_HINT_LETMELIVE
connected_component = null
return ..()
diff --git a/tgstation.dme b/tgstation.dme
index 60432cd813b..cb93d1b161c 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -240,6 +240,7 @@
#include "code\_globalvars\lists\objects.dm"
#include "code\_globalvars\lists\poll_ignore.dm"
#include "code\_globalvars\lists\typecache.dm"
+#include "code\_globalvars\lists\wiremod.dm"
#include "code\_globalvars\lists\xenobiology.dm"
#include "code\_js\byjax.dm"
#include "code\_js\menus.dm"
@@ -3588,6 +3589,7 @@
#include "code\modules\wiremod\port.dm"
#include "code\modules\wiremod\usb_cable.dm"
#include "code\modules\wiremod\components\abstract\compare.dm"
+#include "code\modules\wiremod\components\abstract\module.dm"
#include "code\modules\wiremod\components\action\light.dm"
#include "code\modules\wiremod\components\action\mmi.dm"
#include "code\modules\wiremod\components\action\pull.dm"
diff --git a/tgui/packages/tgui/interfaces/CircuitModule.js b/tgui/packages/tgui/interfaces/CircuitModule.js
new file mode 100644
index 00000000000..bb0ebb983e4
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CircuitModule.js
@@ -0,0 +1,145 @@
+import { useBackend } from "../backend";
+import { Stack, Section, Input, Button, Dropdown } from "../components";
+import { Window } from "../layouts";
+
+export const CircuitModule = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ input_ports,
+ output_ports,
+ global_port_types,
+ } = data;
+ return (
+
+
+
+
+
+
+
+
+
+
+ {input_ports.map((val, index) => (
+ act("remove_input_port", {
+ port_id: index+1,
+ })}
+ onSetType={type => act("set_port_type", {
+ port_id: index+1,
+ is_input: true,
+ port_type: type,
+ })}
+ onEnter={(e, value) => act("set_port_name", {
+ port_id: index+1,
+ is_input: true,
+ port_name: value,
+ })}
+ />
+ ))}
+
+
+
+
+
+
+
+
+ {output_ports.map((val, index) => (
+ act("remove_output_port", {
+ port_id: index+1,
+ })}
+ onSetType={type => act("set_port_type", {
+ port_id: index+1,
+ is_input: false,
+ port_type: type,
+ })}
+ onEnter={(e, value) => act("set_port_name", {
+ port_id: index+1,
+ is_input: false,
+ port_name: value,
+ })}
+ />
+ ))}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const PortEntry = (props, context) => {
+ const {
+ onRemove,
+ onEnter,
+ onSetType,
+ name,
+ datatype,
+ datatypeOptions = [],
+ ...rest
+ } = props;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};