Code Monkey home page Code Monkey logo

fable-elmish-electron-material-ui-demo's Introduction

Fable-Elmish-Electron-Material-UI demo Build status

This is an example of an F# Electron app centered around Fable 2 and Elmish. The actual app contents (which you can easily replace to build your own Electron app) are intended to show how to use Material-UI (including JSS/style-as-code) as well as serve as examples of how to implement some (not always trivial) UX patterns in Elmish.

To use the demo for scaffolding your own Fable/Elmish/Electron apps, simply clone the repo and start hackin' on the Renderer project to get started (details below).

Features/stack:

  • Hot module reloading for both code and styles
  • Time-travel debugging (using Redux DevTools, RemoteDev, and Fable.Elmish.Debugger)
  • Single-command development and packaging with FAKE
  • electron-webpack takes care of most of the webpack config
  • electron-builder packages the app (see their documentation for how to customize)
  • electron-window-state for remembering window state between launches
  • Devtron for Electron-specific debugging/linting

Stuff demoed:

  • Autocomplete
  • Badges
  • Dialogs
  • Saving/loading files
  • Selects (dropdowns)
  • Snackbars
  • Static assets (images etc.)
  • Text fields / input validation

Hot module reloading in action

Animation showing hot module reloading

Time-travel debugging in action

Animation showing time-travel debugging

Requirements

  • .NET Core SDK 3.0
  • Node (for npm)

How to develop

This project uses .NET Core 3 local tools. Therefore, run dotnet tool restore to restore the necessary CLI tools before doing anything else.

Then, to run the app locally in "live mode":

dotnet fake build -t Dev

(Dev is the default target, so you can also just run dotnet fake build.)

After the app starts, edit the renderer project in /src/Renderer and see the changes appear in real-time thanks to hot module reloading.

Place static files in the root /static folder as required by electron-webpack. See the code for the “Static assets” page (and the helpers in Utils.fs) to see how to use them.

Release build to unpacked directory

dotnet fake build -t DistDir

Release build to packed installer

dotnet fake build -t Dist

NuGet package management

Use dotnet paket (after running dotnet tool restore).

Improvements welcome!

If you see anything here that looks wrong, suboptimal or just weird, you may very well be right. Don't be shy about opening an issue or PR.

fable-elmish-electron-material-ui-demo's People

Contributors

cmeeren avatar dependabot[bot] avatar forki avatar klettier avatar mangelmaxime avatar theimowski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fable-elmish-electron-material-ui-demo's Issues

Example to structure more the 'Elmish way'

Just as an example I restructured some code based on your project example to be able to structure my project more the 'Elmish way', using view functions instead of real components with it's own models, inits, updates etc..

So, first I had a StatusBar component that looked like this:

module StatusBar =
    open Fable.Core.JsInterop
    open Fable.Helpers.React
    open Fable.Helpers.React.Props
    open Fable.Import.React
    open Fable.MaterialUI.Core
    open Fable.MaterialUI.Props
    open Fable.MaterialUI.Themes

    type Model =
        { Message : string
          Open : bool }

    let init() =
        { Message = ""
          Open = false }

    type Msg =
        | IsOpen of bool
        | IsOnline of string
        | IsOffLine

    let update msg model =
        match msg with
        | IsOpen b -> { model with Open = b }
        | IsOnline s -> { model with Message = s }
        | IsOffLine -> { model with Message = "Offline" }

    let styles (theme : ITheme) : IStyles list = []

    let view' (classes : IClasses) model dispatch =
        let { Message = s } = model
        snackbar [ Open model.Open
                   OnClose(fun _ _ ->
                       false
                       |> IsOpen
                       |> dispatch)
                   SnackbarProp.AutoHideDuration 1000
                   SnackbarProp.Message(str s) ] []

    // Boilerplate code
    // Workaround for using JSS with Elmish
    // https://github.com/mvsmal/fable-material-ui/issues/4#issuecomment-422781471
    type private IProps =
        abstract model : Model with get, set
        abstract dispatch : (Msg -> unit) with get, set
        inherit IClassesProps

    type private Component(p) =
        inherit PureStatelessComponent<IProps>(p)
        let viewFun (p : IProps) = view' p.classes p.model p.dispatch
        let viewWithStyles = withStyles (StyleType.Func styles) [] viewFun
        override this.render() =
            ReactElementType.create !!viewWithStyles this.props []

    let view (model : Model) (dispatch : Msg -> unit) : ReactElement =
        let props =
            jsOptions<IProps> (fun p ->
                p.model <- model
                p.dispatch <- dispatch)
        ofType<Component, _, _> props []

Then I restructured the code like this:

module StatusBar =
    open Fable.Core.JsInterop
    open Fable.Helpers.React
    open Fable.Helpers.React.Props
    open Fable.Import.React
    open Fable.MaterialUI.Core
    open Fable.MaterialUI.Props
    open Fable.MaterialUI.Themes


    let styles (theme : ITheme) : IStyles list = []

    let view' (classes : IClasses) isOpen txt msg dispatch =
        snackbar [ Open isOpen
                   OnClose(fun _ _ ->
                        false
                        |> msg
                        |> dispatch)
                   SnackbarProp.AutoHideDuration 1000
                   SnackbarProp.Message(str txt) ] []

    // Boilerplate code
    // Workaround for using JSS with Elmish
    // https://github.com/mvsmal/fable-material-ui/issues/4#issuecomment-422781471
    type private IProps =
        abstract isOpen : bool with get, set
        abstract text : string with get, set
        abstract msg : ('T -> 'M) with get, set
        abstract dispatch : ('M -> unit) with get, set
        inherit IClassesProps

    type private Component(p) =
        inherit PureStatelessComponent<IProps>(p)
        let viewFun (p : IProps) = view' p.classes p.isOpen p.text p.msg p.dispatch
        let viewWithStyles = withStyles (StyleType.Func styles) [] viewFun
        override this.render() =
            ReactElementType.create !!viewWithStyles this.props []

    let view isOpen txt msg dispatch : ReactElement =
        let props =
            jsOptions<IProps> (fun p ->
                p.isOpen <- isOpen
                p.text <- txt
                p.msg <- msg
                p.dispatch <- dispatch)
        ofType<Component, _, _> props [] 

So I got rid off all the extra ballast. I can just create the StatusBar by calling:

StatusBar.view true "I am open" StatusBarMsg dispatch

Given your previous comments on how to structure your code, you would agree?

An issue with electron-webpack and @material-ui/pickers

Hi,

So, this is my first time using both F# and electron. I may be missing something.

Basically, I took this repo as a base for my application, but had trouble setting up material-ui/pickers via Feliz.MaterialUI.Pickers bindings.

Just placing Mui.pickerUtilsProvider at the root of my application was giving me the following runtime error:

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

After a while, I found a solution in mobxjs/mobx-react-lite#248 (comment)
Adding module.exports = { externals: ['react', 'react-dom'], }; to the webpack.additions.renderer.js file solves the issue .

I thought it would be nice if somebody modified the webpack config and/or added examples for Feliz.MaterialUI.Pickers

Universalify not properly installed during build

When trying to build the system, yarn does not properly install universalify.

 Directory of D:\repos\fable-elmish-electron-material-ui-demo\node_modules\universalify

04/05/2019  09:14 AM    <DIR>          .
04/05/2019  09:14 AM    <DIR>          ..
10/26/1985  04:15 AM             1,100 LICENSE
10/26/1985  04:15 AM               820 package.json
10/26/1985  04:15 AM             2,008 README.md
               3 File(s)          3,928 bytes
               2 Dir(s)  624,319,066,112 bytes free

This results in an electron failure later in the process.

Starting target 'YarnInstall'
.> "C:\Program Files (x86)\Yarn\bin\yarn.CMD" install (In: false, Out: false, Err: false)
yarn install v1.6.0
[1/4] Resolving packages...
[2/4] Fetching packages...
info [email protected]: The platform "win32" is incompatible with this module.
info "[email protected]" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
[1/3] ⢀ jss:  > https://opencollective.com/jss/donate
[2/3] ⢀ electron
[3/3] ⢀ node-sass
[-/3] ⠠ waiting...
error D:\repos\fable-elmish-electron-material-ui-demo\node_modules\electron: Command failed.
Exit code: 1
Command: node install.js
Arguments:
Directory: D:\repos\fable-elmish-electron-material-ui-demo\node_modules\electron
Output:
module.js:545
    throw err;
    ^

Error: Cannot find module 'universalify'
    at Function.Module._resolveFilename (module.js:543:15)
    ...

I tried adding universalify to paket.dependencies, but this didn't help. I also ran npm i which installed it correctly, including the missing index.js, but as soon as I run fake.cmd build -t Dev, yarn deletes it again and I'm right back where I started.

Restore doesn't restore to packages folder which breaks execution of npm

With the current master the execution of dotnet fake build -t Dev fails with:

---------------------------------------------------------------------
Build Time Report
---------------------------------------------------------------------
Target          Duration
------          --------
Clean           00:00:00.0323490
DotnetRestore   00:00:04.2405479
NpmInstall      00:00:00.0466845   (Start of process 'F:\development\repos\fable-elmish-electron-material-ui-demo\packages\Npm.js\tools\npm.cmd' failed.
The system cannot find the file specified)
Dev             00:00:00           (skipped)
Total:          00:00:04.4899656
Status:         Failure
---------------------------------------------------------------------

The engine "node" is incompatible with this module. Expected version ">=8.12.0".

Starting target 'YarnInstall'
.> "C:\Program Files (x86)\Yarn\bin\yarn.CMD" install (In: false, Out: false, Err: false)
yarn install v1.7.0
[1/4] Resolving packages...
[2/4] Fetching packages...
error [email protected]: The engine "node" is incompatible with this module. Expected version ">=8.12.0".
error Found incompatible module
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
Finished (Failed) 'YarnInstall' in 00:00:19.0569876

Add Docs in Wiki

I am a beginner in F#
it would be nice if you some docs in the wiki section

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.