Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Authors>Shayan Habibi based on work by Vladimir Schur and contributors</Authors>
<Copyright>Copyright (c) Shayan Habibi 2025</Copyright>
<Version>1.1.4</Version>
<PackageVersion>1.1.4</PackageVersion>
<Version>1.1.5</Version>
<PackageVersion>1.1.5</PackageVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
5 changes: 4 additions & 1 deletion Partas.Solid.FablePlugin/Plugin.fs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,10 @@ module internal rec AST =
|> Some
| _, _ -> None
// member val set/get
| Set(IdentExpr(Ident.IdentIs ctx IdentType.ReturnVal), FieldSet(prop), _, expr, _) ->
| Set( // With inheritance within the same module etc, there may be a TypeCast on the IdentExpr.
Expr.TypeCastDrill ctx (IdentExpr(Ident.IdentIs ctx IdentType.ReturnVal)), // TypeCast drill to first expr
FieldSet(prop),
_, expr, _) ->
(prop, transform ctx expr)
|> Some
// Inlined named overloads to `[<DefaultValue>] val mutable` properties/attributes
Expand Down
5 changes: 5 additions & 0 deletions Partas.Solid.FablePlugin/Spec.fs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,11 @@ module internal Expr =
_
) -> Some compiledName
| _ -> None

/// Elucidates the first expr that is not a type cast.
let rec (|TypeCastDrill|) (ctx: PluginContext): Expr -> Expr = function
| TypeCast(TypeCastDrill ctx expr,_)
| expr -> expr
module internal Type =
let rec private (|GetDeclaredType|_|) (ctx: PluginContext): Type -> Type option = function
| Type.DeclaredType(_) as typ -> Some typ
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { twMerge } from "tailwind-merge";
import { clsx } from "clsx";
import { splitProps } from "solid-js";
import { Indicator, Control, Input, Root } from "@kobalte/core/checkbox";

export function Lib_cn_Z35CD86D0(classes) {
return twMerge(clsx(classes));
}

export function Checkbox(props) {
const [PARTAS_LOCAL, PARTAS_OTHERS] = splitProps(props, ["indeterminate", "class"]);
return <Root indeterminate={PARTAS_LOCAL.indeterminate}
class={Lib_cn_Z35CD86D0(["items-top group relative flex space-x-2", PARTAS_LOCAL.class])}
{...PARTAS_OTHERS} bool:n$={false}>
{(_arg) => <>
<Input class="peer" />
<Control class="size-4 shrink-0 rounded-sm border border-primary
ring-offset-background disabled:cursor-not-allowed disabled:opacity-50
peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring
peer-focus-visible:ring-offset-2 data-[checked]:border-none
data-[indeterminate]:border-none data-[checked]:bg-primary
data-[indeterminate]:bg-primary data-[checked]:text-primary-foreground
data-[indeterminate]:text-primary-foreground">
<Indicator>
{PARTAS_LOCAL.indeterminate ? ("😐") : ("😀")}
</Indicator>
</Control>
</>}
</Root>;
}

export const selectColumn = <Checkbox checked={true} />;

export const selectColumn2 = {
header: (headerProps) => <Checkbox checked={true}
indeterminate={true} />,
};

204 changes: 204 additions & 0 deletions Partas.Solid.Tests/IssueCases/InheritedProperty/InheritedProperty.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
module Partas.Solid.Tests.IssueCases.InheritedProperty.InheritedProperty

open Partas.Solid
open Partas.Solid.Aria
open Fable.Core
open Fable.Core.JsInterop

type [<Erase>] Lib =
[<Import("twMerge", "tailwind-merge")>]
static member twMerge (classes: string) : string = jsNative
[<Import("clsx", "clsx")>]
static member clsx(classes: obj): string = jsNative
static member cn (classes: string array): string = classes |> Lib.clsx |> Lib.twMerge
open Partas.Solid
open Fable.Core
open Browser.Types
module Spec =
let [<Literal>] checkbox = "@kobalte/core/checkbox"
module Kobalte =

[<Erase>]
type CheckboxRenderProp =
abstract checked': Accessor<bool> //v0.13.9
abstract indeterminate: Accessor<bool> //v0.13.9
/// <summary>
///
/// </summary>
/// <param name="data-valid">Present when the checkbox is valid according to validation rules</param>
/// <param name="data-invalid">Present when the checkbox is invalid according to the validation rules</param>
/// <param name="data-required">Present when the checkbox is required</param>
/// <param name="data-disabled">Present when the checkbox is disabled</param>
/// <param name="data-readonly">Present when the checkbox is readonly</param>
/// <param name="data-checked">Present when the checkbox is checked</param>
/// <param name="data-indeterminate">Present when the checkbox is indeterminate</param>
[<Erase; Import("Root", Spec.checkbox)>]
type Checkbox() =
interface HtmlTag
interface Polymorph
interface ChildLambdaProvider<CheckboxRenderProp>
[<DefaultValue>] val mutable checked' : bool //v0.13.9
[<DefaultValue>] val mutable defaultChecked : bool //v0.13.9
[<DefaultValue>] val mutable onChange : bool -> unit //v0.13.9
[<DefaultValue>] val mutable indeterminate : bool //v0.13.9
[<DefaultValue>] val mutable name : string //v0.13.9
[<DefaultValue>] val mutable value : string //v0.13.9
[<DefaultValue>] val mutable required : bool //v0.13.9
[<DefaultValue>] val mutable disabled : bool //v0.13.9
[<DefaultValue>] val mutable readOnly : bool //v0.13.9
[<DefaultValue>] val mutable children : CheckboxRenderProp -> HtmlElement //v0.13.9

[<Erase; RequireQualifiedAccess>]
module Checkbox = //v0.13.9
/// <summary>
///
/// </summary>
/// <param name="data-valid">Present when the checkbox is valid according to validation rules</param>
/// <param name="data-invalid">Present when the checkbox is invalid according to the validation rules</param>
/// <param name="data-required">Present when the checkbox is required</param>
/// <param name="data-disabled">Present when the checkbox is disabled</param>
/// <param name="data-readonly">Present when the checkbox is readonly</param>
/// <param name="data-checked">Present when the checkbox is checked</param>
/// <param name="data-indeterminate">Present when the checkbox is indeterminate</param>
[<Erase; Import("Indicator", Spec.checkbox)>]
type Indicator() = //v0.13.9
inherit div()
interface Polymorph
[<DefaultValue>] val mutable forceMount : bool //v0.13.9
/// <summary>
///
/// </summary>
/// <param name="data-valid">Present when the checkbox is valid according to validation rules</param>
/// <param name="data-invalid">Present when the checkbox is invalid according to the validation rules</param>
/// <param name="data-required">Present when the checkbox is required</param>
/// <param name="data-disabled">Present when the checkbox is disabled</param>
/// <param name="data-readonly">Present when the checkbox is readonly</param>
/// <param name="data-checked">Present when the checkbox is checked</param>
/// <param name="data-indeterminate">Present when the checkbox is indeterminate</param>
[<Erase; Import("ErrorMessage", Spec.checkbox)>]
type ErrorMessage() = //v0.13.9
inherit div()
interface Polymorph
[<DefaultValue>] val mutable forceMount : bool //v0.13.9
/// <summary>
///
/// </summary>
/// <param name="data-valid">Present when the checkbox is valid according to validation rules</param>
/// <param name="data-invalid">Present when the checkbox is invalid according to the validation rules</param>
/// <param name="data-required">Present when the checkbox is required</param>
/// <param name="data-disabled">Present when the checkbox is disabled</param>
/// <param name="data-readonly">Present when the checkbox is readonly</param>
/// <param name="data-checked">Present when the checkbox is checked</param>
/// <param name="data-indeterminate">Present when the checkbox is indeterminate</param>
[<Erase; Import("Label", Spec.checkbox)>]
type Label() = //v0.13.9
inherit label()
interface Polymorph
/// <summary>
///
/// </summary>
/// <param name="data-valid">Present when the checkbox is valid according to validation rules</param>
/// <param name="data-invalid">Present when the checkbox is invalid according to the validation rules</param>
/// <param name="data-required">Present when the checkbox is required</param>
/// <param name="data-disabled">Present when the checkbox is disabled</param>
/// <param name="data-readonly">Present when the checkbox is readonly</param>
/// <param name="data-checked">Present when the checkbox is checked</param>
/// <param name="data-indeterminate">Present when the checkbox is indeterminate</param>
[<Erase; Import("Description", Spec.checkbox)>]
type Description() =
inherit div() //v0.13.9
interface Polymorph
/// <summary>
///
/// </summary>
/// <param name="data-valid">Present when the checkbox is valid according to validation rules</param>
/// <param name="data-invalid">Present when the checkbox is invalid according to the validation rules</param>
/// <param name="data-required">Present when the checkbox is required</param>
/// <param name="data-disabled">Present when the checkbox is disabled</param>
/// <param name="data-readonly">Present when the checkbox is readonly</param>
/// <param name="data-checked">Present when the checkbox is checked</param>
/// <param name="data-indeterminate">Present when the checkbox is indeterminate</param>
[<Erase; Import("Control", Spec.checkbox)>]
type Control() = //v0.13.9
inherit div()
interface Polymorph
/// <summary>
///
/// </summary>
/// <param name="data-valid">Present when the checkbox is valid according to validation rules</param>
/// <param name="data-invalid">Present when the checkbox is invalid according to the validation rules</param>
/// <param name="data-required">Present when the checkbox is required</param>
/// <param name="data-disabled">Present when the checkbox is disabled</param>
/// <param name="data-readonly">Present when the checkbox is readonly</param>
/// <param name="data-checked">Present when the checkbox is checked</param>
/// <param name="data-indeterminate">Present when the checkbox is indeterminate</param>
[<Erase; Import("Input", Spec.checkbox)>]
type Input() = //v0.13.9
inherit input()
interface Polymorph

[<Erase; AutoOpen>]
module CheckboxContext =
[<AllowNullLiteral; Interface>]
type CheckboxContext =
abstract member value: Accessor<string> with get
abstract member dataset: Accessor<{|``data-checked``:string option; ``data-indeterminate``: string option|}> with get
abstract member ``checked``: Accessor<bool> with get
abstract member indeterminate: Accessor<bool> with get
abstract member inputRef: Accessor<HTMLInputElement option> with get
abstract member generateId: (string -> string) with get
abstract member toggle: (unit -> unit) with get
abstract member setIsChecked: (bool -> unit) with get
abstract member setIsFocused: (bool -> unit) with get
abstract member setInputRef: (HTMLInputElement -> unit) with get
[<Import("useCheckboxContext", Spec.checkbox)>]
let useCheckboxContext (): CheckboxContext = jsNative



[<Erase>]
type Checkbox() =
inherit Kobalte.Checkbox()
[<SolidTypeComponent>]
member props.checkbox =
Kobalte.Checkbox(
indeterminate = props.indeterminate,
class' = Lib.cn [| "items-top group relative flex space-x-2"; props.class' |]
).spread(props) { yield fun _ -> Fragment() {

Kobalte.Checkbox.Input(class'="peer")
Kobalte.Checkbox.Control(
class' = "size-4 shrink-0 rounded-sm border border-primary
ring-offset-background disabled:cursor-not-allowed disabled:opacity-50
peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring
peer-focus-visible:ring-offset-2 data-[checked]:border-none
data-[indeterminate]:border-none data-[checked]:bg-primary
data-[indeterminate]:bg-primary data-[checked]:text-primary-foreground
data-[indeterminate]:text-primary-foreground"
) {
Kobalte.Checkbox.Indicator() {
if props.indeterminate then
"😐"
// Minus(class' = "size-4", strokeWidth = 2)
else
"😀"
// Check(class' = "size-4", strokeWidth = 2)
}
}
}
}

[<SolidComponent(ComponentFlag.DebugMode)>]
let selectColumn: obj =
Checkbox(checked' = true)

[<SolidComponent>]
let selectColumn2: obj =
{|
header = fun headerProps ->
Checkbox(
checked' = true,
indeterminate = true
)
|}

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="InheritedProperty.fs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Partas.Solid\Partas.Solid.fsproj" />
</ItemGroup>

</Project>
6 changes: 5 additions & 1 deletion Partas.Solid.Tests/IssueTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,8 @@ let ``#27 list CE 'empty' rendered out`` () =

[<Fact>]
let ``#28 ThisArg is transformed`` () =
runIssueCase "ThisArgTransforms"
runIssueCase "ThisArgTransforms"

[<Fact>]
let ``#29 val mutable overloads render 2`` () =
runIssueCase "InheritedProperty"
3 changes: 3 additions & 0 deletions Partas.Solid.Tests/Partas.Solid.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@
<Compile Include="IssueCases\ThisArgTransforms\ThisArgTransformsTypes.fs" />
<Compile Include="IssueCases\ThisArgTransforms\ThisArgTransforms.fs" />
<Content Include="IssueCases\ThisArgTransforms\ThisArgTransforms.expected" />
<Compile Include="IssueCases\InheritedProperty\InheritedProperty.fs" />
<None Include="IssueCases\InheritedProperty\InheritedProperty.fsproj" />
<Content Include="IssueCases\InheritedProperty\InheritedProperty.expected" />
<Compile Include="Common.fs" />
<Compile Include="IssueTests.fs" />
<Compile Include="SolidTests.fs" />
Expand Down
2 changes: 0 additions & 2 deletions Partas.Solid/Builder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ module Builder =
type VoidNode =
inherit HtmlTag

let inline jsx value = JSX.jsx value |> unbox<HtmlElement>

[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
type IChildLambdaProvider = inherit HtmlElement
/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions Partas.Solid/Partas.Solid.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
<PackageTags>Partas;Oxpecker;F#;FSharp;Fable;fable-javascript;Web;Framework;Solid;Solidjs;DSL</PackageTags>
<Authors>Shayan Habibi, Vladimir Schur and Contributors</Authors>
<Copyright>Copyright (c) Shayan Habibi 2025, based on work by Vladimir Schur 2024</Copyright>
<Version>1.1.4</Version>
<Version>1.1.5</Version>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageVersion>1.1.4</PackageVersion>
<PackageReleaseNotes>Start bindings API expanded. Jsx alias as `jsx` which casts to HtmlElement</PackageReleaseNotes>
<PackageVersion>1.1.5</PackageVersion>
<PackageReleaseNotes>Fix SolidStart FileRoutes helper toRoute. Stronger attribute recognition.</PackageReleaseNotes>
</PropertyGroup>

<ItemGroup>
Expand Down
14 changes: 7 additions & 7 deletions Partas.Solid/SolidStartBindings.fs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ module Bindings =
static member defineConfig(config: obj): obj = jsNative
static member inline defineConfig(objList: (string * obj) list) = defineConfig(JsInterop.createObj objList)

[<Import("StartServer", Spec.path + "/server")>]
[<ImportMember(Spec.path + "/server")>]
type StartServer() =
interface FragmentNode
[<DefaultValue>]
Expand All @@ -124,31 +124,31 @@ module Bindings =
[<ImportMember(Spec.path + "/client")>]
let mount (fn: unit -> HtmlElement, el: obj) = jsNative

[<Import("StartClient", Spec.path + "/client")>]
[<ImportMember(Spec.path + "/client")>]
type StartClient() =
interface HtmlElement
member inline this.mount(el: obj) = Client.mount((fun () -> this), el)

type private T = HttpStatusCode

[<Import("HttpStatusCode", Spec.path)>]
[<ImportMember(Spec.path)>]
type HttpStatusCode() =
interface VoidNode
[<DefaultValue>]
val mutable code: T

[<Import("HttpHeader", Spec.path)>]
[<ImportMember(Spec.path)>]
type HttpHeader() =
interface VoidNode
[<DefaultValue>]
val mutable name: string
[<DefaultValue>]
val mutable value: string

[<Import("FileRoutes", Spec.path + "/router")>]
[<ImportMember(Spec.path + "/router")>]
type FileRoutes() =
interface HtmlElement
member inline this.ToRoute() = unbox<Router.Bindings.Route> this
[<Emit("$0")>]
member this.ToRoute() = unbox<Router.Bindings.Route> this

type Exports with
[<ImportMember(Spec.path)>]
Expand Down
Loading