Skip to content
English

Menu API

Create highly customizable Menus within Lua

Initialize Menu

lua
FeatherMenu =  exports['feather-menu'].initiate()
FeatherMenu =  exports['feather-menu'].initiate()

Register Menu

ParameterDescription
topHow far from the top the menu should be
leftHow far from the left the menu should be
720widthWidth of the menu on a 720p window
1080widthWidth of the menu on a 1080p window
2kwidthWidth of the menu on a 2k window
4kwidthWidth of the menu on a 4k window
styleCSS style overrides
contentslot.styleCSS style overrides for the content slot
draggableIf the window can be dragged
cancloseIf the user can close the menu with "X"
callbacksopen/close/topage callbacks

Example Usage:

lua
local MyMenu = FeatherMenu:RegisterMenu('feather:character:menu', {
    top = '40%',
    left = '20%',
    ['720width'] = '500px',
    ['1080width'] = '600px',
    ['2kwidth'] = '700px',
    ['4kwidth'] = '900px',
    style = {
        -- ['height'] = '500px'
        -- ['border'] = '5px solid white',
        -- ['background-image'] = 'none',
        -- ['background-color'] = '#515A5A'
    },
    contentslot = {
        style = { --This style is what is currently making the content slot scoped and scrollable. If you delete this, it will make the content height dynamic to its inner content.
            ['height'] = '300px',
            ['min-height'] = '300px'
        }
    },
    draggable = true,
    --canclose = false
}, {
    opened = function()
        print("MENU OPENED!")
    end,
    closed = function()
        print("MENU CLOSED!")
    end,
    topage = function(data)
        print("PAGE CHANGED ", data.pageid)
    end
})
local MyMenu = FeatherMenu:RegisterMenu('feather:character:menu', {
    top = '40%',
    left = '20%',
    ['720width'] = '500px',
    ['1080width'] = '600px',
    ['2kwidth'] = '700px',
    ['4kwidth'] = '900px',
    style = {
        -- ['height'] = '500px'
        -- ['border'] = '5px solid white',
        -- ['background-image'] = 'none',
        -- ['background-color'] = '#515A5A'
    },
    contentslot = {
        style = { --This style is what is currently making the content slot scoped and scrollable. If you delete this, it will make the content height dynamic to its inner content.
            ['height'] = '300px',
            ['min-height'] = '300px'
        }
    },
    draggable = true,
    --canclose = false
}, {
    opened = function()
        print("MENU OPENED!")
    end,
    closed = function()
        print("MENU CLOSED!")
    end,
    topage = function(data)
        print("PAGE CHANGED ", data.pageid)
    end
})

Register Page

Register a new page to the menu

ParameterDescription
PageIDThe ID of the page (Must be unique to the menu)

Example Usage:

lua
local MyFirstPage = MyMenu:RegisterPage('first:page')
local MyFirstPage = MyMenu:RegisterPage('first:page')

Add Header to Page

image

TIP

This element is what is draggable, without this element, you cannot drag a menu.

ParameterDescription
valueThe text to display
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement('header', {
    value = 'My First Menu',
    slot = "header",
    style = {}
})
MyFirstPage:RegisterElement('header', {
    value = 'My First Menu',
    slot = "header",
    style = {}
})

Add SubHeader to Page

image

ParameterDescription
valueThe text to display
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement('subheader', {
    value = "First Page",
    slot = "header",
    style = {}
})
MyFirstPage:RegisterElement('subheader', {
    value = "First Page",
    slot = "header",
    style = {}
})

Add Line to Page

image

ParameterDescription
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement('line', {
    slot = "header",
    style = {}
})
MyFirstPage:RegisterElement('line', {
    slot = "header",
    style = {}
})

Add BottomLine to Page

image

ParameterDescription
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement('bottomline', {
    -- slot = "header",
    -- style = {}
})
MyFirstPage:RegisterElement('bottomline', {
    -- slot = "header",
    -- style = {}
})

Add Test Display to Page

ParameterDescription
valueThe text to display
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
TextDisplay = MyFirstPage:RegisterElement('textdisplay', {
    value = "Some Text",
    style = {}
})
TextDisplay = MyFirstPage:RegisterElement('textdisplay', {
    value = "Some Text",
    style = {}
})

Add Input to Page

image

ParameterDescription
labelThe text to display
placeholderThe text to display when nothing is entered into the input
slotThere are 3 slots available, (header, content, footer)
persistDetermines if the user input value should persist when changing pages
styleCSS style overrides
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
local inputValue = ''
MyFirstPage:RegisterElement('input', {
    label = "My First Input",
    placeholder = "Type something!",
    -- persist = false,
    style = {
        -- ['background-image'] = 'none',
        -- ['background-color'] = '#E8E8E8',
        -- ['color'] = 'black',
        -- ['border-radius'] = '6px'
    }
}, function(data)
    -- This gets triggered whenever the input value changes
    print("Input Triggered: ", data.value)
    inputValue = data.value
end)
local inputValue = ''
MyFirstPage:RegisterElement('input', {
    label = "My First Input",
    placeholder = "Type something!",
    -- persist = false,
    style = {
        -- ['background-image'] = 'none',
        -- ['background-color'] = '#E8E8E8',
        -- ['color'] = 'black',
        -- ['border-radius'] = '6px'
    }
}, function(data)
    -- This gets triggered whenever the input value changes
    print("Input Triggered: ", data.value)
    inputValue = data.value
end)

Add TextArea to Page

image

ParameterDescription
labelThe text to display
placeholderThe text to display when nothing is entered into the input
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
rowsHow many rows for the areatext to take up
colsHow many cols for the areatext to take up. If not set, it will default to 100% width (dynamic)
resizeIs the textarea resizable
persistDetermines if the user input value should persist when changing pages
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
local inputValue = ''
MyFirstPage:RegisterElement('textarea', {
    label = "My First TextArea",
    placeholder = "Type something!",
    rows = "4",
    -- cols = "14",
    resize = false,
    -- persist = false,
    style = {
        -- ['background-image'] = 'none',
        -- ['background-color'] = '#E8E8E8',
        -- ['color'] = 'black',
        -- ['border-radius'] = '6px'
    }
}, function(data)
    print("Input Triggered: ", data.value)
    inputValue = data.value
end)
local inputValue = ''
MyFirstPage:RegisterElement('textarea', {
    label = "My First TextArea",
    placeholder = "Type something!",
    rows = "4",
    -- cols = "14",
    resize = false,
    -- persist = false,
    style = {
        -- ['background-image'] = 'none',
        -- ['background-color'] = '#E8E8E8',
        -- ['color'] = 'black',
        -- ['border-radius'] = '6px'
    }
}, function(data)
    print("Input Triggered: ", data.value)
    inputValue = data.value
end)

Add Button to Page

image

ParameterDescription
labelThe text to display
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
soundPlay a rdr sound effect
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement('button', {
    label = "Update",
    style = {
        -- ['background-image'] = 'none',
        -- ['background-color'] = '#E8E8E8',
        -- ['color'] = 'black',
        -- ['border-radius'] = '6px'
    },
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- },
}, function()
    -- This gets triggered whenever the button is clicked
end)
MyFirstPage:RegisterElement('button', {
    label = "Update",
    style = {
        -- ['background-image'] = 'none',
        -- ['background-color'] = '#E8E8E8',
        -- ['color'] = 'black',
        -- ['border-radius'] = '6px'
    },
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- },
}, function()
    -- This gets triggered whenever the button is clicked
end)

Add Arrows to Page

image

ParameterDescription
labelThe text to display
startWhat index to default to display
optionsThis is the options to select through, this can be a string or table. If its a table you must have "display" so that a value shows up
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
soundPlay a rdr sound effect
persistDetermines if the user input value should persist when changing pages

Example Usage:

lua
MyFirstPage:RegisterElement('arrows', {
    label = "Hair Color",
    start = 2,
    options = {
        {
            display = "Black",
            extra = "data"
        },
        "Brown",
        "Blonde",
        "Red",
        "Silver",
        "White"
    },
    -- persist = false,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- },
}, function(data)
    -- This gets triggered whenever the arrow selected value changes
    print(TableToString(data.value))
end)
MyFirstPage:RegisterElement('arrows', {
    label = "Hair Color",
    start = 2,
    options = {
        {
            display = "Black",
            extra = "data"
        },
        "Brown",
        "Blonde",
        "Red",
        "Silver",
        "White"
    },
    -- persist = false,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- },
}, function(data)
    -- This gets triggered whenever the arrow selected value changes
    print(TableToString(data.value))
end)

Add Slider to Page

image

ParameterDescription
labelThe text to display
startWhat number to start at
minThe minimum number within the range
maxThe maximum number within the range
stepsHow many numbers to skip per slider tick
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
soundPlay a rdr sound effect
persistDetermines if the user input value should persist when changing pages
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement('slider', {
    label = "Eye Color",
    start = 1,
    min = 0,
    max = 100,
    steps = 1,
    -- persist = false,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- },
}, function(data)
    -- This gets triggered whenever the sliders selected value changes
    print(TableToString(data.value))
end)
MyFirstPage:RegisterElement('slider', {
    label = "Eye Color",
    start = 1,
    min = 0,
    max = 100,
    steps = 1,
    -- persist = false,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- },
}, function(data)
    -- This gets triggered whenever the sliders selected value changes
    print(TableToString(data.value))
end)

Add Toggle to Page

image

ParameterDescription
labelThe text to display
startWhat boolean value to start at (true/false)
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
soundPlay a rdr sound effect
persistDetermines if the user input value should persist when changing pages
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement("toggle", {
    label = "Glasses",
    start = true,
    -- persist = false,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
}, function(data)
    -- This gets triggered whenever the toggle value changes
    print(data.value)
end)
MyFirstPage:RegisterElement("toggle", {
    label = "Glasses",
    start = true,
    -- persist = false,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
}, function(data)
    -- This gets triggered whenever the toggle value changes
    print(data.value)
end)

Add Checkbox to Page

image

ParameterDescription
labelThe text to display
startWhat boolean value to start at (true/false)
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
soundPlay a rdr sound effect
persistDetermines if the user input value should persist when changing pages
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement("checkbox", {
    label = "Test",
    start = true
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
}, function(data)
    print(data.value)
end)
MyFirstPage:RegisterElement("checkbox", {
    label = "Test",
    start = true
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
}, function(data)
    print(data.value)
end)

Add GridSelector to Page

image

ParameterDescription
leftlabelThe text to display on the left side of the grid
rightlabelThe text to display on the right side of the grid
toplabelThe text to display on the top of the grid
bottomlabelThe text to display on the bottom of the grid
maxyThe Maximum number the grid can reach on the y axis
maxxThe Maximum number the grid can reach on the x axis
arrowstepsThe distance the circle moves when utilizing the arrow keys
precisionHow many decimal places (example: 1 = 1.0, 2 = 1.00)
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
soundPlay a rdr sound effect
persistDetermines if the user input value should persist when changing pages
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement('gridslider', {
    leftlabel = 'thin',
    rightlabel = 'heavy',
    toplabel = 'tall',
    bottomlabel = 'short',
    maxx = 1,
    maxy = 1,
    arrowsteps = 10,
    precision = 1
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
}, function(data)
    print(TableToString(data.value)) -- Returns {x = 0, y = 0}
end)
MyFirstPage:RegisterElement('gridslider', {
    leftlabel = 'thin',
    rightlabel = 'heavy',
    toplabel = 'tall',
    bottomlabel = 'short',
    maxx = 1,
    maxy = 1,
    arrowsteps = 10,
    precision = 1
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
}, function(data)
    print(TableToString(data.value)) -- Returns {x = 0, y = 0}
end)

Add Custom HTML to Page

image

ParameterDescription
valueThe html to display
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement("html", {
    value = {
        [[
            <img width="100px" height="100px" style="margin: 0 auto;" src="https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg?cs=srgb&dl=pexels-pixabay-45201.jpg&fm=jpg" />
            <div style="color:red;">
                HELLO!!
            </div>
        ]]
    }
})
MyFirstPage:RegisterElement("html", {
    value = {
        [[
            <img width="100px" height="100px" style="margin: 0 auto;" src="https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg?cs=srgb&dl=pexels-pixabay-45201.jpg&fm=jpg" />
            <div style="color:red;">
                HELLO!!
            </div>
        ]]
    }
})

Add Page Arrows to Page

image

ParameterDescription
totalHow many pages to show (display number) ex: 1/x
currentWhat page number to show (display number) ex: x/3
slotThere are 3 slots available, (header, content, footer)
styleCSS style overrides
soundPlay a rdr sound effect
persistDetermines if the user input value should persist when changing pages
idA custom ID you can give an element (This will prevent duplicates if you are registering an element more than once)

Example Usage:

lua
MyFirstPage:RegisterElement('pagearrows', {
    slot = "footer",
    total = 3,
    current = 1,
    style = {},
    -- persist = false,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
}, function(data)
    if data.value == 'forward' then
        print('Forward')
    else
        print('BACK')
    end
end)
MyFirstPage:RegisterElement('pagearrows', {
    slot = "footer",
    total = 3,
    current = 1,
    style = {},
    -- persist = false,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
}, function(data)
    if data.value == 'forward' then
        print('Forward')
    else
        print('BACK')
    end
end)

Open Menu

ParameterDescription
cursorFocusNui Cursor Focus (true/false)
menuFocusNui Menu Focus (true/false)
startupPageWhat page to default to when opening
soundPlay a rdr sound effect

Example Usage:

lua
MyMenu:Open({
    -- cursorFocus = false,
    -- menuFocus = false,
    startupPage = MyFirstPage,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
})
MyMenu:Open({
    -- cursorFocus = false,
    -- menuFocus = false,
    startupPage = MyFirstPage,
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
})

Close Menu

ParameterDescription
soundPlay a rdr sound effect

Example Usage:

lua
MyMenu:Close({
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
})
MyMenu:Close({
    -- sound = {
    --     action = "SELECT",
    --     soundset = "RDRO_Character_Creator_Sounds"
    -- }
})

Route to/Show a Page

The menu must be open already

Example Usage:

lua
MyFirstPage:RouteTo()
MyFirstPage:RouteTo()

Update a Page Element's data

The parameters and values are based on the original element registered.

Example Usage:

lua
TextDisplay:update({
    value = "Hello World!",
    style = {}
})
TextDisplay:update({
    value = "Hello World!",
    style = {}
})

Eample 2 usage:

Override a value like a slider

lua
SliderDisplay:update({
    value = 1
})
SliderDisplay:update({
    value = 1
})

Unregister Element

Example Usage:

lua
TextDisplay:UnRegister()
TextDisplay:UnRegister()

Events

Opened Menu

lua
RegisterNetEvent('FeatherMenu:opened', function(menudata)
  print("MENU OPENED", menudata.menuid)
end)
RegisterNetEvent('FeatherMenu:opened', function(menudata)
  print("MENU OPENED", menudata.menuid)
end)

Closed Menu

lua
RegisterNetEvent('FeatherMenu:closed', function(menudata)
  print("MENU CLOSED", menudata.menuid)
end)
RegisterNetEvent('FeatherMenu:closed', function(menudata)
  print("MENU CLOSED", menudata.menuid)
end)

Page Routed

lua
RegisterNetEvent('FeatherMenu:Page:RoutedTo', function(menudata)
  print("MENU OPENED", menudata.menuid, menudata.pageid)
end)
RegisterNetEvent('FeatherMenu:Page:RoutedTo', function(menudata)
  print("MENU OPENED", menudata.menuid, menudata.pageid)
end)

Full three page Example

imageimage

lua
local function TableToString(o)
    if type(o) == 'table' then
        local s = '{ '
        for k, v in pairs(o) do
            if type(k) ~= 'number' then k = '"' .. k .. '"' end
            s = s .. '[' .. k .. '] = ' .. TableToString(v) .. ','
        end
        return s .. '} '
    else
        return tostring(o)
    end
end

FeatherMenu =  exports['feather-menu'].initiate()

RegisterCommand('TestMenu', function()
    local MyMenu = FeatherMenu:RegisterMenu('feather:character:menu', {
        top = '40%',
        left = '20%',
        ['720width'] = '500px',
        ['1080width'] = '600px',
        ['2kwidth'] = '700px',
        ['4kwidth'] = '900px',
        style = {
            -- ['height'] = '500px'
            -- ['border'] = '5px solid white',
            -- ['background-image'] = 'none',
            -- ['background-color'] = '#515A5A'
        },
        contentslot = {
            style = {
                ['height'] = '300px',
                ['min-height'] = '300px'
            }
        },
        draggable = true
    })

    local MyFirstPage = MyMenu:RegisterPage('first:page')
    local MySecondPage = MyMenu:RegisterPage('second:page')
    local MyThirdPage = MyMenu:RegisterPage('third:page')

    ------ FIRST PAGE CONTENT  ------
    MyFirstPage:RegisterElement('header', {
        value = 'My First Menu',
        slot = "header",
        style = {}
    })

    MyFirstPage:RegisterElement('subheader', {
        value = "First Page",
        slot = "header",
        style = {}
    })

    MyFirstPage:RegisterElement('line', {
        slot = "header",
    })


    local inputValue = ''
    MyFirstPage:RegisterElement('input', {
        label = "My First Input",
        placeholder = "Type something!",
        style = {
            -- ['background-image'] = 'none',
            -- ['background-color'] = '#E8E8E8',
            -- ['color'] = 'black',
            -- ['border-radius'] = '6px'
        }
    }, function(data)
        print("Input Triggered: ", data.value)
        inputValue = data.value
    end)

    MyFirstPage:RegisterElement('button', {
        label = "Update",
        style = {
            -- ['background-image'] = 'none',
            -- ['background-color'] = '#E8E8E8',
            -- ['color'] = 'black',
            -- ['border-radius'] = '6px'
        }
    }, function()
        TextDisplay:update({
            value = inputValue,
            style = {}
        })
    end)

    MyFirstPage:RegisterElement('arrows', {
        label = "Hair Color",
        start = 2,
        options = {
            {
                display = "Black",
                extra = "data"
            },
            "Brown",
            "Blonde",
            "Red",
            "Silver",
            "White"
        }
    }, function(data)
        print(TableToString(data.value))
    end)


    MyFirstPage:RegisterElement('slider', {
        label = "Eye Color",
        start = 1,
        min = 0,
        max = 100,
        steps = 1
    }, function(data)
        print(TableToString(data.value))
    end)

    MyFirstPage:RegisterElement("toggle", {
        label = "Glasses",
        start = true
    }, function(data)
        print(data.value)
    end)

    MyFirstPage:RegisterElement("html", {
        value = {
            [[
                <img width="100px" height="100px" style="margin: 0 auto;" src="https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg?cs=srgb&dl=pexels-pixabay-45201.jpg&fm=jpg" />
                <div style="color:red;">
                    HELLO!!
                </div>
            ]]
        }
    })


    MyFirstPage:RegisterElement('bottomline')

    TextDisplay = MyFirstPage:RegisterElement('textdisplay', {
        value = "Some Text",
        style = {}
    })

    MyFirstPage:RegisterElement('line', {
        slot = "footer",
    })

    MyFirstPage:RegisterElement('pagearrows', {
        slot = "footer",
        total = 3,
        current = 1,
        style = {}
    }, function(data)
        if data.value == 'forward' then
            MySecondPage:RouteTo()
        else
            print('BACK')
        end
    end)


    ------ SECOND PAGE CONTENT  ------
    MySecondPage:RegisterElement('header', {
        value = 'My First Menu',
        slot = "header",
        style = {}
    })

    MySecondPage:RegisterElement('subheader', {
        value = "Second Page",
        slot = "header",
        style = {}
    })

    MySecondPage:RegisterElement('line', {
        slot = "header",
    })

    MySecondPage:RegisterElement('header', {
        value = 'Awesome Stuff!',
        draggable = false,
        style = {}
    })


    MySecondPage:RegisterElement('line', {
        slot = "footer",
    })

    MySecondPage:RegisterElement('pagearrows', {
        slot = "footer",
        total = 3,
        current = 2,
        style = {}
    }, function(data)
        if data.value == 'forward' then
            MyThirdPage:RouteTo()
        else
            MyFirstPage:RouteTo()
        end
    end)

    ------ THIRD PAGE CONTENT  ------
    MyThirdPage:RegisterElement('header', {
        value = 'My First Menu',
        slot = "header",
        style = {}
    })

    MyThirdPage:RegisterElement('subheader', {
        value = "Third Page",
        slot = "header",
        style = {}
    })

    MyThirdPage:RegisterElement('line', {
        slot = "header"
    })

    MyThirdPage:RegisterElement('line', {
        slot = "footer"
    })

    MyThirdPage:RegisterElement('pagearrows', {
        slot = "footer",
        total = 3,
        current = 3,
        style = {}
    }, function(data)
        if data.value == 'forward' then
            print('FORWARD')
        else
            MySecondPage:RouteTo()
        end
    end)

    MyMenu:Open({
        -- cursorFocus = false,
        -- menuFocus = false,
        startupPage = MyFirstPage
    })
end)
local function TableToString(o)
    if type(o) == 'table' then
        local s = '{ '
        for k, v in pairs(o) do
            if type(k) ~= 'number' then k = '"' .. k .. '"' end
            s = s .. '[' .. k .. '] = ' .. TableToString(v) .. ','
        end
        return s .. '} '
    else
        return tostring(o)
    end
end

FeatherMenu =  exports['feather-menu'].initiate()

RegisterCommand('TestMenu', function()
    local MyMenu = FeatherMenu:RegisterMenu('feather:character:menu', {
        top = '40%',
        left = '20%',
        ['720width'] = '500px',
        ['1080width'] = '600px',
        ['2kwidth'] = '700px',
        ['4kwidth'] = '900px',
        style = {
            -- ['height'] = '500px'
            -- ['border'] = '5px solid white',
            -- ['background-image'] = 'none',
            -- ['background-color'] = '#515A5A'
        },
        contentslot = {
            style = {
                ['height'] = '300px',
                ['min-height'] = '300px'
            }
        },
        draggable = true
    })

    local MyFirstPage = MyMenu:RegisterPage('first:page')
    local MySecondPage = MyMenu:RegisterPage('second:page')
    local MyThirdPage = MyMenu:RegisterPage('third:page')

    ------ FIRST PAGE CONTENT  ------
    MyFirstPage:RegisterElement('header', {
        value = 'My First Menu',
        slot = "header",
        style = {}
    })

    MyFirstPage:RegisterElement('subheader', {
        value = "First Page",
        slot = "header",
        style = {}
    })

    MyFirstPage:RegisterElement('line', {
        slot = "header",
    })


    local inputValue = ''
    MyFirstPage:RegisterElement('input', {
        label = "My First Input",
        placeholder = "Type something!",
        style = {
            -- ['background-image'] = 'none',
            -- ['background-color'] = '#E8E8E8',
            -- ['color'] = 'black',
            -- ['border-radius'] = '6px'
        }
    }, function(data)
        print("Input Triggered: ", data.value)
        inputValue = data.value
    end)

    MyFirstPage:RegisterElement('button', {
        label = "Update",
        style = {
            -- ['background-image'] = 'none',
            -- ['background-color'] = '#E8E8E8',
            -- ['color'] = 'black',
            -- ['border-radius'] = '6px'
        }
    }, function()
        TextDisplay:update({
            value = inputValue,
            style = {}
        })
    end)

    MyFirstPage:RegisterElement('arrows', {
        label = "Hair Color",
        start = 2,
        options = {
            {
                display = "Black",
                extra = "data"
            },
            "Brown",
            "Blonde",
            "Red",
            "Silver",
            "White"
        }
    }, function(data)
        print(TableToString(data.value))
    end)


    MyFirstPage:RegisterElement('slider', {
        label = "Eye Color",
        start = 1,
        min = 0,
        max = 100,
        steps = 1
    }, function(data)
        print(TableToString(data.value))
    end)

    MyFirstPage:RegisterElement("toggle", {
        label = "Glasses",
        start = true
    }, function(data)
        print(data.value)
    end)

    MyFirstPage:RegisterElement("html", {
        value = {
            [[
                <img width="100px" height="100px" style="margin: 0 auto;" src="https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg?cs=srgb&dl=pexels-pixabay-45201.jpg&fm=jpg" />
                <div style="color:red;">
                    HELLO!!
                </div>
            ]]
        }
    })


    MyFirstPage:RegisterElement('bottomline')

    TextDisplay = MyFirstPage:RegisterElement('textdisplay', {
        value = "Some Text",
        style = {}
    })

    MyFirstPage:RegisterElement('line', {
        slot = "footer",
    })

    MyFirstPage:RegisterElement('pagearrows', {
        slot = "footer",
        total = 3,
        current = 1,
        style = {}
    }, function(data)
        if data.value == 'forward' then
            MySecondPage:RouteTo()
        else
            print('BACK')
        end
    end)


    ------ SECOND PAGE CONTENT  ------
    MySecondPage:RegisterElement('header', {
        value = 'My First Menu',
        slot = "header",
        style = {}
    })

    MySecondPage:RegisterElement('subheader', {
        value = "Second Page",
        slot = "header",
        style = {}
    })

    MySecondPage:RegisterElement('line', {
        slot = "header",
    })

    MySecondPage:RegisterElement('header', {
        value = 'Awesome Stuff!',
        draggable = false,
        style = {}
    })


    MySecondPage:RegisterElement('line', {
        slot = "footer",
    })

    MySecondPage:RegisterElement('pagearrows', {
        slot = "footer",
        total = 3,
        current = 2,
        style = {}
    }, function(data)
        if data.value == 'forward' then
            MyThirdPage:RouteTo()
        else
            MyFirstPage:RouteTo()
        end
    end)

    ------ THIRD PAGE CONTENT  ------
    MyThirdPage:RegisterElement('header', {
        value = 'My First Menu',
        slot = "header",
        style = {}
    })

    MyThirdPage:RegisterElement('subheader', {
        value = "Third Page",
        slot = "header",
        style = {}
    })

    MyThirdPage:RegisterElement('line', {
        slot = "header"
    })

    MyThirdPage:RegisterElement('line', {
        slot = "footer"
    })

    MyThirdPage:RegisterElement('pagearrows', {
        slot = "footer",
        total = 3,
        current = 3,
        style = {}
    }, function(data)
        if data.value == 'forward' then
            print('FORWARD')
        else
            MySecondPage:RouteTo()
        end
    end)

    MyMenu:Open({
        -- cursorFocus = false,
        -- menuFocus = false,
        startupPage = MyFirstPage
    })
end)