mirror of
https://github.com/rowtype-yoga/ry-blocks.git
synced 2024-09-11 21:38:06 +03:00
Initial commit
This commit is contained in:
commit
ceece1f1f0
49
.github/workflows/ci.yml
vendored
Normal file
49
.github/workflows/ci.yml
vendored
Normal 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
23
.gitignore
vendored
Normal 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
1
.purs-repl
Normal file
@ -0,0 +1 @@
|
||||
import Prelude
|
1
.storybook/logo.svg
Normal file
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
8
.storybook/main.js
Normal 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
7
.storybook/manager.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { addons } from '@storybook/addons';
|
||||
import rowtypeYogaTheme from './rowtype-yoga-theme';
|
||||
|
||||
addons.setConfig({
|
||||
theme: rowtypeYogaTheme,
|
||||
isToolshown: false,
|
||||
});
|
31
.storybook/rowtype-yoga-theme.js
Normal file
31
.storybook/rowtype-yoga-theme.js
Normal 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
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"editor.formatOnSave": true
|
||||
}
|
9
LICENCE
Normal file
9
LICENCE
Normal 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
6
convert-svgs.js
Normal 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
7
index.dev.js
Normal 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
2
index.prod.js
Normal file
@ -0,0 +1,2 @@
|
||||
var Main = require("./dce-output/Main");
|
||||
Main.main({ serverSideRendering: true})()
|
57
package.json
Normal file
57
package.json
Normal 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
201
packages.dhall
Normal 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
22
spago.dhall
Normal 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
5
src/Components/Box.purs
Normal file
@ -0,0 +1,5 @@
|
||||
module Components.Box
|
||||
( module Components.Box.View
|
||||
) where
|
||||
|
||||
import Components.Box.View (component, Props)
|
19
src/Components/Box/Spec.purs
Normal file
19
src/Components/Box/Spec.purs
Normal 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"
|
14
src/Components/Box/Story.purs
Normal file
14
src/Components/Box/Story.purs
Normal 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" ] })
|
10
src/Components/Box/Style.purs
Normal file
10
src/Components/Box/Style.purs
Normal file
@ -0,0 +1,10 @@
|
||||
module Components.Box.Style where
|
||||
|
||||
import Prelude.Style
|
||||
|
||||
box ∷ Style
|
||||
box =
|
||||
css
|
||||
{ padding: 1.0 # em
|
||||
, backgroundColor: "white" # str
|
||||
}
|
26
src/Components/Box/View.purs
Normal file
26
src/Components/Box/View.purs
Normal 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
|
||||
}
|
5
src/Components/Centre.purs
Normal file
5
src/Components/Centre.purs
Normal file
@ -0,0 +1,5 @@
|
||||
module Components.Centre
|
||||
( module Components.Centre.View
|
||||
) where
|
||||
|
||||
import Components.Centre.View (component, Props)
|
21
src/Components/Centre/Spec.purs
Normal file
21
src/Components/Centre/Spec.purs
Normal 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"
|
56
src/Components/Centre/Story.purs
Normal file
56
src/Components/Centre/Story.purs
Normal 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"
|
||||
}
|
||||
]
|
27
src/Components/Centre/Style.purs
Normal file
27
src/Components/Centre/Style.purs
Normal 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
|
||||
}
|
27
src/Components/Centre/View.purs
Normal file
27
src/Components/Centre/View.purs
Normal 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
|
||||
}
|
5
src/Components/Cluster.purs
Normal file
5
src/Components/Cluster.purs
Normal file
@ -0,0 +1,5 @@
|
||||
module Components.Cluster
|
||||
( module Components.Cluster.View
|
||||
) where
|
||||
|
||||
import Components.Cluster.View (component, Props)
|
21
src/Components/Cluster/Spec.purs
Normal file
21
src/Components/Cluster/Spec.purs
Normal 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"
|
55
src/Components/Cluster/Story.purs
Normal file
55
src/Components/Cluster/Story.purs
Normal 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"
|
||||
}
|
||||
]
|
||||
]
|
28
src/Components/Cluster/Style.purs
Normal file
28
src/Components/Cluster/Style.purs
Normal 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
|
||||
}
|
||||
}
|
29
src/Components/Cluster/View.purs
Normal file
29
src/Components/Cluster/View.purs
Normal 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
|
||||
}
|
5
src/Components/Container.purs
Normal file
5
src/Components/Container.purs
Normal file
@ -0,0 +1,5 @@
|
||||
module Components.Container
|
||||
( module Components.Container.View
|
||||
) where
|
||||
|
||||
import Components.Container.View (component)
|
18
src/Components/Container/Spec.purs
Normal file
18
src/Components/Container/Spec.purs
Normal 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"
|
14
src/Components/Container/Story.purs
Normal file
14
src/Components/Container/Story.purs
Normal 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" ] })
|
44
src/Components/Container/Style.purs
Normal file
44
src/Components/Container/Style.purs
Normal 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"
|
||||
}
|
||||
}
|
19
src/Components/Container/View.purs
Normal file
19
src/Components/Container/View.purs
Normal 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)
|
5
src/Components/Sidebar.purs
Normal file
5
src/Components/Sidebar.purs
Normal file
@ -0,0 +1,5 @@
|
||||
module Components.Sidebar
|
||||
( module Components.Sidebar.View
|
||||
) where
|
||||
|
||||
import Components.Sidebar.View (component, Props)
|
21
src/Components/Sidebar/Spec.purs
Normal file
21
src/Components/Sidebar/Spec.purs
Normal 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"
|
71
src/Components/Sidebar/Story.purs
Normal file
71
src/Components/Sidebar/Story.purs
Normal 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
|
||||
}
|
||||
]
|
||||
]
|
57
src/Components/Sidebar/Style.purs
Normal file
57
src/Components/Sidebar/Style.purs
Normal 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 }
|
29
src/Components/Sidebar/View.purs
Normal file
29
src/Components/Sidebar/View.purs
Normal 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
|
||||
}
|
5
src/Components/Stack.purs
Normal file
5
src/Components/Stack.purs
Normal file
@ -0,0 +1,5 @@
|
||||
module Components.Stack
|
||||
( module Components.Stack.View
|
||||
) where
|
||||
|
||||
import Components.Stack.View (component, Props)
|
21
src/Components/Stack/Spec.purs
Normal file
21
src/Components/Stack/Spec.purs
Normal 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"
|
68
src/Components/Stack/Story.purs
Normal file
68
src/Components/Stack/Story.purs
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
40
src/Components/Stack/Style.purs
Normal file
40
src/Components/Stack/Style.purs
Normal 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 }
|
26
src/Components/Stack/View.purs
Normal file
26
src/Components/Stack/View.purs
Normal 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
5
src/Framer/Motion.js
Normal 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
73
src/Framer/Motion.purs
Normal 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
17
src/Prelude/Default.purs
Normal 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
15
src/Prelude/Spec.purs
Normal 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
11
src/Prelude/Style.purs
Normal 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
19
src/Prelude/View.purs
Normal 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
35
src/Yoga.purs
Normal 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)
|
8
src/Yoga/Blocks/Internal.js
Normal file
8
src/Yoga/Blocks/Internal.js
Normal 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);
|
||||
};
|
||||
|
187
src/Yoga/Blocks/Internal.purs
Normal file
187
src/Yoga/Blocks/Internal.purs
Normal 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
|
||||
)
|
51
src/Yoga/Blocks/Internal/CSS.purs
Normal file
51
src/Yoga/Blocks/Internal/CSS.purs
Normal 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 ~:
|
63
src/Yoga/Blocks/Internal/OptionalProp.purs
Normal file
63
src/Yoga/Blocks/Internal/OptionalProp.purs
Normal 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
14
src/index.html
Normal 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
14
test/Main.purs
Normal 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
11
webpack.common.js
Normal 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
38
webpack.dev.js
Normal 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
21
webpack.prod.js
Normal 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",
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user