Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to simulate the javascript spread operator? #4002

Closed
halcwb opened this issue Jan 5, 2025 · 6 comments
Closed

How to simulate the javascript spread operator? #4002

halcwb opened this issue Jan 5, 2025 · 6 comments

Comments

@halcwb
Copy link

halcwb commented Jan 5, 2025

Question

When this code is transpiled:

            let options =
                {|
                    disableParsingRawHTML = true
                    wrapper = "React.Fragment"
                    overrides =
                        {|
                            // cannot add ...getOverrides(),
                            h1 =
                                {|
                                    props =
                                        {|
                                            sx =
                                                {|
                                                    fontSize = 24
                                                    fontWeight = "bold"
                                                    color = Mui.Colors.Blue.``700``
                                                |}
                                        |}
                                |}

                        |}
                |}

You will get:

        const options = {
            disableParsingRawHTML: true,
            overrides: {
                // this is a way to override the default styles
                // but is not transpiled by Fable
                ...getOverrides(),
                h1: {
                    props: {
                        sx: {
                            color: Colors_Blue_get_700(),
                            fontSize: 24,
                            fontWeight: "bold",
                        },
                    },
                },
            },
            wrapper: "React.Fragment",
        };

Only of course not the commented spread operator spreading the default settings.

So, the question is, if there is a way to spread a javascript object or array to another array or object from the F# perspective?

@joprice
Copy link
Contributor

joprice commented Jan 5, 2025

This issue is similar, but using createObj #3626 (comment). Object.assign (JS.Constructors.Object.assign) was suggested, and I've been using it in a few places to handle forwarding of props in Feliz bindings.

@halcwb
Copy link
Author

halcwb commented Jan 6, 2025

@joprice Thanks for the suggestion.

For now I took another approach as I write all my UI code as JSX. This is working now:

            JSX.jsx
                $"""
            import {{ MuiMarkdown, getOverrides }} from 'mui-markdown';
            import Box from '@mui/material/Box';

            <Box sx={sx}>                
                <MuiMarkdown 
                    options={{ {{
                        disableParsingRawHTML : true,
                        wrapper : "React.Fragment",
                        overrides : {{
                            ...getOverrides(),
                            h1 : 
                                {{
                                    component : "h1",
                                    props : {{ sx : {{ fontSize : 24, fontWeight : "bold" }} }}
                                }},
                            h2 : 
                                {{
                                    component : "h2",
                                    props : {{ sx : {{ fontSize : 20, fontWeight : "bold" }} }}
                                }}, /// etc....

                        }}
                      }}  }}
                    >
                    {report.Markdown}
                </MuiMarkdown>
            </Box>
            """

So, by escaping all "{" I can write a regular javascript object and apply the spread operator in that object.

But still open for potential better alternatives.

@MangelMaxime
Copy link
Member

You can use emit instruction to generate the code you want in JavaScript, however I am not sure if there is a way to get good type safety we because F# doesn't have an equivalent to the spread operator.

open Fable.Core
open Fable.Core.JsInterop

let inline spread (value : obj) : obj =
    emitJsStatement value """...$0"""

let myJsObject =
    {|
        Name = "Maxime"
        Age = "32"
    |}
    

let test () =
    spread myJsObject

generates

export const myJsObject = {
    Age: "32",
    Name: "Maxime",
};

export function test() {
    ...myJsObject;
}

What I mean is that I don't think we can write something equivalent to this while maintaining type safety every where.

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));

@MangelMaxime
Copy link
Member

@halcwb Can we close this issue?

We could in the future decide to add a emitJsSpread helper in Fable.Core but as this is the first time it has been requested I prefer to old onto that until more people voice in.

@halcwb
Copy link
Author

halcwb commented Jan 8, 2025

@MangelMaxime Sure, thanks for your suggestions and attention!

@halcwb halcwb closed this as completed Jan 8, 2025
@halcwb
Copy link
Author

halcwb commented Jan 15, 2025

@MangelMaxime and @joprice

I think the below is the most elegant solution I came up with:

namespace Components


open Fable.Core
open Fable.Core.JsInterop


module Markdown =


    [<Import("getOverrides", from = "mui-markdown")>]
    let private getOverrides () = emitJsExpr () "getOverrides()"

    [<Emit "Object.assign({}, $0, $1)">]
    let private objectAssign (x: obj) (y: obj) : obj = jsNative

    [<Import("Typography", from = "@mui/material")>]
    let private Typography: obj = jsNative


    [<JSX.Component>]
    let View (text: {| md: string |}) =
        let headerSx =
            {|
                fontWeight = "bold"
                color = "primary.main"
                marginTop = 2
                marginBottom = 1
            |}

        let h1 =
            {|
                ``component`` = Typography
                props =
                    {|
                        sx = {| headerSx with fontSize = 24 |}
                    |}
            |}

        let h2 =
            {|
                ``component`` = Typography
                props =
                    {|
                        sx = {| headerSx with fontSize = 20 |}
                    |}
            |}

        let p =
            {|
                ``component`` = Typography
                props =
                    {|
                        fontSize = 12
                        paddingTop = 2
                        paddingBottom = 2
                    |}
            |}

        let a =
            {|
                ``component`` = Typography
                props = {| fontSize = 12 |}
            |}

        let overrides =
            {|
                h1 = h1
                h2 = h2
                p = p
                a = a
            |}

        // merge the overrides with the default overrides
        let overrides = objectAssign (getOverrides ()) overrides

        let options =
            {|
                disableParsingRawHTML = true
                overrides = overrides
            |}

        JSX.jsx
            $"""
            import {{ MuiMarkdown }} from 'mui-markdown';

            <MuiMarkdown 
                options={options}
                >
                {text.md}
            </MuiMarkdown>
            """

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants