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

Merge props / wrap components #23

Open
codingedgar opened this issue Jan 6, 2022 · 4 comments
Open

Merge props / wrap components #23

codingedgar opened this issue Jan 6, 2022 · 4 comments

Comments

@codingedgar
Copy link

codingedgar commented Jan 6, 2022

Hi, 👋

This is more of a PureScript question than anything else, but still figured this is the best place to make it

How can I merge props like in js?

function Loading (extras) {
   return (<Video autoPlay {...extras} />)
}

but in PS

mkLoading
  :: forall attrs attrs_
   . Union attrs attrs_ Props_video
  => Effect (Record attrs -> React.JSX)
mkLoading = do

  React.component "Loading" \extras -> React.do

    pure
      ( DOM.video
          ( merge extras
              { autoPlay: true
              , loop: true
              , muted: true
              , playsInline: true
              , height: "24"
              , className: "loading"
              , children:
                  [ DOM.source
                      { type: "video/webm"
                      , src: "./assets/loader.webm"
                      }
                  , DOM.source
                      { type: "video/mp4"
                      , src: "./assets/loader.mp4"
                      }
                  ]
              }
          )
      )

This is a type error

No type class instance was found for

    Prim.Row.Union t0
                   t1
                   ( _aria :: Object String
                   , _data :: Object String
                   , about :: String
                   , acceptCharset :: String
                   , accessKey :: String
                   , allowFullScreen :: Boolean
                   , allowTransparency :: Boolean
                   , autoFocus :: Boolean
                   , autoPlay :: Boolean
                   , capture :: Boolean
                   , cellPadding :: String
                   , cellSpacing :: String
                   , charSet :: String
                   , children :: Array JSX
                   , classID :: String
                   , className :: String
                   , colSpan :: Int
                   , contentEditable :: Boolean
                   , contextMenu :: String
                   , controls :: Boolean
                   , crossOrigin :: String
                   , dangerouslySetInnerHTML :: { __html :: String
                                                }
                   , datatype :: String
                   , dateTime :: String
                   , dir :: String
                   , draggable :: Boolean
                   , encType :: String
                   , formAction :: String
                   , formEncType :: String
                   , formMethod :: String
                   , formNoValidate :: Boolean
                   , formTarget :: String
                   , frameBorder :: String
                   , height :: String
                   , hidden :: Boolean
                   , hrefLang :: String
                   , htmlFor :: String
                   , httpEquiv :: String
                   , icon :: String
                   , id :: String
                   , inlist :: String
                   , inputMode :: String
                   , is :: String
                   , itemID :: String
                   , itemProp :: String
                   , itemRef :: String
                   , itemScope :: Boolean
                   , itemType :: String
                   , key :: String
                   , keyParams :: String
                   , keyType :: String
                   , lang :: String
                   , loop :: Boolean
                   , marginHeight :: String
                   , marginWidth :: String
                   , maxLength :: Int
                   , mediaGroup :: String
                   , minLength :: Int
                   , muted :: Boolean
                   , noValidate :: Boolean
                   , onAbort :: EffectFn1 SyntheticEvent Unit
                   , onAnimationEnd :: EffectFn1 SyntheticEvent Unit
                   , onAnimationIteration :: EffectFn1 SyntheticEvent Unit
                   , onAnimationStart :: EffectFn1 SyntheticEvent Unit
                   , onBlur :: EffectFn1 SyntheticEvent Unit
                   , onCanPlay :: EffectFn1 SyntheticEvent Unit
                   , onCanPlayThrough :: EffectFn1 SyntheticEvent Unit
                   , onClick :: EffectFn1 SyntheticEvent Unit
                   , onCompositionEnd :: EffectFn1 SyntheticEvent Unit
                   , onCompositionStart :: EffectFn1 SyntheticEvent Unit
                   , onCompositionUpdate :: EffectFn1 SyntheticEvent Unit
                   , onContextMenu :: EffectFn1 SyntheticEvent Unit
                   , onCopy :: EffectFn1 SyntheticEvent Unit
                   , onCut :: EffectFn1 SyntheticEvent Unit
                   , onDoubleClick :: EffectFn1 SyntheticEvent Unit
                   , onDrag :: EffectFn1 SyntheticEvent Unit
                   , onDragEnd :: EffectFn1 SyntheticEvent Unit
                   , onDragEnter :: EffectFn1 SyntheticEvent Unit
                   , onDragExit :: EffectFn1 SyntheticEvent Unit
                   , onDragLeave :: EffectFn1 SyntheticEvent Unit
                   , onDragOver :: EffectFn1 SyntheticEvent Unit
                   , onDragStart :: EffectFn1 SyntheticEvent Unit
                   , onDrop :: EffectFn1 SyntheticEvent Unit
                   , onDurationChange :: EffectFn1 SyntheticEvent Unit
                   , onEmptied :: EffectFn1 SyntheticEvent Unit
                   , onEncrypted :: EffectFn1 SyntheticEvent Unit
                   , onEnded :: EffectFn1 SyntheticEvent Unit
                   , onError :: EffectFn1 SyntheticEvent Unit
                   , onFocus :: EffectFn1 SyntheticEvent Unit
                   , onGotPointerCapture :: EffectFn1 SyntheticEvent Unit
                   , onInvalid :: EffectFn1 SyntheticEvent Unit
                   , onKeyDown :: EffectFn1 SyntheticEvent Unit
                   , onKeyPress :: EffectFn1 SyntheticEvent Unit
                   , onKeyUp :: EffectFn1 SyntheticEvent Unit
                   , onLoadStart :: EffectFn1 SyntheticEvent Unit
                   , onLoadedData :: EffectFn1 SyntheticEvent Unit
                   , onLoadedMetadata :: EffectFn1 SyntheticEvent Unit
                   , onLostPointerCapture :: EffectFn1 SyntheticEvent Unit
                   , onMouseDown :: EffectFn1 SyntheticEvent Unit
                   , onMouseEnter :: EffectFn1 SyntheticEvent Unit
                   , onMouseLeave :: EffectFn1 SyntheticEvent Unit
                   , onMouseMove :: EffectFn1 SyntheticEvent Unit
                   , onMouseOut :: EffectFn1 SyntheticEvent Unit
                   , onMouseOver :: EffectFn1 SyntheticEvent Unit
                   , onMouseUp :: EffectFn1 SyntheticEvent Unit
                   , onPaste :: EffectFn1 SyntheticEvent Unit
                   , onPause :: EffectFn1 SyntheticEvent Unit
                   , onPlay :: EffectFn1 SyntheticEvent Unit
                   , onPlaying :: EffectFn1 SyntheticEvent Unit
                   , onPointerCancel :: EffectFn1 SyntheticEvent Unit
                   , onPointerDown :: EffectFn1 SyntheticEvent Unit
                   , onPointerEnter :: EffectFn1 SyntheticEvent Unit
                   , onPointerLeave :: EffectFn1 SyntheticEvent Unit
                   , onPointerMove :: EffectFn1 SyntheticEvent Unit
                   , onPointerOut :: EffectFn1 SyntheticEvent Unit
                   , onPointerOver :: EffectFn1 SyntheticEvent Unit
                   , onPointerUp :: EffectFn1 SyntheticEvent Unit
                   , onProgress :: EffectFn1 SyntheticEvent Unit
                   , onRateChange :: EffectFn1 SyntheticEvent Unit
                   , onScroll :: EffectFn1 SyntheticEvent Unit
                   , onSeeked :: EffectFn1 SyntheticEvent Unit
                   , onSeeking :: EffectFn1 SyntheticEvent Unit
                   , onSelect :: EffectFn1 SyntheticEvent Unit
                   , onStalled :: EffectFn1 SyntheticEvent Unit
                   , onSubmit :: EffectFn1 SyntheticEvent Unit
                   , onSuspend :: EffectFn1 SyntheticEvent Unit
                   , onTimeUpdate :: EffectFn1 SyntheticEvent Unit
                   , onTouchCancel :: EffectFn1 SyntheticEvent Unit
                   , onTouchEnd :: EffectFn1 SyntheticEvent Unit
                   , onTouchMove :: EffectFn1 SyntheticEvent Unit
                   , onTouchStart :: EffectFn1 SyntheticEvent Unit
                   , onTransitionEnd :: EffectFn1 SyntheticEvent Unit
                   , onVolumeChange :: EffectFn1 SyntheticEvent Unit
                   , onWaiting :: EffectFn1 SyntheticEvent Unit
                   , onWheel :: EffectFn1 SyntheticEvent Unit
                   , playsInline :: Boolean
                   , poster :: String
                   , prefix :: String
                   , preload :: String
                   , property :: String
                   , radioGroup :: String
                   , readOnly :: Boolean
                   , ref :: Ref (Nullable Node)
                   , resource :: String
                   , role :: String
                   , rowSpan :: Int
                   , scoped :: Boolean
                   , seamless :: Boolean
                   , security :: String
                   , slot :: String
                   , spellCheck :: Boolean
                   , src :: String
                   , srcDoc :: JSX
                   , srcLang :: String
                   , srcSet :: String
                   , style :: CSS
                   , suppressContentEditableWarning :: Boolean
                   , tabIndex :: Int
                   , title :: String
                   , typeof :: String
                   , unselectable :: Boolean
                   , useMap :: String
                   , vocab :: String
                   , width :: String
                   , wmode :: String
                   )


while applying a function video
  of type Union @Type t0 t1
            ( _aria :: Object String
            , _data :: Object String
            , about :: String
            , acceptCharset :: String
            , accessKey :: String
            , allowFullScreen :: Boolean
            , allowTransparency :: Boolean
            , autoFocus :: Boolean
            , autoPlay :: Boolean
            , capture :: Boolean
            , cellPadding :: String
            , cellSpacing :: String
            , charSet :: String
            , children :: Array JSX
            , classID :: String
            , className :: String
            , colSpan :: Int
            , contentEditable :: Boolean
            , contextMenu :: String
            , controls :: Boolean
            , crossOrigin :: String
            , dangerouslySetInnerHTML :: { __html :: String
                                         }
            , datatype :: String
            , dateTime :: String
            , dir :: String
            , draggable :: Boolean
            , encType :: String
            , formAction :: String
            , formEncType :: String
            , formMethod :: String
            , formNoValidate :: Boolean
            , formTarget :: String
            , frameBorder :: String
            , height :: String
            , hidden :: Boolean
            , hrefLang :: String
            , htmlFor :: String
            , httpEquiv :: String
            , icon :: String
            , id :: String
            , inlist :: String
            , inputMode :: String
            , is :: String
            , itemID :: String
            , itemProp :: String
            , itemRef :: String
            , itemScope :: Boolean
            , itemType :: String
            , key :: String
            , keyParams :: String
            , keyType :: String
            , lang :: String
            , loop :: Boolean
            , marginHeight :: String
            , marginWidth :: String
            , maxLength :: Int
            , mediaGroup :: String
            , minLength :: Int
            , muted :: Boolean
            , noValidate :: Boolean
            , onAbort :: EffectFn1 SyntheticEvent Unit
            , onAnimationEnd :: EffectFn1 SyntheticEvent Unit
            , onAnimationIteration :: EffectFn1 SyntheticEvent Unit
            , onAnimationStart :: EffectFn1 SyntheticEvent Unit
            , onBlur :: EffectFn1 SyntheticEvent Unit
            , onCanPlay :: EffectFn1 SyntheticEvent Unit
            , onCanPlayThrough :: EffectFn1 SyntheticEvent Unit
            , onClick :: EffectFn1 SyntheticEvent Unit
            , onCompositionEnd :: EffectFn1 SyntheticEvent Unit
            , onCompositionStart :: EffectFn1 SyntheticEvent Unit
            , onCompositionUpdate :: EffectFn1 SyntheticEvent Unit
            , onContextMenu :: EffectFn1 SyntheticEvent Unit
            , onCopy :: EffectFn1 SyntheticEvent Unit
            , onCut :: EffectFn1 SyntheticEvent Unit
            , onDoubleClick :: EffectFn1 SyntheticEvent Unit
            , onDrag :: EffectFn1 SyntheticEvent Unit
            , onDragEnd :: EffectFn1 SyntheticEvent Unit
            , onDragEnter :: EffectFn1 SyntheticEvent Unit
            , onDragExit :: EffectFn1 SyntheticEvent Unit
            , onDragLeave :: EffectFn1 SyntheticEvent Unit
            , onDragOver :: EffectFn1 SyntheticEvent Unit
            , onDragStart :: EffectFn1 SyntheticEvent Unit
            , onDrop :: EffectFn1 SyntheticEvent Unit
            , onDurationChange :: EffectFn1 SyntheticEvent Unit
            , onEmptied :: EffectFn1 SyntheticEvent Unit
            , onEncrypted :: EffectFn1 SyntheticEvent Unit
            , onEnded :: EffectFn1 SyntheticEvent Unit
            , onError :: EffectFn1 SyntheticEvent Unit
            , onFocus :: EffectFn1 SyntheticEvent Unit
            , onGotPointerCapture :: EffectFn1 SyntheticEvent Unit
            , onInvalid :: EffectFn1 SyntheticEvent Unit
            , onKeyDown :: EffectFn1 SyntheticEvent Unit
            , onKeyPress :: EffectFn1 SyntheticEvent Unit
            , onKeyUp :: EffectFn1 SyntheticEvent Unit
            , onLoadStart :: EffectFn1 SyntheticEvent Unit
            , onLoadedData :: EffectFn1 SyntheticEvent Unit
            , onLoadedMetadata :: EffectFn1 SyntheticEvent Unit
            , onLostPointerCapture :: EffectFn1 SyntheticEvent Unit
            , onMouseDown :: EffectFn1 SyntheticEvent Unit
            , onMouseEnter :: EffectFn1 SyntheticEvent Unit
            , onMouseLeave :: EffectFn1 SyntheticEvent Unit
            , onMouseMove :: EffectFn1 SyntheticEvent Unit
            , onMouseOut :: EffectFn1 SyntheticEvent Unit
            , onMouseOver :: EffectFn1 SyntheticEvent Unit
            , onMouseUp :: EffectFn1 SyntheticEvent Unit
            , onPaste :: EffectFn1 SyntheticEvent Unit
            , onPause :: EffectFn1 SyntheticEvent Unit
            , onPlay :: EffectFn1 SyntheticEvent Unit
            , onPlaying :: EffectFn1 SyntheticEvent Unit
            , onPointerCancel :: EffectFn1 SyntheticEvent Unit
            , onPointerDown :: EffectFn1 SyntheticEvent Unit
            , onPointerEnter :: EffectFn1 SyntheticEvent Unit
            , onPointerLeave :: EffectFn1 SyntheticEvent Unit
            , onPointerMove :: EffectFn1 SyntheticEvent Unit
            , onPointerOut :: EffectFn1 SyntheticEvent Unit
            , onPointerOver :: EffectFn1 SyntheticEvent Unit
            , onPointerUp :: EffectFn1 SyntheticEvent Unit
            , onProgress :: EffectFn1 SyntheticEvent Unit
            , onRateChange :: EffectFn1 SyntheticEvent Unit
            , onScroll :: EffectFn1 SyntheticEvent Unit
            , onSeeked :: EffectFn1 SyntheticEvent Unit
            , onSeeking :: EffectFn1 SyntheticEvent Unit
            , onSelect :: EffectFn1 SyntheticEvent Unit
            , onStalled :: EffectFn1 SyntheticEvent Unit
            , onSubmit :: EffectFn1 SyntheticEvent Unit
            , onSuspend :: EffectFn1 SyntheticEvent Unit
            , onTimeUpdate :: EffectFn1 SyntheticEvent Unit
            , onTouchCancel :: EffectFn1 SyntheticEvent Unit
            , onTouchEnd :: EffectFn1 SyntheticEvent Unit
            , onTouchMove :: EffectFn1 SyntheticEvent Unit
            , onTouchStart :: EffectFn1 SyntheticEvent Unit
            , onTransitionEnd :: EffectFn1 SyntheticEvent Unit
            , onVolumeChange :: EffectFn1 SyntheticEvent Unit
            , onWaiting :: EffectFn1 SyntheticEvent Unit
            , onWheel :: EffectFn1 SyntheticEvent Unit
            , playsInline :: Boolean
            , poster :: String
            , prefix :: String
            , preload :: String
            , property :: String
            , radioGroup :: String
            , readOnly :: Boolean
            , ref :: Ref (Nullable Node)
            , resource :: String
            , role :: String
            , rowSpan :: Int
            , scoped :: Boolean
            , seamless :: Boolean
            , security :: String
            , slot :: String
            , spellCheck :: Boolean
            , src :: String
            , srcDoc :: JSX
            , srcLang :: String
            , srcSet :: String
            , style :: CSS
            , suppressContentEditableWarning :: Boolean
            , tabIndex :: Int
            , title :: String
            , typeof :: String
            , unselectable :: Boolean
            , useMap :: String
            , vocab :: String
            , width :: String
            , wmode :: String
            )
           => Record t0 -> JSX
  to argument (merge extras) { autoPlay: true
                             , loop: true
                             , muted: true
                             , playsInline: true
                             , height: "24"
                             , className: "loading"
                             , children: [ ...
                                         , ...
                                         ]
                             }
while inferring the type of video ((merge extras) { autoPlay: ...
                                                  , loop: ...
                                                  , muted: ...
                                                  , playsInline: ...
                                                  , height: ...
                                                  , className: ...
                                                  , children: ...
                                                  }
                                  )
in value declaration mkLoading

where t0 is an unknown type
      t1 is an unknown type
PureScript(NoInstanceFound)

Thank a lot for your time and this lib ✨

@codingedgar codingedgar changed the title Merge props Merge props / wrap components Jan 6, 2022
@megamaddu
Copy link
Member

The way Union is used in the DOM modules is a little bit of a hack which could make this difficult to do. Essentially, it leaves one of the two inputs to Union completely unused, so the compiler is free to assume anything it wants to about it. This works in the DOM module where the implementation is in FFI, but that argument is completely unusable within PureScript.

You could maybe accomplish this with an unsafeCoerce though.. maybe in your call to merge you can merge (unsafeCoerce extras) ...? I'm not on a computer where I can easily play with it at the moment.

@paulyoung
Copy link

I just ran into this. Did anyone find a better way?

@pete-murphy
Copy link
Member

pete-murphy commented Apr 24, 2022

This is what I get after cleaning up the inferred type for @codingedgar's original example. It seems to work as intended without needing unsafeCoerce, though I can't say I fully understand the row type constraints 😅 Try PureScript example here

import Prelude

import Effect (Effect)
import Prim.Row (class Nub, class Union)
import React.Basic.DOM as DOM
import React.Basic.DOM.Generated (Props_video)
import React.Basic.Hooks (Component)
import React.Basic.Hooks as React
import Record (merge)

-- These are the attributes that are pre-supplied by the `mkLoading` wrapper
type DefaultAttrs =
  ( autoPlay :: Boolean
  , children :: Array React.JSX
  , className :: String
  , height :: String
  , loop :: Boolean
  , muted :: Boolean
  , playsInline :: Boolean
  )

mkLoading
  :: forall attrs attrs_ extras unioned
   . Union attrs attrs_ Props_video
  => Union extras DefaultAttrs unioned
  => Nub unioned attrs
  => Component (Record extras)
mkLoading = do

  React.component "Loading" \extras -> React.do

    pure
      ( DOM.video
          ( merge extras
              { autoPlay: true
              , loop: true
              , muted: true
              , playsInline: true
              , height: "24"
              , className: "loading"
              , children:
                  [ DOM.source
                      { type: "video/webm"
                      , src: "./assets/loader.webm"
                      }
                  , DOM.source
                      { type: "video/mp4"
                      , src: "./assets/loader.mp4"
                      }
                  ]
              }
          )
      )

@pete-murphy
Copy link
Member

pete-murphy commented Apr 25, 2022

Maybe another option, if you don't want to declare the DefaultAttrs type (and don't mind the compiler warning about the missing type annotation on mkLoading):

mkLoadingWithDefaults
  :: forall attrs attrs_ extras defaults unioned
   . Union attrs attrs_ Props_video
  => Union extras defaults unioned
  => Nub unioned attrs
  => Record defaults
  -> Component (Record extras)
mkLoadingWithDefaults defaults = do
  React.component "Loading" \extras ->
    pure (DOM.video (merge extras defaults))

mkLoading = mkLoadingWithDefaults
  { autoPlay: true
  , loop: true
  , muted: true
  , playsInline: true
  , height: "24"
  , className: "loading"
  , children:
      [ DOM.source
          { type: "video/webm"
          , src: "./assets/loader.webm"
          }
      , DOM.source
          { type: "video/mp4"
          , src: "./assets/loader.mp4"
          }
      ]
  }

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

4 participants