Initial commit

This commit is contained in:
Mark Eibes 2020-08-06 21:49:43 +02:00
commit ceece1f1f0
No known key found for this signature in database
GPG Key ID: F2339296A7CD5027
62 changed files with 11948 additions and 0 deletions

49
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,49 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Continuous Integration
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
name: Test, Bundle, and Dockerise
runs-on: ubuntu-20.04
steps:
- name: Download Zephyr release
run: curl -Ls https://github.com/coot/zephyr/releases/download/v0.3.2/Linux.tar.gz | tar -xvz -C /tmp
- name: Copy Zephyr to PATH
run: sudo cp /tmp/zephyr/zephyr /usr/local/bin/
- name: Install yarn
run: sudo npm install -g yarn
# Run actual tasks
- uses: actions/checkout@v2
## Frontend
- name: Install Frontend Dependencies
run: yarn install
- name: Test Frontend
run: yarn test
- name: Build Storybook
run: yarn build-storybook
- name: Bundle Frontend
run: yarn bundle
## UI Server
- name: Install UI Server Dependencies
working-directory: ui-server
run: yarn install
- name: Build UI Server
working-directory: ui-server
run: yarn build
## Build docker image
## The Dockerfile requires the two previous steps
- name: Build Docker image
run: docker build .

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# JavaScript dependencies
node_modules
# PureScript dependencies
.spago
# PureScript output
output
# Webpack prod output
dist
# PureScript tooling
.psc-ide-port
.purs-repl
# Dead Code Eliminated (zephyr) output
dce-output
## Miscellaneous
# macOS trash
.DS_Store

1
.purs-repl Normal file
View File

@ -0,0 +1 @@
import Prelude

1
.storybook/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 45 KiB

8
.storybook/main.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
stories: ['../output/**/*.Story/index.js'],
addons: [],
webpackFinal: async config => {
// do mutation to the config
return config;
},
};

7
.storybook/manager.js Normal file
View File

@ -0,0 +1,7 @@
import { addons } from '@storybook/addons';
import rowtypeYogaTheme from './rowtype-yoga-theme';
addons.setConfig({
theme: rowtypeYogaTheme,
isToolshown: false,
});

View File

@ -0,0 +1,31 @@
import logo from './logo.svg';
import { create } from '@storybook/theming/create';
const pink = '#c55397'
const green = '#0f552b'
export default create({
base: 'dark',
colorSecondary: pink,
colorPrimary: green,
// UI
appBg: '#10354a',
appContentBg: '#203f50',
appBorderColor: '#002334',
appBorderRadius: 20,
// Typography
fontBase: '-apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Ubuntu, Arial, sans-serif',
fontCode: '"VictorMono", monospace',
// Text colors
textColor: 'white',
textInverseColor: 'rgba(255,255,255,0.9)',
brandTitle: 'Rowtype Yoga',
brandUrl: 'https://rowtype.yoga',
brandImage: logo,
});

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"editor.formatOnSave": true
}

9
LICENCE Normal file
View File

@ -0,0 +1,9 @@
Copyright 2020 Mark Eibes
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

6
convert-svgs.js Normal file
View File

@ -0,0 +1,6 @@
const path = require('path');
const svg2psreact = require("svg2psreact");
const assetsDir = path.join(__dirname, 'src', 'Assets');
svg2psreact.convertAllSVGsInDirectory("Assets", assetsDir)

7
index.dev.js Normal file
View File

@ -0,0 +1,7 @@
var Main = require("./output/Main");
if (module.hot) {
module.hot.accept();
}
Main.main({ serverSideRendering: false})()

2
index.prod.js Normal file
View File

@ -0,0 +1,2 @@
var Main = require("./dce-output/Main");
Main.main({ serverSideRendering: true})()

57
package.json Normal file
View File

@ -0,0 +1,57 @@
{
"name": "ry-blocks",
"version": "1.0.0",
"description": "A design system for the Web, built in PureScript and React.",
"main": "index.js",
"repository": "git@github.com:rowtype-yoga/ry-components.git",
"author": "Mark Eibes <mark.eibes@gmail.com>",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.10.5",
"@pmmmwh/react-refresh-webpack-plugin": "^0.3.3",
"@storybook/addons": "^6.0.0-rc.18",
"@storybook/react": "^6.0.0-rc.18",
"@storybook/theming": "^6.0.0-rc.18",
"@testing-library/react": "^10.4.7",
"@testing-library/user-event": "^12.0.11",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"compression-webpack-plugin": "^4.0.0",
"framer-motion": "^2.3.0",
"html-webpack-plugin": "^4.3.0",
"jsdom": "^16.3.0",
"jsdom-global": "^3.0.2",
"purescript": "^0.13.8",
"react-refresh": "^0.8.3",
"rimraf": "^3.0.2",
"spago": "^0.15.3",
"svg2psreact": "^2.1.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.0.9"
},
"scripts": {
"clean": "rimraf node_modules output dce-output .spago dist/* *.lock dist",
"build": "spago build",
"convert-svgs": "node convert-svgs.js",
"postinstall": "spago install",
"pretest": "yarn build",
"test": "spago test",
"prestart": "yarn build",
"start": "webpack-dev-server --open --config webpack.dev.js",
"bundle:build": "spago build --purs-args '--codegen corefn'",
"bundle:dce": "zephyr -f Main.main",
"bundle:webpack": "webpack --config webpack.prod.js",
"bundle": "yarn bundle:build && yarn bundle:dce && yarn bundle:webpack",
"prestorybook": "yarn build",
"storybook": "start-storybook -p 6006",
"prebuild-storybook": "yarn build",
"build-storybook": "build-storybook"
},
"dependencies": {
"@emotion/core": "^10.0.28",
"react": "^16.13.1",
"react-dom": "^16.13.1"
}
}

201
packages.dhall Normal file
View File

@ -0,0 +1,201 @@
{-
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 overrides =
{ 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.8/packages.dhall sha256:0e95ec11604dc8afc1b129c4d405dcc17290ce56d7d0665a0ff15617e32bbf03
let overrides =
{ spec-discovery = upstream.spec-discovery // { version = "master" }
, react-basic = upstream.react-basic // { version = "main" }
, react-basic-hooks = upstream.react-basic-hooks // { version = "main " }
}
let additions =
{ literals =
{ dependencies =
[ "assert"
, "effect"
, "console"
, "integers"
, "numbers"
, "partial"
, "psci-support"
, "unsafe-coerce"
, "typelevel-prelude"
]
, repo = "https://github.com/jvliwanag/purescript-literal.git"
, version = "7b2ae20f77c67b7e419a92fdd0dc7a09b447b18e"
}
, justifill =
{ dependencies = [ "record", "typelevel-prelude", "undefined" ]
, repo = "https://github.com/i-am-the-slime/purescript-justifill.git"
, version = "6c9300b7488dbf41a9381eecd9c5c8b886be1d42"
}
, untagged-union =
{ dependencies =
[ "assert"
, "console"
, "effect"
, "foreign"
, "foreign-object"
, "literals"
, "maybe"
, "newtype"
, "proxy"
, "psci-support"
, "tuples"
, "unsafe-coerce"
]
, repo = "https://github.com/jvliwanag/purescript-untagged-union.git"
, version = "master"
}
, react-testing-library =
../purescript-react-testing-library/spago.dhall as Location
, react-basic-dom =
{ dependencies =
[ "effect"
, "foreign-object"
, "react-basic"
, "unsafe-coerce"
, "web-dom"
, "web-events"
, "web-file"
, "web-html"
]
, repo = "https://github.com/lumihq/purescript-react-basic-dom.git"
, version = "main"
}
, react-basic-emotion =
{ dependencies =
[ "colors"
, "foreign"
, "numbers"
, "prelude"
, "react-basic"
, "typelevel-prelude"
, "unsafe-reference"
]
, repo =
"https://github.com/i-am-the-slime/purescript-react-basic-emotion.git"
, version = "add-keyframes"
}
}
in upstream // overrides // additions

22
spago.dhall Normal file
View File

@ -0,0 +1,22 @@
{-
Welcome to a Spago project!
You can edit this file as you like.
-}
{ name = "ry-blocks"
, dependencies =
[ "console"
, "effect"
, "psci-support"
, "react-basic-dom"
, "react-basic-emotion"
, "react-basic-hooks"
, "react-testing-library"
, "record-extra"
, "routing"
, "routing-duplex"
, "spec-discovery"
, "untagged-union"
]
, packages = ./packages.dhall
, sources = [ "src/**/*.purs", "test/**/*.purs" ]
}

5
src/Components/Box.purs Normal file
View File

@ -0,0 +1,5 @@
module Components.Box
( module Components.Box.View
) where
import Components.Box.View (component, Props)

View File

@ -0,0 +1,19 @@
module Components.Box.Spec where
import Prelude.Spec
import Components.Box as Box
import React.Basic.DOM as R
import React.Basic.Hooks (JSX)
import Untagged.Coercible (coerce)
spec ∷ Spec Unit
spec =
after_ cleanup do
describe "The box" do
it "renders without errors" do
void $ renderComponent Box.component (coerce { kids: [] ∷ _ JSX })
it "displays its children" do
let kids = [ R.text "Test Text" ]
{ findByText } <- renderComponent Box.component (coerce { kids })
elem <- findByText "Test Text"
elem `textContentShouldEqual` "Test Text"

View File

@ -0,0 +1,14 @@
module Components.Box.Story where
import Prelude
import Components.Box as Box
import Effect (Effect)
import React.Basic (JSX)
import React.Basic.DOM as R
import Yoga (yogaElement)
default ∷ { title ∷ String }
default = { title: "Layout/Box" }
box ∷ Effect JSX
box = pure (yogaElement Box.component { kids: [ R.text "Content" ] })

View File

@ -0,0 +1,10 @@
module Components.Box.Style where
import Prelude.Style
box ∷ Style
box =
css
{ padding: 1.0 # em
, backgroundColor: "white" # str
}

View File

@ -0,0 +1,26 @@
module Components.Box.View (component, Props) where
import Prelude.View
import Components.Box.Style as Styles
import Data.Nullable (Nullable)
import React.Basic.DOM as R
import React.Basic.Emotion as E
import Unsafe.Coerce (unsafeCoerce)
import Untagged.Union (UndefinedOr)
import Web.DOM (Node)
type Props =
{ kids ∷ Array JSX
, nodeRef ∷ UndefinedOr (Ref (Nullable Node))
}
component ∷ ReactComponent Props
component =
reactComponent "Box" \({ kids, nodeRef } ∷ Props) -> React.do
pure
$ E.element R.div'
{ className: "ry-box"
, css: Styles.box
, children: kids
, ref: unsafeCoerce nodeRef
}

View File

@ -0,0 +1,5 @@
module Components.Centre
( module Components.Centre.View
) where
import Components.Centre.View (component, Props)

View File

@ -0,0 +1,21 @@
module Components.Centre.Spec where
import Prelude.Spec
import Components.Centre as Centre
import React.Basic.DOM as R
spec ∷ Spec Unit
spec =
after_ cleanup do
describe "The centre" do
it "renders without errors" do
void
$ renderComponent Centre.component {}
it "accepts div props" do
{ findByText } <-
renderComponent Centre.component
{ role: "Heinz"
, children: [ R.text "Find me!" ]
}
elem <- findByText "Find me!"
elem `shouldHaveAttribute` "role"

View File

@ -0,0 +1,56 @@
module Components.Centre.Story where
import Prelude
import Components.Centre as Centre
import Components.Container.Style as Styles
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.Emotion as E
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
, title ∷ String
}
default =
{ title: "Layout/Centre"
, decorators:
[ \storyFn ->
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
centre ∷ Effect JSX
centre =
pure
$ fragment
[ element Centre.component
{ children: [ R.text "Default" ]
}
, element Centre.component
{ children: [ R.text "Text Centred" ]
, style: css { backgroundColor: "darkslateblue" }
, andText: true
}
, element Centre.component
{ children: [ R.text "With gutters" ]
, style: css { backgroundColor: "dodgerblue" }
, gutters: 3.0 # E.em
}
, E.element Centre.component
{ children: [ R.text "With custom style" ]
, css:
E.css
{ borderRadius: "1em" # E.str
, backgroundColor: "darkslategrey" # E.str
, textAlign: "center" # E.str
}
, className:
"styled-centre"
}
]

View File

@ -0,0 +1,27 @@
module Components.Centre.Style where
import Prelude.Style
type Props f r =
( css ∷ f Style
, maxWidth ∷ f StyleProperty
, andText ∷ f Boolean
, gutters ∷ f StyleProperty
, padding ∷ f StyleProperty
| r
)
centre ∷ ∀ p. { | Props OptionalProp p } -> Style
centre props = styles <>? props.css
where
styles =
css
{ padding: 1.0 # em
, boxSizing: contentBox
, marginLeft: auto
, marginRight: auto
, maxWidth: props.maxWidth ?|| (60.0 # ch)
, textAlign: if props.andText # isTruthy then center else left
, paddingLeft: props.gutters ?|| _0
, paddingRight: props.gutters ?|| _0
}

View File

@ -0,0 +1,27 @@
module Components.Centre.View (component, Props, PropsF) where
import Prelude.View
import Components.Centre.Style as Style
type PropsF f =
( className ∷ f String
| Style.Props f + DivProps
)
type Props =
PropsF Id
type PropsOptional =
PropsF OptionalProp
component ∷ ∀ p p_. Union p p_ Props => ReactComponent { | p }
component =
mkForwardRefComponent "Centre" do
\(props ∷ { | PropsOptional }) ref -> React.do
pure
$ emotionDiv
props
{ className: "ry-centre " <>? props.className
, css: Style.centre props
, ref
}

View File

@ -0,0 +1,5 @@
module Components.Cluster
( module Components.Cluster.View
) where
import Components.Cluster.View (component, Props)

View File

@ -0,0 +1,21 @@
module Components.Cluster.Spec where
import Prelude.Spec
import Components.Cluster as Cluster
import React.Basic.DOM as R
spec ∷ Spec Unit
spec =
after_ cleanup do
describe "The cluster" do
it "renders without errors" do
void
$ renderComponent Cluster.component {}
it "accepts div props" do
{ findByText } <-
renderComponent Cluster.component
{ role: "Heinz"
, children: [ R.text "Find me!" ]
}
elem <- findByText "Find me!"
elem `shouldHaveAttribute` "role"

View File

@ -0,0 +1,55 @@
module Components.Cluster.Story where
import Prelude
import Components.Container.Style as Styles
import Components.Cluster as Cluster
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.Emotion as E
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
, title ∷ String
}
default =
{ title: "Layout/Cluster"
, decorators:
[ \storyFn ->
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
cluster ∷ Effect JSX
cluster =
pure
$ fragment
[ R.div_
[ R.h2_ [ R.text "No Options" ]
, element Cluster.component
{ children:
[ R.div_
[ R.div { children: [ R.text "Child 1" ], style: css { backgroundColor: "teal" } }
, R.div { children: [ R.text "Child 2" ], style: css { backgroundColor: "rebeccapurple" } }
]
]
, style: css { backgroundColor: "hotpink" }
}
, R.h2_ [ R.text "Zero Space" ]
, element Cluster.component
{ children:
[ R.div_
[ R.div { children: [ R.text "Child 1" ], style: css { backgroundColor: "teal" } }
, R.div { children: [ R.text "Child 2" ], style: css { backgroundColor: "rebeccapurple" } }
]
]
, style: css { backgroundColor: "darkslateblue" }
, space: "0"
}
]
]

View File

@ -0,0 +1,28 @@
module Components.Cluster.Style where
import Prelude.Style
type Props f r =
( css ∷ f Style
, space ∷ f String
| r
)
cluster ∷ ∀ p. { | Props OptionalProp p } -> Style
cluster props = styles <>? props.css
where
styles =
css
{ "& > *":
nest
{ display: flex
, flexWrap: wrap
, alignItems: center
, justifyContent: flexStart
, margin: "calc(" <> props.space ?|| "1rem" <> "/2 * -1)" # str
}
, "& > * > *":
nest
{ margin: "calc(" <> (props.space ?|| "1rem") <> "/2)" # str
}
}

View File

@ -0,0 +1,29 @@
module Components.Cluster.View (component, Props, PropsF) where
import Prelude.View
import Components.Cluster.Style as Style
type PropsF f =
( className ∷ f String
| Style.Props f DivProps
)
type Props =
PropsF Id
type PropsOptional =
PropsF OptionalProp
component ∷
∀ p p_.
Union p p_ Props =>
ReactComponent { | p }
component =
mkForwardRefComponent "Cluster" do
\(props ∷ { | PropsOptional }) ref -> React.do
pure
$ emotionDiv props
{ className: "ry-cluster " <>? props.className
, css: Style.cluster props
, ref
}

View File

@ -0,0 +1,5 @@
module Components.Container
( module Components.Container.View
) where
import Components.Container.View (component)

View File

@ -0,0 +1,18 @@
module Components.Container.Spec where
import Prelude.Spec
import Components.Container as Container
import React.Basic.DOM as R
import React.Basic.Hooks (reactChildrenFromArray)
spec ∷ Spec Unit
spec =
after_ cleanup do
describe "The container" do
it "renders without errors" do
void $ renderComponent Container.component { children: reactChildrenFromArray [] }
it "displays its children" do
let children = reactChildrenFromArray [ R.text "Test Text" ]
{ findByText } <- renderComponent Container.component { children }
elem <- findByText "Test Text"
elem `textContentShouldEqual` "Test Text"

View File

@ -0,0 +1,14 @@
module Components.Container.Story where
import Prelude
import Components.Container as Container
import Effect (Effect)
import React.Basic (JSX, element)
import React.Basic.DOM as R
import React.Basic.Hooks (reactChildrenFromArray)
default ∷ { title ∷ String }
default = { title: "Pages/Container" }
container ∷ Effect JSX
container = pure (element Container.component { children: reactChildrenFromArray [ R.text "Content" ] })

View File

@ -0,0 +1,44 @@
module Components.Container.Style where
import Prelude.Style
global ∷ Style
global =
css
{ "body, html":
nested
$ css
{ minHeight: 100.0 # vh
, minWidth: 100.0 # vw
}
, html:
nested
$ css
{ boxSizing: str "border-box"
}
, body:
nested
$ css
{ fontFamily: str "system-ui, sans-serif"
, color: str "#f7f7f0"
, background: str "linear-gradient(to bottom right, #10354a, #002334)"
, margin: str "0"
}
, h1:
nested
$ css
{ fontSize: em 3.5
, fontWeight: str "black"
}
, h2:
nested
$ css
{ fontSize: em 2.7
, fontWeight: str "black"
}
, "*, *:before, *:after":
nested
$ css
{ boxSizing: str "inherit"
}
}

View File

@ -0,0 +1,19 @@
module Components.Container.View (component) where
import Prelude.View
import Components.Container.Style as Styles
import Data.Array as Array
import React.Basic.DOM as R
import React.Basic.Emotion as E
type Props =
{ children ∷ ReactChildren JSX }
component ∷ ReactComponent Props
component =
reactComponentWithChildren "Container" \({ children } ∷ Props) -> React.do
pure
$ R.div_
$ Array.cons
(element E.global { styles: Styles.global })
(reactChildrenToArray children)

View File

@ -0,0 +1,5 @@
module Components.Sidebar
( module Components.Sidebar.View
) where
import Components.Sidebar.View (component, Props)

View File

@ -0,0 +1,21 @@
module Components.Sidebar.Spec where
import Prelude.Spec
import Components.Sidebar as Sidebar
import React.Basic.DOM as R
spec ∷ Spec Unit
spec =
after_ cleanup do
describe "The sidebar" do
it "renders without errors" do
void
$ renderComponent Sidebar.component {}
it "accepts div props" do
{ findByText } <-
renderComponent Sidebar.component
{ role: "Heinz"
, children: [ R.text "Find me!" ]
}
elem <- findByText "Find me!"
elem `shouldHaveAttribute` "role"

View File

@ -0,0 +1,71 @@
module Components.Sidebar.Story where
import Prelude
import Components.Container.Style as Styles
import Components.Sidebar as Sidebar
import Components.Sidebar.Style (SidebarSide(..))
import Data.Monoid (power)
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.Emotion as E
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
, title ∷ String
}
default =
{ title: "Layout/Sidebar"
, decorators:
[ \storyFn ->
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
sidebar ∷ Effect JSX
sidebar =
pure
$ fragment
[ R.div_
[ R.h2_ [ R.text "No Options" ]
, element Sidebar.component
{ children:
[ R.div_
[ R.div { children: [ R.text "Sidebar" ], style: css { backgroundColor: "teal" } }
, R.div { children: [ R.text $ power "Content " 10 ], style: css { backgroundColor: "darkslateblue" } }
]
]
, style: css { backgroundColor: "oldlace" }
}
, R.h2_ [ R.text "Zero Space" ]
, element Sidebar.component
{ children:
[ R.div_
[ R.div { children: [ R.text "Child 1" ], style: css { backgroundColor: "teal" } }
, R.div { children: [ R.text "Child 2" ], style: css { backgroundColor: "darkslateblue" } }
]
]
, style: css { backgroundColor: "oldlace" }
, space: "0"
}
, R.h2_ [ R.text "Sidebar Right" ]
, element Sidebar.component
{ children:
[ R.div_
[ R.div { children: [ R.text $ power "Content " 50 ], style: css { backgroundColor: "darkslateblue" } }
, R.div { children: [ R.text "Sidebar" ], style: css { backgroundColor: "teal" } }
]
]
, style: css { backgroundColor: "oldlace" }
, sideWidth: "40px"
, contentMin: "80%"
, space: "2em"
, side: SidebarRight
}
]
]

View File

@ -0,0 +1,57 @@
module Components.Sidebar.Style where
import Prelude.Style
data SidebarSide
= SidebarLeft
| SidebarRight
type Props f r =
( css ∷ f Style
, space ∷ f String
, side ∷ f SidebarSide
, sideWidth ∷ f String
, contentMin ∷ f String
, noStretch ∷ f Boolean
| r
)
sidebar ∷ ∀ p. { | Props OptionalProp p } -> Style
sidebar props = styles <>? props.css
where
adjustedSpace = props.space <#> \s -> if s == "0" then "0px" else s
space = adjustedSpace ?|| "1rem"
side = props.side ?|| SidebarLeft
contentMin = props.contentMin ?|| "50%"
nonSidebarStyle =
nest
{ flexBasis: _0
, flexGrow: "999" # str
, minWidth: "calc(" <> contentMin <> " - " <> space <> ")" # str
}
styles =
css
{ overflow: hidden
, "& > *":
nest
{ display: flex
, flexWrap: wrap
, margin: "calc(" <> space <> "/2 * -1)" # str
, alignItems: props.noStretch # ifTrue "flex-start" "" # str
}
, "& > * > *":
nest
{ margin: "calc(" <> space <> "/2)" # str
, flexGrow: "1" # str
, flexBasis: props.sideWidth # foldMap str
}
<> foldMap (nest <<< { flexBasis: _ } <<< str) props.sideWidth
}
<> case side of
SidebarLeft -> css { "& > * > :last-child": nonSidebarStyle }
SidebarRight -> css { "& > * > :first-child": nonSidebarStyle }

View File

@ -0,0 +1,29 @@
module Components.Sidebar.View (component, Props, PropsF) where
import Prelude.View
import Components.Sidebar.Style as Style
type PropsF f =
( className ∷ f String
| Style.Props f DivProps
)
type Props =
PropsF Id
type PropsOptional =
PropsF OptionalProp
component ∷
∀ p p_.
Union p p_ Props =>
ReactComponent { | p }
component =
mkForwardRefComponent "Sidebar" do
\(props ∷ { | PropsOptional }) ref -> React.do
pure
$ emotionDiv props
{ className: "ry-sidebar " <>? props.className
, css: Style.sidebar props
, ref
}

View File

@ -0,0 +1,5 @@
module Components.Stack
( module Components.Stack.View
) where
import Components.Stack.View (component, Props)

View File

@ -0,0 +1,21 @@
module Components.Stack.Spec where
import Prelude.Spec
import Components.Stack as Stack
import React.Basic.DOM as R
spec ∷ Spec Unit
spec =
after_ cleanup do
describe "The stack" do
it "renders without errors" do
void
$ renderComponent Stack.component {}
it "accepts div props" do
{ findByText } <-
renderComponent Stack.component
{ role: "Heinz"
, children: [ R.text "Find me!" ]
}
elem <- findByText "Find me!"
elem `shouldHaveAttribute` "role"

View File

@ -0,0 +1,68 @@
module Components.Stack.Story where
import Prelude
import Components.Container.Style as Styles
import Components.Stack as Stack
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.Emotion as E
import Yoga.Blocks.Internal (_0)
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
, title ∷ String
}
default =
{ title: "Layout/Stack"
, decorators:
[ \storyFn ->
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
stack ∷ Effect JSX
stack =
pure
$ fragment
[ R.div_
[ R.h2_ [ R.text "No Options" ]
, element Stack.component
{ children:
[ R.div { children: [ R.text "Child 1" ], style: css { backgroundColor: "teal" } }
, R.div { children: [ R.text "Child 2" ], style: css { backgroundColor: "rebeccapurple" } }
]
, style: css { backgroundColor: "darkslateblue" }
}
, R.h2_ [ R.text "Zero Space" ]
, element Stack.component
{ children:
[ R.div { children: [ R.text "Child 1" ], style: css { backgroundColor: "teal" } }
, R.div { children: [ R.text "Child 2" ], style: css { backgroundColor: "rebeccapurple" } }
]
, style: css { backgroundColor: "darkslateblue" }
, space: _0
}
, R.h2_ [ R.text "Split After" ]
, R.div
{ style: css { height: "300px", backgroundColor: "tomato" }
, children:
[ element Stack.component
{ children:
[ R.div { children: [ R.div_ [ R.text "Child 1" ] ], style: css { backgroundColor: "teal" } }
, R.div { children: [ R.div_ [ R.text "Child 2" ] ], style: css { backgroundColor: "rebeccapurple" } }
, R.div { children: [ R.div_ [ R.text "Child 3" ] ], style: css { backgroundColor: "teal" } }
, R.div { children: [ R.div_ [ R.text "Child 4" ] ], style: css { backgroundColor: "rebeccapurple" } }
]
, style: css { backgroundColor: "darkslateblue" }
, splitAfter: 2
}
]
}
]
]

View File

@ -0,0 +1,40 @@
module Components.Stack.Style where
import Prelude.Style
type Props f r =
( css ∷ f Style
, space ∷ f StyleProperty
, splitAfter ∷ f Int
| r
)
stack ∷ ∀ p. { | Props OptionalProp p } -> Style
stack props = splitStyles <> styles <>? props.css
where
styles =
css
{ display: flex
, flexDirection: column
, justifyContent: flexStart
, "& > *":
nest
{ marginTop: _0
, marginBottom: _0
}
, "& > * + *":
nest
{ marginTop: props.space ?|| (1.5 # rem)
}
}
splitStyles = props.splitAfter # foldMap \n -> onlyChildStyle <> nthChildStyle n
where
onlyChildStyle =
css
{ "&:only-child": nest { height: 100.0 # percent }
}
nthChild n = "& > :nth-child(" <> show n <> ")"
nthChildStyle n = (nthChild n) ~: { marginBottom: auto }

View File

@ -0,0 +1,26 @@
module Components.Stack.View (component, Props, PropsF) where
import Prelude.View
import Components.Stack.Style as Style
type PropsF f =
( className ∷ f String
| Style.Props f Props_div
)
type Props =
PropsF Id
type PropsOptional =
PropsF OptionalProp
component ∷ ∀ p p_. Union p p_ Props => ReactComponent { | p }
component =
mkForwardRefComponent "Stack" do
\(props ∷ { | PropsOptional }) ref -> React.do
pure
$ emotionDiv props
{ className: "ry-stack " <>? props.className
, css: Style.stack props
, ref
}

5
src/Framer/Motion.js Normal file
View File

@ -0,0 +1,5 @@
const framerMotion = require("framer-motion")
exports.divImpl = framerMotion.motion.div
exports.infinity = Infinity

73
src/Framer/Motion.purs Normal file
View File

@ -0,0 +1,73 @@
module Framer.Motion
( animate
, div
, Exit
, MotionProps
, Transition
, transition
, Initial
, Animate
, Infinity
, infinity
, VariantLabel
, AnimationControls
) where
import Prim.Row (class Union)
import React.Basic (ReactComponent)
import React.Basic.DOM (CSS, Props_div, css)
import Type.Row (type (+))
import Untagged.Coercible (class Coercible, coerce)
import Untagged.Union (type (|+|))
foreign import divImpl ∷ ∀ a. ReactComponent { | a }
type Transition =
CSS
newtype VariantLabel = VariantLabel String
data Target
= Target
type Exit =
CSS |+| Array VariantLabel
foreign import data AnimationControls ∷ Type
type Animate =
CSS |+| String |+| Array VariantLabel |+| AnimationControls
type Initial =
Boolean |+| CSS |+| String |+| Array VariantLabel
type MotionProps r =
( initial ∷ Initial
, animate ∷ Animate
, variants ∷ Array CSS
, transition ∷ Transition
, exit ∷ Exit
| r
)
animate ∷ ∀ a. Coercible a Animate => a -> Animate
animate = coerce
initial ∷ ∀ a. Coercible a Initial => a -> Initial
initial = coerce
transition ∷ ∀ r. { | r } -> CSS
transition = css
variants ∷ ∀ a. Coercible a (Array VariantLabel) => a -> (Array VariantLabel)
variants = coerce
div ∷
∀ attrs attrs_.
Union attrs attrs_ (MotionProps + Props_div) =>
ReactComponent { | attrs }
div = divImpl
foreign import data Infinity ∷ Type
foreign import infinity ∷ Infinity

17
src/Prelude/Default.purs Normal file
View File

@ -0,0 +1,17 @@
module Prelude.Default
( module Prelude
, module Data.Maybe
, module Data.Either
, module Effect
, module Effect.Class
, module Data.Monoid
, module Data.Foldable
) where
import Prelude
import Data.Either (Either(..), note, hush)
import Data.Foldable (foldMap, for_, intercalate, traverse_)
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Monoid (guard)
import Effect (Effect)
import Effect.Class (liftEffect)

15
src/Prelude/Spec.purs Normal file
View File

@ -0,0 +1,15 @@
module Prelude.Spec
( module Prelude.Default
, module Effect.Aff
, module React.TestingLibrary
, module Test.Spec
, module Test.Spec.Assertions.DOM
, module React.Basic
) where
import Prelude.Default
import Effect.Aff (Aff)
import React.Basic (ReactComponent)
import React.TestingLibrary (class TextMatch, FakeKeyboardEvent, RenderQueries, cleanup, defaultKeyboardEvent, describeComponent, findByText, fireEvent, fireEventAbort, fireEventAnimationEnd, fireEventAnimationIteration, fireEventAnimationStart, fireEventBlur, fireEventCanPlay, fireEventCanPlayThrough, fireEventChange, fireEventClick, fireEventCompositionEnd, fireEventCompositionStart, fireEventCompositionUpdate, fireEventContextMenu, fireEventCopy, fireEventCut, fireEventDblClick, fireEventDoubleClick, fireEventDrag, fireEventDragEnd, fireEventDragEnter, fireEventDragExit, fireEventDragLeave, fireEventDragOver, fireEventDragStart, fireEventDrop, fireEventDurationChange, fireEventEmptied, fireEventEncrypted, fireEventEnded, fireEventError, fireEventFocus, fireEventFocusIn, fireEventFocusOut, fireEventInput, fireEventInvalid, fireEventKeyDown, fireEventKeyPress, fireEventKeyUp, fireEventLoad, fireEventLoadStart, fireEventLoadedData, fireEventLoadedMetadata, fireEventMouseDown, fireEventMouseEnter, fireEventMouseLeave, fireEventMouseMove, fireEventMouseOut, fireEventMouseOver, fireEventMouseUp, fireEventPaste, fireEventPause, fireEventPlay, fireEventPlaying, fireEventPointerCancel, fireEventPointerDown, fireEventPointerMove, fireEventPointerOut, fireEventPointerOver, fireEventPointerUp, fireEventProgress, fireEventRateChange, fireEventScroll, fireEventSeeked, fireEventSeeking, fireEventSelect, fireEventStalled, fireEventSubmit, fireEventSuspend, fireEventTimeUpdate, fireEventTouchCancel, fireEventTouchEnd, fireEventTouchMove, fireEventTouchStart, fireEventTransitionEnd, fireEventVolumeChange, fireEventWaiting, fireEventWheel, render, renderComponent, typeText)
import Test.Spec (class Example, class FocusWarning, ActionWith, ComputationType(..), Item(..), Spec, SpecT(..), SpecTree, Tree(..), after, afterAll, afterAll_, after_, around, aroundWith, around_, before, beforeAll, beforeAll_, beforeWith, before_, collect, describe, describeOnly, evaluateExample, focus, hoistSpec, it, itOnly, mapSpecTree, parallel, pending, pending', sequential)
import Test.Spec.Assertions.DOM (shouldHaveAttribute, shouldHaveAttributeWithValue, textContentShouldEqual, valueShouldEqual)

11
src/Prelude/Style.purs Normal file
View File

@ -0,0 +1,11 @@
module Prelude.Style
( module Prelude.Default
, module React.Basic.Emotion
, module Yoga.Blocks.Internal.OptionalProp
, module Yoga.Blocks.Internal.CSS
) where
import Prelude.Default
import React.Basic.Emotion (class IsStyle, class IsStyleProperty, Style, StyleProperty, ch, cm, color, css, element, elementKeyed, em, ex, fallbacks, global, important, inches, inherit, int, keyframes, merge, mm, nested, none, num, pc, percent, prop, pt, px, px', rem, str, style, unset, url, vh, vmax, vmin, vw)
import Yoga.Blocks.Internal.OptionalProp
import Yoga.Blocks.Internal.CSS

19
src/Prelude/View.purs Normal file
View File

@ -0,0 +1,19 @@
module Prelude.View
( module Prelude.Default
, module React.Basic.Hooks
, module React.Basic.Events
, module Prim.Row
, module Yoga
, module Yoga.Blocks.Internal
, module React.Basic.DOM
, module Type.Row
) where
import Prelude.Default
import React.Basic.Events (class Merge, EventFn, EventHandler, SyntheticEvent, handler, handler_, merge, mergeImpl, syntheticEvent, unsafeEventFn)
import React.Basic.Hooks (type (/\), Component, Hook, JSX, Pure, ReactChildren, ReactComponent, ReactContext, Ref, Render, UnsafeReference(..), UseContext, UseDebugValue, UseEffect, UseLayoutEffect, UseMemo, UseReducer, UseRef, UseState, coerceHook, consumer, contextConsumer, contextProvider, createContext, displayName, element, elementKeyed, empty, fragment, keyed, memo, provider, reactChildrenFromArray, reactChildrenToArray, reactComponentFromHook, readRef, readRefMaybe, unsafeHook, unsafeRenderEffect, useContext, useDebugValue, useEffect, useEffectAlways, useEffectOnce, useLayoutEffect, useLayoutEffectAlways, useLayoutEffectOnce, useMemo, useReducer, useRef, useState, useState', writeRef, (/\))
import Prim.Row (class Union)
import Yoga.Blocks.Internal
import React.Basic.DOM (Props_div)
import Type.Row (type (+))
import Yoga

35
src/Yoga.purs Normal file
View File

@ -0,0 +1,35 @@
module Yoga where
import Prelude
import Data.Nullable (Nullable)
import Effect.Unsafe (unsafePerformEffect)
import Foreign.Object (Object)
import Prim.Row (class Lacks)
import React.Basic.DOM (CSS)
import React.Basic.Events (EventHandler)
import React.Basic.Hooks (JSX, ReactComponent, Ref, Render)
import React.Basic.Hooks as Hooks
import Untagged.Coercible (class Coercible, coerce)
import Web.DOM (Node)
reactComponent ∷
∀ hooks props.
Lacks "children" props =>
Lacks "key" props =>
Lacks "ref" props =>
String ->
({ | props } -> Render Unit hooks JSX) ->
ReactComponent { | props }
reactComponent = (map map map) unsafePerformEffect Hooks.reactComponent
reactComponentWithChildren ∷
∀ hooks props children.
Lacks "key" props =>
Lacks "ref" props =>
String ->
({ children ∷ Hooks.ReactChildren children | props } -> Render Unit hooks JSX) ->
ReactComponent { children ∷ Hooks.ReactChildren children | props }
reactComponentWithChildren = (map map map) unsafePerformEffect Hooks.reactComponentWithChildren
yogaElement ∷ ∀ allProps givenProps. Coercible givenProps (Record allProps) => ReactComponent (Record allProps) -> givenProps -> JSX
yogaElement el props = Hooks.element el (coerce props)

View File

@ -0,0 +1,8 @@
const React = require("react")
exports.mkForwardRefComponent = (displayName) => (renderFn) => {
const component = (props, ref) => renderFn(props)(ref)();
component.displayName = displayName;
return React.forwardRef(component);
};

View File

@ -0,0 +1,187 @@
module Yoga.Blocks.Internal
( mkForwardRefComponent
, unsafeEmotion
, dangerous
, DivProps
, DivPropsF
, emotionDiv
, module Yoga.Blocks.Internal.OptionalProp
, module Yoga.Blocks.Internal.CSS
) where
import Data.Nullable (Nullable)
import Effect.Unsafe (unsafePerformEffect)
import Foreign.Object (Object)
import Prelude (Unit, (<<<))
import Prim.Row (class Union)
import React.Basic.DOM (unsafeCreateDOMComponent, CSS)
import React.Basic.Emotion (Style)
import React.Basic.Emotion as E
import React.Basic.Events (EventHandler)
import React.Basic.Hooks (JSX, ReactComponent, Ref, Render)
import Record.Extra (pick)
import Record.Unsafe.Union (unsafeUnion)
import Web.DOM (Node)
import Yoga.Blocks.Internal.CSS (_0, auto, borderBox, center, column, contentBox, flex, flexStart, left, nest, nestDynamic, right, (~:))
import Yoga.Blocks.Internal.OptionalProp
foreign import mkForwardRefComponent ∷
∀ inputProps props a hooks.
String ->
({ | inputProps } -> Ref a -> Render Unit hooks JSX) ->
ReactComponent { | props }
unsafeEmotion ∷ ∀ propsA propsB ref. String -> Record propsA -> { className ∷ String, css ∷ E.Style, ref ∷ Ref ref | propsB } -> JSX
unsafeEmotion name propsA propsB = E.element (dangerous name) (unsafeUnion propsA propsB)
emotionDiv_ ∷
∀ props props_.
Union props props_ ( className ∷ String, css ∷ Style, ref ∷ Ref (Nullable Node) | DivProps ) =>
(Record (DivProps)) ->
{ className ∷ String
, css ∷ Style
, ref ∷ Ref (Nullable Node)
| props
} ->
JSX
emotionDiv_ = unsafeEmotion "div"
emotionDiv = emotionDiv_ <<< pick
dangerous ∷ ∀ props. String -> ReactComponent (Record props)
dangerous = unsafePerformEffect <<< unsafeCreateDOMComponent
type DivProps =
DivPropsF Id
type DivPropsF f =
( _aria ∷ f (Object String)
, _data ∷ f (Object String)
, about ∷ f String
, acceptCharset ∷ f String
, accessKey ∷ f String
, allowFullScreen ∷ f Boolean
, autoFocus ∷ f Boolean
, autoPlay ∷ f Boolean
, capture ∷ f Boolean
, cellPadding ∷ f String
, cellSpacing ∷ f String
, charSet ∷ f String
, children ∷ f (Array JSX)
, classID ∷ f String
, colSpan ∷ f Int
, contentEditable ∷ f Boolean
, contextMenu ∷ f String
, crossOrigin ∷ f String
, dangerouslySetInnerHTML ∷ f { __html ∷ f String }
, datatype ∷ f String
, dateTime ∷ f String
, dir ∷ f String
, draggable ∷ f Boolean
, encType ∷ f String
, formAction ∷ f String
, formEncType ∷ f String
, formMethod ∷ f String
, formNoValidate ∷ f Boolean
, formTarget ∷ f String
, frameBorder ∷ f String
, hidden ∷ f Boolean
, hrefLang ∷ f String
, htmlFor ∷ f String
, httpEquiv ∷ f String
, icon ∷ f String
, id ∷ f String
, inlist ∷ f String
, inputMode ∷ f String
, is ∷ f String
, itemID ∷ f String
, itemProp ∷ f String
, itemRef ∷ f String
, itemScope ∷ f Boolean
, itemType ∷ f String
, key ∷ f String
, keyParams ∷ f String
, keyType ∷ f String
, lang ∷ f String
, marginHeight ∷ f String
, marginWidth ∷ f String
, maxLength ∷ f Int
, mediaGroup ∷ f String
, minLength ∷ f Int
, noValidate ∷ f Boolean
, onAnimationEnd ∷ f EventHandler
, onAnimationIteration ∷ f EventHandler
, onAnimationStart ∷ f EventHandler
, onBlur ∷ f EventHandler
, onClick ∷ f EventHandler
, onCompositionEnd ∷ f EventHandler
, onCompositionStart ∷ f EventHandler
, onCompositionUpdate ∷ f EventHandler
, onContextMenu ∷ f EventHandler
, onCopy ∷ f EventHandler
, onCut ∷ f EventHandler
, onDoubleClick ∷ f EventHandler
, onDrag ∷ f EventHandler
, onDragEnd ∷ f EventHandler
, onDragEnter ∷ f EventHandler
, onDragExit ∷ f EventHandler
, onDragLeave ∷ f EventHandler
, onDragOver ∷ f EventHandler
, onDragStart ∷ f EventHandler
, onDrop ∷ f EventHandler
, onFocus ∷ f EventHandler
, onGotPointerCapture ∷ f EventHandler
, onInvalid ∷ f EventHandler
, onKeyDown ∷ f EventHandler
, onKeyPress ∷ f EventHandler
, onKeyUp ∷ f EventHandler
, onLostPointerCapture ∷ f EventHandler
, onMouseDown ∷ f EventHandler
, onMouseEnter ∷ f EventHandler
, onMouseLeave ∷ f EventHandler
, onMouseMove ∷ f EventHandler
, onMouseOut ∷ f EventHandler
, onMouseOver ∷ f EventHandler
, onMouseUp ∷ f EventHandler
, onPaste ∷ f EventHandler
, onPointerCancel ∷ f EventHandler
, onPointerDown ∷ f EventHandler
, onPointerEnter ∷ f EventHandler
, onPointerLeave ∷ f EventHandler
, onPointerMove ∷ f EventHandler
, onPointerOut ∷ f EventHandler
, onPointerOver ∷ f EventHandler
, onPointerUp ∷ f EventHandler
, onSelect ∷ f EventHandler
, onSubmit ∷ f EventHandler
, onTouchCancel ∷ f EventHandler
, onTouchEnd ∷ f EventHandler
, onTouchMove ∷ f EventHandler
, onTouchStart ∷ f EventHandler
, onTransitionEnd ∷ f EventHandler
, onWheel ∷ f EventHandler
, prefix ∷ f String
, property ∷ f String
, radioGroup ∷ f String
, readOnly ∷ f Boolean
, ref ∷ f (Ref (Nullable Node))
, resource ∷ f String
, role ∷ f String
, rowSpan ∷ f Int
, scoped ∷ f Boolean
, seamless ∷ f Boolean
, security ∷ f String
, spellCheck ∷ f Boolean
, srcDoc ∷ f JSX
, srcLang ∷ f String
, srcSet ∷ f String
, style ∷ f CSS
, suppressContentEditableWarning ∷ f Boolean
, tabIndex ∷ f Int
, title ∷ f String
, typeof ∷ f String
, unselectable ∷ f Boolean
, useMap ∷ f String
, vocab ∷ f String
, wmode ∷ f String
)

View File

@ -0,0 +1,51 @@
module Yoga.Blocks.Internal.CSS where
import Prelude
import React.Basic.Emotion
import Foreign.Object (Object)
import Foreign.Object as Object
import Type.Row.Homogeneous (class Homogeneous)
import Unsafe.Coerce (unsafeCoerce)
center ∷ StyleProperty
center = str "center"
left ∷ StyleProperty
left = str "left"
right ∷ StyleProperty
right = str "right"
auto ∷ StyleProperty
auto = str "auto"
contentBox ∷ StyleProperty
contentBox = str "content-box"
borderBox ∷ StyleProperty
borderBox = str "border-box"
_0 ∷ StyleProperty
_0 = str "0"
flex = str "flex"
hidden = str "hidden"
wrap = str "wrap"
column = str "column"
flexStart = str "flex-start"
nest = nested <<< css
nestDynamic ∷
∀ r.
Homogeneous r StyleProperty =>
String -> { | r } -> Style
nestDynamic key sp =
unsafeCoerce
$ Object.singleton key (css sp)
infixr 6 nestDynamic as ~:

View File

@ -0,0 +1,63 @@
module Yoga.Blocks.Internal.OptionalProp where
import Prelude
import Control.Alt (class Alt, (<|>))
import Data.Foldable (class Foldable, foldMap, foldl, foldr)
import Data.Maybe (Maybe, maybe)
import Data.Newtype (class Newtype)
import Unsafe.Coerce (unsafeCoerce)
import Untagged.Union (UndefinedOr, defined, fromUndefinedOr, maybeToUor, uorToMaybe)
type Id a =
a
newtype OptionalProp a = OptionalProp (UndefinedOr a)
unsafeUnOptional ∷ ∀ a. OptionalProp a -> a
unsafeUnOptional = unsafeCoerce
opToMaybe ∷ ∀ a. OptionalProp a -> Maybe a
opToMaybe (OptionalProp x) = uorToMaybe x
maybeToOp ∷ ∀ a. Maybe a -> OptionalProp a
maybeToOp mb = OptionalProp (maybeToUor mb)
derive instance ntOptionalProp ∷ Newtype (OptionalProp a) _
instance semigroupOptionalProp ∷ Semigroup a => Semigroup (OptionalProp a) where
append (OptionalProp a) (OptionalProp b) =
OptionalProp
(maybeToUor (uorToMaybe a <> uorToMaybe b))
instance monoidOptionalProp ∷ Monoid a => Monoid (OptionalProp a) where
mempty = OptionalProp (defined mempty)
instance foldableOptionalProp ∷ Foldable OptionalProp where
foldMap fn = foldMap fn <<< opToMaybe
foldr fn acc = foldr fn acc <<< opToMaybe
foldl fn acc = foldl fn acc <<< opToMaybe
instance functorOptionalProp ∷ Functor OptionalProp where
map fn opt = maybeToOp $ (map fn (opToMaybe opt))
instance altOptionalProp ∷ Alt OptionalProp where
alt op1 op2 = maybeToOp $ (opToMaybe op1) <|> (opToMaybe op2)
getOr ∷ ∀ a. a -> OptionalProp a -> a
getOr default (OptionalProp o) = fromUndefinedOr default o
getOrFlipped ∷ ∀ a. OptionalProp a -> a -> a
getOrFlipped = flip getOr
ifTrue ∷ ∀ a. a -> a -> OptionalProp Boolean -> a
ifTrue v alt x = if (x ?|| false) then v else alt
isTruthy ∷ OptionalProp Boolean -> Boolean
isTruthy = getOr false
infixr 6 getOrFlipped as ?||
appendIfDefined ∷ ∀ a. (Semigroup a) => a -> OptionalProp a -> a
appendIfDefined a undefOrA = maybe a (a <> _) (opToMaybe undefOrA)
infixr 7 appendIfDefined as <>?

14
src/index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://cdn.jsdelivr.net/npm/victormono@latest/dist/index.min.css" rel="stylesheet">
</head>
<body>
<div id="container"></div>
</body>
</html>

14
test/Main.purs Normal file
View File

@ -0,0 +1,14 @@
module Test.Main where
import Prelude
import Effect (Effect)
import Effect.Aff (launchAff_)
import Test.Spec.Discovery (discover)
import Test.Spec.Reporter (consoleReporter)
import Test.Spec.Runner (runSpec)
main ∷ Effect Unit
main =
launchAff_ do
specs <- discover ".*Spec"
runSpec [ consoleReporter ] specs

11
webpack.common.js Normal file
View File

@ -0,0 +1,11 @@
"use strict";
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [
// This plugin deletes (cleans) the output folder
// `./dist` in our case
new CleanWebpackPlugin(),
],
};

38
webpack.dev.js Normal file
View File

@ -0,0 +1,38 @@
"use strict";
const path = require("path");
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = merge(common, {
mode: 'development',
// The JavaScript file to be injected into the HTML file
entry: path.resolve(__dirname, "index.dev.js"),
devtool: 'inline-source-map',
// This plugin updates React components without losing their state
plugins: [
new ReactRefreshWebpackPlugin(),
// This plugin allows us to use a HTML file template.
// In the settings we specify its title and 'entry'
// specifies a script to be injected into the template
new HtmlWebpackPlugin({
title: "PureScript Starter",
template: path.resolve(__dirname, "src", "index.html"),
})
],
devServer: {
contentBase: path.join(__dirname, 'dist'),
historyApiFallback: true,
compress: true,
port: 9000,
hotOnly: true,
hot: true,
},
});

21
webpack.prod.js Normal file
View File

@ -0,0 +1,21 @@
"use strict";
const path = require("path");
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
plugins: [new CompressionPlugin()],
// The JavaScript file to be injected into the HTML file
entry: path.resolve(__dirname, "index.prod.js"),
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
});

10148
yarn.lock Normal file

File diff suppressed because it is too large Load Diff