mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-11 23:54:52 +09:00
Let the framework own sets and lists instead of the features
Feature.to_set made every feature turn its config lists into sets on
each lookup, which is work the framework should do once. Sets are now a
field type of their own. Feature.set{...} declares one, the controller
sends a list, and _apply converts it when the override is applied, on
load and on update alike. Features index the config directly, and
to_set and its cache are gone.
Lists are explicit too. Feature.list{...} declares one, where before
any table of strings, and any empty table, counted as a list. Untagged
tables with string keys are nested sections, and other tables can not
be overridden. Feature.optional uses the same marker as list and set.
On the web side a string list can no longer name an input component,
so the cast that made one look like an object field is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
628e4df514
commit
f8ca285ff6
9 files changed
+128
-90
No files matched your search
@@ -17,16 +17,20 @@ export type FeatureField = {
|
||||
name: string,
|
||||
title: string,
|
||||
description: string,
|
||||
} & (
|
||||
| ScalarField<{ type: "boolean", default: boolean }>
|
||||
| ScalarField<{ type: "number", default: number | null, min?: number, unit?: string }>
|
||||
| ScalarField<{ type: "string", default: string | null, enum?: string[] }>
|
||||
/** Feature.list or Feature.set on the lua side, both are sent as a list */
|
||||
| { type: "string_list", default: string[] }
|
||||
);
|
||||
|
||||
type ScalarField<T> = T & {
|
||||
/** Allows null, the lua side uses Feature.optional */
|
||||
optional?: boolean,
|
||||
/** Name of an input component registered with the web interface, such as "role" */
|
||||
inputComponent?: string,
|
||||
} & (
|
||||
| { type: "boolean", default: boolean }
|
||||
| { type: "number", default: number | null, min?: number, unit?: string }
|
||||
| { type: "string", default: string | null, enum?: string[] }
|
||||
| { type: "string_list", default: string[] }
|
||||
);
|
||||
};
|
||||
|
||||
export type Feature = {
|
||||
name: string,
|
||||
@@ -238,7 +242,7 @@ export function validateFeatureValues(name: string, values: Record<string, Featu
|
||||
|
||||
function checkFieldValue(field: FeatureField, value: FeatureValue, label: string) {
|
||||
if (value === null) {
|
||||
if (!field.optional) {
|
||||
if (field.type === "string_list" || !field.optional) {
|
||||
throw new Error(`${label} can not be null`);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -243,7 +243,7 @@ return Feature.register("spawn_area", {
|
||||
resource_refill_nearby = {
|
||||
enabled = false,
|
||||
range = 128,
|
||||
resources_name = {
|
||||
resources_name = Feature.list{
|
||||
"iron-ore",
|
||||
"copper-ore",
|
||||
"stone",
|
||||
|
||||
@@ -16,7 +16,7 @@ local feature = Feature.register("discord_alerts", {
|
||||
player_kicks = true, --- @setting player_kicks Alert when a player is kicked
|
||||
player_promotes = false, --- @setting player_promotes Alert when a player is promoted or demoted
|
||||
player_jail = true, --- @setting player_jail Alert when a player is jailed or unjailed
|
||||
logged_commands = { --- @setting logged_commands Alert when a player uses one of these commands
|
||||
logged_commands = Feature.set{ --- @setting logged_commands Alert when a player uses one of these commands
|
||||
"config", "purge", "c", "command", "silent-command", "measured-command", "banlist", "permissions", "editor", "cheat",
|
||||
},
|
||||
})
|
||||
@@ -259,7 +259,7 @@ end)
|
||||
events[e.on_console_command] = function(event)
|
||||
if event.player_index then
|
||||
local player_name = get_player_name(event)
|
||||
if Feature.to_set(config.logged_commands)[event.command] then
|
||||
if config.logged_commands[event.command] then
|
||||
emit_event{
|
||||
title = event.command:gsub("^%l", string.upper),
|
||||
description = "/" .. event.command .. " was used",
|
||||
|
||||
@@ -8,7 +8,7 @@ local Roles = require("modules/exp_roles")
|
||||
|
||||
local feature = Feature.register("nuke_protection", {
|
||||
ignore_admins = true, --- @setting ignore_admins Admins can hold banned items
|
||||
banned_items = { "atomic-bomb" }, --- @setting banned_items Items which are removed from the inventory of players without the bypass permission
|
||||
banned_items = Feature.set{ "atomic-bomb" }, --- @setting banned_items Items which are removed from the inventory of players without the bypass permission
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
@@ -28,7 +28,7 @@ local function check_items(player, type)
|
||||
if Roles.player_has_permission(player, "exp_scenario.bypass.nuke_protection") then return end
|
||||
if config.ignore_admins and player.admin then return end
|
||||
|
||||
local banned_items = Feature.to_set(config.banned_items)
|
||||
local banned_items = config.banned_items
|
||||
local items = {} --- @type LuaItemStack[]
|
||||
local inventory = assert(player.get_inventory(type))
|
||||
-- Check what items the player has
|
||||
|
||||
@@ -10,12 +10,12 @@ local feature = Feature.register("protection", {
|
||||
ignore_admins = true, --- @setting ignore_admins If admins are ignored by the protection filter
|
||||
repeat_count = 5, --- @setting repeat_count Number of protected entities that must be removed within repeat_minutes in order to trigger repeated removal protection
|
||||
repeat_minutes = 20, --- @setting repeat_minutes The length of time, in minutes, that protected removals will be remembered for
|
||||
always_protected_names = {}, --- @setting always_protected_names Names of entities which are always protected
|
||||
always_protected_types = { --- @setting always_protected_types Types of entities which are always protected
|
||||
always_protected_names = Feature.set{}, --- @setting always_protected_names Names of entities which are always protected
|
||||
always_protected_types = Feature.set{ --- @setting always_protected_types Types of entities which are always protected
|
||||
"boiler", "generator", "offshore-pump", "reactor", "heat-exchanger", "heat-pipe", "fusion-reactor", "fusion-generator", "power-switch", "rocket-silo",
|
||||
},
|
||||
always_trigger_repeat_names = {}, --- @setting always_trigger_repeat_names Names of entities which always trigger repeated removal protection
|
||||
always_trigger_repeat_types = { --- @setting always_trigger_repeat_types Types of entities which always trigger repeated removal protection
|
||||
always_trigger_repeat_names = Feature.set{}, --- @setting always_trigger_repeat_names Names of entities which always trigger repeated removal protection
|
||||
always_trigger_repeat_types = Feature.set{ --- @setting always_trigger_repeat_types Types of entities which always trigger repeated removal protection
|
||||
"reactor", "fusion-reactor", "rocket-silo",
|
||||
},
|
||||
})
|
||||
@@ -45,16 +45,27 @@ local Protection = {
|
||||
--- @field last uint Tick of the last protected removal
|
||||
--- @field count number Protected removals since the last repeat violation
|
||||
|
||||
--- Get the keys of a set as a list, for the filters of find_entities_filtered
|
||||
--- @param set table<string, true>
|
||||
--- @return string[]
|
||||
local function set_to_list(set)
|
||||
local list = {}
|
||||
for item in pairs(set) do
|
||||
list[#list + 1] = item
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
--- Names of entities which are always protected
|
||||
--- @return string[]
|
||||
function Protection.get_protected_entity_names()
|
||||
return config.always_protected_names
|
||||
return set_to_list(config.always_protected_names)
|
||||
end
|
||||
|
||||
--- Types of entities which are always protected
|
||||
--- @return string[]
|
||||
function Protection.get_protected_entity_types()
|
||||
return config.always_protected_types
|
||||
return set_to_list(config.always_protected_types)
|
||||
end
|
||||
|
||||
local protected_entities = {} --- @type table<uint, table<string, LuaEntity>> Keyed by surface index then entity key
|
||||
@@ -115,7 +126,7 @@ end
|
||||
--- @param entity LuaEntity
|
||||
--- @return boolean
|
||||
function Protection.is_entity_protected(entity)
|
||||
if Feature.to_set(config.always_protected_names)[entity.name] or Feature.to_set(config.always_protected_types)[entity.type] then return true end
|
||||
if config.always_protected_names[entity.name] or config.always_protected_types[entity.type] then return true end
|
||||
local entities = protected_entities[entity.surface.index]
|
||||
if not entities then return false end
|
||||
return entities[Protection.get_entity_key(entity)] == entity
|
||||
@@ -193,7 +204,7 @@ local function raise_violation(event, player)
|
||||
script.raise_event(Protection.on_player_mined_protected, event)
|
||||
|
||||
local entity = event.entity
|
||||
local always_repeat = Feature.to_set(config.always_trigger_repeat_names)[entity.name] or Feature.to_set(config.always_trigger_repeat_types)[entity.type]
|
||||
local always_repeat = config.always_trigger_repeat_names[entity.name] or config.always_trigger_repeat_types[entity.type]
|
||||
if always_repeat or player_repeats.count >= config.repeat_count then
|
||||
player_repeats.count = 0
|
||||
script.raise_event(Protection.on_repeat_violation, event)
|
||||
|
||||
@@ -3,9 +3,10 @@ Features which the controller can enable, disable, and configure while the game
|
||||
|
||||
A feature's config table is changed in place when the controller sends overrides,
|
||||
so read values when they are used rather than copying them into locals when the
|
||||
file loads. Booleans, numbers, strings, and lists of strings can be overridden,
|
||||
including those in nested tables which are addressed as "section.key". Use
|
||||
Feature.optional for a value which has no default. Values used while the file
|
||||
file loads. Booleans, numbers, and strings can be overridden, as can lists and
|
||||
sets of strings made with Feature.list and Feature.set, and values with no default
|
||||
made with Feature.optional. Tables with string keys are nested sections whose
|
||||
values are addressed as "section.key", other tables can not be overridden. Values used while the file
|
||||
loads, such as on_nth_tick periods, stay as plain values and are not listed in
|
||||
features.ts.
|
||||
|
||||
@@ -14,6 +15,8 @@ local Feature = require("modules/exp_scenario/features")
|
||||
local feature = Feature.register("my_feature", {
|
||||
show_message = true,
|
||||
role_id = Feature.optional("number"),
|
||||
item_names = Feature.set{ "iron-plate" },
|
||||
surface_names = Feature.list{ "nauvis" },
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
@@ -30,9 +33,11 @@ Gui.toolbar.create_button{
|
||||
--- Commands are denied and left out of help while disabled:
|
||||
Commands.new("my_command", { "..." }):add_flags{ feature = feature }
|
||||
|
||||
--- Changes are raised per value, values derived from a list can use Feature.to_set:
|
||||
--- Sets are kept as sets, the controller sends a list which is converted when it is applied:
|
||||
if config.item_names[item.name] then end
|
||||
|
||||
--- Changes are raised per value:
|
||||
[Feature.events.on_config_updated] = function(event) end, -- { feature_name, path, old_value, new_value }
|
||||
if Feature.to_set(config.names)[name] then end
|
||||
]]
|
||||
|
||||
local Storage = require("modules/exp_util/storage")
|
||||
@@ -41,7 +46,7 @@ local Storage = require("modules/exp_util/storage")
|
||||
--- @field parent table The table holding the value within the config
|
||||
--- @field key string The key of the value within its parent
|
||||
--- @field default any
|
||||
--- @field type string "boolean", "number", "string", "list" of strings, or a type which can not be overridden
|
||||
--- @field type string "boolean", "number", "string", "list" or "set" of strings, or a type which can not be overridden
|
||||
|
||||
--- @class ExpScenario.Feature
|
||||
--- @field name string
|
||||
@@ -77,31 +82,48 @@ Storage.register(stored_overrides, function(tbl)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Check if a table is a list of strings, empty tables count as lists of strings
|
||||
--- @param tbl table
|
||||
--- Check if a value is a list of strings, as sent by the controller for lists and sets
|
||||
--- @param value any
|
||||
--- @return boolean
|
||||
local function is_string_list(tbl)
|
||||
if next(tbl) ~= nil and tbl[1] == nil then return false end
|
||||
for _, item in pairs(tbl) do
|
||||
local function is_string_list(value)
|
||||
if type(value) ~= "table" then return false end
|
||||
if next(value) ~= nil and value[1] == nil then return false end
|
||||
for _, item in pairs(value) do
|
||||
if type(item) ~= "string" then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Check if two values are equal, lists are compared by their items
|
||||
--- Check if two values are equal, lists and sets are compared by their contents
|
||||
--- @param a any
|
||||
--- @param b any
|
||||
--- @return boolean
|
||||
local function values_equal(a, b)
|
||||
if type(a) ~= "table" or type(b) ~= "table" then return a == b end
|
||||
if #a ~= #b then return false end
|
||||
for index, item in ipairs(a) do
|
||||
if b[index] ~= item then return false end
|
||||
for key, value in pairs(a) do
|
||||
if b[key] ~= value then return false end
|
||||
end
|
||||
for key in pairs(b) do
|
||||
if a[key] == nil then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Collect the values of a config table, recursing into nested tables which are not lists
|
||||
--- Marks a default made by Feature.list, Feature.set, or Feature.optional with the type of the field
|
||||
local typed_mt = {}
|
||||
|
||||
--- Convert a list of strings into a set
|
||||
--- @param list string[]
|
||||
--- @return table<string, true>
|
||||
local function list_to_set(list)
|
||||
local set = {}
|
||||
for _, item in ipairs(list) do
|
||||
set[item] = true
|
||||
end
|
||||
return set
|
||||
end
|
||||
|
||||
--- Collect the values of a config table, recursing into nested sections
|
||||
--- @param fields table<string, ExpScenario.FeatureField>
|
||||
--- @param tbl table
|
||||
--- @param prefix string
|
||||
@@ -110,15 +132,12 @@ local function collect_fields(fields, tbl, prefix)
|
||||
if type(key) == "string" then
|
||||
local field = { parent = tbl, key = key, default = value, type = type(value) }
|
||||
if type(value) == "table" then
|
||||
if value.__feature_optional then
|
||||
field.default = nil
|
||||
field.type = value.__feature_optional
|
||||
tbl[key] = nil
|
||||
elseif is_string_list(value) then
|
||||
field.type = "list"
|
||||
elseif value[1] ~= nil then
|
||||
field.type = "table"
|
||||
else
|
||||
local typed = getmetatable(value) == typed_mt and value
|
||||
if typed then
|
||||
field.type = typed.type
|
||||
field.default = typed.default
|
||||
tbl[key] = typed.default
|
||||
elseif type(next(value)) == "string" then
|
||||
collect_fields(fields, value, prefix .. key .. ".")
|
||||
field = nil
|
||||
end
|
||||
@@ -130,11 +149,25 @@ local function collect_fields(fields, tbl, prefix)
|
||||
end
|
||||
end
|
||||
|
||||
--- Mark a value as having no default, the argument is the type it can be overridden with
|
||||
--- @param value_type "boolean" | "number" | "string" | "list"
|
||||
--- Declare a value with no default, the argument is the type it can be overridden with
|
||||
--- @param value_type "boolean" | "number" | "string"
|
||||
--- @return any
|
||||
function Feature.optional(value_type)
|
||||
return { __feature_optional = value_type }
|
||||
return setmetatable({ type = value_type }, typed_mt)
|
||||
end
|
||||
|
||||
--- Declare a list of strings which can be overridden
|
||||
--- @param items string[]
|
||||
--- @return string[]
|
||||
function Feature.list(items)
|
||||
return setmetatable({ type = "list", default = items }, typed_mt)
|
||||
end
|
||||
|
||||
--- Declare a set of strings which can be overridden, the controller sends a list which is converted when applied
|
||||
--- @param items string[]
|
||||
--- @return table<string, true>
|
||||
function Feature.set(items)
|
||||
return setmetatable({ type = "set", default = list_to_set(items) }, typed_mt)
|
||||
end
|
||||
|
||||
--- Register a feature and its config, features are enabled unless the config says otherwise
|
||||
@@ -174,8 +207,8 @@ end
|
||||
--- @param value any
|
||||
--- @return boolean
|
||||
local function is_valid(field, value)
|
||||
if field.type == "list" then
|
||||
return type(value) == "table" and is_string_list(value)
|
||||
if field.type == "list" or field.type == "set" then
|
||||
return is_string_list(value)
|
||||
end
|
||||
return type(value) == field.type and (field.type == "boolean" or field.type == "number" or field.type == "string")
|
||||
end
|
||||
@@ -190,6 +223,8 @@ function Feature:_apply()
|
||||
elseif not is_valid(field, value) then
|
||||
log("[WARNING] Invalid override for " .. self.name .. "." .. path .. ", using the default")
|
||||
value = field.default
|
||||
elseif field.type == "set" then
|
||||
value = list_to_set(value)
|
||||
end
|
||||
field.parent[field.key] = value
|
||||
end
|
||||
@@ -254,24 +289,6 @@ function Feature:update_config(enabled, values)
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets made from lists in the config, keyed by the list so a replaced list gets a new set
|
||||
local sets = setmetatable({}, { __mode = "k" }) --- @type table<string[], table<string, true>>
|
||||
|
||||
--- Get a lookup set of the items in a list, cached until the list is replaced
|
||||
--- @param list string[]
|
||||
--- @return table<string, true>
|
||||
function Feature.to_set(list)
|
||||
local set = sets[list]
|
||||
if not set then
|
||||
set = {}
|
||||
for _, item in ipairs(list) do
|
||||
set[item] = true
|
||||
end
|
||||
sets[list] = set
|
||||
end
|
||||
return set
|
||||
end
|
||||
|
||||
--- @class ExpScenario.FeatureUpdate
|
||||
--- @field name string
|
||||
--- @field enabled boolean
|
||||
|
||||
@@ -18,7 +18,7 @@ const moduleFiles = luaFiles(path.join(import.meta.dirname, "..", "module"));
|
||||
|
||||
/**
|
||||
* Read a default from a Feature.register call, a path of "a.b" looks for b after the table a starts.
|
||||
* Only covers booleans, numbers, strings, lists of strings, and Feature.optional.
|
||||
* Only covers booleans, numbers, strings, Feature.list, Feature.set, and Feature.optional.
|
||||
*/
|
||||
function luaDefault(source, path) {
|
||||
const keys = path.split(".");
|
||||
@@ -32,7 +32,7 @@ function luaDefault(source, path) {
|
||||
}
|
||||
|
||||
const key = keys.at(-1);
|
||||
const match = new RegExp(`^\\s*${key} = (true|false|-?[\\d.]+|"[^"]*"|Feature\\.optional\\("\\w+"\\)|\\{[^{}]*\\})`, "m")
|
||||
const match = new RegExp(`^\\s*${key} = (true|false|-?[\\d.]+|"[^"]*"|Feature\\.optional\\("\\w+"\\)|(?:Feature\\.(?:list|set))?\\{[^{}]*\\})`, "m")
|
||||
.exec(source.slice(start));
|
||||
if (!match) {
|
||||
return undefined;
|
||||
@@ -41,7 +41,7 @@ function luaDefault(source, path) {
|
||||
if (literal.startsWith("Feature.optional")) {
|
||||
return null;
|
||||
}
|
||||
if (literal.startsWith("{")) {
|
||||
if (literal.includes("{")) {
|
||||
return [...literal.matchAll(/"([^"]*)"/g)].map(item => item[1]);
|
||||
}
|
||||
return JSON.parse(literal);
|
||||
|
||||
@@ -34,7 +34,7 @@ Suite.test("update_config() changes the config in place", function(env)
|
||||
end)
|
||||
|
||||
Suite.test("update_config() raises on_config_updated for each changed value", function(env)
|
||||
local feature = env.Feature.register("test", { flag = true, names = { "a" }, section = { count = 1 } })
|
||||
local feature = env.Feature.register("test", { flag = true, names = env.Feature.set{ "a" }, section = { count = 1 } })
|
||||
feature:update_config(false, { names = { "a" }, ["section.count"] = 2 })
|
||||
Suite.eq(#env.events, 2, "unchanged values raise nothing")
|
||||
Suite.eq(env.Feature.events.on_config_updated, assert(env.events[1]).name, "the event is on_config_updated")
|
||||
@@ -97,21 +97,33 @@ Suite.test("register() addresses nested values by path", function(env)
|
||||
local feature = env.Feature.register("test", { section = { flag = true, count = 1 }, offset = offset })
|
||||
feature:update_config(true, { ["section.flag"] = false, offset = { "x" } })
|
||||
Suite.eq(feature.config.section, { flag = false, count = 1 }, "the nested value changes in place")
|
||||
Suite.eq(feature.config.offset, offset, "lists of other types can not be overridden")
|
||||
Suite.eq(feature.config.offset, offset, "tables which are not sections can not be overridden")
|
||||
|
||||
feature:update_config(true, {})
|
||||
Suite.eq(feature.config.section.flag, true, "and goes back to its default")
|
||||
end)
|
||||
|
||||
Suite.test("register() allows lists of strings to be overridden", function(env)
|
||||
local feature = env.Feature.register("test", { names = { "a", "b" }, empty = {} })
|
||||
feature:update_config(true, { names = { "c" }, empty = { "d" } })
|
||||
Suite.test("list() declares a list of strings which can be overridden", function(env)
|
||||
local feature = env.Feature.register("test", { names = env.Feature.list{ "a", "b" }, empty = env.Feature.list{}, plain = { "x" } })
|
||||
Suite.eq(feature.config.names, { "a", "b" }, "the default is the list")
|
||||
feature:update_config(true, { names = { "c" }, empty = { "d" }, plain = { "y" } })
|
||||
Suite.eq(feature.config.names, { "c" }, "a list is replaced")
|
||||
Suite.eq(feature.config.empty, { "d" }, "an empty default is a list")
|
||||
Suite.eq(feature.config.empty, { "d" }, "an empty list can be overridden")
|
||||
Suite.eq(feature.config.plain, { "x" }, "a plain table can not be overridden")
|
||||
feature:update_config(true, { names = { 1 } })
|
||||
Suite.eq(feature.config.names, { "a", "b" }, "a list with other types is invalid")
|
||||
end)
|
||||
|
||||
Suite.test("set() declares a set of strings which is overridden with a list", function(env)
|
||||
local feature = env.Feature.register("test", { names = env.Feature.set{ "a" }, empty = env.Feature.set{} })
|
||||
Suite.eq(feature.config.names, { a = true }, "the default is a set")
|
||||
Suite.eq(feature.config.empty, {}, "and can be empty")
|
||||
feature:update_config(true, { names = { "b", "c" } })
|
||||
Suite.eq(feature.config.names, { b = true, c = true }, "the list is converted to a set")
|
||||
env.on_load{ test = { enabled = true, names = { "d" } } }
|
||||
Suite.eq(feature.config.names, { d = true }, "also when the overrides are loaded")
|
||||
end)
|
||||
|
||||
Suite.test("optional() declares a value without a default", function(env)
|
||||
local feature = env.Feature.register("test", { role_id = env.Feature.optional("number") })
|
||||
Suite.eq(feature.config.role_id, nil, "nil until overridden")
|
||||
@@ -121,14 +133,6 @@ Suite.test("optional() declares a value without a default", function(env)
|
||||
Suite.eq(feature.config.role_id, nil, "and back to nil")
|
||||
end)
|
||||
|
||||
Suite.test("to_set() is cached until the list is replaced", function(env)
|
||||
local feature = env.Feature.register("test", { names = { "a" } })
|
||||
local set = env.Feature.to_set(feature.config.names)
|
||||
Suite.eq(set, { a = true }, "a set of the items")
|
||||
Suite.eq(env.Feature.to_set(feature.config.names) == set, true, "the same set while the list is unchanged")
|
||||
env.on_load{ test = { enabled = true, names = { "b" } } }
|
||||
Suite.eq(env.Feature.to_set(feature.config.names), { b = true }, "a new set after load replaces the list")
|
||||
end)
|
||||
|
||||
Suite.test("config files register their feature", function(env)
|
||||
local feature = env.load_config("popups")
|
||||
|
||||
@@ -78,15 +78,17 @@ function searchFields(feature: Feature, query: string) {
|
||||
function FieldInput(props: { field: FeatureField, value: FeatureValue, disabled: boolean, onChange: (value: FeatureValue) => void }) {
|
||||
const { field, value, disabled, onChange } = props;
|
||||
const control = useContext(ControlContext);
|
||||
const CustomInput = field.inputComponent ? control.inputComponents.get(field.inputComponent) : undefined;
|
||||
if (CustomInput) {
|
||||
const fieldDefinition = {
|
||||
type: field.type === "string_list" ? "object" : field.type,
|
||||
const CustomInput = field.type !== "string_list" && field.inputComponent
|
||||
? control.inputComponents.get(field.inputComponent)
|
||||
: undefined;
|
||||
if (CustomInput && field.type !== "string_list") {
|
||||
const fieldDefinition: lib.FieldDefinition = {
|
||||
type: field.type,
|
||||
title: field.title,
|
||||
description: field.description,
|
||||
optional: field.optional,
|
||||
inputComponent: field.inputComponent,
|
||||
} as lib.FieldDefinition;
|
||||
};
|
||||
return <CustomInput
|
||||
fieldDefinition={fieldDefinition}
|
||||
value={value as Exclude<FeatureValue, string[]>}
|
||||
|
||||
Reference in new issue
Block a user