Partas.Solid 1.1.6

There is a newer version of this package available.
See the version list below for details.
dotnet add package Partas.Solid --version 1.1.6
                    
NuGet\Install-Package Partas.Solid -Version 1.1.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Partas.Solid" Version="1.1.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Partas.Solid" Version="1.1.6" />
                    
Directory.Packages.props
<PackageReference Include="Partas.Solid" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Partas.Solid --version 1.1.6
                    
#r "nuget: Partas.Solid, 1.1.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Partas.Solid@1.1.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Partas.Solid&version=1.1.6
                    
Install as a Cake Addin
#tool nuget:?package=Partas.Solid&version=1.1.6
                    
Install as a Cake Tool

<div id="top"></div>

<br />

<div align="center"> <a href="https://github.com/shayanhabibi/Partas.Solid" target="_blank"> <img src="https://github.com/shayanhabibi/Partas.Solid/blob/master/Public/Partas_d00b_00a%20icon.png" height="42px"/> </a> <h3 align="center">Partas.Solid</h3> <p align="center"> <img src="https://www.solidjs.com/img/logo/without-wordmark/logo.svg" height="24px" style="border-radius:8px;" /> <kbd>Solid-JS wrapper in Oxpecker style.</kbd> <img src="https://fsharp.org/img/logo/fsharp256.png" height="24px" /> </p> </div>

<div align="center">

Scc Count Badge Scc Count Badge Scc Count Badge

</div>


Getting Started

See the docs to get started!

Legacy Docs

Solid-ui (Shadcn port) Partas.Solid.UI

Bindings for different libraries Partas.Solid.Bindings

Oxpecker.Solid

This is an opinionated fork of Oxpecker.Solid that keeps the original DSL style, but more aggressively transforms F# input to produce correct JSX.

Please support the original release.

Differences

See the docs

Legacy Docs

<details> <summary>Aggressive transformation of AST</summary> <p>

Reduce undefined behaviour

I feel this iteration has less specific pattern matchers, which prevents what might some deem as undocumented behaviour.

As an example, currently Oxpecker would perform the following conversion:

let mutable show = true

[<SolidComponent>]
let Button () =
    let this = button() {
        "some boiler plate"
    }
    div(class'="MyButton") {
        if show then this else ()
    }
export let show = createAtom(true);

export function Button() {
    return <button>
        some boiler plate
    </button>;
}

As opposed to Partas:

export let show = createAtom(true);

export function Button() {
    const this$ = <button>
        some boiler plate
    </button>;
    return <div class="MyButton">
        {show() ? this$ : undefined}
    </div>;
}

Using ternary conditional expressions in solid-js works, although there is also the <Match> or <Show> tags. </p>

<p align="right">(<a href="#top">back to top</a>)</p> </details>

<details> <summary> Ability to define custom components/tags and use them in the same DSL style </summary>

<p> Partas.Solid provides an extra attribute which can be applied to members of a Tag type definition using props as the self identifier. The self identifier, props, allows type safe access to your defined properties which can be set in Oxpecker style.

[<Erase>]
type MyCustomDiv() =
    inherit div()
    [<Erase>]
    member val bordered: bool = jsNative with get,set
    [<SolidTypeComponent>]
    member props.constructor =
    // the props self identifier is a requirement
    // the member name has no influence on output
        div(class' = if props.bordered then "border border-border" else "") { props.children }

[<SolidComponent>]
let App() =
    MyCustomDiv(bordered = true) {
        "Hello world!"
    }

</p>

<p align="right">(<a href="#top">back to top</a>)</p>

</details>


Example

See the Partas.Solid.UI.Playground for a comprehensive example of a component library built ENTIRELY in F# using <kbd>Partas.Solid</kbd>, <kbd>Tailwind</kbd> and a host of other libraries like <kbd>TanStack Table</kbd> and <kbd>Kobalte</kbd>.

<details>

<summary> A comprehensive component and example output </summary>

[<Erase>]
type Sidebar() =
    inherit div()
    member val side: sidebar.Side = unbox null with get,set
    member val variant: sidebar.Variant = unbox null with get,set
    member val collapsible: sidebar.Collapsible = unbox null with get,set
    [<SolidTypeComponentAttribute>]
    member props.constructor =
        props.side <- Left
        props.variant <- sidebar.Sidebar
        props.collapsible <- Offcanvas
        let ctx = Context.useSidebar()
        let (isMobile, state, openMobile, setOpenMobile) = (ctx.isMobile, ctx.state, ctx.openMobile, ctx.openMobile)
        Switch() {
            Match(when' = (props.collapsible = sidebar.None)) {
                
                div(class' = Lib.cn [|
                    "test flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground"
                    props.class'
                |]).spread props
                    { props.children }
                
            }
            Match(when' = isMobile()) {
                
                Sheet( open' = openMobile(), onOpenChange = !!setOpenMobile )
                    .spread props {
                        SheetContent(
                            class' = "w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
                            position = !!props.side
                            ).data("sidebar", !!sidebar.Sidebar)
                            .data("mobile", "true")
                            .style'(createObj [ "--sidebar-width" ==> sidebarWidthMobile ])
                            { div(class' = "flex size-full flex-col") { props.children } }
                    }
                
            }
            Match(when' = (isMobile() |> not)) {
                // gap handler on desktop
                div(
                class' = Lib.cn [|
                    "relative h-svh w-[--sidebar-width] bg-transparent transition-[width] duration-200 ease-linear"
                    "group-data-[collapsible=offcanvas]:w-0"
                    "group-data-[side=right]:rotate-180"
                    if (props.variant = sidebar.Floating || props.variant = sidebar.Inset) then
                        "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
                    else "group-data-[collapsible=icon]:w-[--sidebar-width-icon]"
                |]
                )
                
                div(
                class' = Lib.cn [|
                    "fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex"
                    if props.side = sidebar.Left then
                        "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
                    else "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]"
                    // Adjust the padding for floating and inset variants.
                    if props.variant = sidebar.Floating || props.variant = sidebar.Inset then
                        "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
                    else "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l"
                    props.class' 
                |]
                    ).spread props
                    {
                        div(
                            class' = "flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow"
                        ).data("sidebar", !!sidebar.Sidebar)
                            { props.children }
                    }
            }
            
        }

export function Sidebar(props) {
    props = mergeProps({
        side: "left", variant: "sidebar", collapsible: "offcanvas",
    }, props);
    const [PARTAS_LOCAL, PARTAS_OTHERS] = splitProps(props, ["collapsible", "class", "children", "side", "variant"]);
    const ctx = Context_useSidebar();
    const isMobile = ctx.isMobile;
    return <Switch>
        <Match when={PARTAS_LOCAL.collapsible === "none"}>
            <div
                class={twMerge(clsx(["test flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground", PARTAS_LOCAL.class]))}
                {...PARTAS_OTHERS} bool:n $={false}>
                {PARTAS_LOCAL.children}
            </div>
        </Match>
        <Match when={isMobile()}>
            <Sheet open={ctx.openMobile()}
                   onOpenChange={ctx.openMobile}
                   {...PARTAS_OTHERS} bool:n $={false}>
                <SheetContent class="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
                              position={PARTAS_LOCAL.side}
                              data-sidebar="sidebar"
                              data-mobile="true"
                              style={{
                                  "--sidebar-width": sidebar_sidebarWidthMobile,
                              }}>
                    <div class="flex size-full flex-col">
                        {PARTAS_LOCAL.children}
                    </div>
                </SheetContent>
            </Sheet>
        </Match>
        <Match when={!isMobile()}>
            <div
                class={twMerge(clsx(["relative h-svh w-[--sidebar-width] bg-transparent transition-[width] duration-200 ease-linear", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180", ((PARTAS_LOCAL.variant === "floating") ? (true) : (PARTAS_LOCAL.variant === "inset")) ? ("group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]") : ("group-data-[collapsible=icon]:w-[--sidebar-width-icon]")]))}/>
            <div
                class={twMerge(clsx(["fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex", (PARTAS_LOCAL.side === "left") ? ("left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]") : ("right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]"), ((PARTAS_LOCAL.variant === "floating") ? (true) : (PARTAS_LOCAL.variant === "inset")) ? ("p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]") : ("group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l"), PARTAS_LOCAL.class]))}
                {...PARTAS_OTHERS} bool:n $={false}>
                <div
                    class="flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow"
                    data-sidebar="sidebar">
                    {PARTAS_LOCAL.children}
                </div>
            </div>
        </Match>
    </Switch>;
}

</details>

Dev

To develop the plugin, ensure you exclude the plugin on compilation:

fable --exclude Partas.Solid.FablePlugin --noCache -o output -e .fs.jsx --run dotnet restore

There are a suite of tests to run to help inform if any changes have broken something else.

I've done my best to heavily document the plugin and the method of transformations.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (34)

Showing the top 5 NuGet packages that depend on Partas.Solid:

Package Downloads
Partas.Solid.Kobalte

Bindings for Kobalte for Partas.Solid

Partas.Solid.Primitives.Tween

Bindings for Solid-js primitives

Partas.Solid.Primitives

Bindings for Solid-js primitives

Partas.Solid.Corvu

Bindings for Solid-Corvu in Oxpecker.Solid Style compatible with Partas.Solid

Partas.Solid.Cmdk

Bindings for Solid-Cmdk in Oxpecker.Solid Style compatible with Partas.Solid

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 227 7/20/2025
1.1.6 181 7/19/2025
1.1.5 231 7/10/2025
1.1.4 142 7/1/2025
1.1.3 134 6/25/2025
1.1.2 135 6/25/2025
1.0.2 95 6/6/2025
1.0.1 81 6/6/2025
1.0.0 268 6/5/2025
1.0.0-alpha97 302 5/31/2025 1.0.0-alpha97 is deprecated because it is no longer maintained.
1.0.0-alpha96 297 5/31/2025 1.0.0-alpha96 is deprecated because it is no longer maintained.
1.0.0-alpha92 261 5/31/2025 1.0.0-alpha92 is deprecated because it is no longer maintained.
1.0.0-alpha91 348 5/29/2025 1.0.0-alpha91 is deprecated because it is no longer maintained.
1.0.0-alpha9 339 5/29/2025 1.0.0-alpha9 is deprecated because it is no longer maintained.
1.0.0-alpha8 340 5/29/2025 1.0.0-alpha8 is deprecated because it is no longer maintained.
1.0.0-alpha7 348 5/29/2025 1.0.0-alpha7 is deprecated because it is no longer maintained.
1.0.0-alpha6 343 5/29/2025 1.0.0-alpha6 is deprecated because it is no longer maintained.
1.0.0-alpha5 353 5/25/2025 1.0.0-alpha5 is deprecated because it is no longer maintained.
1.0.0-alpha4 432 5/24/2025 1.0.0-alpha4 is deprecated because it is no longer maintained.
1.0.0-alpha3 327 5/23/2025 1.0.0-alpha3 is deprecated because it is no longer maintained.
1.0.0-alpha2 329 5/23/2025 1.0.0-alpha2 is deprecated because it is no longer maintained.
1.0.0-alpha11 339 5/29/2025 1.0.0-alpha11 is deprecated because it is no longer maintained.
1.0.0-alpha1 351 5/22/2025 1.0.0-alpha1 is deprecated because it is no longer maintained.
0.2.35 338 5/17/2025 0.2.35 is deprecated because it is no longer maintained.
0.2.34 362 4/29/2025 0.2.34 is deprecated because it is no longer maintained.
0.2.33 359 4/29/2025 0.2.33 is deprecated because it is no longer maintained.
0.2.32 369 4/22/2025 0.2.32 is deprecated because it is no longer maintained.
0.2.31 366 4/21/2025 0.2.31 is deprecated because it is no longer maintained.
0.2.29 363 4/20/2025 0.2.29 is deprecated because it is no longer maintained.
0.2.28 388 4/16/2025 0.2.28 is deprecated because it is no longer maintained.
0.2.27 376 3/22/2025 0.2.27 is deprecated because it is no longer maintained.
0.2.26 297 3/22/2025 0.2.26 is deprecated because it is no longer maintained.
0.2.25 287 3/22/2025 0.2.25 is deprecated because it is no longer maintained.
0.2.24 309 3/21/2025 0.2.24 is deprecated because it is no longer maintained.
0.2.23 328 3/21/2025 0.2.23 is deprecated because it is no longer maintained.
0.2.22 412 3/20/2025 0.2.22 is deprecated because it is no longer maintained.
0.2.21 354 3/20/2025 0.2.21 is deprecated because it is no longer maintained.
0.2.20 361 3/20/2025 0.2.20 is deprecated because it is no longer maintained.
0.2.19 354 3/19/2025 0.2.19 is deprecated because it is no longer maintained.
0.2.18 342 3/16/2025 0.2.18 is deprecated because it is no longer maintained.
0.2.17 346 3/16/2025 0.2.17 is deprecated because it is no longer maintained.
0.2.16 324 3/14/2025 0.2.16 is deprecated because it is no longer maintained.
0.2.15 344 3/14/2025 0.2.15 is deprecated because it is no longer maintained.
0.2.14 360 3/12/2025 0.2.14 is deprecated because it is no longer maintained.
0.2.13 372 3/12/2025 0.2.13 is deprecated because it is no longer maintained.
0.2.12 363 3/12/2025 0.2.12 is deprecated because it is no longer maintained.
0.2.11 371 3/11/2025 0.2.11 is deprecated because it is no longer maintained.
0.2.10 382 3/9/2025 0.2.10 is deprecated because it is no longer maintained.
0.2.9 245 3/9/2025 0.2.9 is deprecated because it is no longer maintained and has critical bugs.
0.2.8 259 3/9/2025 0.2.8 is deprecated because it is no longer maintained and has critical bugs.
0.2.7 251 3/9/2025 0.2.7 is deprecated because it is no longer maintained and has critical bugs.
0.2.6 282 3/8/2025 0.2.6 is deprecated because it is no longer maintained and has critical bugs.
0.2.5 300 3/7/2025 0.2.5 is deprecated because it is no longer maintained and has critical bugs.
0.2.4 300 3/7/2025 0.2.4 is deprecated because it is no longer maintained and has critical bugs.
0.2.3 283 3/7/2025 0.2.3 is deprecated because it is no longer maintained and has critical bugs.
0.2.2 279 3/7/2025 0.2.2 is deprecated because it is no longer maintained and has critical bugs.
0.2.1 298 3/7/2025 0.2.1 is deprecated because it is no longer maintained and has critical bugs.
0.2.0 300 3/6/2025 0.2.0 is deprecated because it is no longer maintained and has critical bugs.
0.1.0 205 3/1/2025 0.1.0 is deprecated because it is no longer maintained and has critical bugs.