Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- NOTE: HTTP must be enabled to retrieve the latest API!
- -- Table of instances to convert to JSX
- local INPUT = game.Selection:Get()
- -- SourceContainer to dump the JSX into
- local OUTPUT = workspace:FindFirstChild("InstanceToJsx Result") or (function()local m = Instance.new("ModuleScript"); m.Name = "InstanceToJsx Result"; m.Parent = workspace return m end)()
- -- Priority of props to display; those at the top of the list will be displayed first.
- local PROP_ORDER = {
- "Name",
- "className", -- Special key for provided CSS classes
- "Text",
- "Image",
- "Visible",
- "Active",
- "ZIndex",
- "AnchorPoint",
- "Position",
- "Size",
- }
- -- Map of JSX tags from their instance class names
- local INTRINSIC = {
- UIAspectRatioConstraint = "uiaspectratioconstraint",
- ScreenGui = "screengui",
- BillboardGui = "billboardgui",
- SurfaceGui = "surfacegui",
- ImageLabel = "imagelabel",
- ImageButton = "imagebutton",
- TextLabel = "textlabel",
- TextButton = "textbutton",
- TextBox = "textbox",
- Frame = "frame",
- ViewportFrame = "viewportframe",
- ScrollingFrame = "scrollingframe",
- UIGridLayout = "uigridlayout",
- UIListLayout = "uilistlayout",
- UIPageLayout = "uipagelayout",
- UITableLayout = "uitablelayout",
- UIPadding = "uipadding",
- UIScale = "uiscale",
- UISizeConstraint = "uisizeconstraint",
- UITextSizeConstraint = "uitextsizeconstraint",
- }
- -- Function for controlling how numbers are rounded when converted to JSX literals
- local function ROUND_TO(number, precision)
- if precision > 0 then
- local rounded = string.format("%0." .. tonumber(precision) .. "f", number)
- -- Remove trailing zeroes
- while (rounded:sub(#rounded) == '0') do
- rounded = rounded:sub(1, #rounded - 1)
- end
- -- Remove trailing dot
- if (rounded:sub(#rounded) == ".") then
- rounded = rounded:sub(1, #rounded - 1)
- end
- return rounded
- else
- return tostring(math.floor(number + 0.5))
- end
- end
- -- Function to convert a prop value to its JSX literal representation
- local function PROP_TO_STRING(prop)
- if type(prop) == "string" then
- return '"' .. prop .. '"'
- elseif type(prop) == "number" then
- return '{' .. ROUND_TO(prop, 5) .. '}'
- elseif type(prop) == "boolean" or typeof(prop) == "EnumItem" then
- return '{' .. tostring(prop) .. '}'
- elseif typeof(prop) == "UDim2" then
- return (
- '{new UDim2('
- .. ROUND_TO(prop.X.Scale, 3) .. ", "
- .. ROUND_TO(prop.X.Offset, 3) .. ", "
- .. ROUND_TO(prop.Y.Scale, 3) .. ", "
- .. ROUND_TO(prop.Y.Offset, 3) .. ')}'
- )
- elseif typeof(prop) == "Vector2" then
- return (
- '{new Vector2('
- .. ROUND_TO(prop.X, 5) .. ", "
- .. ROUND_TO(prop.Y, 5) .. ')}'
- )
- elseif typeof(prop) == "Vector3" then
- return (
- '{new Vector3('
- .. ROUND_TO(prop.X, 5) .. ", "
- .. ROUND_TO(prop.Y, 5) .. ", "
- .. ROUND_TO(prop.Z, 5) .. ')}'
- )
- elseif typeof(prop) == "CFrame" then
- local componentsStr = ""
- local prefix = ""
- for _,v in pairs({prop:components()}) do
- componentsStr = componentsStr .. prefix .. ROUND_TO(v, 5)
- prefix = ", "
- end
- return (
- '{new CFrame(' .. componentsStr .. ')}'
- )
- elseif typeof(prop) == "Color3" then
- return (
- '{Color3.fromRGB('
- .. ROUND_TO(prop.r * 255, 0) .. ", "
- .. ROUND_TO(prop.g * 255, 0) .. ", "
- .. ROUND_TO(prop.b * 255, 0) .. ')}'
- )
- end
- end
- --[[
- Optional: a map of CSS class information by name.
- Class information has two values: {
- Precondition: A map from property names to the necessary value for this CSS class to be used
- Override: An array of property names that this class overrides (which will be ommitted from the JSX)
- }
- e.g. I have a .ts module that exports the following:
- export namespace CSS {
- export const noBackground = {
- BackgroundTransparency: 1,
- BorderSizePixel: 0,
- }
- export const centered = {
- Position: new UDim2(0.5, 0, 0.5, 0),
- AnchorPoint: new Vector2(0.5, 0.5),
- }
- }
- To generate the use of these CSS styles, I could set this table to the following:
- local CSS_CLASSES = {
- ["CSS.noBackground"] = {
- Precondition = {
- BackgroundTransparency = 1,
- },
- Override = {"BackgroundTransparency", "BackgroundColor3", "BorderSizePixel"},
- },
- ["CSS.centered"] = {
- Precondition = {
- Position = UDim2.new(0.5, 0, 0.5, 0),
- AnchorPoint = Vector2.new(0.5, 0.5),
- },
- Override = {"Position", "AnchorPoint"},
- },
- }
- A frame with a BackgroundTransparency of 1 will then output as follows:
- <frame {...CSS.noBackground} Name="MyFrame"/>
- --]]
- local CSS_CLASSES = {}
- do
- local dictionary = {}
- for i,v in ipairs(PROP_ORDER) do
- dictionary[v] = i
- end
- PROP_ORDER = dictionary
- end
- local HttpService = game:GetService("HttpService")
- _G.API = _G.API or HttpService:JSONDecode(HttpService:GetAsync('http://anaminus.github.io/rbx/json/api/latest.json'))
- -- Add props to base classes
- local classProps
- local classSupers
- local dummies = {}
- if (_G.InstanceToJSXCache) then
- classProps = _G.InstanceToJSXCache.Props
- classSupers = _G.InstanceToJSXCache.Supers
- dummies = _G.InstanceToJSXCache.Dummies
- else
- baseProps = {}
- classSupers = {}
- for _,data in pairs(_G.API) do
- if data.type == "Property" then
- local readOnly = false
- for _,v in pairs(data.tags) do
- if v == "readonly" or v == "deprecated" or v == "hidden" then
- readOnly = true
- break
- end
- end
- local dummy = dummies[data.Class]
- if (not dummy) then
- local instPtr = {}
- local creatable = pcall(function()
- instPtr.instance = Instance.new(data.Class)
- end)
- if (creatable) then
- dummy = instPtr.instance
- dummies[data.Class] = dummy
- end
- end
- if (not readOnly) then
- local props = baseProps[data.Class]
- if (not props) then
- props = {}
- baseProps[data.Class] = props
- end
- table.insert(props, data.Name)
- end
- elseif data.type == "Class" then
- local super = data.Superclass
- if (super) then
- classSupers[data.Name] = super
- end
- if (not dummies[data.Name]) then
- local instPtr = {}
- local creatable = pcall(function()
- instPtr.instance = Instance.new(data.Name)
- end)
- if (creatable) then
- dummies[data.Name] = instPtr.instance
- end
- end
- if (not baseProps[data.Name]) then
- baseProps[data.Name] = {}
- end
- end
- end
- local function getProps(className)
- local props = {}
- while className do
- if baseProps[className] then
- for _,v in pairs(baseProps[className]) do
- table.insert(props, v)
- end
- end
- className = classSupers[className]
- end
- return props
- end
- classProps = {}
- for className in pairs(baseProps) do
- -- Filter for classes that can be created
- local dummy = dummies[className]
- if dummy then
- -- Combine props for all instantiable classes
- local allProps = getProps(className)
- local assigned = {}
- for _,propName in pairs(allProps) do
- if (not assigned[propName]) then
- assigned[propName] = true
- local defaultValuePtr = {}
- local writeable = pcall(function()
- local defaultVal = dummy[propName]
- dummy[propName] = defaultVal
- defaultValuePtr.exists = true --defaultVal ~= nil
- end)
- if (writeable and defaultValuePtr.exists) then
- local props = classProps[className]
- if (not props) then
- props = {}
- classProps[className] = props
- end
- table.insert(props, propName)
- end
- end
- end
- end
- end
- _G.InstanceToJSXCache = {
- Props = classProps,
- Supers = classSupers,
- Dummies = dummies,
- }
- end
- local getJSX; function getJSX(element, indentation)
- local dummy = dummies[element.ClassName]
- local writeableProps = classProps[element.ClassName] or {}
- -- Collect and format props
- local propsToDisplay = {}
- for _,propName in pairs(writeableProps) do
- local prop = element[propName]
- local propToStr = PROP_TO_STRING(prop)
- if (propToStr and dummy[propName] ~= prop) then
- table.insert(propsToDisplay, {Name = propName, Value = prop, ValueToString = propToStr})
- end
- end
- -- Filter for CSS replacement
- for cssClassName, cssClassData in pairs(CSS_CLASSES) do
- local matchesPrecondition = true
- for propName, value in pairs(cssClassData.Precondition) do
- local propMatchFound = false
- for i,prop in ipairs(propsToDisplay) do
- if prop.Name == propName then
- if (prop.Value == value) then
- propMatchFound = true
- end
- break
- end
- end
- if (not propMatchFound) then
- matchesPrecondition = false
- break
- end
- end
- if (matchesPrecondition) then
- -- Mark indices for removal
- local indicesToRemove = {}
- for _,propName in ipairs(cssClassData.Override) do
- for i,prop in ipairs(propsToDisplay) do
- if prop.Name == propName then
- table.insert(indicesToRemove, i)
- break
- end
- end
- end
- -- Remove overridden props
- table.sort(indicesToRemove)
- for removed, index in ipairs(indicesToRemove) do
- local offset = removed - 1
- table.remove(propsToDisplay, index - offset)
- end
- -- Add class prop
- table.insert(propsToDisplay, {
- Name = "className",
- ValueToString = "{..." .. cssClassName .. "}",
- })
- end
- end
- -- Sort props
- table.sort(propsToDisplay, function(tifb, tifa)
- if not PROP_ORDER[tifb.Name] then
- return false
- end
- if not PROP_ORDER[tifa.Name] then
- return true
- end
- return PROP_ORDER[tifb.Name] < PROP_ORDER[tifa.Name]
- end)
- local indentation = indentation or 0
- local outputStr
- local shortForm = INTRINSIC[element.ClassName]
- if (shortForm) then
- -- Type header
- outputStr = string.rep("\t", indentation) .. "<" .. shortForm
- -- Display props
- for _,propData in pairs(propsToDisplay) do
- if (propData.Name == "className") then
- -- Display as CSS class
- outputStr = outputStr .. " " .. propData.ValueToString
- elseif (propData.Name == "Name") then
- -- Display as Key
- outputStr = outputStr .. " Key=" .. propData.ValueToString
- else
- outputStr = outputStr .. " " .. propData.Name .. "=" .. propData.ValueToString
- end
- end
- -- Children
- local children = element:GetChildren()
- if #children == 0 then
- outputStr = outputStr .. "/>"
- else
- outputStr = outputStr .. ">\n"
- for _,child in pairs(children) do
- outputStr = outputStr .. getJSX(child, indentation + 1) .. "\n"
- end
- outputStr = outputStr .. string.rep("\t", indentation) .. "</" .. shortForm .. ">"
- end
- else
- -- Type header
- outputStr = string.rep("\t", indentation) .. "Roact.createElement('" .. element.ClassName .. "', {"
- -- Props
- if (#propsToDisplay == 0) then
- outputStr = outputStr .. "}, {"
- else
- for _,propData in pairs(propsToDisplay) do
- -- Omit braces for non-JSX literals
- local propToStr = propData.ValueToString
- if propToStr:sub(1, 1) == "{" and propToStr:sub(#propToStr) == "}" then
- propToStr = propToStr:sub(2, #propToStr - 1)
- end
- outputStr = outputStr .. "\n" .. string.rep("\t", indentation + 1) .. propData.Name .. ": " .. propToStr .. ","
- end
- outputStr = outputStr .. "\n" .. string.rep("\t", indentation) .. "}, {"
- end
- -- Children
- local children = element:GetChildren()
- if #children == 0 then
- outputStr = outputStr .. "})"
- else
- for i,child in ipairs(children) do
- local name = "[" .. tostring(i) .. "]"
- local isUnique = true
- for i2,child2 in ipairs(children) do
- if i2 ~= i then
- if child2.Name == child.Name then
- isUnique = false
- break
- end
- end
- end
- if isUnique then
- name = child.Name
- end
- outputStr = outputStr .. "\n" .. string.rep("\t", indentation + 1) .. name .. ": ("
- outputStr = outputStr .. "\n" .. getJSX(child, indentation + 2)
- outputStr = outputStr .. "\n" .. string.rep("\t", indentation + 1) .. "),"
- end
- outputStr = outputStr .. "\n" .. string.rep("\t", indentation) .. "})"
- end
- end
- return outputStr
- end
- local output = ""
- local prefix = ""
- for _,v in pairs(INPUT) do
- local jsx = getJSX(v)
- if (jsx) then
- output = output .. prefix .. jsx
- prefix = "\n"
- end
- end
- OUTPUT.Source = output
- if (_G.ExposedPlugin) then
- _G.ExposedPlugin:OpenScript(OUTPUT)
- else
- game.Selection:Set({OUTPUT})
- end
Advertisement
Add Comment
Please, Sign In to add comment