diff --git a/modules/react-roblox/src/client/ReactRoblox.luau b/modules/react-roblox/src/client/ReactRoblox.luau index 5233439b..084734e5 100644 --- a/modules/react-roblox/src/client/ReactRoblox.luau +++ b/modules/react-roblox/src/client/ReactRoblox.luau @@ -80,6 +80,7 @@ local getClosestInstanceFromNode = ReactRobloxComponentTree.getClosestInstanceFr local Event = require("@pkg/@jsdotlua/shared").Event local Change = require("@pkg/@jsdotlua/shared").Change local Tag = require("@pkg/@jsdotlua/shared").Tag +local Attributes = require("@pkg/@jsdotlua/shared").Attributes -- setAttemptSynchronousHydration(attemptSynchronousHydration) -- setAttemptUserBlockingHydration(attemptUserBlockingHydration) @@ -248,6 +249,7 @@ local exports = { Event = Event, Change = Change, Tag = Tag, + Attributes = Attributes, unstable_isNewReconciler = enableNewReconciler, -- ROBLOX deviation: Export `act` function for testing purposes; in diff --git a/modules/react-roblox/src/client/roblox/RobloxComponentProps.luau b/modules/react-roblox/src/client/roblox/RobloxComponentProps.luau index 226e6c53..154380c0 100644 --- a/modules/react-roblox/src/client/roblox/RobloxComponentProps.luau +++ b/modules/react-roblox/src/client/roblox/RobloxComponentProps.luau @@ -29,12 +29,25 @@ local getDefaultInstanceProperty = require("./getDefaultInstanceProperty") local ReactRobloxHostTypes = require("../ReactRobloxHostTypes.roblox.luau") type HostInstance = ReactRobloxHostTypes.HostInstance local Tag = require("@pkg/@jsdotlua/react").Tag +local Attributes = require("@pkg/@jsdotlua/react").Attributes -- ROBLOX deviation: Essentially a placeholder for dom-specific logic, taking the place -- of ReactDOMComponent. Most of the logic will differ pretty dramatically type Array = { [number]: T } type Object = { [any]: any } +type AcceptableAttributeType = + string + | boolean + | number + | UDim + | UDim2 + | BrickColor + | Color3 + | Vector2 + | Vector3 + | EnumItem +type AttributesDictionary = { [string]: AcceptableAttributeType } -- deviation: Can't assign attributes to Roblox instances, so we use maps to -- store associated data for host instance features like binding and event @@ -168,6 +181,40 @@ local function removeAllTags(hostInstance: Instance) end end +local function applyAttributes( + hostInstance: Instance, + oldAttributes: AttributesDictionary, + newAttributes: AttributesDictionary +) + if __DEV__ then + if newAttributes ~= nil then + if typeof(newAttributes) ~= "table" then + console.error( + "Type provided for ReactRoblox.Attributes is invalid - attributes should be " + .. "specified as a dictionary, where the key-value pairs represent the " + .. "attribute names and their respective values. Instead received:\n%s", + inspect(newAttributes) + ) + end + return + end + end + + if oldAttributes then + for attributeName in oldAttributes do + if newAttributes == nil or newAttributes[attributeName] == nil then + hostInstance:SetAttribute(attributeName, nil) + end + end + end + + if newAttributes then + for attributeName, value in newAttributes do + hostInstance:SetAttribute(attributeName, value) + end + end +end + local function applyProp(hostInstance: Instance, key, newValue, oldValue): () -- ROBLOX performance: gets checked in applyProps so we can assume the key is valid -- if key == "ref" or key == "children" then @@ -208,6 +255,8 @@ local function applyProp(hostInstance: Instance, key, newValue, oldValue): () attachBinding(hostInstance, key, newValue) elseif key == Tag then applyTags(hostInstance, oldValue, newValue) + elseif key == Attributes then + applyAttributes(hostInstance, oldValue, newValue) else setRobloxInstanceProperty(hostInstance, key, newValue) end diff --git a/modules/react/src/React.luau b/modules/react/src/React.luau index 6d2ac006..c58f2c70 100644 --- a/modules/react/src/React.luau +++ b/modules/react/src/React.luau @@ -141,10 +141,11 @@ return { -- the renderer in order to update properly __subscribeToBinding = ReactBinding.subscribe, - -- ROBLOX DEVIATION: export Change, Event, and Tag from React + -- ROBLOX DEVIATION: export Change, Event, Tag and Attributes from React Event = require("@pkg/@jsdotlua/shared").Event, Change = require("@pkg/@jsdotlua/shared").Change, Tag = require("@pkg/@jsdotlua/shared").Tag, + Attributes = require("@pkg/@jsdotlua/shared").Attributes, -- ROBLOX DEVIATION: used by error reporters to parse caught errors. React -- stringifies at its boundaries to maintain compatibility with diff --git a/modules/shared/src/PropMarkers/Attributes.luau b/modules/shared/src/PropMarkers/Attributes.luau new file mode 100644 index 00000000..c3bf1e85 --- /dev/null +++ b/modules/shared/src/PropMarkers/Attributes.luau @@ -0,0 +1,22 @@ +--[[ + * Copyright (c) Roblox Corporation. All rights reserved. + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/MIT + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +]] +--[[ + Special value for assigning attributes to roblox instances via Roact +]] +local Symbol = require("../Symbol.roblox.luau") + +local Attributes = Symbol.named("RobloxAttributes") + +return Attributes diff --git a/modules/shared/src/init.luau b/modules/shared/src/init.luau index c9855915..1239285e 100644 --- a/modules/shared/src/init.luau +++ b/modules/shared/src/init.luau @@ -137,4 +137,5 @@ return { Change = require("./PropMarkers/Change"), Event = require("./PropMarkers/Event"), Tag = require("./PropMarkers/Tag"), + Attributes = require("./PropMarkers/Attributes"), }