UniversalDashboard.MaterialUI.psm1

$TAType = [psobject].Assembly.GetType('System.Management.Automation.TypeAccelerators')
$TAtype::Add('DashboardColor', 'UniversalDashboard.Models.DashboardColor')
$TAtype::Add('Endpoint', 'UniversalDashboard.Models.Endpoint')
$TAtype::Add('FontAwesomeIcons', 'UniversalDashboard.Models.FontAwesomeIcons')

function New-UDRow {
    [CmdletBinding(DefaultParameterSetName = 'static')]
    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter(ParameterSetName = "static", Position = 0)]
        [ScriptBlock]$Columns,
        [Parameter(ParameterSetName = "dynamic")]
        [object]$Endpoint,
        [Parameter(ParameterSetName = "dynamic")]
        [Switch]$AutoRefresh,
        [Parameter(ParameterSetName = "dynamic")]
        [int]$RefreshInterval = 5
    )

    End 
    {
        # if ($PSCmdlet.ParameterSetName -eq 'dynamic')
        # {
        # throw "dynamic parameterset not yet supported"
        # }

        New-UDGrid -Container -Content {
            & $Columns 
        }
    }
}

function New-UDColumn {
    [CmdletBinding(DefaultParameterSetName = 'content')]
    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),

        [Parameter()]
        [Alias('Size')]
        [ValidateRange(1,12)]
        [int]$SmallSize = 12,
        [Parameter()]
        [ValidateRange(1,12)]
        [int]$LargeSize = 12,
        [Parameter()]
        [ValidateRange(1,12)]
        [int]$MediumSize = 12,

        [Parameter()]
        [ValidateRange(1,12)]
        [int]$SmallOffset = 1,
        [Parameter()]
        [ValidateRange(1,12)]
        [int]$MediumOffset = 1,
        [Parameter()]
        [ValidateRange(1,12)]
        [int]$LargeOffset = 1,

        [Parameter(ParameterSetName = 'content', Position = 1)]
        [ScriptBlock]$Content,

        [Parameter(ParameterSetName = "endpoint")]
        [Endpoint]$Endpoint,
        [Parameter(ParameterSetName = "endpoint")]
        [Switch]$AutoRefresh,
        [Parameter(ParameterSetName = "endpoint")]
        [int]$RefreshInterval = 5
    )
    
    if ($PSCmdlet.ParameterSetName -eq 'content') {
        $GridContent = $Content
        New-UDGrid -Item -SmallSize $SmallSize -MediumSize $MediumSize -LargeSize $LargeSize -Content {
            & $GridContent 
        }
    } else {        
        New-UDGrid -Item -SmallSize $SmallSize -MediumSize $MediumSize -LargeSize $LargeSize -Content {
            New-UDDynamic -AutoRefresh:$AutoRefresh -AutoRefreshInterval $RefreshInterval -Content $Endpoint
        }
    }
}function New-UDAlert {
    <#
    .SYNOPSIS
    Creates an alert.
     
    .DESCRIPTION
    Creates an alert.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Severity
    The severity of this alert.
     
    .PARAMETER Children
    Content of the alert.
 
    .PARAMETER Text
    Text for the body of the alert.
     
    .PARAMETER Title
    A title for this alert.
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [ValidateSet("success", "error", "warning", "info")]
        [string]$Severity = "success",
        [Parameter(ParameterSetName = "Content")]
        [Alias("Content")]
        [scriptblock]$Children,
        [Parameter(ParameterSetName = "Text")]
        [string]$Text,
        [Parameter()]
        [string]$Title
    )

    if ($PSCmdlet.ParameterSetName -eq 'Text')
    {
        $Children = { $Text }
    }

    @{
        type = "mu-alert"
        id = $id 
        isPlugin = $true 
        assetId = $MUAssetId 

        severity = $Severity.ToLower()
        children = & $Children
        title = $Title
    }
}
function New-UDAppBar {
    <#
    .SYNOPSIS
    Creates an AppBar.
     
    .DESCRIPTION
    Creates an AppBar. This can be used to replace the built-in AppBar.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Drawer
    A drawer that can be opened from this AppBar. Use New-UDDrawer to create a drawer to pass to this parameter.
     
    .PARAMETER Children
    Children of this AppBar. The children of an AppBar are commonly text and buttons.
     
    .PARAMETER Position
    The position of this AppBar. A fixed position will override the default AppBar.
 
    .PARAMETER Footer
    Positions the app bar at the bottom of the page to create a footer
 
    .PARAMETER DisableThemeToggle
    Removes the theme toggle switch from the app bar.
     
    .EXAMPLE
    Creates a new AppBar that is relative to other components.
 
    New-UDAppBar -Children { New-UDTypography -Text 'Hello' -Paragraph } -Position relative
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [Hashtable]$Drawer,
        [Parameter()]
        [Alias('Content')]
        [ScriptBlock]$Children,
        [Parameter()]
        [ValidateSet('absolute', 'fixed', 'relative', 'static', 'sticky')]
        [string]$Position = 'fixed',
        [Parameter()]
        [switch]$Footer,
        [Parameter()]
        [Switch]$DisableThemeToggle
    )

    @{
        id = $Id 
        type = 'mu-appbar'
        assetId = $MUAssetId 
        isPlugin = $true 

        children = & $Children
        drawer = $Drawer
        position = $Position
        footer = $Footer.IsPresent
        disableThemeToggle = $DisableThemeToggle.IsPresent
    }
}
function New-UDAutocomplete {
    <#
    .SYNOPSIS
    Creates an autocomplete textbox.
     
    .DESCRIPTION
    Creates an autocomplete textbox. Autocomplete text boxes can be used to allow the user to select from a large list of static or dynamic items. Typing in the textbox will filter the list.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Label
    The label to show for the textbox.
     
    .PARAMETER Value
    The value of the textbox.
     
    .PARAMETER OnChange
    A script block that is invoked when the selection changes.
     
    .PARAMETER OnLoadOptions
    A script block that is called when the user starts typing in the text box. The $Body variable will contain the typed text. You should return a JSON array of values that are a result of what the user has typed.
     
    .PARAMETER Options
    Static options to display in the selection drop down. When the user types, these options will be filtered.
     
    .EXAMPLE
    Creates a autocomplete with a static list of options.
 
    New-UDAutocomplete -Id 'autoComplete' -Options @('Test', 'Test2', 'Test3', 'Test4')
     
    .EXAMPLE
    Creates an autocomplete with a dynamically filtered set of options
 
    New-UDAutocomplete -Id 'autoCompleteDynamic' -OnLoadOptions {
        @('Test', 'Test2', 'Test3', 'Test4') | Where-Object { $_ -match $Body } | ConvertTo-Json
    }
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [string]$Label,
        [Parameter()]
        [string]$Value,
        [Parameter()]
        [Endpoint]$OnChange, 
        [Parameter(Mandatory, ParameterSetName = "Dynamic")]
        [Endpoint]$OnLoadOptions,
        [Parameter(Mandatory, ParameterSetName = "Static")]
        [Array]$Options = @(),
        [Parameter()]
        [Switch]$FullWidth,
        [Parameter()]
        [ValidateSet("filled", "outlined", "standard")]
        [string]$Variant = "standard",
        [Parameter()]
        [Switch]$Multiple
    )

    if ($OnChange) {
        $OnChange.ContentType = 'text/plain';
        $OnChange.Register($Id + "onChange", $PSCmdlet)
    }

    if ($PSCmdlet.ParameterSetName -eq 'Dynamic')
    {
        if ($OnLoadOptions) {
            $OnLoadOptions.ContentType = 'text/plain';
            $OnLoadOptions.Register($Id + "onLoadOptions", $PSCmdlet)
        }
    }
    
    @{
        id = $id 
        assetId = $MUAssetId 
        isPlugin = $true 
        type = "mu-autocomplete"

        label = $Label
        value = $value 
        onChange = $onChange 
        onLoadOptions = $OnLoadOptions
        options = $Options
        fullWidth = $FullWidth.IsPresent
        variant = $Variant.ToLower()
        multiple = $Multiple.IsPresent
    }
}

function New-UDAvatar {
    <#
    .SYNOPSIS
    Creates a new Avatar.
     
    .DESCRIPTION
    Creates a new Avatar. An avatar is typically an image of a user.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Image
    The URL of an image to show in the avatar.
     
    .PARAMETER Alt
    The alt text to assign to the avatar.
     
    .PARAMETER ClassName
    Classes to assign to the avatar component.
     
    .PARAMETER Variant
    The variant type of the avatar.
     
    .EXAMPLE
    A small avatar using Alon's image.
 
    New-UDAvatar -Image 'https://avatars2.githubusercontent.com/u/34351424?s=460&v=4' -Alt 'alon gvili avatar' -Id 'avatarContent' -Variant small
    #>

    param(
        [Parameter ()][string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter ()][string]$Image,
        [Parameter ()][string]$Alt,
        [Parameter ()][string]$ClassName,
        [Parameter ()][string]$Variant
    )
    End {
        $Avatar = @{
            type     = 'mu-avatar'
            isPlugin = $true
            assetId  = $MUAssetId

            id       = $Id
            image    = $Image
            alt      = $Alt
            variant = $Variant
            className = $ClassName
        }
        $Avatar.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.Avatar") | Out-Null
        $Avatar
    }
}
function New-UDBackdrop {
    <#
    .SYNOPSIS
    Creates an overlay over the current page.
     
    .DESCRIPTION
    Creates an overlay over the current page.
     
    .PARAMETER Id
    The ID of this component
     
    .PARAMETER Color
    The color of the backdrop.
     
    .PARAMETER Children
    Child components to include in the backdrop.
     
    .PARAMETER Open
    Whether the backdrop is open.
     
    .PARAMETER OnClick
    A script block to invoke when the backdrop is clicked.
    #>

    param(
        [Parameter ()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter()]
        [DashboardColor]$Color = '#fff', 
        [Parameter(Mandatory)]
        [Alias("Content")]
        [ScriptBlock]$Children,
        [Parameter()]
        [Switch]$Open,
        [Parameter()]
        [Endpoint]$OnClick
    )

    if ($OnClick)
    {
        $OnClick.Register($Id, $PSCmdlet)
    }

    @{
        type     = 'mu-backdrop'
        isPlugin = $true
        assetId  = $MUAssetId

        id       = $Id
        color    = $Color.HtmlColor 
        children = & $Children
        open     = $Open.IsPresent
        onClick  = $OnClick
    }
}
function New-UDButton {
    <#
    .SYNOPSIS
    Creates a new button.
     
    .DESCRIPTION
    Creates a new button. Buttons are used to allow the user to take action.
     
    .PARAMETER Text
    The text to show within the button.
     
    .PARAMETER Icon
    An icon to show within the button. Use New-UDIcon to create an icon for this parameter.
     
    .PARAMETER Variant
    The variant type for this button.
     
    .PARAMETER IconAlignment
    How to align the icon within the button.
     
    .PARAMETER FullWidth
    Whether the button takes the full width of the it's container.
     
    .PARAMETER OnClick
    The action to take when the button is clicked.
     
    .PARAMETER Size
    The size of the button.
     
    .PARAMETER Style
    Styles to apply to the button.
     
    .PARAMETER Href
    A URL that the user should be redirected to when clicking the button.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
 
    .PARAMETER Color
    The color of the component. It supports those theme colors that make sense for this component.
     
    .EXAMPLE
    Creates a button with the GitHub logo and redirects the user to GitHub when clicked.
 
    $Icon = New-UDIcon -Icon 'github'
    New-UDButton -Text "Submit" -Id "btnClick" -Icon $Icon -OnClick {
        Invoke-UDRedirect https://github.com
    }
 
    .EXAMPLE
    Creates a button with a blue background.
 
    New-UDButton -Text "Submit" -Style @{ backgroundColor = "blue"} -OnClick {
        Invoke-UDRedirect https://github.com
    }
    #>

    param
    (
        [Parameter (Position = 0)]
        [string]$Text,

        [Parameter (Position = 1)]
        [PSTypeName('UniversalDashboard.Icon')]$Icon,

        [Parameter (Position = 2)]
        [ValidateSet("text", "outlined", "contained")]
        [string]$Variant = "contained",

        [Parameter (Position = 3)]
        [ValidateSet("left", "right")]
        [string]$IconAlignment = "left",

        [Parameter (Position = 6)]
        [switch]$FullWidth,

        [Parameter (Position = 7)]
        [Endpoint]$OnClick,

        [Parameter (Position = 8)]
        [ValidateSet("small", "medium", "large")]
        [string]$Size,

        [Parameter (Position = 9)]
        [Hashtable]$Style,

        [Parameter (Position = 10)]
        [string]$Href,

        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter()]
        [ValidateSet('default', 'inherit', 'primary', 'secondary')]
        [string]$Color = "default",

        [Parameter()]
        [Switch]$Disabled

    )

    End {

        if ($OnClick)
        {
            $OnClick.Register($Id, $PSCmdlet)
        }

        @{
            # Mandatory properties to load as plugin.
            type          = 'mu-button'
            isPlugin      = $true
            assetId       = $MUAssetId

            # Command properties.
            id            = $Id
            text          = $Text
            variant       = $Variant
            onClick       = $OnClick
            iconAlignment = $IconAlignment
            disabled      = $Disabled.IsPresent
            icon          = $Icon
            fullWidth     = $FullWidth.IsPresent
            size          = $Size
            href          = $Href
            style         = $Style
            color         = $Color
        }

    }
}
function New-UDCard {
    <#
    .SYNOPSIS
    Creates a new card.
     
    .DESCRIPTION
    Creates a new card. Cards are used to display related content.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER ClassName
    A CSS class to assign to this card.
     
    .PARAMETER ShowToolBar
    Whether to show the toolbar for this card.
     
    .PARAMETER ToolBar
    The toolbar for this card. Use New-UDCardToolbar to create a toolbar.
     
    .PARAMETER Header
    The header for this card. The header typically contains a title for the card. Use New-UDCardHeader to create a header.
     
    .PARAMETER Body
    The body for this card. This is the main content for the card. Use New-UDCardHeader to create a body.
     
    .PARAMETER Expand
    Th expand content for this card. Expand content is show when the user clicks the expansion button. Use New-UDCardExpand to create an expand.
     
    .PARAMETER Footer
    The footer for this card. Footer contents typically contain actions that are relavent to the card. Use New-UDCardFooter to create a footer.
     
    .PARAMETER Style
    Styles to apply to the card.
     
    .PARAMETER Elevation
    The amount of elevation to provide the card. The more elevation, the more it will appear the card is floating off the page.
     
    .PARAMETER Title
    A title for the card.
     
    .PARAMETER TitleAlignment
    The alignment for the title.
     
    .PARAMETER Content
    The content of the card.
     
    .PARAMETER Image
    An image to show in the card.
     
    .EXAMPLE
    Shows a card with a title, image and content.
 
    New-UDCard -Id 'SimpleCard' -Title "Alon" -Content {
        "Content"
    } -Image 'https://avatars2.githubusercontent.com/u/34351424?s=460&v=4'
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter()]
        [string]$ClassName,

        [Parameter()]
        [switch]$ShowToolBar,
     
        [Parameter (ParameterSetName = "Advanced")]
        [PSTypeName('UniversalDashboard.MaterialUI.CardToolbar')]$ToolBar,

        [Parameter(ParameterSetName = "Advanced")]
        [PSTypeName('UniversalDashboard.MaterialUI.CardHeader')]$Header,

        [Parameter(ParameterSetName = "Advanced")]
        [PSTypeName('UniversalDashboard.MaterialUI.CardBody')]$Body,

        [Parameter(ParameterSetName = "Advanced")]
        [PSTypeName('UniversalDashboard.MaterialUI.CardExpand')]$Expand,

        [Parameter(ParameterSetName = "Advanced")]
        [PSTypeName('UniversalDashboard.MaterialUI.CardFooter')]$Footer,

        [Parameter(ParameterSetName = "Advanced")]
        [PSTypeName('UniversalDashboard.MaterialUI.CardMedia')]$Media,

        [Parameter()]
        [Hashtable]$Style,

        [Parameter()]
        [ValidateSet("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24")]
        [int]$Elevation,

        [Parameter(ParameterSetName = "Simple")]
        [Parameter(ParameterSetName = "Text")]
        [String]$Title,
        [Parameter(ParameterSetName = "Simple")]
        [Parameter(ParameterSetName = "Text")]
        [ValidateSet('left', 'center', 'right')]
        [String]$TitleAlignment = 'left',
        [Parameter(ParameterSetName = "Simple")]
        [ScriptBlock]$Content,
        [Parameter(ParameterSetName = "Simple")]
        [string]$Image,
        [Parameter(ParameterSetName = "Text")]
        [string]$Text
    )

    End {

        if ($PSCMdlet.ParameterSetName -eq 'Advanced')
        {
            # Card toolbar checks
            if ($null -ne $ToolBar) {
                if ($ToolBar.psobject.typenames -notcontains "UniversalDashboard.MaterialUI.CardToolbar") {
                    throw "ToolBar must be a UniversalDashboard.MaterialUI.CardToolbar object, please use New-UDCardToolBar command."
                }
            }

            # Card Media checks
            if ($null -ne $Media) {
                if ($Media.psobject.typenames -notcontains "UniversalDashboard.MaterialUI.CardMedia") {
                    throw "Media must be a UniversalDashboard.MaterialUI.CardMedia object, please use New-UDCardMedia command."
                }
            }

            # Card header checks
            if ($null -ne $Header) {
                if ($Header.psobject.typenames -notcontains "UniversalDashboard.MaterialUI.CardHeader") {
                    throw "Header must be a UniversalDashboard.MaterialUI.CardHeader object, please use New-UDCardHeader command."
                }
            }

            # Card Content checks
            if ($null -ne $Content) {
                if ($Content.psobject.typenames -notcontains "UniversalDashboard.MaterialUI.CardBody") {
                    throw "Body must be a UniversalDashboard.MaterialUI.CardBody object, please use New-UDCardBody command."
                }
            }

            # Card Expand checks
            if ($null -ne $Expand) {
                if ($Expand.psobject.typenames -notcontains "UniversalDashboard.MaterialUI.CardExpand") {
                    throw "Expand must be a UniversalDashboard.MaterialUI.CardExpand object, please use New-UDCardExpand command."
                }
            }

            # Card footer checks
            if ($null -ne $Footer) {
                if ($Footer.psobject.typenames -notcontains "UniversalDashboard.MaterialUI.CardFooter") {
                    throw "Footer must be a UniversalDashboard.MaterialUI.CardFooter object, please use New-UDCardFooter command."
                }
            }
            
            $Parts = @{
                    toolbar         = $ToolBar
                    header          = $Header
                    body            = $Body
                    expand          = $Expand
                    footer          = $Footer
            }
            $Content = {$Parts}
        }
        else 
        {
            $Header = New-UDCardHeader -Title $Title

            if ($Image)
            {
                $Media = New-UDCardMedia -Height 120 -Image $Image
            }

            if ($PSCmdlet.ParameterSetName -eq 'Text')
            {
                $Element = New-UDElement -Tag div -Content {
                    $Text -split "`r`n" | ForEach-Object {
                        $_
                        New-UDElement -Tag "br"
                    }
                }
                $Content = { $Element }
            }

            $Body = New-UDCardBody -Content $Content

            $Parts = @{
                toolbar         = $ToolBar
                header          = $Header
                body            = $Body
                expand          = $Expand
                footer          = $Footer
            }
            $Content = {$Parts}
        }

        $Card = @{
            type            = "mu-card"
            isPlugin        = $true
            assetId         = $MUAssetId
            id              = $Id
            className       = $ClassName
            showToolBar     = $ShowToolBar.IsPresent
            media           = $Media
            toolbar         = $ToolBar
            header          = $Header
            body            = $Body
            expand          = $Expand
            footer          = $Footer
            style           = $Style
            elevation       = $Elevation

        }

        $Card.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.Card") | Out-Null
        $Card
    }
}


function New-UDCardToolbar {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter()]
        [string]$ClassName,

        [Parameter(Mandatory)]
        [scriptblock]$Content,

        [Parameter ()]
        [PSTypeName("UniversalDashboard.MaterialUI.Icon")]$Icon,
        
        [Parameter()]
        [object]$Title,


        [Parameter()]
        [Switch]$ShowButtons,

        [Parameter()]
        [Hashtable]$Style

    )
    End {

        $CardToolbar = @{
            type        = "mu-card-toolbar"
            isPlugin    = $true
            assetId     = $MUAssetId
            id          = $Id
            className   = $ClassName
            content     = & $Content
            title       = $Title
            style       = $Style
            icon        = $Icon
            showButtons = $ShowButtons.IsPresent
            # PSTypeName = "UniversalDashboard.MaterialUI.CardToolbar"
        }
        $CardToolbar.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.CardToolbar") | Out-Null
        $CardToolbar
    }
}
function New-UDCardHeader {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter()]
        [string]$Title

    )
    End {
        $Header = @{
            type            = "mu-card-header"
            isPlugin        = $true
            assetId         = $MUAssetId
            id              = $Id
            title           = $Title
        }
        $Header.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.CardHeader") | Out-Null
        $Header
    }
}


function New-UDCardBody {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter()]
        [string]$ClassName,

        [Parameter()]
        [scriptblock]$Content,

        [Parameter()]
        [Hashtable]$Style
    )
    End {

        $cContent = @{
            type            = "mu-card-body"
            isPlugin        = $true
            assetId         = $MUAssetId
            id              = $Id
            className       = $ClassName
            content         = New-UDErrorBoundary -Content $Content
            style           = $Style
            # PSTypeName = "UniversalDashboard.MaterialUI.CardContent"

        }
        $cContent.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.CardBody") | Out-Null
        $cContent
    }
}


function New-UDCardExpand {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter()]
        [string]$ClassName,

        [Parameter()]
        [scriptblock]$Content,

        [Parameter()]
        [Hashtable]$Style
    )
    End {
        $Expand = @{
            type            = "mu-card-expand"
            isPlugin        = $true
            assetId         = $MUAssetId
            id              = $Id
            className       = $ClassName
            content         = New-UDErrorBoundary -Content $Content
            style           = $Style
            isEndpoint      = $isEndPoint.IsPresent
            refreshInterval = $RefreshInterval
            autoRefresh     = $AutoRefresh.IsPresent
            # PSTypeName = "UniversalDashboard.MaterialUI.CardExpand"
        }
        $Expand.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.CardExpand") | Out-Null
        $Expand
    }
}


function New-UDCardFooter {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter()]
        [string]$ClassName,

        [Parameter()]
        [scriptblock]$Content,

        [Parameter()]
        [Hashtable]$Style
    )
    End {
        $Footer = @{
            type            = "mu-card-footer"
            isPlugin        = $true
            assetId         = $MUAssetId
            id              = $Id
            className       = $ClassName
            content         = New-UDErrorBoundary -Content $Content
            style           = $Style
            isEndpoint      = $isEndPoint.IsPresent
            refreshInterval = $RefreshInterval
            autoRefresh     = $AutoRefresh.IsPresent
            # PSTypeName = "UniversalDashboard.MaterialUI.CardFooter"
        }
        $Footer.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.CardFooter") | Out-Null
        $Footer
    }
}
function New-UDCardMedia {
    [CmdletBinding()]
    [OutputType([Hashtable])]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter()]
        [ValidateSet("img", "video", "audio")]       
        [string]$Component = "img",

        [Parameter()]
        [string]$Alt,

        [Parameter()]
        [string]$Height,

        [Parameter (ParameterSetName = 'image')]
        [string]$Image,

        [Parameter()]
        [string]$Title,

        [Parameter(ParameterSetName = 'media')]
        [string]$Source

    )
    End {
        $CardMedia = @{
            type      = "mu-card-media"
            isPlugin  = $true
            assetId   = $MUAssetId
            id        = $Id
            component = $Component
            alt       = $Alt
            height    = $Height
            image     = $Image
            title     = $Title
            source    = $Source
            # PSTypeName = "UniversalDashboard.MaterialUI.CardMedia"
        }
        $CardMedia.PSTypeNames.Insert(0, "UniversalDashboard.MaterialUI.CardMedia") | Out-Null
        $CardMedia
    }
}
function New-UDCheckBox {
    <#
    .SYNOPSIS
    Creates a checkbox.
 
    .DESCRIPTION
    Creates a checkbox. Checkboxes can be used in forms or by themselves.
 
    .PARAMETER Label
    The label to show next to the checkbox.
 
    .PARAMETER Icon
    The icon to show instead of the default icon.
 
    .PARAMETER CheckedIcon
    The icon to show instead of the default checked icon.
 
    .PARAMETER OnChange
    Called when the value of the checkbox changes. The $EventData variable will have the current value of the checkbox.
 
    .PARAMETER Style
    A hashtable of styles to apply to the checkbox.
 
    .PARAMETER Disabled
    Whether the checkbox is disabled.
 
    .PARAMETER Checked
    Whether the checkbox is checked.
 
    .PARAMETER ClassName
    A CSS class to assign to the checkbox.
 
    .PARAMETER LabelPlacement
    Where to place the label.
 
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
 
    .EXAMPLE
 
    Creates a checkbox with a custom icon and style.
 
    $Icon = New-UDIcon -Icon angry -Size lg -Id 'demo-checkbox-icon' -Regular
    $CheckedIcon = New-UDIcon -Icon angry -Size lg -Id 'demo-checkbox-icon-checked'
    New-UDCheckBox -Id 'btnCustomIcon' -Icon $Icon -CheckedIcon $CheckedIcon -OnChange {} -Style @{color = '#2196f3'}
 
    #>

    param
    (
        [Parameter (Position = 0)]
        [string]$Label,

        [Parameter (Position = 1)]
        [PSTypeName('UniversalDashboard.Icon')]$Icon,

        [Parameter (Position = 2)]
        [PSTypeName('UniversalDashboard.Icon')]$CheckedIcon,

        [Parameter (Position = 3)]
        [Endpoint]$OnChange,

        [Parameter (Position = 4)]
        [Hashtable]$Style,

        [Parameter (Position = 5)]
        [switch]$Disabled,

        [Parameter (Position = 6)]
        [bool]$Checked,

        [Parameter (Position = 7)]
        [string]$ClassName,

        [Parameter (Position = 7)]
        [ValidateSet("top","start","bottom","end")]
        [string]$LabelPlacement,

        [Parameter(Position = 8)]
        [string]$Id = ([Guid]::NewGuid()).ToString()

    )

    End {

        if ($OnChange)
        {
            $OnChange.Register($Id + "onChange", $PSCmdlet)
        }

        @{
            # Mandatory properties to load as plugin.
            type        = 'mu-checkbox'
            isPlugin    = $true
            assetId     = $MUAssetId

            # Command properties.
            id          = $Id
            className   = $ClassName
            checked     = $Checked
            onChange    = $OnChange
            icon        = $Icon
            checkedIcon = $CheckedIcon
            disabled    = $Disabled.IsPresent
            style       = $Style
            label       = $Label
            labelPlacement = $LabelPlacement
        }
    }
}
function New-UDChip {
    <#
    .SYNOPSIS
    Creates a new chip.
     
    .DESCRIPTION
    Creates a new chip. Chips can be used to display tags or little bits of data.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Label
    The label for the chip.
     
    .PARAMETER OnDelete
    A script block to call when the chip is deleted.
     
    .PARAMETER OnClick
    A script block to call when the chip is clicked.
     
    .PARAMETER Icon
    An icon to show within the chip.
     
    .PARAMETER Style
    CSS styles to apply to the chip.
     
    .PARAMETER Variant
    The theme variant to apply to the chip.
     
    .PARAMETER Avatar
    An avatar to show within the chip.
     
    .PARAMETER AvatarType
    The type of avatar to show in the chip.
     
    .EXAMPLE
    Creates a clickable chip with a custom style and icon.
 
    $Icon = New-UDIcon -Icon 'user' -Size sm -Style @{color = '#fff'}
    New-UDChip -Label "Demo User" -Id "chipIcon" -Icon $Icon -OnClick {Show-UDToast -Message 'test'} -Clickable -Style @{backgroundColor = '#00838f'}
 
    #>

    [CmdletBinding(DefaultParameterSetName = 'Icon')]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter(Position = 0)]
        [string]$Label,

        [Parameter(Position = 8)]
        [object]$OnDelete,

        [Parameter(Position = 7)]
        [object]$OnClick,

        [Parameter (Position = 1, ParameterSetName = "Icon")]
        [PSTypeName('UniversalDashboard.Icon')]$Icon,

        [Parameter(Position = 2)]
        [Hashtable]$Style,

        [Parameter(Position = 3)]
        [ValidateSet("outlined","default")]
        [string]$Variant = "default",

        [Parameter(Position = 4, ParameterSetName = "Avatar")]
        [string]$Avatar,

        [Parameter(Position = 5, ParameterSetName = "Avatar" )]
        [ValidateSet("letter","image")]
        [string]$AvatarType
    )

    End {

        if ($null -ne $OnClick) {
            if ($OnClick -is [scriptblock]) {
                $OnClick = New-UDEndpoint -Endpoint $OnClick -Id ($Id + "onClick")
            }
            elseif ($OnClick -isnot [UniversalDashboard.Models.Endpoint]) {
                throw "OnClick must be a script block or UDEndpoint"
            }
        }

        $Delete = $False
        if ($null -ne $OnDelete) {
            $Delete = $true
            if ($OnDelete -is [scriptblock]) {
                $OnDelete = New-UDEndpoint -Endpoint $OnDelete -Id ($Id + "onDelete")
            }
            elseif ($OnDelete -isnot [UniversalDashboard.Models.Endpoint]) {
                throw "OnDelete must be a script block or UDEndpoint"
            }
        }

        @{
            #This needs to match what is in the register function call of chips.jsx
            type = "mu-chip"
            #Eventually everything will be a plugin so we wont need this.
            isPlugin = $true
            #This was set in the UniversalDashboard.MaterialUI.psm1 file
            assetId = $MUAssetId

            id = $Id
            label = $Label
            icon = $Icon 
            style = $Style 
            variant = $Variant 
            clickable = $null -ne $OnClick
            onClick = $OnClick
            delete  = $null -ne $OnDelete
            avatar = $Avatar
            avatarType = $AvatarType
        }
    }
}
function New-UDContainer 
{
    <#
    .SYNOPSIS
    Creates a Material UI container.
     
    .DESCRIPTION
    Creates a Material UI container. Containers pad the left and right side of the contained content to center it on larger resolution screens.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Children
    The child items to include within the container.
     
    .EXAMPLE
    Creates a container with some text.
 
    New-UDContainer -Content {
        New-UDTypography -Text 'Nice'
    }
    #>

    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Alias("Content")]
        [Parameter(Mandatory, Position = 0)]
        [ScriptBlock]$Children        
    )

    Process {
        try {
            $c = New-UDErrorBoundary -Content $Children    
        }
        catch {
            $c = New-UDError -Message $_
        }
        

        @{
            isPlugin = $true 
            id = $id 
            assetId = $MUAssetId
            type = "mu-container"

            children = $c
        }
    }
}
function New-UDDashboard
{
    <#
    .SYNOPSIS
    Creates a new dashboard.
     
    .DESCRIPTION
    Creates a new dashboard. This component is the root element for all dashboards. You can define content, pages, themes and more.
     
    .PARAMETER Title
    The title of the dashboard.
     
    .PARAMETER Content
    The content for this dashboard. When using content, it creates a dashboard with a single page.
     
    .PARAMETER Pages
    Pages for this dashboard. Use New-UDPage to define a page and pass an array of pages to this parameter.
     
    .PARAMETER Theme
    The theme for this dashboard. You can define a theme with New-UDTheme.
     
    .PARAMETER Scripts
    JavaScript files to run when this dashboard is loaded. These JavaScript files can be absolute and hosted in a third-party CDN or you can host them yourself with New-PSUPublishedFolder.
     
    .PARAMETER Stylesheets
    CSS files to run when this dashboard is loaded. These CSS files can be absolute and hosted in a third-party CDN or you can host them yourself with New-PSUPublishedFolder.
     
    .PARAMETER Logo
    A logo to display in the navigation bar. You can use New-PSUPublishedFolder to host this logo file.
     
    .PARAMETER DefaultTheme
    The default theme to show when the page is loaded. The default is to use the light theme.
     
    .EXAMPLE
    Creates a new dashboard with a single page.
 
    New-UDDashboard -Title 'My Dashboard' -Content {
        New-UDTypography -Text 'Hello, world!'
    }
 
    .EXAMPLE
    Creates a new dashboard with multiple pages.
 
    $Pages = @(
        New-UDPage -Name 'HomePage' -Content {
            New-UDTypography -Text 'Home Page'
        }
        New-UDPage -Name 'Page2' -Content {
            New-UDTypography -Text 'Page2'
        }
    )
 
    New-UDDashboard -Title 'My Dashboard' -Pages $Pages
     
    #>

    param(
        [Parameter()]
        [string]$Title = "PowerShell Universal Dashboard",
        [Parameter(ParameterSetName = "Content", Mandatory)]
        [Endpoint]$Content,
        [Parameter(ParameterSetName = "Pages", Mandatory)]
        [Hashtable[]]$Pages = @(),
        [Parameter()]
        [Hashtable]$Theme = @{},
        [Parameter()]
        [string[]]$Scripts = @(),
        [Parameter()]
        [string[]]$Stylesheets = @(),
        [Parameter()]
        [string]$Logo,
        [Parameter()]
        [ValidateSet("Light", "Dark")]
        [string]$DefaultTheme = "Light",
        [Parameter()]
        [switch]$DisableThemeToggle
    )    

    if ($PSCmdlet.ParameterSetName -eq 'Content')
    {
        $Pages += New-UDPage -Name 'Home' -Content $Content -Logo $Logo
    }

    $Cache:Pages = $Pages

    @{
        title = $Title 
        pages = $Pages
        theme = $Theme
        scripts = $Scripts
        stylesheets = $Stylesheets
        defaultTheme = $DefaultTheme.ToLower()
        disableThemeToggle = $DisableThemeToggle.IsPresent
    }
}

function New-UDDatePicker {
    <#
    .SYNOPSIS
    Creates a new date picker.
     
    .DESCRIPTION
    Creates a new date picker. Date pickers can be used stand alone or within New-UDForm.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Label
    The label to show next to the date picker.
     
    .PARAMETER Variant
    The theme variant to apply to the date picker.
     
    .PARAMETER DisableToolbar
    Disables the date picker toolbar.
     
    .PARAMETER OnChange
    A script block to call with the selected date. The $EventData variable will be the date selected.
     
    .PARAMETER Format
    The format of the date when it is selected.
     
    .PARAMETER Value
    The current value of the date picker.
 
    .PARAMETER Locale
    Change the language of the date picker.
     
    .EXAMPLE
    Creates a new date picker with the default date value.
 
    New-UDDatePicker -Id 'dateDateDefault' -Value '1-2-2020'
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid().ToString(),
        [Parameter()]
        [string]$Label,
        [Parameter()]
        [ValidateSet('inline', 'dialog', 'static')]
        [string]$Variant = 'inline',
        [Parameter()]
        [Switch]$DisableToolbar,
        [Parameter()]
        [Endpoint]$OnChange, 
        [Parameter()]
        [string]$Format = 'MM/dd/yyyy',
        [Parameter()]
        [DateTime]$Value = (Get-Date).Date,
        [Parameter()]
        [ValidateSet("en", "de", 'ru', 'fr')]
        [string]$Locale = "en"
    )

    if ($OnChange) {
        $OnChange.Register($Id, $PSCmdlet)
    }

    @{
        id       = $Id 
        type     = 'mu-datepicker'
        asset    = $MUAssetId
        isPlugin = $true 

        onChange = $OnChange 
        variant  = $Variant 
        format   = $Format 
        value    = $Value
        label    = $Label
        locale   = $Locale.ToLower()
    }
}
function New-UDDateTime {
    <#
    .SYNOPSIS
    This date and time component is used for formatting dates and times using the user's browser settings.
     
    .DESCRIPTION
    This date and time component is used for formatting dates and times using the user's browser settings. Since Universal Dashboard PowerShell scripts run within the server, the date and time settings of the user's system are not taken into account. This component formats date and time within the client's browser to take into account their locale and time zone.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER InputObject
    The date and time object to format.
     
    .PARAMETER Format
    The format of the date and time. This component uses Day.JS. You can learn more about formatting options on their documentation: https://day.js.org/docs/en/display/format
     
    .PARAMETER LocalizedFormat
    The localized format for the date and time. Use this format if you would like to take the user's browser locale and time zone settings into account.
     
    .EXAMPLE
    Formats a date and time using the format 'DD/MM/YYYY'
 
    New-UDDateTime -InputObject (Get-Date) -Format 'DD/MM/YYYY'
    #>

    [CmdletBinding(DefaultParameterSetName = "LocalizedFormat")]
    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter(Mandatory, Position = 0)]
        [string]$InputObject,
        [Parameter(ParameterSetName = "Format")]
        [string]$Format = "DD/MM/YYYY",
        [Parameter(ParameterSetName = "LocalizedFormat")]
        [ValidateSet("LT", "LTS", "L", "LL", "LLL", "LLLL", "l", "ll", "lll", "llll")]
        [string]$LocalizedFormat = "LLL"
    )

    $f = $Format 
    if ($PSCmdlet.ParameterSetName -eq 'LocalizedFormat')
    {
        $f = $LocalizedFormat
    }

    @{
        type = 'mu-datetime'
        id = $Id 
        isPlugin = $true 
        assetId = $MUAssetId

        inputObject = $InputObject 
        format = $f
    }
}
function  Debug-PSUDashboard {
    <#
    .SYNOPSIS
    Provides a utility function for debugging scripts running PowerShell Universal Dashboard.
     
    .DESCRIPTION
    Provides a utility function for debugging scripts running PowerShell Universal Dashboard. This cmdlet integrates with the VS Code PowerShell Universal extension to automatically connect the debugger to endpoints running in UD.
     
    .EXAMPLE
    Creates an element that invokes the Debug-PSUDashboard cmdlet.
 
    New-UDElement -Tag div -Endpoint {
        Debug-PSUDashboard
    }
    #>

    [CmdletBinding()]
    param()

    $DebugPreference = 'continue'

    $Runspace = ([runspace]::DefaultRunspace).id

    Show-UDModal -Header {
        New-UDTypography -Text 'Debug Dashboard' -Variant h4
    } -Content {
        Write-Debug "IN DEBUG MODE: Enter-PSHostProcess -Id $PID then Debug-Runspace -Id $Runspace"
        New-UDTypography -Text "You can run the following PowerShell commands in any PowerShell host to debug this dashboard."
        New-UDElement -Tag 'pre' -Content {
            "Enter-PSHostProcess -Id $PID`r`nDebug-Runspace -Id $Runspace"
        }
    } -Footer {
        New-UDLink -Children {
            New-UDButton -Text 'Debug with VS Code' 
        } -Url "vscode://ironmansoftware.powershell-universal/debug?PID=$PID&RS=$Runspace" 
        New-UDLink -Children {
            New-UDButton -Text 'Debug with VS Code Insiders' 
        } -Url "vscode-insiders://ironmansoftware.powershell-universal/debug?PID=$PID&RS=$Runspace" 
        New-UDButton -Text 'Close' -OnClick { Hide-UDModal }
    }

    Wait-Debugger 
}
function New-UDDrawer 
{
    <#
    .SYNOPSIS
    Creates a new drawer.
     
    .DESCRIPTION
    Creates a new drawer. A drawer is a navigational component that is typically used for navigating between pages. It can be used with New-UDAppBar to provide a custom nav bar.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Children
    Navgiation controls to show within the drawer. Use New-UDList and New-UDListItem to generate links within the drawer.
     
    .EXAMPLE
    Creates a custom navbar using New-UDDrawer
 
    $Drawer = New-UDDrawer -Id 'drawer' -Children {
        New-UDList -Content {
            New-UDListItem -Id 'lstHome' -Label 'Home' -OnClick {
                Set-TestData 'Home'
                } -Content {
                    New-UDListItem -Id 'lstNested' -Label 'Nested' -OnClick {
                    Set-TestData 'Nested'
                    }
                }
        }
    }
 
    New-UDElement -Tag 'main' -Content {
        New-UDAppBar -Children { New-UDTypography -Text 'Hello' -Paragraph } -Position relative -Drawer $Drawer
    }
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [Alias("Content")]
        [ScriptBlock]$Children,
        [ValidateSet("persistent", "permanent", "temporary")]
        [string]$Variant = "temporary",
        [ValidateSet("left", "right", "top", "bottom")]
        [string]$Anchor = "left"
    )

    try {
        $c = & $Children
    } catch {
        $c = New-UDError -Message $_
    }

    @{
        type = 'mu-drawer'
        id = $Id 
        isPlugin = $true 
        assetId = $MUAssetId
        children = $c
        variant = $Variant.ToLower()
        anchor = $Anchor.ToLower()
    }
}
function New-UDDynamic
{
    <#
    .SYNOPSIS
    Defines a new dynamic region in a dashboard.
     
    .DESCRIPTION
    Defines a new dynamic region in a dashboard. Dynamic regions are used for loading data when the page is loaded or for loading data dynamically through user interaction or auto-reloading.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER ArgumentList
    Arguments to pass to this dynamic endpoint.
     
    .PARAMETER Content
    The content of this dynamic region.
     
    .PARAMETER AutoRefresh
    Whether this dynamic region should refresh on an interval.
     
    .PARAMETER AutoRefreshInterval
    The amount of seconds between refreshes for this dynamic region.
     
    .PARAMETER LoadingComponent
    A component to display while this dynamic region is loading.
     
    .EXAMPLE
    A simple dynamic region that executes when the user loads the page.
 
    New-UDDynamic -Content {
        New-UDTypography -Text (Get-Date)
    }
 
    .EXAMPLE
    A dynamic region that refreshes every 3 seconds
 
    New-UDDynamic -Content {
        New-UDTypography -Text (Get-Date)
    } -AutoRefresh -AutoRefreshInterval 3
 
    .EXAMPLE
    A dynamic region that is updated when a button is clicked.
 
    New-UDDynamic -Content {
        New-UDTypography -Text (Get-Date)
    } -Id 'dynamic'
 
    New-UDButton -Text 'Refresh' -OnClick {
        Sync-UDElement -Id 'dynamic'
    }
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [object[]]$ArgumentList,
        [Parameter(Position = 0, Mandatory)]
        [Endpoint]$Content,
        [Parameter()]
        [Switch]$AutoRefresh,
        [Parameter()]
        [int]$AutoRefreshInterval = 10,
        [Parameter()]
        [ScriptBlock]$LoadingComponent
    )

    $Content.ArgumentList = $ArgumentList
    $Content.Register($Id, $PSCmdlet)

    $LC = $null
    if ($LoadingComponent)
    {
        $LC = New-UDErrorBoundary -Content $LoadingComponent
    }

    @{
        id = $Id 
        autoRefresh = $AutoRefresh.IsPresent
        autoRefreshInterval = $AutoRefreshInterval
        type = "dynamic"
        isPlugin = $true 
        loadingComponent = $LC
    }
}
function New-UDError {
    <#
    .SYNOPSIS
    Creates a new error card.
     
    .DESCRIPTION
    Creates a new error card.
     
    .PARAMETER Message
    The message to display.
     
    .PARAMETER Title
    A title for the card.
     
    .EXAMPLE
    Displays the error 'Invalid data' on the page.
 
    New-UDError -Message 'Invalid data'
    #>

    param(
        [Parameter(Mandatory)]
        [string]$Message,
        [Parameter()]
        [string]$Title
    )

    @{
        type = "error"
        isPlugin = $true 
        assetId = $AssetId 

        errorRecords = @(@{message= $Message})
        title = $Title
    }
}
function New-UDErrorBoundary {
    <#
    .SYNOPSIS
    Defines a new error boundary around a section of code.
     
    .DESCRIPTION
    Defines a new error boundary around a section of code. Error boundaries are used to trap errors and display them on the page.
     
    .PARAMETER Content
    The content to trap in an error boundary.
     
    .EXAMPLE
    Defines an error boundary that traps the exception that is thrown and displays it on the page.
 
    New-UDErrorBoundary -Content {
        throw 'This is an error'
    }
    #>

    param(
        [Parameter(Mandatory)]
        [ScriptBlock]$Content
    )

    try 
    {
        & $Content 
    }
    catch
    {
        New-UDError -Message $_
    }
}
function Invoke-UDEvent {
    param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            Position = 0
        )]
        [String]$Id,
        [Parameter(
            Mandatory = $true,
            Position = 1,
            ParameterSetName = "onClick"
        )]
        [ValidateSet("onClick")]
        [string]$event
    )

    Begin {

    }

    Process {
        if ($PSCmdlet.ParameterSetName -eq "onClick") {
            Invoke-UDJavaScript -javaScript "
                document.getElementById('$Id').click();
            "

        }
    }

    End {

    }
}

function New-UDExpansionPanelGroup {
    <#
    .SYNOPSIS
    Creates a group of expansion panels.
     
    .DESCRIPTION
    Creates a group of expansion panels. Use New-UDExpansionPanel to create children for a group.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Children
    Expansion panels to include in this group.
     
    .PARAMETER Popout
    Creates a popout style expansion panel group.
     
    .PARAMETER Type
    The type of expansion panel group.
     
    .EXAMPLE
     
    Creates an expansion panel group.
 
    New-UDExpansionPanelGroup -Items {
        New-UDExpansionPanel -Title "Hello" -Id 'expTitle' -Content {}
 
        New-UDExpansionPanel -Title "Hello" -Id 'expContent' -Content {
            New-UDElement -Tag 'div' -id 'expContentDiv' -Content { "Hello" }
        }
 
        New-UDExpansionPanel -Title "Hello" -Id 'expEndpoint' -Endpoint {
            New-UDElement -Tag 'div' -id 'expEndpointDiv' -Content { "Hello" }
        }
    }
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter(Mandatory = $true, Position = 0)]
        [Alias("Content")]
        [ScriptBlock]$Children,
        [Parameter()]
        [Switch]$Popout,
        [Parameter()]
        [ValidateSet("Expandable", "Accordion")]
        [String]$Type = 'Expandable'
    )
    
    $c = New-UDErrorBoundary -Content $Children

    @{
        type = 'mu-expansion-panel-group'
        isPlugin = $true
        assetId = $AssetId

        id = $id
        children = $c
        popout = $Popout.IsPresent
        accordion = $Type -eq 'Accordion'
    }
}

function New-UDExpansionPanel {
    <#
    .SYNOPSIS
    Creates an expansion panel.
     
    .DESCRIPTION
    Creates an expansion panel. An expansion panel can hide content that isn't necessary to view when a page is loaded.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Title
    The title show within the header of the expansion panel.
     
    .PARAMETER Icon
    An icon to show within the header of the expansion panel.
     
    .PARAMETER Children
    Children components to put within the expansion panel.
     
    .PARAMETER Active
    Whether the expansion panel is currently active (open).
     
    .EXAMPLE
 
    Creates an expansion panel with some content.
     
    New-UDExpansionPanel -Title "Hello" -Id 'expContent' -Content {
        New-UDElement -Tag 'div' -id 'expContentDiv' -Content { "Hello" }
    }
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [String]$Title,
        [Parameter()]
        [UniversalDashboard.Models.FontAwesomeIcons]$Icon,
        [Parameter()]
        [Alias("Content")]
        [ScriptBlock]$Children,
        [Parameter()]
        [Switch]$Active
    )

    $iconName = $null
    if ($PSBoundParameters.ContainsKey("Icon")) {
        $iconName = [UniversalDashboard.Models.FontAwesomeIconsExtensions]::GetIconName($Icon)
    }

    @{
        id = $Id 
        title = $Title 
        children = & $Children
        active = $Active.IsPresent
        icon = $iconName
    }
}
function New-UDFloatingActionButton {
    <#
    .SYNOPSIS
    Creates a new floating action button.
     
    .DESCRIPTION
    Creates a new floating action button. Floating action buttons are good for actions that make sense for an entire page. They can be pinned to the bottom of a page.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Icon
    The icon to put within the floating action button.
     
    .PARAMETER Size
    The size of the button.
     
    .PARAMETER OnClick
    A script block to execute when the floating action button is clicked.
     
    .EXAMPLE
    Creates a floating action button with a user icon and shows a toast when clicked.
 
    New-UDFloatingActionButton -Icon user -OnClick {
        Show-UDToast -Message 'Hello'
    }
 
    #>

    param(
        [Parameter()]
        [string] $Id = ([Guid]::NewGuid()),
        [Parameter()]
        [PSTypeName('UniversalDashboard.Icon')]$Icon,
        [Parameter()]
        [ValidateSet("small", "medium", "large")]
        $Size = "large",
        [Parameter()]
        [object]$OnClick
    )

    if ($null -ne $OnClick) {
        if ($OnClick -is [scriptblock]) {
            $OnClick = New-UDEndpoint -Endpoint $OnClick -Id $Id
        }
        elseif ($OnClick -isnot [UniversalDashboard.Models.Endpoint]) {
            throw "OnClick must be a script block or UDEndpoint"
        }
    }

    @{
        type = "mu-fab"
        assetId = $AssetId
        isPlugin = $true 

        id = $id
        size = $Size.tolower()
        backgroundColor = $ButtonColor.HtmlColor
        color = $IconColor.HtmlColor
        icon = $icon
        onClick = $OnClick.Name
    }
}
function New-UDForm 
{
    <#
    .SYNOPSIS
    Creates a new form.
     
    .DESCRIPTION
    Creates a new form. Forms can contain any set of input controls. Each of the controls will report its value back up to the form when the submit button is clicked.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Children
    Controls that make up this form. This can be any combination of controls. Input controls will report their state to the form.
     
    .PARAMETER OnSubmit
    A script block that is execute when the form is submitted. You can return controls from this script block and the form will be replaced by the script block. The $EventData variable will contain a hashtable of all the input fields and their values.
     
    .PARAMETER OnValidate
    A script block that validates the form. Return the result of a call to New-UDFormValidationResult.
 
    .PARAMETER OnProcessing
    A script block that is called when the form begins processing. The return value of this script block should be a component that displays a loading dialog. The script block will receive the current form data.
 
    .PARAMETER OnCancel
    A script block that is called when a form is cancelled. Useful for closing forms in modals.
 
    .PARAMETER SubmitText
    Text to show within the submit button
 
    .PARAMETER CancelText
    Text to show within the cancel button.
 
    .PARAMETER ButtonVariant
    Type of button to display.
 
     
    .EXAMPLE
    Creates a form that contains many input controls and displays the $eventdata hashtable as a toast.
 
    New-UDForm -Id 'defaultForm' -Content {
        New-UDTextbox -Id 'txtNameDefault' -Value 'Name'
        New-UDTextbox -Id 'txtLastNameDefault' -Value 'LastName'
        New-UDCheckbox -Id 'chkYesDefault' -Label YesOrNo -Checked $true
 
        New-UDSelect -Label '1-3' -Id 'selectDefault' -Option {
            New-UDSelectOption -Name "OneDefault" -Value 1
            New-UDSelectOption -Name "TwoDefault" -Value 2
            New-UDSelectOption -Name "ThreeDefault" -Value 3
        } -DefaultValue '1'
 
        New-UDSwitch -Id 'switchYesDefault' -Checked $true
 
        New-UDDatePicker -Id 'dateDateDefault' -Value '1-2-2020'
 
        New-UDTimePicker -Id 'timePickerDefault' -Value '10:30 AM'
 
        New-UDRadioGroup -Label 'group' -Id 'simpleRadioDefault' -Children {
            New-UDRadio -Value 'Adam' -Label 'Adam' -Id 'adamDefault'
            New-UDRadio -Value 'Alon' -Label 'Alon' -Id 'alonDefault'
            New-UDRadio -Value 'Lee' -Label 'Lee' -Id 'leeDefault'
        } -Value 'Adam'
    } -OnSubmit {
        Show-UDToast -Message $Body
    }
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid().ToString(),
        [Parameter(Mandatory)]
        [ALias("Content")]
        [ScriptBlock]$Children,
        [Parameter(Mandatory)]
        [Endpoint]$OnSubmit,
        [Parameter()]
        [Endpoint]$OnValidate,
        [Parameter()]
        [ScriptBlock]$OnProcessing,
        [Parameter()]
        [Endpoint]$OnCancel,
        [Parameter()]
        [string]$SubmitText = 'Submit',
        [Parameter()]
        [string]$CancelText = 'Cancel',
        [ValidateSet('text', 'contained', 'outlined')]
        [string]$ButtonVariant = 'text'
    )

    if ($null -ne $OnValidate) {
       $OnValidate.Register($id + "validate", $PSCmdlet) 
    }

    $LoadingComponent = $null 
    if ($null -ne $OnProcessing) {
        $LoadingComponent = New-UDErrorBoundary -Content $OnProcessing
     }

     if ($OnCancel)
     {
         $OnCancel.Register($id + 'cancel', $PSCmdlet)
     }

    $OnSubmit.Register($id, $PSCmdlet)

    try {
        $c = New-UDErrorBoundary -Content $Children 
    }
    catch {
        $c = New-UDError -Message $_
    }

    @{
        id = $Id 
        assetId = $MUAssetId 
        isPlugin = $true 
        type = "mu-form"

        onSubmit = $OnSubmit 
        onValidate = $OnValidate
        loadingComponent = $LoadingComponent
        children = $c
        onCancel = $OnCancel
        cancelText = $CancelText
        submitText = $SubmitText
        buttonVariant = $ButtonVariant 
    }
}

New-Alias -Name 'New-UDFormValidationResult' -Value 'New-UDValidationResult'

function New-UDValidationResult {
    <#
    .SYNOPSIS
    Creates a new validation result.
     
    .DESCRIPTION
    Creates a new validation result. This cmdlet should return its value from the OnValidate script block parameter on New-UDForm or New-UDStepper.
     
    .PARAMETER Valid
    Whether the status is considered valid.
     
    .PARAMETER ValidationError
    An error to display if the is not valid.
 
    .PARAMETER Context
    Update the context based on validation. This is only used for New-UDStepper.
 
    .PARAMETER DisablePrevious
    Disables the previous button. This is only used for New-UDStepper.
 
    .PARAMETER DisablePrevious
    Sets the active step. This is only used for New-UDStepper.
    #>

    param(
        [Parameter()]
        [Switch]$Valid,
        [Parameter()]
        [string]$ValidationError = "Form is invalid.",
        [Parameter()]
        [HashTable]$Context,
        [Parameter()]
        [Switch]$DisablePrevious,
        [Parameter()]
        [int]$ActiveStep = -1
    )

    @{
        valid = $Valid.IsPresent
        validationError = $ValidationError
        context = $Context 
        disablePrevious = $DisablePrevious.IsPresent
        activeStep = $ActiveStep
    }
}

function Test-UDForm {
    <#
    .SYNOPSIS
    Invokes validation for a form.
     
    .DESCRIPTION
    Invokes validation for a form.
     
    .PARAMETER Id
    Id of the form to invoke validation for.
     
    .EXAMPLE
    New-UDButton -Text 'Validate' -OnClick {
        Test-UDForm -Id 'myForm'
    }
    #>

    param(
        [Parameter(Mandatory)]
        [string]$Id
    )

    $DashboardHub.SendWebSocketMessage($ConnectionId, "testForm", $Id)
}
function New-UDGrid {
    <#
    .SYNOPSIS
    Creates a grid to layout components.
     
    .DESCRIPTION
    Creates a grid to layout components. The grid is a 24-point grid system that can adapt based on the size of the screen that is showing the controls.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER ExtraSmallSize
    The size (1-24) for extra small devices.
     
    .PARAMETER SmallSize
    The size (1-24) for small devices.
     
    .PARAMETER MediumSize
    The size (1-24) for medium devices.
     
    .PARAMETER LargeSize
    The size (1-24) for large devices.
     
    .PARAMETER ExtraLargeSize
    The size (1-24) for extra large devices.
     
    .PARAMETER Container
    Whether this is a container. A container can be best described as a row.
     
    .PARAMETER Spacing
    Spacing between the items.
     
    .PARAMETER Item
    Whether this is an item in a container.
     
    .PARAMETER Children
    Components included in this grid item.
     
    .EXAMPLE
    An example
     
    .NOTES
    General notes
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid().ToString(),
        [Alias("Size")]
        [ValidateRange(1, 12)]
        [Parameter(ParameterSetName = "Item")]
        [int]$ExtraSmallSize,
        [Parameter(ParameterSetName = "Item")]
        [ValidateRange(1, 12)]
        [int]$SmallSize,
        [Parameter(ParameterSetName = "Item")]
        [ValidateRange(1, 12)]
        [int]$MediumSize,
        [Parameter(ParameterSetName = "Item")]
        [ValidateRange(1, 12)]
        [int]$LargeSize,
        [Parameter(ParameterSetName = "Item")]
        [ValidateRange(1, 12)]
        [int]$ExtraLargeSize,
        [Parameter(ParameterSetName = "Container")]
        [Switch]$Container,
        [Parameter(ParameterSetName = "Container")]
        [int]$Spacing,
        [Parameter(ParameterSetName = "Item")]
        [Switch]$Item,
        [Parameter()]
        [Alias("Content")]
        [ScriptBlock]$Children
    )

    End {
        $c = New-UDErrorBoundary -Content $Children

        @{
            id = $Id 
            isPlugin = $true 
            type = "mu-grid"
            assetId = $MUAssetId

            xs = $ExtraSmallSize
            sm = $SmallSize
            md = $MediumSize
            lg = $LargeSize
            xl = $ExtraLargeSize
            spacing = $Spacing

            container = $Container.IsPresent
            item = $Item.IsPresent

            children = $c
        }
    }
}
function New-UDHeading {
    <#
    .SYNOPSIS
    Defines a new heading
     
    .DESCRIPTION
    Defines a new heading. This generates a new H tag.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Content
    The content of the heading.
     
    .PARAMETER Text
    The text of the heading.
     
    .PARAMETER Size
    This size of this heading. This can be 1,2,3,4,5 or 6.
     
    .PARAMETER Color
    The text color.
     
    .EXAMPLE
    A heading
 
    New-UDHeading -Text 'Heading 1' -Size 1 -Color Blue
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter(ParameterSetName = "Content")]
        [ScriptBlock]$Content,
        [Parameter(ParameterSetName = "Text")]
        [string]$Text,
        [Parameter()]
        [ValidateSet("1", "2", "3", "4", "5", "6")]
        $Size,
        [Parameter()]
        [UniversalDashboard.Models.DashboardColor]$Color = 'black'
    )

    if ($PSCmdlet.ParameterSetName -eq "Text") {
        $Content = { $Text }
    }

    New-UDElement -Id $Id -Tag "h$size" -Content $Content -Attributes @{
        style = @{
            color = $Color.HtmlColor
        }
    }
}
function New-UDHidden {
    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter(ParameterSetName = 'Down')]
        [ValidateSet('xs', 'sm', 'md', 'lg', 'xl')]
        [string]$Down,
        [Parameter(ParameterSetName = 'Up')]
        [ValidateSet('xs', 'sm', 'md', 'lg', 'xl')]
        [string]$Up,
        [Parameter(ParameterSetName = 'Only')]
        [ValidateSet('xs', 'sm', 'md', 'lg', 'xl')]
        [string[]]$Only,
        [Parameter()]
        [ScriptBlock]$Content
    )

    $Component = @{
        type = 'mu-hidden'
        id = $Id
        isPlugin = $true 

        children = & $Content
    }

    if ($PSCmdlet.ParameterSetName -eq 'Only')
    {
        $Component['only'] = $Only 
    }

    if ($PSCmdlet.ParameterSetName -eq 'Down')
    {
        $Component["$($Down.ToLower())Down"] = $true 
    }

    if ($PSCmdlet.ParameterSetName -eq 'Up')
    {
        $Component["$($Up.ToLower())Up"] = $true 
    }

    $Component 
} 
$Script:FontAwesomeSolid = Get-Content -Path (Join-Path $PSScriptRoot 'fontawesome.solid.txt')
$Script:FontAwesomeRegular = Get-Content -Path (Join-Path $PSScriptRoot 'fontawesome.regular.txt')
$Script:FontAwesomeBrands = Get-Content -Path (Join-Path $PSScriptRoot 'fontawesome.brands.txt')

function New-UDIcon {
    <#
    .SYNOPSIS
    Creates a new icon.
     
    .DESCRIPTION
    Creates a new icon.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Icon
    The Icon to display. This is a FontAwesome 5 icon name.
     
    .PARAMETER FixedWidth
    Whether to use a fixed with.
     
    .PARAMETER Inverse
    Whether to invert the colors of the icon.
     
    .PARAMETER Rotation
    The amount of degrees to rotate the icon.
     
    .PARAMETER ClassName
    Custom CSS class names to apply to the icon.
     
    .PARAMETER Transform
    A CSS transform to apply to the icon.
     
    .PARAMETER Flip
    Whether to flip the icon.
     
    .PARAMETER Pull
    Whether to flip the icon.
     
    .PARAMETER ListItem
     
     
    .PARAMETER Spin
    Whether the icon should spin.
     
    .PARAMETER Border
    Defines the border for this icon.
     
    .PARAMETER Pulse
    Whether this icon should publse.
     
    .PARAMETER Size
    The size of the icon.
     
    .PARAMETER Style
    A CSS style to apply to the icon.
     
    .PARAMETER Title
     
     
    .PARAMETER Regular
     
     
    .PARAMETER Color
    The color of this icon.
     
    .EXAMPLE
    Displays a user icon.
 
    New-UDIcon -Icon User
    #>

    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter()]
        [string]$Icon, 
        [Parameter()]
        [Switch]$FixedWidth,
        [Parameter()]
        [switch]$Inverse,
        [Parameter()]
        [int]$Rotation,
        [Parameter()]
        [string]$ClassName,
        [Parameter()]
        [string]$Transform,
        [Parameter()]
        [ValidateSet("horizontal", 'vertical', 'both')]
        [string]$Flip,
        [Parameter()]
        [ValidateSet('right', 'left')]
        [string]$Pull,
        [Parameter()]
        [switch]$ListItem,
        [Parameter()]
        [switch]$Spin,
        [Parameter()]
        [switch]$Border,
        [Parameter()]
        [switch]$Pulse,
        [Parameter ()]
        [ValidateSet("xs", "sm", "lg", "2x", "3x", "4x", "5x", "6x", "7x", "8x", "9x", "10x")]
        [string]$Size = "sm",
        [Parameter()]
        [Hashtable]$Style,
        [Parameter()]
        [string]$Title,
        [Parameter()]
        [switch]$Regular,
        [Parameter()]
        [UniversalDashboard.Models.DashboardColor]
        $Color
    )

    End {
        if ($Icon.Contains('_'))
        {
            $iconName = [UniversalDashboard.Models.FontAwesomeIconsExtensions]::GetIconName($Icon)
        }
        else 
        {
            if ($Regular)
            {
                $IconName = $Script:FontAwesomeRegular.Where({ $_ -eq $Icon })
            }
            else 
            {
                $IconName = $Script:FontAwesomeSolid.Where({ $_ -eq $Icon })
                if (-not $IconName)
                {
                    $IconName = $Script:FontAwesomeBrands.Where({ $_ -eq $Icon })
                }
            }
        }

        if (-not $IconName) {
            throw "Unknown icon $Icon"
        }

        $MUIcon = @{
            type       = "icon"
            id         = $id 
            size       = $Size
            fixedWidth = $FixedWidth
            color      = $Color.HtmlColor
            listItem   = $ListItem.IsPresent
            inverse    = $Inverse.IsPresent
            rotation   = $Rotation
            flip       = $Flip
            spin       = $Spin.IsPresent
            pulse      = $Pulse.IsPresent
            border     = $Border.IsPresent
            pull       = $Pull
            className  = $ClassName
            transform  = $Transform
            style      = $Style
            title      = $Title
            regular    = $Regular.IsPresent
            icon       = $iconName
        }

        $MUIcon.PSTypeNames.Insert(0, "UniversalDashboard.Icon") | Out-Null

        $MUIcon
    }
}

$scriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    if ($fakeBoundParameters.ContainsKey('Regular'))
    {
        $Script:FontAwesomeRegular | Where-Object {
            $_ -like "$wordToComplete*"
        }
    }
    else {
        $Script:FontAwesomeSolid | Where-Object {
            $_ -like "$wordToComplete*"
        } 
        $Script:FontAwesomeBrands | Where-Object {
            $_ -like "$wordToComplete*"
        } 
    }   
}

Register-ArgumentCompleter -CommandName New-UDIcon -ParameterName Icon -ScriptBlock $scriptBlock
function New-UDIconButton {
    <#
    .SYNOPSIS
    Creates a button with an icon.
     
    .DESCRIPTION
    Creates a button with an icon.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Icon
    The icon to display within the button.
     
    .PARAMETER OnClick
    A script block to execute when the button is clicked.
     
    .PARAMETER Disabled
    Whether the button is disabled.
     
    .PARAMETER Href
    A link to navigate to when the button is clicked.
     
    .PARAMETER Style
    The CSS sytle to apply to the icon.
     
    .EXAMPLE
    Creates an icon button with a user icon with a custom style.
 
    New-UDIconButton -Icon (New-UDIcon -Icon user -Size sm -Style @{color = '#000'}) -Id 'iconButtonContent'
    #>

    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter ()]
        [PSTypeName('UniversalDashboard.Icon')]$Icon,
        [Parameter ()]
        [object] $OnClick, 
        [Parameter ()]
        [Switch] $Disabled, 
        [Parameter ()]
        [string] $Href, 
        [Parameter ()]
        [Hashtable] $Style


    )

    End 
    {
        if ($null -ne $OnClick) {
            if ($OnClick -is [scriptblock]) {
                $OnClick =  New-UDEndpoint -Endpoint $OnClick -Id ($Id + "onClick")
            }
            elseif ($OnClick -isnot [UniversalDashboard.Models.Endpoint]) {
                throw "OnClick must be a script block or UDEndpoint"
            }
        }

        @{
            type = 'mu-icon-button'
            isPlugin = $true
            assetId = $MUAssetId
            id = $Id
            disabled = $Disabled.IsPresent
            style = $Style
            onClick = $OnClick
            icon = $Icon
            href = $Href
        }
    }
}
function New-UDIFrame {
    <#
    .SYNOPSIS
    An HTML IFrame component.
     
    .DESCRIPTION
    An HTML IFrame component.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Uri
    The URI for the iframe.
     
    .EXAMPLE
    Defines an IFrame for Google.
 
    New-UDIFrame -Uri https://www.google.com
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        $Uri
    )

    New-UDElement -Id $Id -Tag "iframe" -Attributes @{
        src = $Uri
    }
}
function New-UDImage {
    <#
    .SYNOPSIS
    An image component.
     
    .DESCRIPTION
    An image component.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Url
    The URL to the image.
     
    .PARAMETER Path
    The path to a local image.
     
    .PARAMETER Height
    The height in pixels.
     
    .PARAMETER Width
    The width in pixels.
     
    .PARAMETER Attributes
    Additional attributes to apply to the img tag.
     
    .EXAMPLE
    Displays an image.
 
    New-UDImage -Url 'https://ironmansoftware.com/logo.png'
    #>

    [CmdletBinding(DefaultParameterSetName = 'url')]
    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter(ParameterSetName = 'url')]
        [String]$Url,
        [Parameter(ParameterSetName = 'path')]
        [String]$Path,
        [Parameter()]
        [int]$Height,
        [Parameter()]
        [int]$Width,
        [Parameter()]
        [Hashtable]$Attributes = @{}
    )

    switch ($PSCmdlet.ParameterSetName) {
        'path' {
            if (-not [String]::IsNullOrEmpty($Path)) {
                if (-not (Test-Path $Path)) {
                    throw "$Path does not exist."
                }
        
                $mimeType = 'data:image/png;base64, '
                if ($Path.EndsWith('jpg') -or $Path.EndsWith('jpeg')) {
                    $mimeType = 'data:image/jpg;base64, '
                }
        
                $base64String = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes($Path))
        
                $Attributes.'src' = "$mimeType $base64String"
            }
        }
        'url' {
            $Attributes.'src' = $Url
        }
    }
    if ($PSBoundParameters.ContainsKey('Height')) {
        $Attributes.'height' = $Height
    }
    if ($PSBoundParameters.ContainsKey('Width')) {
        $Attributes.'width' = $Width
    }

    $Attributes["id"] = $Id

    New-UDElement -Tag 'img' -Attributes $Attributes
}

function New-UDLayout {
    <#
    .SYNOPSIS
    Create a layout based on the number of components within the layout.
     
    .DESCRIPTION
    Create a layout based on the number of components within the layout. The component wraps New-UDRow and New-UDColumn to automatically layout components in the content.
     
    .PARAMETER Columns
    The number of columns in this layout.
     
    .PARAMETER Content
    The content for this layout.
     
    .EXAMPLE
    New-UDLayout -Columns 2 -Content {
        New-UDTypography "Row 1, Col 1"
        New-UDTypography "Row 1, Col 2"
        New-UDTypography "Row 2, Col 1"
        New-UDTypography "Row 2, Col 2"
    }
    #>

    param(
        [Parameter(Mandatory = $true)]
        [ValidateRange(1, 12)]
        [int]$Columns,
        [Parameter(Mandatory = $true)]
        [ScriptBlock]$Content
    )

    $Components = $Content.Invoke()

    if ($Columns -eq 1) 
    {
        $LargeColumnSize = 12
        $MediumColumnSize = 12
        $SmallColumnSize = 12
    }
    else 
    {
        $LargeColumnSize = 12 / $Columns
        $MediumColumnSize = 12 / ($Columns / 2)
        $SmallColumnSize = 12
    }

    $Offset = 0
    $ComponentCount = ($Components | Measure-Object).Count

    while ($Offset -lt $ComponentCount) {
        $ColumnObjects = $Components | Select-Object -Skip $Offset -First $Columns | ForEach-Object {
            New-UDColumn -SmallSize $SmallColumnSize -MediumSize $MediumColumnSize -LargeSize $LargeColumnSize -Content {
                $_
            }
        }

        New-UDRow -Columns {
            $ColumnObjects
        }
        
        $Offset += $Columns
    }
}
function New-UDLink {
    <#
    .SYNOPSIS
    Short description
     
    .DESCRIPTION
    Long description
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Url
    The URL of the link.
     
    .PARAMETER Underline
    Whether to underline the link.
     
    .PARAMETER Style
    A custom style to apply to the link.
     
    .PARAMETER Variant
    The theme variant to apply to the link.
     
    .PARAMETER ClassName
    The CSS class to apply to the link.
     
    .PARAMETER OpenInNewWindow
    Opens the link in a new window.
     
    .PARAMETER Children
    The components to link.
     
    .PARAMETER Text
    Text to include in the link.
     
    .PARAMETER OnClick
    A script block to invoke when clicked.
 
    .EXAMPLE
    An example
     
    .NOTES
    General notes
    #>

    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter()]
        [string]$url,
        [Parameter()]
        [ValidateSet('none','hover','always')]
        [string]$Underline = "none",
        [Parameter()]
        [hashtable]$Style,
        [Parameter()]
        [ValidateSet('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'subtitle1', 'subtitle2', 'body1', 'body2', 'caption', 'button', 'overline', 'srOnly', 'inherit')]
        [string]$Variant,
        [Parameter ()]
        [string]$ClassName,
        [Parameter()]
        [switch]$OpenInNewWindow,
        [Parameter(ParameterSetName = 'content')]
        [Alias("Content")]
        [scriptblock]$Children,
        [Parameter(ParameterSetName = 'text')]
        [string]$Text,
        [Parameter()]
        [Endpoint]$OnClick
    )
    End {
        if ($OnClick)
        {
            $OnClick.Register($Id, $PSCmdlet)
        }

        if($null -ne $Children)
        {
            $Object = & $Children
        }
        else
        {
            $Object = $null
        }

        @{
            type            = 'mu-link'
            isPlugin        = $true
            assetId         = $MUAssetId

            id              = $Id
            url             = $url
            underline       = $underline
            style           = $style
            variant         = $variant
            className       = $ClassName
            openInNewWindow = $openInNewWindow.IsPresent
            content         = $Object
            text            = $text
            onClick         = $OnClick
        }
    }
}
function New-UDList {
    <#
    .SYNOPSIS
    Creates a list.
     
    .DESCRIPTION
    Creates a list. Use New-UDListItem to add new items to the list. Lists are good for links in UDDrawers.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Children
    The items in the list.
     
    .PARAMETER SubHeader
    Text to show within the sub header.
     
    .EXAMPLE
    Creates a new list with two items and nested list items.
 
    New-UDList -Id 'listContent' -Content {
 
        New-UDListItem -Id 'listContentItem' -AvatarType Avatar -Source 'https://pbs.twimg.com/profile_images/1065243723217416193/tg3XGcVR_400x400.jpg' -Label 'Adam Driscoll' -Content {
 
            New-UDListItem -Id 'list-item-security' -Label 'username and passwords'
            New-UDListItem -Id 'list-item-api' -Label 'api keys'
 
        }
    }
    #>

    param(
        [Parameter ()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter ()]
        [Alias("Content")]
        [scriptblock]$Children,
        [Parameter ()]
        [string]$SubHeader
    )
    End
    {
        try {
            $c = & $Children
        }
        catch {
            $c = New-UDError -Message $_
        }

        @{
            type = 'mu-list'
            isPlugin = $true
            assetId = $MUAssetId

            id = $Id
            children = $c
            subHeader = $SubHeader
            style = $Style
        }
    }
}

function New-UDListItem {
    <#
    .SYNOPSIS
    Creates a new list item.
     
    .DESCRIPTION
    Creates a new list item. List items are used with New-UDList.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER AvatarType
    The type of avatar to show within the list item.
     
    .PARAMETER OnClick
    A script block to execute when the list item is clicked.
     
    .PARAMETER Label
    The label to show within the list item.
     
    .PARAMETER Children
    Nested list items to show underneath this list item.
     
    .PARAMETER SubTitle
    The subtitle to show within the list item.
     
    .PARAMETER Icon
    The icon to show within the list item.
     
    .PARAMETER Source
    Parameter description
     
    .PARAMETER SecondaryAction
    The secondary action to issue with this list item.
     
    .EXAMPLE
    Creates a new list with two items and nested list items.
 
    New-UDList -Id 'listContent' -Content {
 
        New-UDListItem -Id 'listContentItem' -AvatarType Avatar -Source 'https://pbs.twimg.com/profile_images/1065243723217416193/tg3XGcVR_400x400.jpg' -Label 'Adam Driscoll' -Content {
 
            New-UDListItem -Id 'list-item-security' -Label 'username and passwords'
            New-UDListItem -Id 'list-item-api' -Label 'api keys'
 
        }
    }
    #>

    [CmdletBinding()]
    param(
        [Parameter ()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter ()]
        [ValidateSet("Icon","Avatar")][string]$AvatarType = 'Icon',
        [Parameter ()]
        [Endpoint]$OnClick, 
        [Parameter ()]
        [string]$Label, 
        [Parameter ()]
        [Alias("Content")]
        [scriptblock]$Children, 
        [Parameter ()]
        [string]$SubTitle,
        [Parameter ()]
        $Icon,
        [Parameter ()]
        [string]$Source,
        [Parameter ()]
        [scriptblock]$SecondaryAction
    )
    # DynamicParam {
        
    # $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

    # if ($AvatarType -eq "Icon") {
    # #create a new ParameterAttribute Object
    # $IconAttribute = New-Object System.Management.Automation.ParameterAttribute
    # $IconAttribute.Mandatory = $true
    # $IconAttribute.HelpMessage = "Use New-UDIcon to create new icon"
    # $attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
    # #add our custom attribute
    # $attributeCollection.Add($IconAttribute)
    # #add our paramater specifying the attribute collection
    # $IconParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Icon', [object], $attributeCollection)
    # $paramDictionary.Add('Icon', $IconParam)

    # }
    # elseif($AvatarType -eq "Avatar"){
    # #create a new ParameterAttribute Object
    # $AvatarAttribute = New-Object System.Management.Automation.ParameterAttribute
    # $AvatarAttribute.Mandatory = $true
    # $AvatarAttribute.HelpMessage = "Enter the path to the avatar image it can be local or url"
    # $AvatarCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]

    # #add our custom attribute
    # $AvatarCollection.Add($AvatarAttribute)
    # #add our paramater specifying the attribute collection
    # $AvatarParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Source', [string], $AvatarCollection)
    # $paramDictionary.Add('Source', $AvatarParam)
    # }
    # return $paramDictionary
    # }
    begin{}
    Process{}
    End 
    {
        if ($OnClick) {
            $OnClick.Register($Id, $PSCmdlet)
        }

        if($null -ne $Children){
            try {
                $CardContent = & $Children    
            }
            catch {
                $CardContent = New-UDError -Message $_
            }
            
        }else{
            $CardContent = $null
        }

        if($null -ne $SecondaryAction){
            $Action = $SecondaryAction.Invoke()
        }else{
            $Action = $null
        }
        
        @{
            type = 'mu-list-item'
            isPlugin = $true
            assetId = $MUAssetId

            id = $Id
            subTitle = $SubTitle
            label = $Label
            onClick = $OnClick
            children = $CardContent
            secondaryAction = $Action
            icon =  $Icon
            source = $Source
            avatarType = $AvatarType
            labelStyle = $LabelStyle
            style = $Style
        }
    }
}
function New-UDPage 
{
    <#
    .SYNOPSIS
    Defines a new page.
     
    .DESCRIPTION
    Defines a new page. Dashboards can contain multiple pages that each contain different components.
     
    .PARAMETER Name
    The name of the page.
     
    .PARAMETER Content
    The content of the page.
     
    .PARAMETER Url
    The URL for the page.
     
    .PARAMETER DefaultHomePage
    Whether this page is the default home page. Only one page can be the default home page.
     
    .PARAMETER Title
    The title of the page.
     
    .PARAMETER Blank
    Whether to define a blank page. Blank pages won't have a navigation bar.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER OnLoading
    A component to return while this page is loading.
     
    .PARAMETER Role
    The role of the user that is allowed to access this page.
     
    .PARAMETER NavigationLayout
    How the navigation is layed out on the page.
     
    .PARAMETER Navigation
    Custom navigation to show for this page. You can use New-UDList and New-UDListItem to define custom navigation links.
     
    .PARAMETER Logo
    The logo to display on this page.
     
    .PARAMETER LoadNavigation
    An endpoint that is called when loading the navigation for the page.
     
    .EXAMPLE
    Creates a basic page.
 
    New-UDPage -Name 'Test' -Content {
        New-UDTypography -Text 'Page'
    }
    #>

    [CmdletBinding(DefaultParameterSetName = "Simple")]
    param(
        [Parameter(Position = 0, Mandatory)]
        [string]$Name,
        [Parameter(Position = 2, Mandatory)]
        [Alias("Endpoint")]
        [Endpoint]$Content,
        [Parameter(Position = 0)]
        [string]$Url,
        [Parameter(Position = 3)]
        [Switch]$DefaultHomePage,
        [Parameter(Position = 4)]
        [string]$Title,
        [Parameter(ParameterSetName = 'Advanced')]
        [Switch]$Blank,
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [ScriptBlock]$OnLoading,
        [Parameter()]
        [string[]]$Role,
        [Parameter(ParameterSetName = 'Simple')]
        [Parameter(ParameterSetName = 'DynamicNav')]
        [ValidateSet("Temporary", "Permanent")]
        [string]$NavigationLayout = 'Temporary',
        [Parameter(ParameterSetName = 'Simple')]
        [Hashtable[]]$Navigation,
        [Parameter(ParameterSetName = "Simple")]
        [Parameter(ParameterSetName = 'DynamicNav')]
        [string]$Logo,
        [Parameter(ParameterSetName = 'DynamicNav')]
        [Endpoint]$LoadNavigation
    )

    $Content.Register($Id, $Role, $PSCmdlet)

    if (-not [string]::IsNullOrEmpty($Url) -and -not $Url.StartsWith("/"))
    {
        $Url = "/" + $Url
    }

    if ([string]::IsNullOrEmpty($Url) -and $null -ne $Name)
    {
        $Url = "/" + $Name.Replace(' ', '-');
    }

    if ($OnLoading)
    {
        $LoadingContent = New-UDErrorBoundary -Content $OnLoading
    }

    if ($LoadNavigation)
    {
        $LoadNavigation.Register('nav' + $Id, $Role, $PSCmdlet)
    }
    
    @{
        name = $Name
        url = $Url
        id = $Id
        defaultHomePage = $DefaultHomePage.IsPresent
        title = $Title
        blank = $Blank.IsPresent
        loading = $LoadingContent
        content = $Content 
        navLayout = $NavigationLayout.ToLower()
        navigation = $navigation
        role = $Role
        logo = $Logo
        loadNavigation = $LoadNavigation
    }
}
function New-UDPaper {
    <#
    .SYNOPSIS
    Creates a paper.
     
    .DESCRIPTION
    Creates a paper. Paper is used to highlight content within a page.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Children
    The content of this paper element.
     
    .PARAMETER Width
    The width of this paper.
     
    .PARAMETER Height
    The height of this paper.
     
    .PARAMETER Square
    Whether this paper is square.
     
    .PARAMETER Style
    The CSS style to apply to this paper.
     
    .PARAMETER Elevation
    The elevation of this paper.
     
    .EXAMPLE
    Creates paper with a heading, custom style and an elevation of 4.
 
    New-UDPaper -Children {
        New-UDHeading -Text "hi" -Id 'hi'
    } -Style @{
        backgroundColor = '#90caf9'
    } -Id 'paperElevation' -Elevation 4
     
    .NOTES
    General notes
    #>

    param(
        [Parameter()][string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter()][Alias("Content")][ScriptBlock]$Children,
        [Parameter()][string]$Width = '500',
        [Parameter()][string]$Height = '500',
        [Parameter()][Switch]$Square,
        [Parameter()][Hashtable]$Style,
        [Parameter()][int]$Elevation
    )

    End 
    {
        $c = New-UDErrorBoundary -Content $Children
        
        @{
            type = 'mu-paper'
            isPlugin = $true
            assetId = $MUAssetId
            
            id = $Id
            width  = $Width 
            children = $c
            height = $Height
            square = $Square.IsPresent
            style = $Style
            elevation = $Elevation
        }
    }
}
function New-UDParagraph {
    <#
    .SYNOPSIS
    A paragraph.
     
    .DESCRIPTION
    A paragraph. Used to define a P HTML tag.
     
    .PARAMETER Content
    The content of the paragraph.
     
    .PARAMETER Text
    The text of the paragraph.
     
    .PARAMETER Color
    The font color of the paragraph.
     
    .EXAMPLE
    A simple paragraph.
 
    New-UDParagraph -Text 'Hello, world!'
    #>

    param(
        [Parameter(ParameterSetName = 'content')]
        [ScriptBlock]$Content,
        [Parameter(ParameterSetName = 'text')]
        [string]$Text,
        [Parameter()]
        [UniversalDashboard.Models.DashboardColor]$Color = 'black'
    )

    if ($PSCmdlet.ParameterSetName -eq 'content') {
        New-UDElement -Tag 'p' -Content $Content -Attributes @{
            style = @{
                color = $Color.HtmlColor
            }
        }
    }
    else {
        New-UDElement -Tag 'p' -Content {
            $Text
        } -Attributes @{
            style = @{
                color = $Color.HtmlColor
            }
        }
    }
   
}
function New-UDProgress {
    <#
    .SYNOPSIS
    Creates a progress dialog.
     
    .DESCRIPTION
    Creates a progress dialog. Progress dialogs can show both determinate and indeterminate progress. They can also be circular or linear.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER PercentComplete
    The percent complete for the progress.
     
    .PARAMETER BackgroundColor
    The background color.
     
    .PARAMETER ProgressColor
    The progress bar color.
     
    .PARAMETER Circular
    Whether the progress is circular.
     
    .PARAMETER Size
    The size of the progress.
     
    .EXAMPLE
    Creates a progress bar at 75%.
 
    New-UDProgress -PercentComplete 75
    #>

    [CmdletBinding(DefaultParameterSetName = "indeterminate")]
    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid().ToString(),
        [Parameter(ParameterSetName = "determinate")]
        [ValidateRange(0, 100)]
        $PercentComplete,
        [Parameter(ParameterSetName = "indeterminate")]
        [Parameter(ParameterSetName = "determinate")]
        [UniversalDashboard.Models.DashboardColor]$BackgroundColor,
        [Parameter()]
        [Alias("Color")]
        [UniversalDashboard.Models.DashboardColor]$ProgressColor,
        [Parameter(ParameterSetName = 'circular')]
        [Switch]$Circular,
        [Parameter(ParameterSetName = 'circular')]
        [ValidateSet('small', 'medium', 'large')]
        [string]$Size
        )

        End {
            @{
                id = $Id
                assetId = $MUAssetId 
                isPlugin = $true 
                type = "mu-progress"
          
                variant = $PSCmdlet.ParameterSetName
                percentComplete = $PercentComplete
                backgroundColor = $BackgroundColor.HtmlColor
                progressColor = $ProgressColor.HtmlColor
                circular = $Circular.IsPresent
                color = $Color
                size = $Size
            }          
        }


}
function New-UDRadioGroup {
    <#
    .SYNOPSIS
    Creates a group of radio buttons.
     
    .DESCRIPTION
    Creates a group of radio buttons. Within a group, only a single radio will be able to be selected.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Label
    The label to show for this radio group.
     
    .PARAMETER Children
    The radio buttons to include within this group.
     
    .PARAMETER OnChange
    A script block to call when the radio group selection changes. The $EventData variable will include the value of the radio that is selected.
     
    .PARAMETER Value
    The selected value for this radio group.
     
    .EXAMPLE
    Creates a radio group that shows a toast message when a radio is selected.
 
    New-UDRadioGroup -Label 'group' -Id 'onChangeRadio' -Children {
        New-UDRadio -Value 'Adam' -Label 'Adam' -Id 'adamOnChange'
        New-UDRadio -Value 'Alon' -Label 'Alon' -Id 'alonOnChange'
        New-UDRadio -Value 'Lee' -Label 'Lee' -Id 'leeOnChange'
    } -OnChange {
        Show-UDToast $EventData
    }
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [String]$Label,
        [Parameter()]
        [Alias("Content")]
        [ScriptBlock]$Children,
        [Parameter()]
        [Endpoint]$OnChange,
        [Parameter()]
        [string]$Value
    )

    if ($OnChange) {
        $OnChange.Register($Id, $PSCmdlet)
    }

    @{
        assetId = $MUAssetId 
        id = $Id 
        isPlugin = $true 
        type = 'mu-radiogroup'

        onChange = $OnChange 
        value = $Value 
        label = $Label 
        children = & $Children
    }
}

function New-UDRadio {
    <#
    .SYNOPSIS
    Creates a radio.
     
    .DESCRIPTION
    Creates a radio. Radios should be included within a New-UDRadioGroup.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Label
    The label to show next to the radio.
     
    .PARAMETER Disabled
    Whether the radio is disabled.
     
    .PARAMETER Value
    The value of the radio.
     
    .PARAMETER LabelPlacement
    The position to place the label in relation to the radio.
     
    .EXAMPLE
    Creates a radio group that shows a toast message when a radio is selected.
 
    New-UDRadioGroup -Label 'group' -Id 'onChangeRadio' -Children {
        New-UDRadio -Value 'Adam' -Label 'Adam' -Id 'adamOnChange'
        New-UDRadio -Value 'Alon' -Label 'Alon' -Id 'alonOnChange'
        New-UDRadio -Value 'Lee' -Label 'Lee' -Id 'leeOnChange'
    } -OnChange {
        Show-UDToast $EventData
    }
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [String]$Label,
        [Parameter()]
        [Switch]$Disabled,
        [Parameter()]
        [string]$Value,
        [Parameter()]
        [ValidateSet('start', 'end')]
        [string]$LabelPlacement = 'end'
    )

    @{
        assetId = $MUAssetId 
        id = $Id 
        isPlugin = $true 
        type = 'mu-radio'

        label = $Label 
        disabled = $Disabled.IsPresent 
        value = $value 
        labelPlacement = $LabelPlacement
    }
}
function New-UDSelect {
    <#
    .SYNOPSIS
    Creates a new select.
     
    .DESCRIPTION
    Creates a new select. Selects can have multiple options and option groups. Selects can also be multi-select.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Option
    Options to include in this select. This can be either New-UDSelectOption or New-UDSelectGroup.
     
    .PARAMETER Label
    The label to show with the select.
     
    .PARAMETER OnChange
    A script block that is executed when the script changes. $EventData will be an array of the selected values.
     
    .PARAMETER DefaultValue
    The default selected value.
     
    .PARAMETER Disabled
    Whether this select is disabled.
     
    .PARAMETER Multiple
    Whether you can select multiple values.
     
    .EXAMPLE
    Creates a new select with 3 options and shows a toast when one is selected.
 
    New-UDSelect -Label '1-3' -Id 'select' -Option {
        New-UDSelectOption -Name "One" -Value 1
        New-UDSelectOption -Name "Two" -Value 2
        New-UDSelectOption -Name "Three" -Value 3
    } -DefaultValue 2 -OnChange {
        Show-UDToast -Mesage $EventData
    }
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [ScriptBlock]$Option,
        [Parameter()]
        [String]$Label,
        [Parameter()]
        [Endpoint]$OnChange,
        [Parameter()]
        $DefaultValue,
        [Parameter()]
        [Switch]$Disabled,
        [Parameter()]
        [Switch]$Multiple,
        [Parameter()]
        [string]$MaxWidth
    )

    if ($OnChange) {
        $OnChange.Register($Id + "onChange", $PSCmdlet)
    }

    @{
        type = 'mu-select'
        assetId = $MUAssetId
        isPlugin = $true 

        id = $id 
        options = $Option.Invoke()
        label = $Label
        onChange = $OnChange
        defaultValue = $DefaultValue
        disabled = $Disabled.IsPresent
        multiple = $Multiple.IsPresent
        maxWidth = $MaxWidth
    }
}

function New-UDSelectGroup {
    <#
    .SYNOPSIS
    Creates a new select group.
     
    .DESCRIPTION
    Creates a new select group. This cmdlet is to be used with New-UDSelect. Pass the result of this cmdlet to the -Option parameter to create a new select group.
     
    .PARAMETER Option
    Options to include in this group.
     
    .PARAMETER Name
    The name of the group. This will be displayed in the select.
     
    .EXAMPLE
    Creates a new select with two select groups.
 
    New-UDSelect -Id 'selectGrouped' -Option {
        New-UDSelectGroup -Name "Category 1" -Option {
            New-UDSelectOption -Name "One" -Value 1
            New-UDSelectOption -Name "Two" -Value 2
            New-UDSelectOption -Name "Three" -Value 3
        }
        New-UDSelectGroup -Name "Category 2" -Option {
            New-UDSelectOption -Name "Four" -Value 4
            New-UDSelectOption -Name "Five" -Value 5
            New-UDSelectOption -Name "Six" -Value 6
        }
    } -DefaultValue 2 -OnChange { Show-UDToast -Message $EventData }
     
    #>

    param(
        [Parameter(Mandatory = $true)]
        [ScriptBlock]$Option,
        [Parameter(Mandatory = $true)]
        [String]$Name
    )

    @{
        type = 'mu-select-group'
        name = $Name 
        options = $Option.Invoke()
    }

}

function New-UDSelectOption {
    <#
    .SYNOPSIS
    Creates a new select option.
     
    .DESCRIPTION
    Creates a new select option. This cmdlet is to be used with New-UDSelect. Pass the result of this cmdlet to the -Option parameter to create a new select group.
     
    .PARAMETER Name
    The name of the select option. This will be shown in the select.
     
    .PARAMETER Value
    Thevalue of the select option. This will be passed back to New-UDForm -OnSubmit or the $EventData for -OnChange on New-UDSelect.
     
    .EXAMPLE
    Creates a new select with three options.
 
    New-UDSelect -Label '1-3' -Id 'select' -Option {
        New-UDSelectOption -Name "One" -Value 1
        New-UDSelectOption -Name "Two" -Value 2
        New-UDSelectOption -Name "Three" -Value 3
    } -DefaultValue 2 -OnChange {
        $EventData = $Body | ConvertFrom-Json
        Set-TestData $EventData
    }
 
    #>

    param(
        [Parameter(Mandatory = $true)]
        [String]$Name,
        [Parameter(Mandatory = $true)]
        [String]$Value
    )

    @{
        type = 'mu-select-option'
        name = $Name 
        value = $Value 
    }
}

function New-UDSkeleton {
    <#
    .SYNOPSIS
    Creates a loading skeleton
     
    .DESCRIPTION
    Creates a loading skeleton
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Variant
    The type of skeleton to display.
     
    .PARAMETER Height
    The static height of the skeleton.
     
    .PARAMETER Width
    The static width of the skeleton.
     
    .PARAMETER Animation
    The type of animation to use for the skeleton.
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [ValidateSet("text", "rect", "circle")]
        [string]$Variant = 'text',
        [Parameter()]
        [int]$Height,
        [Parameter()]
        [int]$Width,
        [Parameter()]
        [ValidateSet("wave", "disabled", "pulsate")]
        [string]$Animation = "pulsate"
    )

    @{
        type = "mu-skeleton"
        id = $Id 
        isPlugin = $true
        assetId = $MUAssetId

        variant = $Variant.ToLower()
        height = $Height
        width = $Width 
        animation = $Animation.ToLower()
    }
}
function New-UDSlider {
    <#
    .SYNOPSIS
    A slider component.
     
    .DESCRIPTION
    A slider component. Sliders can be used to define values within a range or selecting a range of values. You can use this component with New-UDForm.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Value
    The value of the slider.
     
    .PARAMETER Minimum
    The minimum value of the slider.
     
    .PARAMETER Maximum
    The maximum value of the slider.
     
    .PARAMETER Disabled
    Whether the slider is disabled.
     
    .PARAMETER Marks
    Whether to display marks on the slider.
     
    .PARAMETER OnChange
    A script block that is invoked when the slider value changes. You can access the slider value within the script block by referencing the $EventData variable.
     
    .PARAMETER Orientation
    The orientation of the slider.
     
    .PARAMETER Step
    Step size of the slider.
     
    .PARAMETER ValueLabelDisplay
    Whether to display value labels.
     
    .EXAMPLE
    An example
 
    New-UDSlider
     
    .NOTES
    General notes
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [int[]]$Value = 0, 
        [Parameter()]
        [int]$Minimum = 0, 
        [Parameter()]
        [int]$Maximum = 100,
        [Parameter()]
        [Switch]$Disabled, 
        [Parameter()]
        [Switch]$Marks, 
        [Parameter()]
        [Endpoint]$OnChange,
        [Parameter()]
        [ValidateSet('horizontal', 'vertical')]
        [string]$Orientation = 'horizontal',
        [Parameter()]
        [int]$Step = 1,
        [Parameter()]
        [ValidateSet('on', 'auto', 'off')]
        [string]$ValueLabelDisplay = 'auto'
    )

    if ($OnChange) {
        $OnChange.Register($Id, $PSCmdlet)
    }

    if (-not $PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Value"))
    {
        $Value = $Minimum
    }

    if ($Value -lt $Minimum) 
    {
        throw "Value cannot be less than minimum"
    }

    if ($Value -gt $Maximum) 
    {
        throw "Value cannot be more than maximum"
    }

    $Val = $Value 
    if ($Value.Length -eq 1)
    {
        $Val = $Value | Select-Object -First 1
    }

    @{
        type = 'mu-slider'
        isPlugin = $true 
        assetId = $MUAssetId 
        id = $Id 

        value = $val 
        min = $Minimum
        max = $Maximum
        disabled = $Disabled.IsPresent
        marks = $Marks.IsPresent
        onChange = $OnChange 
        orientation = $Orientation 
        step = $Step
        valueLabelDisplay = $ValueLabelDisplay
    }
}
function New-UDSpan {
    <#
    .SYNOPSIS
    A span component.
     
    .DESCRIPTION
    A span component. Defines a span HTML tag.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Content
    The content of the span.
     
    .EXAMPLE
    An example
 
    New-UDSpan -Content {
        New-UDTypography -Text 'Text'
    }
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        $Content
    )

    New-UDElement -Id $Id -Tag "span" -Content {
        $Content
    }
}
function New-UDSplitPane {
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter(Mandatory)]
        [ScriptBlock]$Content,
        [Parameter()]
        [ValidateSet("vertical", "horizontal")]
        [string]$Direction = "vertical",
        [Parameter()]
        [int]$MinimumSize,
        [Parameter()]
        [int]$DefaultSize
    )

    try {
        $Children = & $Content
    }
    catch {
        $Children = New-UDError -Message $_
    }

    if ($Children.Length -ne 2) {
        Write-Error "Split pane requires exactly two components in Content"
        return
    }

    $Options = @{
        content = $Children
        id = $Id
        split = $Direction.ToLower()
        type = "ud-splitpane"
    }

    if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("MinimumSize")) {
        $Options["minSize"] = $MinimumSize
    }

    if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("DefaultSize")) {
        $Options["defaultSize"] = $DefaultSize
    }

    $Options
}
function New-UDStepper {
    <#
    .SYNOPSIS
    Creates a new stepper component.
     
    .DESCRIPTION
    Creates a new stepper component. Steppers can be used as multi-step forms or to display information in a stepped manner.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER ActiveStep
    Sets the active step. This should be the index of the step.
     
    .PARAMETER Children
    The steps for this stepper. Use New-UDStep to create new steps.
     
    .PARAMETER NonLinear
    Allows the user to progress to steps out of order.
     
    .PARAMETER AlternativeLabel
    Places the step label under the step number.
     
    .PARAMETER OnFinish
    A script block that is executed when the stepper is finished.
 
    .PARAMETER Orientation
    Sets the orientation of the stepper.
 
    .PARAMETER NextButtonText
    The text to display in the next button.
 
    .PARAMETER BackButtonText
    The text to display in the back button.
 
    .PARAMETER FinsihButtonText
    The text to display in the finish button.
     
    .EXAMPLE
    Creates a stepper that reports the stepper context with each step.
 
    New-UDStepper -Id 'stepper' -Steps {
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 1" }
            New-UDTextbox -Id 'txtStep1'
        } -Label "Step 1"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 2" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep2'
        } -Label "Step 2"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 3" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep3'
        } -Label "Step 3"
    } -OnFinish {
        New-UDTypography -Text 'Nice! You did it!' -Variant h3
        New-UDElement -Tag 'div' -Id 'result' -Content {$Body}
    }
 
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [int]$ActiveStep = 0,
        [Alias("Steps")]
        [Parameter(Mandatory)]
        [ScriptBlock]$Children,
        [Parameter()]
        [Switch]$NonLinear,
        [Parameter()]
        [Switch]$AlternativeLabel,
        [Parameter(Mandatory)]
        [Endpoint]$OnFinish,
        # [Parameter()]
        # [Endpoint]$OnCompleteStep,
        [Parameter()]
        [Endpoint]$OnValidateStep,
        [Parameter()]
        [ValidateSet("vertical", "horizontal")]
        [string]$Orientation = "horizontal",
        [Parameter()]
        [string]$NextButtonText = "Next",
        [Parameter()]
        [string]$BackButtonText = "Back",
        [Parameter()]
        [string]$FinishButtonText = "Finish"
    )

    $OnFinish.Register($Id + "onFinish", $PSCmdlet)

    if ($OnCompleteStep) {
        $OnCompleteStep.Register($Id + "onComplete", $PSCmdlet)
    }

    if ($OnValidateStep) {
        $OnValidateStep.Register($Id + "onValidate", $PSCmdlet)
    }

    $c = New-UDErrorBoundary -Content $Children

    @{
        id = $id 
        isPlugin = $true 
        type = 'mu-stepper'
        assetId = $MUAssetId 

        children = $c
        nonLinear = $NonLinear.IsPresent 
        alternativeLabel = $AlternativeLabel.IsPresent
        onFinish = $OnFinish
        activeStep = $ActiveStep
        onValidateStep = $OnValidateStep 
        onCompleteStep = $OnCompleteStep
        orientation = $Orientation.ToLower()
        nextButtonText = $NextButtonText
        finishButtonText = $FinishButtonText
        backButtonText = $BackButtonText
    }
}

function New-UDStep {
    <#
    .SYNOPSIS
    Creates a new step for a stepper.
     
    .DESCRIPTION
    Creates a new step for a stepper. Add to the Children (alias Steps) parameter for New-UDStepper.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER OnLoad
    The script block that is executed when the step is loaded. The script block will receive the $Body parameter which contains JSON for the current state of the stepper. If you are using form controls, their data will be availalble in the $Body.Context property.
     
    .PARAMETER Label
    A label for this step.
     
    .PARAMETER Optional
    Whether this step is optional.
     
    .EXAMPLE
    Creates a stepper that reports the stepper context with each step.
 
    New-UDStepper -Id 'stepper' -Steps {
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 1" }
            New-UDTextbox -Id 'txtStep1'
        } -Label "Step 1"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 2" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep2'
        } -Label "Step 2"
        New-UDStep -OnLoad {
            New-UDElement -tag 'div' -Content { "Step 3" }
            New-UDElement -tag 'div' -Content { "Previous data: $Body" }
            New-UDTextbox -Id 'txtStep3'
        } -Label "Step 3"
    } -OnFinish {
        New-UDTypography -Text 'Nice! You did it!' -Variant h3
        New-UDElement -Tag 'div' -Id 'result' -Content {$Body}
    }
     
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Alias("Content")]
        [Parameter(Mandatory)]
        [Endpoint]$OnLoad,
        [Parameter()]
        [string]$Label,
        [Parameter()]
        [Switch]$Optional
    )

    $OnLoad.Register($Id + "onLoad", $PSCmdlet)

    @{
        id = $id 
        isPlugin = $true 
        type = 'mu-stepper-step'
        assetId = $MUAssetId 

        onLoad = $OnLoad
        label = $Label
        optional = $Optional.IsPresent 
    }
}


function New-UDSwitch {
    <#
    .SYNOPSIS
    Creates a new switch.
     
    .DESCRIPTION
    Creates a new switch. A switch behaves like a checkbox but looks a little different.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Disabled
    Whether this switch is disabled.
     
    .PARAMETER OnChange
    A script block that is called when this switch changes. The $EventData variable will contain the checked value ($true\$false).
     
    .PARAMETER Checked
    Whether this switch is checked.
     
    .EXAMPLE
    Creates a switch that shows a toast when changed.
     
    New-UDSwitch -Id 'switchOnChange' -OnChange {
        Show-UDToast -Message $EventData
    }
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [Switch]$Disabled,
        [Parameter()]
        [Endpoint]$OnChange,
        [Parameter()]
        [bool]$Checked
    )

    if ($OnChange) {
        $OnChange.Register($Id, $PSCmdlet)
    }

    @{
        type = 'mu-switch'
        id = $Id 
        assetId = $MUAssetId 
        isPlugin = $true 

        disabled = $Disabled.IsPresent 
        checked = $Checked 
        onChange = $onChange
    }
}
function ConvertTo-FlatObject {
    param(
        [Parameter(ValueFromPipeline = $true)]
        $InputObject
    )

    Process {
        $OutputObject = @{}

        if ($null -eq $InputObject) {
            return
        }

        if ($InputObject -is [Hashtable]) {
            foreach ($key in $InputObject.Keys) {
                if ($key -and $key.StartsWith('rendered')) { 
                    $OutputObject[$key] = $InputObject[$key]
                }
                else {
                    $Value = $InputObject[$key]
                    if ($Value -is [DateTime]) {
                        $OutputObject[$key] = $Value
                    }
                    else {
                        $OutputObject[$key] = if ($Value) { $Value.ToString() } else { "" } 
                    }
                    
                } 
            }
            [PSCustomObject]$OutputObject
        }
        else {
            $InputObject | Get-Member -MemberType Properties | ForEach-Object {
                if ($_.Name -and $_.Name.StartsWith('rendered')) { 
                    $OutputObject[$_.Name] = $InputObject."$($_.Name)"
                }
                else {
                    $Value = $InputObject."$($_.Name)"
                    if ($Value -is [DateTime]) {
                        $OutputObject[$_.Name] = $Value
                    }
                    else {
                        $OutputObject[$_.Name] = if ($Value) { $Value.ToString() } else { "" } 
                    }
                } 
            }
            [PSCustomObject]$OutputObject
        }
    }
}

function New-UDTable {
    <#
    .SYNOPSIS
    Creates a table.
     
    .DESCRIPTION
    Creates a table. Tables are used to show both static and dynamic data. You can define columns and data to show within the table. The columns can be used to render custom components based on row data. You can also enable paging, filtering, sorting and even server-side processing.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Title
    The title to show at the top of the table's card.
     
    .PARAMETER Data
    The data to put into the table.
     
    .PARAMETER LoadData
    When using dynamic tables, this script block is called. The $Body parameter will contain a hashtable the following options:
  
    filters: @()
    orderBy: string
    orderDirection: string
    page: int
    pageSize: int
    properties: @()
    search: string
    totalCount: int
 
    You can use these values to perform server-side processing, like SQL queries, to improve the performance of large grids.
 
    After processing the data with these values, output the data via Out-UDTableData.
             
    .PARAMETER Columns
    Defines the columns to show within the table. Use New-UDTableColumn to define these columns. If this parameter isn't specified, the properties of the data that you pass in will become the columns.
     
    .PARAMETER Sort
    Whether sorting is enabled in the table.
     
    .PARAMETER Filter
    Whether filtering is enabled in the table.
     
    .PARAMETER Search
    Whether search is enabled in the table.
     
    .PARAMETER Export
    Whether exporting is enabled within the table.
 
    .PARAMETER Icon
    Sets an icon next to the title. Use New-UDIcon to create the icon.
     
    .EXAMPLE
    Creates a static table whether the columns of the table are the properties of the data specified.
 
    $Data = @(
        @{Dessert = 'Frozen yoghurt'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = 159; Fat = 6.0; Carbs = 24; Protein = 4.0}
    )
 
    New-UDTable -Id 'defaultTable' -Data $Data
 
    .EXAMPLE
    Creates a table where there are custom columns defined for that table.
 
     $Columns = @(
        New-UDTableColumn -Property Dessert -Title "A Dessert"
        New-UDTableColumn -Property Calories -Title Calories
        New-UDTableColumn -Property Fat -Title Fat
        New-UDTableColumn -Property Carbs -Title Carbs
        New-UDTableColumn -Property Protein -Title Protein
    )
 
    New-UDTable -Id 'customColumnsTable' -Data $Data -Columns $Columns
 
    .EXAMPLE
    Creates a table where the table has custom rendering for one of the columns and an export button.
 
    $Columns = @(
        New-UDTableColumn -Property Dessert -Title Dessert -Render {
            $Item = $Body | ConvertFrom-Json
            New-UDButton -Id "btn$($Item.Dessert)" -Text "Click for Dessert!" -OnClick { Show-UDToast -Message $Item.Dessert }
        }
        New-UDTableColumn -Property Calories -Title Calories
        New-UDTableColumn -Property Fat -Title Fat
        New-UDTableColumn -Property Carbs -Title Carbs
        New-UDTableColumn -Property Protein -Title Protein
    )
 
    New-UDTable -Id 'customColumnsTableRender' -Data $Data -Columns $Columns -Sort -Export
 
    .EXAMPLE
    Creates a table within a New-UDDynamic that refreshes automatically on an interval.
 
    New-UDDynamic -Content {
        $DynamicData = @(
            @{Dessert = 'Frozen yoghurt'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
            @{Dessert = 'Ice cream sandwich'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
            @{Dessert = 'Eclair'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
            @{Dessert = 'Cupcake'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
            @{Dessert = 'Gingerbread'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
        )
 
        New-UDTable -Id 'dynamicTable' -Data $DynamicData
    } -AutoRefresh -AutoRefreshInterval 2
 
    .EXAMPLE
    Creates a table that uses the LoadData script block to load data dynamically.
     
    New-UDTable -Id 'loadDataTable' -Columns $Columns -LoadData {
    $Query = $Body | ConvertFrom-Json
 
    @(
        @{Dessert = 'Frozen yoghurt'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Ice cream sandwich'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Eclair'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Cupcake'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
        @{Dessert = 'Gingerbread'; Calories = (Get-Random); Fat = 6.0; Carbs = 24; Protein = 4.0}
    ) | Out-UDTableData -Page 0 -TotalCount 5 -Properties $Query.Properties
     
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid().ToString(),
        [Parameter()]
        [string]$Title = "",
        [Parameter(Mandatory, ParameterSetName = "Static")]
        [AllowEmptyCollection()]
        [AllowNull()]
        [object[]]$Data,
        [Parameter(Mandatory, ParameterSetName = "Dynamic")]
        [Endpoint]$LoadData,
        [Parameter(ParameterSetName = "Static")]
        [Parameter(Mandatory, ParameterSetName = "Dynamic")]
        [Hashtable[]]$Columns,
        [Parameter()]
        [Endpoint]$OnRowSelection,
        [Parameter()]
        [Alias("Sort")]
        [Switch]$ShowSort,
        [Parameter()]
        [Alias("Filter")]
        [Switch]$ShowFilter,
        [Parameter()]
        [Alias("Search")]
        [Switch]$ShowSearch,
        [Parameter()]
        [Switch]$Dense,
        [Parameter()]
        [Alias("Export")]
        [Switch]$ShowExport,
        [Parameter()]
        [Switch]$StickyHeader,
        [Parameter()]
        [int]$PageSize = 5,
        [Parameter()]
        [int[]]$PageSizeOptions = @(),
        [Parameter()]
        [Alias("Select")]
        [Switch]$ShowSelection,
        [Parameter()]
        [Alias("Paging")]
        [Switch]$ShowPagination,
        [Parameter()]
        [ValidateSet("default", "checkbox", "none")]
        [string]$Padding = "default",
        [Parameter()]
        [ValidateSet("small", "medium")]
        [string]$Size = "medium",
        [Parameter()]
        [Hashtable]$TextOption = (New-UDTableTextOption),
        [Parameter()]
        [string[]]$ExportOption = @("XLSX", "PDF", "JSON", "CSV"),
        [Parameter()]
        [Endpoint]$OnExport,
        [Parameter()]
        [Switch]$DisablePageSizeAll,
        [Parameter()]
        [ValidateSet('ascending', 'descending')]
        [string]$DefaultSortDirection,
        [Parameter()]
        [Switch]$HideToggleAllRowsSelected,
        [Parameter()]
        [Switch]$DisableMultiSelect,
        [Parameter()]
        [Switch]$DisableSortRemove,
        [Parameter()]
        [Hashtable]$Icon
    )

    Begin {
        function getDefaultSortColumn {
            param(
                [Parameter()]    
                [object[]]$Columns
            )
            $DefaultSortColumn = $Columns.Where( { $_.DefaultSortColumn })
            $DefaultSortColumn.field
        }
    }
    Process {

        if ($OnExport) {
            $OnExport.Register($Id + 'Export', $PSCmdlet)
        }

        if (($null -eq $Columns) -and ($null -ne $Data)) {
            $item = $Data | Select-Object -First 1 | ConvertTo-FlatObject
    
            if ($item -is [Hashtable]) {
                $Columns = foreach ($member in $item.Keys) {
                    if ($ShowSearch) {
                        New-UDTableColumn -Property $member -IncludeInSearch
                    }
                    elseif ($ShowExport) {
                        New-UDTableColumn -Property $member -IncludeInExport
                    }
                    elseif ($ShowSearch -and $ShowExport) {
                        New-UDTableColumn -Property $member -IncludeInExport -IncludeInSearch
                    }
                    else {
                        New-UDTableColumn -Property $member
                    }
                }
                
            }
            else {
                $Columns = foreach ($member in $item.PSObject.Properties) {
                    if ($ShowSearch) {
                        New-UDTableColumn -Property $member.Name -IncludeInSearch
                    }
                    elseif ($ShowExport) {
                        New-UDTableColumn -Property $member.Name -IncludeInExport
                    }
                    elseif ($ShowSearch -and $ShowExport) {
                        New-UDTableColumn -Property $member.Name -IncludeInExport -IncludeInSearch
                    }
                    else {
                        New-UDTableColumn -Property $member.Name
                    }
                }
                
            }
        }

        if ($LoadData) {
            $LoadData.Register($Id, $PSCmdlet, @{ "TableColumns" = $Columns })
        }
            
        if ($OnRowSelection) {
            $OnRowSelection.Register($Id + 'OnRowSelection', $PSCmdlet)
        }        
        
        if ($Columns) {
            $RenderedColumns = $Columns.Where( { $null -ne $_.Render })
            if ($Data.Count -ge 1) {
                foreach ($Item in $Data) {
                    foreach ($Column in $RenderedColumns) {
                        $EventData = $Item
                        $RenderedData = & ([ScriptBlock]::Create($Column.Render.ToString()))
                        if (-not $RenderedData) {
                            $RenderedData = ""
                        }

                        if ($Item -isnot [hashtable]) {
                            Add-Member -InputObject $Item -MemberType NoteProperty -Name "rendered$($Column.field)" -Value $RenderedData -Force
                        }
                        else {
                            $Item["rendered$($Column.field)"] = $RenderedData
                        }

                    }
                }
            }
        }

    }

    End {

        $defaultSortColumn = getDefaultSortColumn($Columns)
        if ($defaultSortColumn -and -not $DefaultSortDirection) {
            $DefaultSortDirection = 'ascending'
        }

        if ($Data) { 
            $Data = [Array]($Data | ConvertTo-FlatObject) 
            if ($Data -isnot [Array]) {
                $Data = @($Data)
            }
        }
        else {
            $Data = @()
        }

        @{
            id                        = $Id 
            assetId                   = $MUAssetId 
            isPlugin                  = $true 
            type                      = "mu-table"
    
            title                     = $Title
            columns                   = $Columns
            defaultSortColumn         = $defaultSortColumn
            data                      = $Data
            showSort                  = $ShowSort.IsPresent
            showFilter                = $ShowFilter.IsPresent
            showSearch                = $ShowSearch.IsPresent
            showExport                = $ShowExport.IsPresent 
            showSelection             = $ShowSelection.IsPresent 
            showPagination            = $ShowPagination.IsPresent
            isStickyHeader            = $StickyHeader.IsPresent
            isDense                   = $Dense.IsPresent
            loadData                  = $LoadData
            onRowSelection            = $OnRowSelection
            userPageSize              = $PageSize
            userPageSizeOptions       = if ($PageSizeOptions.Count -gt 0) { $PageSizeOptions }else { @(5, 10, 20, 50) }
            padding                   = $Padding.ToLower()
            size                      = $Size
            textOption                = $TextOption
            exportOption              = $ExportOption | ForEach-Object { $_.ToUpper() }
            onExport                  = $OnExport
            disablePageSizeAll        = $DisablePageSizeAll.IsPresent
            defaultSortDirection      = $DefaultSortDirection.ToLower()
            hideToggleAllRowsSelected = $HideToggleAllRowsSelected.IsPresent
            disableMultiSelect        = $DisableMultiSelect.IsPresent
            disableSortRemove         = $DisableSortRemove.IsPresent
            icon                      = $Icon
        }
    }
}

function New-UDTableTextOption {
    <#
    .SYNOPSIS
    Creates a hashtable to set the text options of a table.
     
    .DESCRIPTION
    Creates a hashtable to set the text options of a table.
     
    .PARAMETER ExportAllCsv
    Overrides the Export All to CSV text.
     
    .PARAMETER ExportCurrentViewCsv
    Overrides the Export Current View as CSV text.
     
    .PARAMETER ExportAllXLSX
    Overrides the Export All to XLSX text.
     
    .PARAMETER ExportCurrentViewXLSX
    Overrides the Export Current View as XLSX text.
     
    .PARAMETER ExportAllPDF
    Overrides the Export All to PDF text.
     
    .PARAMETER ExportCurrentViewPDF
    Overrides the Export Current View as PDF text.
     
    .PARAMETER ExportAllJson
    Overrides the Export All to JSON text.
     
    .PARAMETER ExportCurrentViewJson
    Overrides the Export Current View as JSON text.
     
    .PARAMETER Search
    Overrides the Search text. You can use {0} to use as a place holder for the number of rows.
     
    .PARAMETER FilterSearch
    Overrides the column filter text. You can use {0} to use as a place holder for the number of rows.
     
    .EXAMPLE
    $Options = New-UDTableTextOption -Search "Filter all the rows"
    New-UDTable -Data $Data -TextOption $Ootions
     
    .NOTES
    General notes
    #>

    param(
        [Parameter()]
        [string]$ExportAllCsv = "Export all as CSV",
        [Parameter()]
        [string]$ExportCurrentViewCsv = "Export Current View as CSV",
        [Parameter()]
        [string]$ExportAllXLSX = "Export all as XLSX",
        [Parameter()]
        [string]$ExportCurrentViewXLSX = "Export Current View as XLSX",
        [Parameter()]
        [string]$ExportAllPDF = "Export all as PDF",
        [Parameter()]
        [string]$ExportCurrentViewPDF = "Export Current View as PDF",
        [Parameter()]
        [string]$ExportAllJson = "Export all as JSON",
        [Parameter()]
        [string]$ExportCurrentViewJson = "Export Current View as JSON",
        [Parameter()]
        [string]$ExportFileName = "File Name",
        [Parameter()]
        [string]$Search = "Search {0} records...",
        [Parameter()]
        [string]$FilterSearch = "Search {0} records..."
    )

    @{
        exportAllCsv          = $ExportAllCsv
        exportCurrentViewCsv  = $ExportCurrentViewCsv
        exportAllXlsx         = $ExportAllXLSX
        exportCurrentViewXlsx = $ExportCurrentViewXLSX
        exportAllPdf          = $ExportAllPDF
        exportCurrentViewPdf  = $ExportCurrentViewPDF
        exportAllJson         = $ExportAllJson
        exportCurrentViewJson = $ExportCurrentViewJson
        exportFileName        = $ExportFileName
        search                = $Search
        filterSearch          = $FilterSearch
    }
}

function New-UDTableColumn {
    <#
    .SYNOPSIS
    Defines a table column.
     
    .DESCRIPTION
    Defines a table column. Use this cmdlet in conjunction with New-UDTable's -Column property. Table columns can be used to control many aspects of the columns within a table.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Property
    The property to select from the data.
     
    .PARAMETER Title
    The title of the column to show at the top of the table.
     
    .PARAMETER Render
    How to render this table. Use this parameter instead of property to render custom content within a column. The $Body variable will contain the current row being rendered.
     
    .PARAMETER Sort
    Whether this column supports sorting.
     
    .PARAMETER Filter
    Whether this column supports filtering.
     
    .PARAMETER Search
    Whether this column supports searching.
 
    .PARAMETER Hidden
    Includes a column in the table but does not show it. This is useful for columns that are used for filtering and exporting but are not meant to be displayed in the table.
     
    .EXAMPLE
    See New-UDTable for examples.
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid().ToString(),
        [Parameter(Mandatory)]
        [string]$Property, 
        [Parameter()]
        [string]$Title,
        [Parameter()]
        [ScriptBlock]$Render,
        [Parameter()]
        [Alias("Sort")]
        [switch]$ShowSort,
        [Parameter()]
        [Alias("Filter")]
        [switch]$ShowFilter,
        [Parameter()]
        [ValidateSet("text", "select", "fuzzy", "slider", "range", "date", "number", 'autocomplete')]
        [string]$FilterType = "text",
        [Parameter()]
        [hashtable]$Style = @{},
        [Parameter()]
        [int]$Width,
        [Parameter()]
        [Alias("Search")]
        [switch]$IncludeInSearch,
        [Parameter()]
        [Alias("Export")]
        [switch]$IncludeInExport,
        [Parameter()]
        [switch]$DefaultSortColumn,
        [Parameter()]
        [ValidateSet('center', 'inherit', 'justify', 'left', 'right')]
        [string]$Align = 'inherit',
        [Parameter()]
        [Switch]$Truncate,
        [Parameter()]
        [ValidateSet('basic', 'datetime', 'alphanumeric')]
        [string]$SortType = 'alphanumeric',
        [Parameter()]
        [Switch]$Hidden
    )

    if ($null -eq $Title -or $Title -eq '') {
        $Title = $Property
    }

    if ($Width -gt 0) {
        $style["maxWidth"] = $width
        $style["width"] = $width
    }

    if ($Truncate) {
        $style["whiteSpace"] = "nowrap"
        $style["overflow"] = "hidden"
        $style["textOverflow"] = "ellipsis"
    }

    @{
        id                  = $Id 
        field               = $Property.ToLower()
        title               = $Title 
        showSort            = $ShowSort.IsPresent 
        showFilter          = $ShowFilter.IsPresent
        filterType          = $FilterType.ToLower()
        includeInSearch     = $IncludeInSearch.IsPresent
        includeInExport     = $IncludeInExport.IsPresent
        isDefaultSortColumn = $DefaultSortColumn.IsPresent
        render              = $Render
        width               = $Width
        align               = $Align
        style               = $Style
        sortType            = $SortType
        hidden              = $Hidden.IsPresent

    }
}
function Out-UDTableData {
    <#
    .SYNOPSIS
    Formats data to be output from New-UDTable's -LoadData script block.
     
    .DESCRIPTION
    Formats data to be output from New-UDTable's -LoadData script block.
     
    .PARAMETER Data
    The data to return from LoadData.
     
    .PARAMETER Page
    The current page we are on within the table.
     
    .PARAMETER TotalCount
    The total count of items within the data set.
     
    .PARAMETER Properties
    The properties that are currently passed from the table. You can return the array from the $EventData.Properties array.
     
    .EXAMPLE
    See New-UDTable for examples.
    #>

    param(
        [Parameter(ValueFromPipeline = $true, Mandatory)]
        [object]$Data,
        [Parameter(Mandatory)]
        [int]$Page,
        [Parameter(Mandatory)]
        [int]$TotalCount,
        [Parameter(Mandatory)]
        [Alias("Property")]
        [string[]]$Properties
    )

    Begin {
        $DataPage = @{
            data       = @() 
            page       = $Page 
            totalCount = $TotalCount
        }
    }

    Process {
        $item = @{}
        foreach ($property in $Properties) {
            $RenderedColumn = $TableColumns.Where( { $_.field -eq $property -and $_.Render })
            if ($RenderedColumn) {
                $EventData = $Data
                $item["rendered" + $property] = & ([ScriptBlock]::Create($RenderedColumn.Render.ToString()))
            }

            $item[$property] = $Data[$property]
        }
        $DataPage.data += $item
    }

    End {
        if ($DataPage.data) {
            $DataPage.data = [Array]($DataPage.data | ConvertTo-FlatObject)
            $DataPage
        }
    }
}
function New-UDTabs {
    <#
    .SYNOPSIS
    Creates a new set of tabs.
     
    .DESCRIPTION
    Creates a new set of tabs. Tabs can be used to show lots of content on a single page.
     
    .PARAMETER Tabs
    The tabs to put within this container.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER RenderOnActive
    Whether to render the tabs when they are clicked. Is this value isn't present, all the tabs are rendered, even if they are not shown.
     
    .PARAMETER Orientation
    The orientation of the tabs. Valid values are horizontal and vertical.
 
    .PARAMETER Variant
    The variantion of tabs. Valid values are standard, fullWidth and scrollable.
 
    .PARAMETER ScrollButtons
    The behavior of the scrollbuttons. Valid values are on, off, auto and desktop. On will enable scroll buttons no matter what. off will disable all scroll buttons. Auto will show scrollbuttons when necessary. Desktop will show scrollbuttons on medium and large screens.
     
    .EXAMPLE
    Creates a basic set of tabs.
 
    New-UDTabs -Tabs {
        New-UDTab -Text "Tab1" -Id 'Tab1' -Content {
            New-UDElement -Tag div -Id 'tab1Content' -Content { "Tab1Content"}
        }
        New-UDTab -Text "Tab2" -Id 'Tab2' -Content {
            New-UDElement -Tag div -Id 'tab2Content' -Content { "Tab2Content"}
        }
        New-UDTab -Text "Tab3" -Id 'Tab3' -Content {
            New-UDElement -Tag div -Id 'tab3Content' -Content { "Tab3Content"}
        }
    }
 
    .EXAMPLE
    Creates a set of tabs that only render when they are clicked.
 
    New-UDTabs -RenderOnActive -Id 'DynamicTabs' -Tabs {
        New-UDTab -Text "Tab1" -Id 'DynamicTab1' -Dynamic -Content {
            New-UDElement -Tag div -Id 'DynamicTab1Content' -Content { Get-Date }
        }
        New-UDTab -Text "Tab2" -Id 'DynamicTab2' -Dynamic -Content {
            New-UDElement -Tag div -Id 'DynamicTab2Content' -Content { Get-Date }
        }
        New-UDTab -Text "Tab3" -Id 'DynamicTab2' -Dynamic -Content {
            New-UDElement -Tag div -Id 'DynamicTab3Content' -Content { Get-Date }
        }
    }
 
    .EXAMPLE
    Creates a vertical set of tabs.
 
    New-UDTabs -Id 'verticalTabs' -Orientation 'vertical' -Tabs {
        New-UDTab -Text "Tab1" -Content {
            New-UDElement -Tag div -Content { Get-Date }
        }
        New-UDTab -Text "Tab2" -Content {
            New-UDElement -Tag div -Content { Get-Date }
        }
        New-UDTab -Text "Tab3" -Content {
            New-UDElement -Tag div -Content { Get-Date }
        }
    }
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ScriptBlock]$Tabs,
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter()]
        [Switch]$RenderOnActive,
        [Parameter()]
        [ValidateSet('horizontal', 'vertical')]
        [string]$Orientation = "horizontal",
        [Parameter()]
        [ValidateSet('fullWidth', 'scrollable', 'standard')]
        [string]$Variant = 'standard',
        [Parameter()]
        [ValidateSet('on', 'off', 'auto', 'desktop')]
        [string]$ScrollButtons = 'auto',
        [Parameter()]
        [switch]$Centered
    )

    End {

        $c = New-UDErrorBoundary -Content $Tabs

        @{
            isPlugin        = $true
            assetId         = $MUAssetId
            type            = "mu-tabs"
            tabs            = $c
            id              = $id
            renderOnClick   = $RenderOnActive.IsPresent
            orientation     = $Orientation
            variant         = $Variant
            scrollButtons   = $ScrollButtons.ToLower()
            centered        = $Centered.IsPresent
        }
    }
}

function New-UDTab {
    <#
    .SYNOPSIS
    Creates a new tab.
     
    .DESCRIPTION
    Creates a new tab. Use New-UDTabs as a container for tabs.
     
    .PARAMETER Text
    The text to display for this tab.
     
    .PARAMETER Content
    The content to display when the tab is selected.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Dynamic
    Whether this tab is dynamic. Dynamic tabs won't render until they are displayed.
     
    .PARAMETER Icon
    The Icon to display within the tab header.
     
    .PARAMETER Disabled
    Whether this tab is disabled.
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Text,
        [Parameter(Mandatory)]
        [ScriptBlock]$Content,
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),
        [Parameter()]
        [switch]$Dynamic,
        [Parameter()]
        [object]$Icon,
        [Parameter()]
        [switch]$Disabled
    )

    End {

        if ($null -ne $Content -and $Dynamic) {
            New-UDEndpoint -Id $Id -Endpoint $Content | Out-Null
        }

        $c = New-UDErrorBoundary -Content $Content

        @{
            isPlugin = $true
            assetId  = $MUAssetId
            type     = "mu-tab"
            label     = $Text
            icon = $Icon
            content  = $c
            id       = $Id
            dynamic = $Dynamic.IsPresent
            disabled = $Disabled.IsPresent
        }
    }
}
function New-UDTextbox {
    <#
    .SYNOPSIS
    Creates a textbox.
     
    .DESCRIPTION
    Creates a textbox. Textboxes can be used by themselves or within a New-UDForm.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
 
    .PARAMETER Label
    A label to show above this textbox.
     
    .PARAMETER Placeholder
    A placeholder to place within the text box.
     
    .PARAMETER Value
    The current value of the textbox.
     
    .PARAMETER Type
    The type of textbox. This can be values such as text, password or email.
     
    .PARAMETER Disabled
    Whether this textbox is disabled.
     
    .PARAMETER Icon
    The icon to show next to the textbox.
     
    .PARAMETER Autofocus
    Whether to autofocus this textbox.
     
    .EXAMPLE
    Creates a standard textbox.
 
    New-UDTextbox -Label 'text' -Id 'txtLabel'
 
    .EXAMPLE
    Creates a password textbox.
 
    New-UDTextbox -Label 'password' -Id 'txtPassword' -Type 'password'
    #>

    param(
        [Parameter()]
        [String]$Id = ([Guid]::NewGuid()),
        [Parameter()]
        [string]$Label,
        [Parameter()]
        [string]$Placeholder,
        [Parameter()]
        $Value,
        [Parameter()]
        [ValidateSet('text', 'password', 'email')]
        [String]$Type = 'text',
        [Parameter()]
        [Switch]$Disabled,
        [Parameter()]
        [PSTypeName('UniversalDashboard.Icon')]$Icon,
        [Parameter()]
        [Switch]$Autofocus,
        [Parameter()]
        [Switch]$Multiline,
        [Parameter()]
        [int]$Rows = 1,
        [Parameter()]
        [int]$RowsMax = 9999,
        [Parameter()]
        [Switch]$FullWidth,
        [Parameter()]
        [string[]]$Mask,
        [Parameter()]
        [ValidateSet("filled", "outlined", "standard")]
        [string]$Variant = "standard"
        
    )

    @{
        id = $id 
        assetId = $MUAssetId 
        isPlugin = $true 
        type = "mu-textbox"

        label = $Label
        helperText = $placeholder
        value = $value 
        textType = $type 
        disabled = $Disabled.IsPresent 
        autofocus = $AutoFocus.IsPresent
        icon = $icon
        multiline = $Multiline.IsPresent
        rows = $Rows 
        rowsMax = $RowsMax
        fullWidth = $FullWidth.IsPresent
        mask = $Mask
        variant = $Variant.ToLower()
    }
}


class ThemeColors {
    [string]$primary
    [string]$secondary
    [string]$background
    [string]$text
    [string]$muted

    ThemeColors() { 
    }

    ThemeColors([string]$primary, [string]$secondary) {
        $this.primary = $primary
        $this.secondary = $secondary
    }

    ThemeColors([string]$primary, [string]$secondary, [string]$background, [string]$text, [string]$muted) {
        $this.Primary = $Primary
        $this.Secondary = $Secondary
        $this.Background = $Background
        $this.Text = $Text
        $this.Muted = $Muted
    }

}

class ThemeColorModes {
    [ThemeColors]$Dark

    ThemeColorModes() {
    }

    ThemeColorModes([ThemeColors]$Dark) {
        $this.Dark = $Dark
    }
}

function New-UDTheme {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$name,
        [Parameter()]
        [ThemeColors]$Colors,
        [Parameter()]
        [ThemeColorModes]$ColorModes,
        [Parameter()]
        [hashtable]$Variants
    )
    end {
        $theme = [ordered]@{
            name     = $Name
            colors   = if ($Colors) {
                $Colors 
            }
            else {
                [ThemeColors]::new() 
            }
            modes    = if ($ColorModes) {
                $ColorModes 
            }
            else {
                [ThemeColorModes]::new([ThemeColors]::new()) 
            }
            variants = $Variants
        }
        $Result = $theme | ConvertTo-Json -Depth 10 
        $Result
    }
}





function New-UDTimePicker {
    <#
    .SYNOPSIS
    Creates a time picker.
     
    .DESCRIPTION
    Creates a time picker. This component can be used stand alone or within New-UDForm.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Label
    The label to show with the time picker.
     
    .PARAMETER OnChange
    A script block to call when the time is changed. The $EventData variable contains the currently selected time.
     
    .PARAMETER Value
    The current value of the time picker.
 
    .PARAMETER Locale
    Change the language of the time picker.
     
    .EXAMPLE
    Creates a new time picker
 
    New-UDTimePicker -Id 'timePicker'
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid().ToString(),
        [Parameter()]
        [string]$Label,
        [Parameter()]
        [Endpoint]$OnChange, 
        [Parameter()]
        [string]$Value,
        [Parameter()]
        [ValidateSet("en", "de", 'ru', 'fr')]
        [string]$Locale = "en"
    )

    if ($OnChange) {
        $OnChange.Register($Id, $PSCmdlet)
    }

    @{
        id       = $Id 
        type     = 'mu-timepicker'
        asset    = $MUAssetId
        isPlugin = $true 

        onChange = $OnChange 
        value    = $Value
        label    = $Label
        locale   = $Locale.ToLower()
    }
}
function New-UDTooltip {
    <#
    .SYNOPSIS
    A tooltip component.
     
    .DESCRIPTION
    A tooltip component. Tooltips can be placed over an other component to display a popup when the user hovers over the nested component.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Place
    Where to place the tooltip.
     
    .PARAMETER Type
    The type of tooltip.
     
    .PARAMETER Effect
    An effect to apply to the tooltip.
     
    .PARAMETER TooltipContent
    Content to display within the tooltip.
     
    .PARAMETER Content
    Content that activates the tooltip when hovered.
     
    .EXAMPLE
    A simple tooltip.
 
    New-UDTooltip -Content {
        New-UDTypography -Text 'Hover me'
    } -TooltipContent {
        New-UDTypography -Text 'I'm a tooltip'
    }
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [ValidateSet("top", "bottom", "left", "right")]
        [string]$Place = "top",
        [Parameter()]
        [ValidateSet("dark", "success", "warning", "error", "info", "light")]
        [string]$Type = "dark",
        [Parameter()]
        [ValidateSet("float", "solid")]
        [string]$Effect,
        [Parameter(Mandatory)]
        [ScriptBlock]$TooltipContent,
        [Parameter(Mandatory)]
        [ScriptBlock]$Content
    )

    @{
        type = "ud-tooltip"
        tooltipType = $Type
        effect = $Effect 
        place = $Place
        id = $Id
        tooltipContent = New-UDErrorBoundary -Content $TooltipContent
        content = New-UDErrorBoundary -Content $Content
    }
}
function New-UDTransferList 
{
    <#
    .SYNOPSIS
    Creates a transfer list component.
     
    .DESCRIPTION
    A transfer list (or "shuttle") enables the user to move one or more list items between lists.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Item
    A list of items that can be transferred between lists. Use New-UDTransferListItem to create an item.
     
    .PARAMETER SelectedItem
    A list of selected items. Use the value of item to transfer items between lists.
     
    .PARAMETER OnChange
    A script block that is executed when the user changes the selected items.
     
    .EXAMPLE
    New-UDTransferList -Item {
        New-UDTransferListItem -Name 'test1' -Value 1
        New-UDTransferListItem -Name 'test2' -Value 2
        New-UDTransferListItem -Name 'test3' -Value 3
        New-UDTransferListItem -Name 'test4' -Value 4
        New-UDTransferListItem -Name 'test5' -Value 5
    }
 
    Creates a basic transfer list.
     
    .EXAMPLE
    New-UDTransferList -Item {
        New-UDTransferListItem -Name 'test1' -Value 1
        New-UDTransferListItem -Name 'test2' -Value 2
        New-UDTransferListItem -Name 'test3' -Value 3
        New-UDTransferListItem -Name 'test4' -Value 4
        New-UDTransferListItem -Name 'test5' -Value 5
    } -OnChange {
        Show-UDToast ($EventData | ConvertTo-Json)
    }
 
    Creates a basic transfer list that shows a toast when the values are changed.
 
    .EXAMPLE
    New-UDForm -Content {
        New-UDTransferList -Item {
            New-UDTransferListItem -Name 'test1' -Value 1
            New-UDTransferListItem -Name 'test2' -Value 2
            New-UDTransferListItem -Name 'test3' -Value 3
            New-UDTransferListItem -Name 'test4' -Value 4
            New-UDTransferListItem -Name 'test5' -Value 5
        }
    } -OnSubmit {
        Show-UDToast ($EventData | ConvertTo-Json)
    }
 
    Creates a transfer list that is part of a form.
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [ScriptBlock]$Item,
        [Parameter()]
        [string[]]$SelectedItem = @(),
        [Parameter()]
        [Endpoint]$OnChange
    )

    if ($OnChange) {
        $OnChange.Register($Id + "onChange", $PSCmdlet)
    }

    @{
        type = 'mu-transfer-list'
        assetId = $MUAssetId
        isPlugin = $true 

        id = $id 
        item = $Item.Invoke()
        selectedItem = $SelectedItem
        onChange = $OnChange
    }
}

function New-UDTransferListItem {
    <#
    .SYNOPSIS
    Creates an item for use in a transfer list.
     
    .DESCRIPTION
    Creates an item for use in a transfer list.
     
    .PARAMETER Name
    The display name of the item.
     
    .PARAMETER Value
    The value of the item.
    #>

    param(
        [Parameter(Mandatory = $true)]
        [String]$Name,
        [Parameter(Mandatory = $true)]
        [String]$Value
    )

    @{
        name = $Name 
        value = $Value 
    }
}

function New-UDTransition {
    <#
    .SYNOPSIS
    Creates a transition effect.
     
    .DESCRIPTION
    Creates a transition effect.
     
    .PARAMETER Id
    The ID of this component.
     
    .PARAMETER Collapse
    Creates a collapse transition.
     
    .PARAMETER CollapseHeight
    The height of the content when collapsed.
     
    .PARAMETER Fade
    Creates a fade transition.
     
    .PARAMETER Grow
    Creates a grow transition.
     
    .PARAMETER Slide
    Creates a slide transition.
     
    .PARAMETER SlideDirection
    The direction of the slide transition.
     
    .PARAMETER Zoom
    Creates a zoom transition.
     
    .PARAMETER Children
    The content or children to transition.
     
    .PARAMETER In
    Whether the content is transitioned in. You can use Set-UDElement to trigger a transition.
     
    .PARAMETER Timeout
    The number of milliseconds it takes to transition.
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid().ToString(),
        [Parameter(ParameterSetName = "Collapse")]
        [Switch]$Collapse,
        [Parameter(ParameterSetName = "Collapse")]
        [int]$CollapseHeight,
        [Parameter(ParameterSetName = "Fade")]
        [Switch]$Fade,
        [Parameter(ParameterSetName = "Grow")]
        [Switch]$Grow,
        [Parameter(ParameterSetName = "Slide")]
        [Switch]$Slide,
        [Parameter(ParameterSetName = "Slide")]
        [ValidateSet("Left", "Right", "Down", "Up")]
        [string]$SlideDirection = "Down",
        [Parameter(ParameterSetName = "Zoom")]
        [Switch]$Zoom,
        [Parameter(Mandatory)]
        [Alias("Content")]
        [scriptblock]$Children,
        [Parameter()]
        [Switch]$In,
        [Parameter()]
        [int]$Timeout
    )

    @{
        type = "mu-transition"
        id = $Id 
        asset = $MUAssetId
        isPlugin = $true 

        transition = $PSCmdlet.ParameterSetName.ToLower()
        collapseHeight = $CollapseHeight
        slideDirection = $SlideDirection
        timeout = $Timeout
        in = $In.IsPresent
        children = & $Children 
    }
}
function New-UDTreeView {
    <#
    .SYNOPSIS
    Creates a new tree view.
     
    .DESCRIPTION
    Creates a new tree view.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Node
    A collection of root nodes to show within the tree view.
     
    .PARAMETER OnNodeClicked
    A script block that is called when a node is clicked. $EventData will contain the node that was clicked.
     
    .EXAMPLE
    Creates a basic tree view.
 
    New-UDTreeView -Node {
        New-UDTreeNode -Id 'Root' -Name 'Root' -Children {
            New-UDTreeNode -Id 'Level1' -Name 'Level 1' -Children {
                New-UDTreeNode -Id 'Level2' -Name 'Level 2'
            }
            New-UDTreeNode -Name 'Level 1' -Children {
                New-UDTreeNode -Name 'Level 2'
            }
            New-UDTreeNode -Name 'Level 1' -Children {
                New-UDTreeNode -Name 'Level 2'
            }
        }
        New-UDTreeNode -Id 'Root2' -Name 'Root 2'
    }
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter(Mandatory)]
        [ScriptBlock]$Node,
        [Parameter()]
        [Endpoint]$OnNodeClicked,
        [Parameter()]
        [Hashtable]$Style
    )

    End {
        if ($OnNodeClicked) {
            $OnNodeClicked.Register($Id, $PSCmdlet)
        }
        
        @{
            assetId = $AssetId 
            isPlugin = $true 
            id = $Id 
            type = 'mu-treeview'

            node = & $Node 
            onNodeClicked = $OnNodeClicked
            style = $Style
        }
    }
}

function New-UDTreeNode {
    <#
    .SYNOPSIS
    Creates a tree node.
     
    .DESCRIPTION
    Creates a tree node. This cmdlet should be used with New-UDTreeView.
     
    .PARAMETER Name
    The name of the node. This is displayed within the UI.
     
    .PARAMETER Id
    The ID of the node. This is passed to the $EventData property when the OnNodeClicked script block is set.
     
    .PARAMETER Children
    The children of this node. This should be a collection of New-UDTreeNodes.
     
    .EXAMPLE
    See New-UDTreeView for examples.
    #>

    param(
        [Parameter(Mandatory, Position = 1)]
        [string]$Name,
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [ScriptBlock]$Children
    )

    End {
        $ChildrenArray = $null
        if ($PSBoundParameters.ContainsKey("Children"))
        {
            $ChildrenArray = & $Children
        }
        
        @{
            name = $Name 
            id = $Id 
            children = $ChildrenArray 
            icon = $IconName 
            expandedIcon = $ExpandedIconName
        }
    }
}
function New-UDTypography {
    <#
    .SYNOPSIS
    Creates typography.
     
    .DESCRIPTION
    Creates typography. Typography allows you to configure text within a dashboard.
     
    .PARAMETER Id
    The ID of the component. It defaults to a random GUID.
     
    .PARAMETER Variant
    The type of text to display.
     
    .PARAMETER Text
    The text to format.
     
    .PARAMETER Content
    The content to format.
     
    .PARAMETER Style
    A set of CSS styles to apply to the typography.
     
    .PARAMETER ClassName
    A CSS className to apply to the typography.
     
    .PARAMETER Align
    How to align the typography.
     
    .PARAMETER GutterBottom
    The gutter bottom.
     
    .PARAMETER NoWrap
    Disables text wrapping.
     
    .PARAMETER Paragraph
    Whether this typography is a paragraph.
     
    .EXAMPLE
     
    New-UDTypography -Text 'Hello' -Paragraph
 
    #>

    [CmdletBinding(DefaultParameterSetName = "text")]
    param(
        [Parameter()]
        [string]$Id = ([Guid]::NewGuid()).ToString(),

        [Parameter()]
        [ValidateSet ("h1", "h2", "h3", "h4", "h5", "h6", "subtitle1", "subtitle2", "body1", "body2", "caption", "button", "overline", "srOnly", "inherit", "display4", "display3", "display2", "display1", "headline", "title", "subheading")]
        [string]$Variant,

        [Parameter(ParameterSetName = "text", Position = 0)]
        [string]$Text,

        [Parameter(ParameterSetName = "endpoint")]
        [scriptblock]$Content,

        [Parameter()]
        [Hashtable]$Style = @{},

        [Parameter()]
        [string]$ClassName,

        [Parameter()]
        [ValidateSet ("inherit", "left", "center", "right", "justify")]
        [string]$Align,

        [Parameter()]
        [switch]$IsEndPoint,

        [Parameter()]
        [Switch]$GutterBottom,

        [Parameter()]
        [Switch]$NoWrap,

        [Parameter()]
        [Switch]$Paragraph,

        [Parameter()]
        [ValidateSet('normal', 'bold', 'lighter', 'bolder', '100', '200', '300', '400', '500', '600', '700', '800', '900')]
        [string]$FontWeight
        
    )

    End {

        if ($FontWeight)
        {
            $Style["fontWeight"] = $FontWeight
        }

        $MUTypography = @{
            #This needs to match what is in the register function call of chips.jsx
            type = "mu-typography"
            #Eventually everything will be a plugin so we wont need this.
            isPlugin = $true
            #This was set in the UniversalDashboard.MaterialUI.psm1 file
            assetId = $MUAssetId

            id = $Id
            className = $ClassName
            variant = $Variant
            noWrap = $NoWrap.IsPresent
            isParagraph = $Paragraph.IsPresent
            text = $Text
            style = $Style
            align = $Align
            content = $TextContent 
            gutterBottom = $GutterBottom.IsPresent
        }

        $MUTypography.PSTypeNames.Insert(0, 'UniversalDashboard.MaterialUI.Typography') | Out-Null

        $MUTypography
    }
}
function New-UDUpload {
    <#
    .SYNOPSIS
    Upload files
     
    .DESCRIPTION
    Upload files. This component works with UDForm and UDStepper.
     
    .PARAMETER Id
    The ID of the uploader.
     
    .PARAMETER Accept
    The type of files to accept. By default, this component accepts all files.
     
    .PARAMETER OnUpload
    A script block to execute when a file is uploaded. This $body parameter will contain JSON in the following format:
 
    {
        data: 'base64 encoded string of file data',
        name: 'filename',
        type: 'type of file, if known'
    }
     
    .PARAMETER Text
    The text to display on the upload button.
     
    .PARAMETER Variant
    The variant of button
     
    .PARAMETER Color
    The color of the button.
 
    .EXAMPLE
    A file uploader
 
    New-UDDashboard -Title "Hello, World!" -Content {
        New-UDUpload -Text "Upload" -OnUpload {
            Show-UDToast $Body
        }
    }
     
    .EXAMPLE
    A file uploader in a form
 
    New-UDDashboard -Title "Hello, World!" -Content {
        New-UDForm -Content {
            New-UDUpload -Text "Upload"
        } -OnSubmit {
            Show-UDToast $Body
        }
    }
    #>

    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [string]$Accept = "*",
        [Parameter()]
        [Endpoint]$OnUpload,
        [Parameter()]
        [string]$Text,
        [Parameter()]
        [ValidateSet("text", "outlined", "contained")]
        [string]$Variant = 'contained',
        [Parameter()]
        [string]$Color = "primary"
    )

    if ($OnUpload)
    {
        $OnUpload.Register($Id, $PSCmdlet)
    }

    @{
        type = "mu-upload"
        isPlugin = $true
        assetId = $MUAssetId
        id = $id

        accept = $Accept 
        onUpload = $OnUpload
        text = $Text
        variant = $Variant
        color = $Color
    }
}
function Add-UDElement {
    param(
        [Parameter(Mandatory)]
        [string]$ParentId,
        [Parameter(Mandatory)]
        [ScriptBlock]$Content,
        [Parameter()]
        [Switch]$Broadcast
    )

    $NewContent = & $Content

    $Data = @{
        componentId = $ParentId
        elements = $NewContent
    }

    if ($Broadcast)
    {
        $DashboardHub.SendWebSocketMessage("addElement", $Data)
    }
    else 
    {
        $DashboardHub.SendWebSocketMessage($ConnectionId, "addElement", $Data)
    }    
}
function Clear-UDElement
{
    param(
        [Parameter(Mandatory)]
        [string]$Id,
        [Parameter()]
        [Switch]$Broadcast
    )

    if ($Broadcast)
    {
        $DashboardHub.SendWebSocketMessage("clearElement", $Id)
    }
    else 
    {
        $DashboardHub.SendWebSocketMessage($ConnectionId, "clearElement", $Id)
    }
}

function Get-UDElement 
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Id
    )

    $requestId = ''

    $requestId = [Guid]::NewGuid().ToString()

    $Data = @{
        requestId = $requestId 
        componentId = $Id
    }

    $DashboardHub.SendWebSocketMessage($ConnectionId, "requestState", $Data)
    $stateRequestService.Get($requestId)    
}

function Hide-UDModal 
{
    $DashboardHub.SendWebSocketMessage($ConnectionId, "closeModal", $null)
}
function Hide-UDToast
{
    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Id
    )

    $DashboardHub.SendWebSocketMessage($ConnectionId, "hideToast", $Id)
}

function Invoke-UDJavaScript
{
    param(
        [Parameter(Mandatory)]
        [string]$JavaScript
    )

    $DashboardHub.SendWebSocketMessage($ConnectionId, "invokejavascript", $JavaScript)
}
function Invoke-UDRedirect
{
    param(
        [Parameter(Mandatory)]
        [string]$Url,
        [Parameter()]
        [Switch]$OpenInNewWindow
    )

    $Data = @{
        url = $Url 
        openInNewWindow = $OpenInNewWindow.IsPresent
    }

    $DashboardHub.SendWebSocketMessage($ConnectionId, "redirect", $Data)
}
function Remove-UDElement
{
    param(
        [Parameter(Mandatory)]
        [string]$Id, 
        [Parameter()]
        [string]$ParentId,
        [Parameter()]
        [Switch]$Broadcast
    )

    $Data = @{
        componentId = $Id 
        parentId = $ParentId
    }

    if ($Broadcast)
    {
        $DashboardHub.SendWebSocketMessage("removeElement", $Data)
    }
    else 
    {
        $DashboardHub.SendWebSocketMessage($ConnectionId, "removeElement", $Data)
    }
}

function Select-UDElement 
{   
    param(
        [Parameter(Mandatory, ParameterSetName = "Normal")]
        [string]$Id,
        [Parameter(ParameterSetName = "Normal")]
        [Switch]$ScrollToElement
    )

    $Data = @{
        id = $Id 
        scrollToElement = $ScrollToElement
    }

    $DashboardHub.SendWebSocketMessage($ConnectionId, "select", $Data)
}
function Set-UDClipboard
{
    param(
        [Parameter(Mandatory)]
        [string]$Data,
        [Parameter()]
        [Switch]$ToastOnSuccess,
        [Parameter()]
        [Switch]$ToastOnError
    )

    $cpData = @{
        data = $Data 
        toastOnSuccess = $ToastOnSuccess.IsPresent
        toastOnError = $ToastOnError.IsPresent
    }

    $DashboardHub.SendWebSocketMessage($ConnectionId, "clipboard", $cpData)
}

function Set-UDElement
{
    param(
        [Parameter(Mandatory)]
        [string]$Id,
        [Alias("Attributes")]
        [Parameter()]
        [Hashtable]$Properties,
        [Parameter()]
        [Switch]$Broadcast,
        [Parameter()]
        [ScriptBlock]$Content
    )

    if ($Content -and -not $Properties)
    {
        $Properties = @{}
    }

    if ($Content)
    {
        $Properties['content'] = [Array](& $Content)
    }

    $Data = @{
        componentId = $Id 
        state = $Properties
    }

    if ($Broadcast)
    {
        $DashboardHub.SendWebSocketMessage("setState", $data)
    }
    else
    {
        $DashboardHub.SendWebSocketMessage($ConnectionId, "setState", $Data)
    }
}
function Show-UDModal
{
    param(
        [Parameter()]
        [Switch]$FullScreen,
        [Parameter()]
        [ScriptBlock]$Footer,
        [Parameter()]
        [ScriptBlock]$Header,
        [Parameter()]
        [ScriptBlock]$Content,
        [Parameter()]
        [Switch]$Persistent,
        [Parameter()]
        [Switch]$FullWidth,
        [Parameter()]
        [ValidateSet("xs", "sm", "md", "lg", "xl")]
        [string]$MaxWidth
    )

    $Modal = @{
        dismissible = -not $Persistent.IsPresent
        maxWidth = $MaxWidth
        fullWidth = $FullWidth.IsPresent
        fullScreen = $FullScreen.IsPresent
    }

    if ($null -ne $Footer)
    {
        $Modal['footer'] = & $Footer
    }

    if ($null -ne $Header)
    {
        $Modal['header'] = & $Header
    }

    if ($null -ne $Content)
    {
        $Modal['content'] = & $Content
    }

    $DashboardHub.SendWebSocketMessage($ConnectionId, "showModal", $modal)
}

function Show-UDToast 
{
    <#
    .SYNOPSIS
    Displays a toast message to the user.
     
    .DESCRIPTION
    Displays a toast message to the user.
     
    .PARAMETER Message
    The message to display.
     
    .PARAMETER MessageColor
    The text color of the message.
     
    .PARAMETER MessageSize
    The size of the message.
     
    .PARAMETER Duration
    The duration in milleseconds before the message disappears.
     
    .PARAMETER Title
    The title to display.
     
    .PARAMETER TitleColor
    The text color of the title.
     
    .PARAMETER TitleSize
    The size of the title.
     
    .PARAMETER Id
    The ID of the toast. For use with Hide-UDToast.
     
    .PARAMETER BackgroundColor
    The background color of the toast.
     
    .PARAMETER Theme
    Light or dark theme.
     
    .PARAMETER Icon
    The icon to display in the toast.
     
    .PARAMETER IconColor
    The color of the icon.
     
    .PARAMETER Position
    Where to display the toast.
     
    .PARAMETER HideCloseButton
    Hides the close button.
     
    .PARAMETER CloseOnClick
    Closes the toast when clicked.
     
    .PARAMETER CloseOnEscape
    Closes the toast when esc is pressed.
     
    .PARAMETER ReplaceToast
    Replaces an existing toast if one is already showing.
     
    .PARAMETER RightToLeft
    Right to left text.
     
    .PARAMETER Balloon
    Creates a balloon toast.
     
    .PARAMETER Overlay
    Displays an overlay behind the toast.
     
    .PARAMETER OverlayClose
    Allow the user to close the overlay.
     
    .PARAMETER OverlayColor
    The color of the overlay.
     
    .PARAMETER TransitionIn
    The transition in effect.
     
    .PARAMETER TransitionOut
    The transition out effect.
     
    .PARAMETER Broadcast
    Broadcasts the toast to all connected users.
     
    .PARAMETER Persistent
    Prevents the toast from closing due to the duration.
     
    .EXAMPLE
    Show-UDToast -Message 'Hello, World!'
 
    Shows a toast message.
    #>

    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Message,
        [Parameter()]
        [DashboardColor]$MessageColor,
        [Parameter()]
        [string]$MessageSize,
        [Parameter()]
        [int]$Duration = 1000,
        [Parameter()]
        [string]$Title, 
        [Parameter()]
        [DashboardColor]$TitleColor,
        [Parameter()]
        [string]$TitleSize,
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [DashboardColor]$BackgroundColor,
        [Parameter()]
        [ValidateSet("light", "dark")]
        [string]$Theme,
        [Parameter()]
        [FontAwesomeIcons]$Icon,
        [Parameter()]
        [DashboardColor]$IconColor,
        [Parameter()]
        [ValidateSet("bottomRight", "bottomLeft", "topRight", "topLeft", "topCenter", "bottomCenter", "center")]
        [string]$Position = "topRight",
        [Parameter()]
        [Switch]$HideCloseButton,
        [Parameter()]
        [Switch]$CloseOnClick,
        [Parameter()]
        [Switch]$CloseOnEscape,
        [Parameter()]
        [Switch]$ReplaceToast,
        [Parameter()]
        [Switch]$RightToLeft,
        [Parameter()]
        [Switch]$Balloon,
        [Parameter()]
        [Switch]$Overlay,
        [Parameter()]
        [Switch]$OverlayClose,
        [Parameter()]
        [DashboardColor]$OverlayColor,
        [Parameter()]
        [ValidateSet("bounceInLeft", "bounceInRight", "bounceInUp", "bounceInDown", "fadeIn", "fadeInDown", "fadeInUp", "fadeInLeft", "fadeInRight", "flipInX")]
        [string]$TransitionIn = "fadeInUp",
        [Parameter()]
        [ValidateSet("bounceInLeft", "bounceInRight", "bounceInUp", "bounceInDown", "fadeIn", "fadeInDown", "fadeInUp", "fadeInLeft", "fadeInRight", "flipInX")]
        [string]$TransitionOut = "fadeOut",
        [Parameter()]
        [Switch]$Broadcast,
        [Parameter()]
        [Switch]$Persistent
    )

    $faIcon = $null
    if ($PSBoundParameters.ContainsKey('Icon'))
    {
        $faIcon = "fa fa-$($Icon.ToString().Replace("_", "-"))"
    }

    if ($Persistent)
    {
        $Duration = $false
    }

    $options = @{
        close = -not $HideCloseButton.IsPresent
        id = $Id
        message = $Message
        messageColor = $MessageColor.HtmlColor
        messageSize = $MessageSize
        title = $Title
        titleColor = $TitleColor.HtmlColor
        titleSize = $TitleSize
        timeout = $Duration
        position = $Position
        backgroundColor = $BackgroundColor.HtmlColor
        theme = $Theme
        icon = $faIcon
        iconColor = $IconColor.HtmlColor
        displayMode = if ($ReplaceToast.IsPresent) {2 } else { 0 }
        rtl = $RightToLeft.IsPresent
        balloon = $Balloon.IsPresent
        overlay = $Overlay.IsPresent
        overlayClose = $OverlayClose.IsPresent
        overlayColor = $OverlayColor.HtmlColor
        closeOnClick = $CloseOnClick.IsPresent
        closeOnEscape = $CloseOnEscape.IsPresent
        transitionIn = $TransitionIn
        transitionOut = $TransitionOut
    }

    if ($Broadcast)
    {
        $DashboardHub.SendWebSocketMessage("showToast", $options)
    }
    else 
    {
        $DashboardHub.SendWebSocketMessage($ConnectionId, "showToast", $options)
    }
}

function Sync-UDElement
{
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string[]]$Id,
        [Parameter()]
        [Switch]$Broadcast
    )

    Process 
    {
        foreach($i in $Id) 
        {
            if ($Broadcast)
            {
                $DashboardHub.SendWebSocketMessage("syncElement", $I)
            }
            else
            {
                $DashboardHub.SendWebSocketMessage($ConnectionId, "syncElement", $I)
            }
        } 
    }
}