@@ -0,0 +1,201 @@ | |||
Apache License | |||
Version 2.0, January 2004 | |||
http://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS | |||
APPENDIX: How to apply the Apache License to your work. | |||
To apply the Apache License to your work, attach the following | |||
boilerplate notice, with the fields enclosed by brackets "[]" | |||
replaced with your own identifying information. (Don't include | |||
the brackets!) The text should be enclosed in the appropriate | |||
comment syntax for the file format. We also recommend that a | |||
file or class name and description of purpose be included on the | |||
same "printed page" as the copyright notice for easier | |||
identification within third-party archives. | |||
Copyright 2020 Ken Aguilar | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. |
@@ -0,0 +1 @@ | |||
# notes-examples |
@@ -0,0 +1,14 @@ | |||
bower_components/ | |||
node_modules/ | |||
.pulp-cache/ | |||
output/ | |||
generated-docs/ | |||
.psc-package/ | |||
.psc* | |||
.purs* | |||
.psa* | |||
.spago | |||
dist/ | |||
.spago/ | |||
.cache/ | |||
index.js |
@@ -0,0 +1,20 @@ | |||
build: | |||
spago build | |||
build-watch: | |||
spago build --watch --clear-screen | |||
build-production: | |||
spago bundle-app && parcel npx build html/index.html | |||
bundle: | |||
spago bundle-app | |||
bundle-watch: | |||
spago bundle-app --watch | |||
clean: | |||
rm -rf .cache .spago .psci_modules node_modules output dist index.js | |||
start: | |||
npx parcel html/index.html |
@@ -0,0 +1,30 @@ | |||
# spago-react-scaffold | |||
Build the project | |||
``` sh | |||
make build | |||
``` | |||
Build and watch the project | |||
``` sh | |||
make build-watch | |||
``` | |||
Bundle the app to `index.js` | |||
``` sh | |||
make bundle | |||
``` | |||
### App development workflow | |||
This is probably not ideal. | |||
On one terminal run | |||
``` sh | |||
make bundle-watch | |||
``` | |||
On another, run this command so the application will run on the browser | |||
``` sh | |||
make start | |||
``` | |||
@@ -0,0 +1,10 @@ | |||
<!doctype html> | |||
<html> | |||
<head> | |||
<title>Spago React Scaffold</title> | |||
</head> | |||
<body> | |||
<div id="app"><div> | |||
<script type="text/javascript" src="../index.js"></script> | |||
</body> | |||
</html> |
@@ -0,0 +1,14 @@ | |||
{ | |||
"name": "spago-react-scaffold", | |||
"version": "1.0.0", | |||
"description": "", | |||
"main": "index.js", | |||
"keywords": [], | |||
"author": "", | |||
"license": "ISC", | |||
"devDependencies": { | |||
"react": "^16.13.0", | |||
"react-dom": "^16.13.0", | |||
"parcel-bundler": "^1.12.4" | |||
} | |||
} |
@@ -0,0 +1,128 @@ | |||
{- | |||
Welcome to your new Dhall package-set! | |||
Below are instructions for how to edit this file for most use | |||
cases, so that you don't need to know Dhall to use it. | |||
## Warning: Don't Move This Top-Level Comment! | |||
Due to how `dhall format` currently works, this comment's | |||
instructions cannot appear near corresponding sections below | |||
because `dhall format` will delete the comment. However, | |||
it will not delete a top-level comment like this one. | |||
## Use Cases | |||
Most will want to do one or both of these options: | |||
1. Override/Patch a package's dependency | |||
2. Add a package not already in the default package set | |||
This file will continue to work whether you use one or both options. | |||
Instructions for each option are explained below. | |||
### Overriding/Patching a package | |||
Purpose: | |||
- Change a package's dependency to a newer/older release than the | |||
default package set's release | |||
- Use your own modified version of some dependency that may | |||
include new API, changed API, removed API by | |||
using your custom git repo of the library rather than | |||
the package set's repo | |||
Syntax: | |||
Replace the overrides' "{=}" (an empty record) with the following idea | |||
The "//" or "⫽" means "merge these two records and | |||
when they have the same value, use the one on the right:" | |||
------------------------------- | |||
let override = | |||
{ packageName = | |||
upstream.packageName // { updateEntity1 = "new value", updateEntity2 = "new value" } | |||
, packageName = | |||
upstream.packageName // { version = "v4.0.0" } | |||
, packageName = | |||
upstream.packageName // { repo = "https://www.example.com/path/to/new/repo.git" } | |||
} | |||
------------------------------- | |||
Example: | |||
------------------------------- | |||
let overrides = | |||
{ halogen = | |||
upstream.halogen // { version = "master" } | |||
, halogen-vdom = | |||
upstream.halogen-vdom // { version = "v4.0.0" } | |||
} | |||
------------------------------- | |||
### Additions | |||
Purpose: | |||
- Add packages that aren't already included in the default package set | |||
Syntax: | |||
Replace the additions' "{=}" (an empty record) with the following idea: | |||
------------------------------- | |||
let additions = | |||
{ package-name = | |||
{ dependencies = | |||
[ "dependency1" | |||
, "dependency2" | |||
] | |||
, repo = | |||
"https://example.com/path/to/git/repo.git" | |||
, version = | |||
"tag ('v4.0.0') or branch ('master')" | |||
} | |||
, package-name = | |||
{ dependencies = | |||
[ "dependency1" | |||
, "dependency2" | |||
] | |||
, repo = | |||
"https://example.com/path/to/git/repo.git" | |||
, version = | |||
"tag ('v4.0.0') or branch ('master')" | |||
} | |||
, etc. | |||
} | |||
------------------------------- | |||
Example: | |||
------------------------------- | |||
let additions = | |||
{ benchotron = | |||
{ dependencies = | |||
[ "arrays" | |||
, "exists" | |||
, "profunctor" | |||
, "strings" | |||
, "quickcheck" | |||
, "lcg" | |||
, "transformers" | |||
, "foldable-traversable" | |||
, "exceptions" | |||
, "node-fs" | |||
, "node-buffer" | |||
, "node-readline" | |||
, "datetime" | |||
, "now" | |||
] | |||
, repo = | |||
"https://github.com/hdgarrood/purescript-benchotron.git" | |||
, version = | |||
"v7.0.0" | |||
} | |||
} | |||
------------------------------- | |||
-} | |||
let upstream = | |||
https://github.com/purescript/package-sets/releases/download/psc-0.13.6-20200127/packages.dhall sha256:06a623f48c49ea1c7675fdf47f81ddb02ae274558e29f511efae1df99ea92fb8 | |||
let overrides = {=} | |||
let additions = {=} | |||
in upstream // overrides // additions |
@@ -0,0 +1,22 @@ | |||
{- | |||
Welcome to a Spago project! | |||
You can edit this file as you like. | |||
-} | |||
{ name = "spago-react-scaffold" | |||
, dependencies = | |||
[ "aff" | |||
, "affjax" | |||
, "argonaut-codecs" | |||
, "argonaut-core" | |||
, "console" | |||
, "effect" | |||
, "exceptions" | |||
, "psci-support" | |||
, "react-basic" | |||
, "react-basic-hooks" | |||
, "web-dom" | |||
, "web-html" | |||
] | |||
, packages = ./packages.dhall | |||
, sources = [ "src/**/*.purs", "test/**/*.purs" ] | |||
} |
@@ -0,0 +1,76 @@ | |||
module Component.Login where | |||
import Prelude ( pure, ($), (>>>), Unit, bind, void, const ) | |||
import Data.Maybe ( fromMaybe, Maybe(..) ) | |||
import Data.Argonaut.Encode.Class ( encodeJson ) | |||
import Data.Argonaut.Core ( stringify ) | |||
import Data.Either ( Either(..) ) | |||
-- Aff | |||
import Affjax as AX | |||
import Affjax.ResponseFormat as ResponseFormat | |||
import Affjax.RequestBody as RequestBody | |||
import Effect.Aff ( launchAff ) | |||
-- Effect | |||
import Effect ( Effect ) | |||
import Effect.Class.Console ( log ) | |||
import Effect.Uncurried ( EffectFn1 ) | |||
-- React | |||
import React.Basic.DOM as R | |||
import React.Basic.Events ( handler, SyntheticEvent, EventHandler ) | |||
import React.Basic.DOM.Events ( targetValue, preventDefault, stopPropagation ) | |||
import React.Basic.Hooks as React | |||
import React.Basic.Hooks ( component, ReactComponent, Hook, UseState, useState, | |||
(/\) ) | |||
mkLogin :: Effect ( ReactComponent {} ) | |||
mkLogin = do | |||
component "Login" \props -> React.do | |||
email <- useInput "" | |||
password <- useInput "" | |||
pure | |||
$ R.form | |||
{ onSubmit: submitHandler $ { email: email.value, password: password.value } | |||
, children: | |||
[ R.input { onChange: email.onChange, placeholder: "email" } | |||
, R.input { onChange: password.onChange, placeholder: "password" } | |||
, R.button_ [ R.text "Sign-In" ] | |||
] | |||
} | |||
submitHandler :: Login -> EffectFn1 SyntheticEvent Unit | |||
submitHandler login = | |||
handler ( preventDefault >>> stopPropagation ) $ const do | |||
submitLogin login | |||
type Login = | |||
{ email :: String | |||
, password :: String | |||
} | |||
submitLogin :: Login -> Effect Unit | |||
submitLogin login = void $ launchAff $ do | |||
result <- AX.post | |||
ResponseFormat.json "http://localhost:8080/login" | |||
( Just $ RequestBody.json $ encodeJson login ) | |||
case result of | |||
Left err -> log $ AX.printError err | |||
Right res -> log $ stringify res.body | |||
useInput | |||
:: String | |||
-> Hook | |||
( UseState { value :: String } ) | |||
{ onChange :: EventHandler | |||
, value :: String | |||
} | |||
useInput initialValue = React.do | |||
{ value } /\ replaceState <- useState { value: initialValue } | |||
pure | |||
{ onChange: | |||
handler | |||
( preventDefault >>> stopPropagation >>> targetValue ) \val -> do | |||
replaceState \_ -> | |||
{ value: fromMaybe "" val | |||
} | |||
, value | |||
} |
@@ -0,0 +1,79 @@ | |||
module Component.LoginAff where | |||
import Prelude | |||
import Data.Maybe ( Maybe(..), fromMaybe ) | |||
import Data.Argonaut.Encode.Class ( encodeJson ) | |||
import Data.Argonaut.Core ( Json ) | |||
import Data.Either ( Either ) | |||
-- Effect | |||
import Effect ( Effect ) | |||
-- Aff | |||
import Affjax as AX | |||
import Affjax.ResponseFormat as ResponseFormat | |||
import Affjax.RequestBody as RequestBody | |||
import Effect.Aff ( Aff, never ) | |||
-- React | |||
import React.Basic.Hooks as React | |||
import React.Basic.Hooks ( ReactComponent, component, Hook, UseState, useState, (/\) ) | |||
import React.Basic.Hooks.Aff ( useAff ) | |||
import React.Basic.DOM as R | |||
import React.Basic.Events ( handler, EventHandler ) | |||
import React.Basic.DOM.Events ( preventDefault, stopPropagation, targetValue ) | |||
mkLoginAff :: Effect ( ReactComponent {} ) | |||
mkLoginAff = do | |||
component "LoginAff" $ const React.do | |||
isSubmitting/\setSubmitting <- useState false | |||
email <- useInput "" | |||
password <- useInput "" | |||
login/\setLogin <- useState { email: "", password: "" } | |||
useAff login $ do | |||
void $ submitLogin isSubmitting { email: login.email, password: login.password } | |||
pure $ setSubmitting \_ -> false | |||
pure | |||
$ R.form_ | |||
[ R.input | |||
{ placeholder: "email" | |||
, onChange: email.onChange | |||
} | |||
, R.input | |||
{ placeholder: "password" | |||
, onChange: password.onChange | |||
} | |||
, R.button | |||
{ onClick: handler ( preventDefault >>> stopPropagation >>> targetValue ) \_ -> do | |||
setSubmitting \_ -> true | |||
setLogin \_ -> login { email = email.value, password = password.value } | |||
, children: [ R.text "Sign-In" ] | |||
} | |||
] | |||
type Login = | |||
{ email :: String | |||
, password :: String | |||
} | |||
submitLogin :: Boolean -> Login -> Aff ( Either AX.Error Json ) | |||
submitLogin isSubmitting login = do | |||
if not isSubmitting | |||
then never | |||
else do | |||
result <- AX.post ResponseFormat.json "http://localhost:8080/login" | |||
( Just $ RequestBody.json $ encodeJson login ) | |||
pure $ _.body <$> result | |||
useInput | |||
:: String | |||
-> Hook | |||
( UseState String ) | |||
{ onChange :: EventHandler | |||
, value :: String | |||
} | |||
useInput initialValue = React.do | |||
value /\ replaceValue <- useState initialValue | |||
pure | |||
{ onChange: handler | |||
( preventDefault >>> stopPropagation >>> targetValue ) \val -> do | |||
replaceValue \_ -> fromMaybe "" val | |||
, value | |||
} |
@@ -0,0 +1,18 @@ | |||
module Component.Title | |||
( mkTitle | |||
) where | |||
import Prelude ( ($), pure ) | |||
import React.Basic.DOM as R | |||
import React.Basic.Hooks ( ReactComponent, component ) | |||
import Effect ( Effect ) | |||
type Props = { text :: String } | |||
mkTitle :: Effect ( ReactComponent Props ) | |||
mkTitle = do | |||
component "Title" $ \props -> React.do | |||
pure $ | |||
R.h1_ | |||
[ R.text props.text ] | |||
@@ -0,0 +1,37 @@ | |||
module Main where | |||
import Prelude ( Unit, bind, map, ($), (=<<), const, pure ) | |||
import React.Basic.DOM as R | |||
import Web.DOM.NonElementParentNode ( getElementById ) | |||
import Web.HTML.HTMLDocument ( toNonElementParentNode ) | |||
import Web.HTML.Window ( document ) | |||
import Web.HTML ( window ) | |||
import Data.Maybe ( Maybe(..) ) | |||
import Effect.Exception ( throw ) | |||
import Component.Title | |||
import Component.Login ( mkLogin ) | |||
import Component.LoginAff ( mkLoginAff ) | |||
import Effect ( Effect ) | |||
import React.Basic.Hooks ( element, ReactComponent, component ) | |||
mkMainComponent :: Effect ( ReactComponent {} ) | |||
mkMainComponent = do | |||
title <- mkTitle | |||
login <- mkLogin | |||
loginAff <- mkLoginAff | |||
component "Main" $ const React.do | |||
pure $ | |||
R.div_ | |||
[ element title { text: "Hello, World" } | |||
-- , element login {} | |||
, element loginAff {} | |||
] | |||
main :: Effect Unit | |||
main = do | |||
mApp <- getElementById "app" =<< ( map toNonElementParentNode $ document =<< window ) | |||
case mApp of | |||
Nothing -> throw "App element not found." | |||
Just app -> do | |||
mainComponent <- mkMainComponent | |||
R.render ( element mainComponent {} ) app |
@@ -0,0 +1,11 @@ | |||
module Test.Main where | |||
import Prelude | |||
import Effect (Effect) | |||
import Effect.Class.Console (log) | |||
main :: Effect Unit | |||
main = do | |||
log "🍝" | |||
log "You should add some tests." |