{-# LANGUAGE FlexibleInstances, GADTs, MultiParamTypeClasses,
  FlexibleContexts, IncoherentInstances, LambdaCase, DataKinds #-}
-- Note on IncoherentInstances: the two instances below will both work fine
-- for `GeneralizeSignal Void Void`. They should never be called.
module React.Local (GeneralizeSignal(..), locally) where

import Control.Applicative
import Data.Void

import React.Types


-- |
-- Implement when a local signal can be generalized to a higher level one. Used
-- by 'locally'
class GeneralizeSignal sigloc siggen where
    generalizeSignal :: sigloc -> siggen


instance GeneralizeSignal a a where
    generalizeSignal = id


instance GeneralizeSignal Void a where
    generalizeSignal = absurd


-- |
-- 'locally' exists to make it simple to embed classes with local concerns. An
-- example will be helpful:
--
-- We have some page which can respond to many different events.
--
-- @
-- data Transition
--     = UserTyping JSString
--     | Toggle
--     ...
--
-- globalPage_ :: [AttrOrHandler Transition] -> ReactNode Transition
-- @
--
-- And we want to be able to embed some components that don't care about all
-- that. 'inputBox_' can only output 'JSString' and 'pageHeader_' can't send
-- any signals at all.
--
-- @
-- inputBox_ :: ReactNode JSString
-- pageHeader_ :: ReactNode Void
-- @
--
-- With locally we can easily embed them in 'globalPage_':
--
-- @
-- instance GeneralizeSignal JSString Transition where
--     generalizeSignal = UserTyping
--
-- -- (globalPage_)
-- renderFn = \props state -> div_ [ class_ "global-page" ] $ do
--     locally pageHeader_
--     ...
--     locally inputBox_
--     ...
-- @
locally :: GeneralizeSignal sigloc siggen
        => ReactNode sigloc
        -> ReactNode siggen
locally = LocalNode generalizeSignal