├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── public ├── assets │ └── error.svg ├── converter │ └── index.html ├── font-awesome │ ├── css │ │ ├── font-awesome.css │ │ ├── font-awesome.css.map │ │ └── font-awesome.min.css │ └── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── images │ ├── autocomplete.gif │ ├── book │ │ ├── augmentTypes.png │ │ ├── borderbox.png │ │ ├── cover.png │ │ ├── flex │ │ │ ├── content-overflow.png │ │ │ ├── content-overflow.xml │ │ │ ├── content-unused.png │ │ │ ├── content-unused.xml │ │ │ ├── flex-large.png │ │ │ ├── flex-large.xml │ │ │ ├── flex-small.png │ │ │ ├── flex-small.xml │ │ │ ├── horizontal-large.png │ │ │ ├── horizontal-large.xml │ │ │ ├── horizontal-small.png │ │ │ ├── horizontal-small.xml │ │ │ ├── horizontal-solution.png │ │ │ ├── horizontal-solution.xml │ │ │ ├── horizontal.png │ │ │ ├── horizontal.xml │ │ │ ├── multiple-flex-large.png │ │ │ ├── multiple-flex-large.xml │ │ │ ├── multiple-flex-scaling.png │ │ │ ├── multiple-flex-scaling.xml │ │ │ ├── multiple-flex-small.png │ │ │ ├── multiple-flex-small.xml │ │ │ ├── vertical-large.png │ │ │ ├── vertical-large.xml │ │ │ ├── vertical-small.png │ │ │ ├── vertical-small.xml │ │ │ ├── vertical-solution.png │ │ │ ├── vertical-solution.xml │ │ │ ├── vertical.png │ │ │ └── vertical.xml │ │ ├── inline.png │ │ ├── inlineBlock.png │ │ ├── marginchild.png │ │ ├── marginchild.xml │ │ ├── marginpadding.gif │ │ ├── marginsibling.png │ │ ├── marginsibling.xml │ │ └── play │ │ │ ├── autocomplete.png │ │ │ ├── error.png │ │ │ └── full.png │ └── course.png ├── index.html └── play │ └── index.html ├── scripts └── publish.ts ├── src ├── app.tsx ├── app_spec.ts ├── augmentTypes.ts ├── components │ ├── bookSection.tsx │ ├── gls.tsx │ ├── index.ts │ ├── markdown.tsx │ ├── styles.tsx │ └── txt.tsx ├── converter.tsx ├── docs │ ├── advanced.md │ ├── box.md │ ├── colors.md │ ├── core.md │ ├── flex.md │ ├── intro.md │ ├── page.md │ ├── play.md │ ├── raw.md │ ├── reviews.md │ ├── server.md │ ├── utilities.md │ └── why.md ├── play.tsx ├── play │ ├── codeEditor │ │ ├── addons │ │ │ ├── autocomplete │ │ │ │ ├── autocomplete.css │ │ │ │ ├── autocomplete.ts │ │ │ │ ├── autocompleteShared.tsx │ │ │ │ ├── codemirror-showhint.d.ts │ │ │ │ ├── snippets.ts │ │ │ │ ├── templates.css │ │ │ │ └── templates.ts │ │ │ ├── fontAwesomeToCharCode.ts │ │ │ ├── lint.css │ │ │ ├── lint.ts │ │ │ ├── linter.ts │ │ │ ├── textHover.css │ │ │ └── textHover.ts │ │ └── codeEditor.tsx │ ├── codeOutput │ │ └── codeOutput.tsx │ ├── events.ts │ ├── globals.d.ts │ ├── languageServiceHost.ts │ ├── liner.ts │ ├── playState.ts │ ├── projectService.ts │ ├── srcLoader.ts │ └── typings │ │ └── fuzzaldrin.d.ts ├── routing │ └── router.tsx └── utils.ts ├── tsconfig.json └── webpack.config.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - run: npm install 11 | - run: | 12 | git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git 13 | npm run publish 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .alm 2 | .vscode 3 | node_modules 4 | public/build -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | `npm install` 4 | 5 | Then `npm start` and visit http://localhost:8080 6 | 7 | # How doc search is configured 8 | 9 | For our main docs app : 10 | 11 | - Our docs are rendered in an element with id `doc-search`. 12 | - Applied for search at algolia and gave them the id 13 | - Added the algolia styles to `index.html` 14 | - Added an input and styled it in `app.tsx` 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 TypeStyle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typestyle.github.io 2 | Website for TypeStyle. Check it out [here](https://typestyle.github.io/). 3 | 4 | [![Build Status](https://travis-ci.org/typestyle/typestyle.github.io.svg?branch=source)](https://travis-ci.org/typestyle/typestyle.github.io) 5 | 6 | # Contributing 7 | * Add commits to `source` and they get built and published to `master` by travis 8 | * See the website locally by running `npm start` and `localhost:8080` 9 | 10 | ### More: 11 | Update the latest typestyle using `npm run u`. 12 | 13 | # About the images 14 | Drawn using [draw.io](https://draw.io). Just open the xml files from the images directory. 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typestyle.github.io", 3 | "version": "0.0.0", 4 | "description": "Website for TypeStyle", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npm prune && npm install && (npm run unittest:live & npm run build:live)", 8 | "test": "npm run unittest && npm run build", 9 | "unittest": "mocha --require ts-node/register \"src/**/*_spec@(.ts|.tsx)\"", 10 | "unittest:live": "npm run unittest -- --watch --watch-extensions tsx,ts", 11 | "build": "webpack -p --config ./webpack.config.js", 12 | "build:live": "webpack-dev-server --host 0.0.0.0 --hot --inline --no-info --content-base ./public --config ./webpack.config.js", 13 | "u": "npm uninstall typestyle && npm uninstall csstips && npm uninstall csx && npm install typestyle@latest csstips@latest csx@latest --save-exact --save", 14 | "publish": "npm test && ts-node ./scripts/publish.ts" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/typestyle/typestyle.github.io.git" 19 | }, 20 | "author": "", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/typestyle/typestyle.github.io/issues" 24 | }, 25 | "homepage": "https://github.com/typestyle/typestyle.github.io#readme", 26 | "dependencies": { 27 | "@types/codemirror": "0.0.34", 28 | "@types/escape-html": "0.0.19", 29 | "@types/history": "^2.0.39", 30 | "@types/marked": "0.0.28", 31 | "@types/node": "^9.4.0", 32 | "@types/react": "0.14.43", 33 | "@types/react-dom": "0.14.18", 34 | "@types/react-router": "2.0.38", 35 | "babel-polyfill": "^6.9.1", 36 | "byots": "^2.1.0-dev.20161101.22.10", 37 | "codemirror": "^5.20.2", 38 | "css-to-typestyle": "^0.0.9", 39 | "csstips": "^0.2.2", 40 | "csx": "^8.5.0", 41 | "escape-html": "^1.0.3", 42 | "fuzzaldrin": "^2.1.0", 43 | "gh-pages": "^0.11.0", 44 | "marked": "^0.3.12", 45 | "mobx": "^2.5.1", 46 | "mobx-react": "^3.5.6", 47 | "mocha": "^3.1.2", 48 | "raw-loader": "^0.5.1", 49 | "react": "15.3.2", 50 | "react-dom": "15.3.2", 51 | "react-router": "3.0.0", 52 | "source-map-support": "0.4.3", 53 | "ts-loader": "^3.4.0", 54 | "ts-node": "^3.3.0", 55 | "typescript": "2.7.1", 56 | "typestyle": "1.7.0", 57 | "webpack": "^3.10.0", 58 | "webpack-dev-server": "^2.11.1" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /public/assets/error.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/converter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TypeStyle 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /public/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /public/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /public/images/autocomplete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/autocomplete.gif -------------------------------------------------------------------------------- /public/images/book/augmentTypes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/augmentTypes.png -------------------------------------------------------------------------------- /public/images/book/borderbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/borderbox.png -------------------------------------------------------------------------------- /public/images/book/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/cover.png -------------------------------------------------------------------------------- /public/images/book/flex/content-overflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/content-overflow.png -------------------------------------------------------------------------------- /public/images/book/flex/content-overflow.xml: -------------------------------------------------------------------------------- 1 | 5ZXNUoMwEICfhjsQi/Uq1nrx1IPnlCwk08AyIS3Up3eBUMDW0R6cqSMXdr/9S3bYxWNx3qwNL+UrCtBe6IvGY09eGAYsuqNXS449uY+iHmRGCec0go16Bwd9R/dKQDVztIjaqnIOEywKSOyMcWOwnrulqOdVS57BGdgkXJ/TNyWs7OnC90f+AiqTrnI4GLY82WUG94Ur54Us7Z7enPMhlfOvJBdYTxBbeSw2iLaX8iYG3bZ26Fof9/yF9XRsA4X9SUDYBxy43sNw4khT6KNQBxKzVhzQ1nwmlPiC318OpQ/Ktr277VP+x1A1ADyASTVNzZhBfZe1mzV7HMa7lsrCpuRJq9e0wshJ2lyTFrTVeSVBOCWlL8ItqGDR6krrGDWaLhUTHJZpQryyBncwsUTJErbpqfx0KN2c0kUsNBPkhnQNmIM1R3Kpx+0TRG5pyOnmWTjI3cbLTrHj2JPgJv/yFmAXtsB1HZs2pcACfvnWwcP1tyZ13KudbfLvYqsP -------------------------------------------------------------------------------- /public/images/book/flex/content-unused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/content-unused.png -------------------------------------------------------------------------------- /public/images/book/flex/content-unused.xml: -------------------------------------------------------------------------------- 1 | 7ZXNUoMwFIWfhqUzkFisS6W1blx14TolF5JpIBhCoT69gYQCUmesM3bVbki++5OT25kTD0dZs1GkYG+SgvCQTxsPrzyEAhzem09LjpY8hKEFqeLUJQ1gyz/BQd/RilMoJ4laSqF5MYWxzHOI9YQRpWQ9TUukmJ5akBRmYBsTMafvnGpm6cL3B/4KPGXuZNQHdiTep0pWuTvOQzjpfjackb6Vyy8ZobIeIbz2cKSk1HaVNRGIdrT91Gzdyw/Rk2wFuf5NAbYFByIq6BWHwpQ+U34wy7Rd9iiRpqnRrI9uTOFHJfvAXdn9iU8mIVgUzRD83mWnvhMjz7ae4TMibrpuuq6s62zHa16J96DKqxJo27AgMYzU8D/eGU3EoZpxDduuN17VxtpNEtOZMLvglD92F2c4B1AamhFybrMBmYFWR5NSDzYahM792MhCg0cHibPu9FQ7+JdZOAs7b2doZmfmgdCt2gtvSknJgLpNO0j3RAWLds+FiKSQqmuFKYFlEhteaiX3MIqE8RJ2yX8PLrh8cGY7vDFdbPSO4/UX -------------------------------------------------------------------------------- /public/images/book/flex/flex-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/flex-large.png -------------------------------------------------------------------------------- /public/images/book/flex/flex-large.xml: -------------------------------------------------------------------------------- 1 | zZOxcoMwDIafhh1wQzOXpunSKUNnxxbYV4M44wTSp6/BIsDR3rVbWbA+SZb0245YXvVHyxv1hhJMlMayj9hzlKYJyx78byC3QB6zLIDSaklBMzjpTyAYE71oCe0q0CEap5s1FFjXINyKcWuxW4cVaNZVG17CBpwEN1v6rqVTge7ieOavoEtFldPJcebio7R4qalclLJi/IK74tNWFN8qLrFbIHaIWG4RXVhVfQ5mkHZSLeS9/OC9t22hdr9JYCHhys0Fpo7HvtxtkqJT2sGp4WKwO3/cEXtSrjLeSvxyW5GauIJ10C8QdXAErMDZmw/pZmmTjBRRS1n3BDkdZ3nPnWfyCxrr+xHTzYiF8X39cUzJWwWSjAJrR3c22Q22NiZHg3bcikkO+0J43jqLH7DwZGIP5+IfqubN+dKNvsXDZocv -------------------------------------------------------------------------------- /public/images/book/flex/flex-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/flex-small.png -------------------------------------------------------------------------------- /public/images/book/flex/flex-small.xml: -------------------------------------------------------------------------------- 1 | zZPBcoMgEIafhrtKY9NrbZpeesqhZwKrMEHXQYymT18UjDq2M+2tXmS/3WV3f4DQrOyPhtXyHQVokkSiJ/SFJElM0wf3G8jNk8c09aAwSoSgGZzUJwQYBdoqAc0q0CJqq+o15FhVwO2KMWOwW4flqNdVa1bABpw401v6oYSVnu6iaOZvoAoZKieT48z4pTDYVqEcSWg+ft5dsmmrEN9IJrBbIHogNDOI1q/KPgM9SDup5vNef/De2zZQ2d8kUJ9wZbqFqeOxL3ubpOiksnCqGR/szh03oc/SltpZsVtuK4YmrmAs9AsUOjgClmDNzYV0s7RxGhSRC1njpwBZOM7injvP5BZhrO9HTDYj5tr19ccxBWskiGDkWNlwZ+PdYCutM9Roxq2oYLDPueONNXiBhSflezjn/1A1Z86XbvQtHjY9fAE= -------------------------------------------------------------------------------- /public/images/book/flex/horizontal-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/horizontal-large.png -------------------------------------------------------------------------------- /public/images/book/flex/horizontal-large.xml: -------------------------------------------------------------------------------- 1 | 1ZXBboMwDIafhjslpWPXsq677NTDzoEYEjVgFNICe/qlJRRYWmmTWmnjgOLfNrG/WMEjcdFuFa34OzKQXuCz1iMvXhA8h755n4SuF8LwqRdyJVgvLUZhJz7BijYvPwgG9SxQI0otqrmYYllCqmcaVQqbeViGcr5rRXNwhF1Kpat+CKa57cL3R/0NRM7tzsHgSGi6zxUeSrudF5Ds/PTugg6fsvE1pwybiUQ2HokVou5XRRuDPJEdqPV5rze8l7IVlPonCaRPOFJ5gKHic126G1A0XGjYVTQ92Y05bY+suS6ksRZmmQkpY5SojF1iaYLWbhG2riMoDe1EskVtAQvQqjMhzUh7GVlI/Bppak84v+SObZqF7fR614HTdW3GLaHqt80zWnNgAwkstZ3kRfiNjJkDRiHKUqPXWuEeJp5VGkGS3Q/cw7itHG4Jsu4fQ2vnwCYMSfAoiNGfHj4H2hW0NzkufRfkvYbRmOO1ePZN/jxk8wU= -------------------------------------------------------------------------------- /public/images/book/flex/horizontal-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/horizontal-small.png -------------------------------------------------------------------------------- /public/images/book/flex/horizontal-small.xml: -------------------------------------------------------------------------------- 1 | 1ZXBboMwDIafhjsQ0bFrWdddduph50AMiRowCmmBPf3SJhQYrbRJrbRxQPFvm9hfrOCRpOy2itb8HRlIL/RZ55EXLwyfI9+8T0JvhZUfWKFQgllpIuzEJzjR5RUHwaCZBWpEqUU9FzOsKsj0TKNKYTsPy1HOd61pAQthl1G5VD8E09yqke+P+huIgrudw8GR0mxfKDxUbjsvJPn5se6SDp9y8Q2nDNuJRDYeSRSitquyS0CeyA7UbN7rDe+lbAWV/kkCsQlHKg8wVHyuS/cDipYLDbuaZie7NaftkTXXpTRWYJa5kDJBicrYFVYmaL0swtV1BKWhm0iuqC1gCVr1JqQdaRPiIPFrpKk74eKSO7ZpFq7T612Hi64bM24pVb9tntGGAxtIYKXdJAfRNzJmDhiFOM+M3miFe5h4VlkMaX4/cPGjuK0W3FJk/T+G1s2BTRgGT4+CGP/p4VtAu4L2JscwWoK81zAac7wWz77Jn4dsvgA= -------------------------------------------------------------------------------- /public/images/book/flex/horizontal-solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/horizontal-solution.png -------------------------------------------------------------------------------- /public/images/book/flex/horizontal-solution.xml: -------------------------------------------------------------------------------- 1 | 7VZNj5swEP01SO1lBXiDssdNut1eKlXKoWfHHsBag5FxAtlf33EwX4HNksNupaq5xH4ez5j3hoc9ss3qZ02L9KfiIL3Q57VHvnlhGPpRhH8WOTVIEPirBkm04A7rgZ14BQf6Dj0IDuUo0CgljSjGIFN5DsyMMKq1qsZhsZLjqgVNYALsGJVT9LfgJm3Qle/3+A8QSWraJ3YLe8peEq0OuSvnhSQ+/5rljLapXHyZUq6qAUSePLLVSplmlNVbkJbblrVm3/c3Vrtja8jNkg1hs+FI5cE9eYnE76n2wkhihg0XRxwmdviFlaUV4A5JN5j/axuD6Qdh7sHMqeWySoWBXUGZnVfYMBiUmkziLLAVaJkCd5MYU7tuCFZ2LqTcKqn0ORXhFNYxQ7w0Wr3AYCVia9jHXfkhA46UI2gD9QByjDyDysDoE4a41XsnTtu8rVhV3wkdls51AXXdl3Spewlw4FSYVySaKLJX+BpdkSOWUP+zWgTRAjHWHyXGw0SMCaOQ80drODhjkpalYGNKJ+z46I6EdCutvVwyvnbz2Z0pLWyQVd4Vf5tr4COrmzJdDQ1uSmSLaZDUiOPYIOfIdRV+KYEn6YUMybyQbYpSHTQDt2toWReJkIdRotVFHkN1AmaSB3mip0FYYQPK5ed98G861TgcB039vus6ARY1Yvtd/G/UQ3Mgf9Wpyfvm8A6jQ9JylcMNdhF+2qfu/sMIDIIJg+ebz2xH13ep0uIV247KpQ2NJJhrbuw4n5GBSpHk1tKRWkB8YykVeD98dAuZ4NyW2cxJvNTHz3HuuDNXt9s/lquZ/o+uePoN6uG0v5Y2Dtbf/cnTHw== -------------------------------------------------------------------------------- /public/images/book/flex/horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/horizontal.png -------------------------------------------------------------------------------- /public/images/book/flex/horizontal.xml: -------------------------------------------------------------------------------- 1 | 7ZZNc6MwDIZ/DcfdATuh6XE3249LTzns2cECe2oQNU4g/fUVYAdotjPtTHvZaS5YjyTbvJGURHxbdndW1OoBJZiIxbKL+J+IsWTFUnr05DSSq+vVCAqrpQ+awE4/g4expwctoVkEOkTjdL2EGVYVZG7BhLXYLsNyNMtTa1HABdhlwlzSv1o6NdJ1HE/8HnSh/MksOPYieywsHip/XMR4PnxGdynCVj6+UUJiO0P8JuJbi+jGVdltwfTSBtXGvNs3vOdrW6jcexL4mHAU5gDhxqmh1N9SH2lZ9MuAcqRN6c7u5GVKnw4YHD+a4Uv8RQHJuu4m5+td9AyIsqZFtW/qwY6/0TdaoKxp+p7/qdDqZ6oyYUIZUU3PKymwsUYv8KKa2aKIWau0g10tst5uaZ5RkHKlISs5x89bynfZEayDboZ8i90BluDsiULaaXbw1Le8ms2NMxR+XhXn3KlpaeH79t89zC56OKhG09H1t/7gG0vRKJDe6AX18zlZ97Y2ZosG7bAVlwI2eUa8cRYfYeZJsw3s888TMGFfJeDmPxaweyXeVwhK5vSLNfhm/wr4zQs= -------------------------------------------------------------------------------- /public/images/book/flex/multiple-flex-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/multiple-flex-large.png -------------------------------------------------------------------------------- /public/images/book/flex/multiple-flex-large.xml: -------------------------------------------------------------------------------- 1 | 3ZXBcoMgEIafhmtHpdqca9P00lMOPRNYhQmKgyRqn76oGHVspuk0OTReZL/dhd3fdUA4zuqNJgV/VwwkCjxWI/yCgiDwosi+WtL0xPe9sCepFsyxEWzFJzjoOXoQDMpZoFFKGlHMIVV5DtTMGNFaVfOwRMn5qQVJYQG2lMgl/RDM8J6GnjfyNxApN0PHzrEjdJ9qdcjdcSjASff07owMW7n4khOmqgnCa4RjrZTpV1kdg2y1HVTr817PeE9la8jNJQm4TzgSeYCh4q4u0wxSVFwY2BaEtnZlvzfCz9xk0lq+XS5PdEUcQRuoJ8hVsAGVgdGNDalGaXHkFOETWcNBJuI+Z3rKHXuyC9fW9y0GixZpWbaD9GCHx7RV/7JjRkoOzBmJ3cONrx+2tpAyVlLpbivMCKwSanlptNrDxBPRFeySGwsYXUG/6Kx+ibTl/V/xmrlGP2l5+sf/IubTHQ+j0/PxUkGvMZyrOx/Om06nNcebpvNNrnO8/gI= -------------------------------------------------------------------------------- /public/images/book/flex/multiple-flex-scaling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/multiple-flex-scaling.png -------------------------------------------------------------------------------- /public/images/book/flex/multiple-flex-scaling.xml: -------------------------------------------------------------------------------- 1 | 3ZXBUoMwEIafhqsDRLBexVovnnrwnCYLyTQQJoQCPr0BQoHBauvUGZUL2X93k92PHeKgKK03CufsRVIQju/S2kGPju/7bhiaV6s0veJ5btArieLUaqOw5W9gRdeqJadQzAK1lELzfC4SmWVA9EzDSslqHhZLMT81xwkshC3BYqm+cqpZrwauO+rPwBOmh46tY4fJPlGyzOxxjo/i7undKR62svEFw1RWEwmtHRQpKXW/SusIRMt2oNbnPZ3wHstWkOlzElCfcMCihKHiri7dDCgqxjVsc0xauzLf20EPTKfCWJ5ZLk+0RRxAaagnkq1gAzIFrRoTUo1oUWiJsAnWYBgHbD9ncswdezIL29bHLfqLFklRtIN0Y4ZHt1Vf2DHFBQNqjdjsYcfXC1qbCxFJIVW3FaIYVjExeqGV3MPEE5IV7OIfBhhegV94kl8soPb+ML1mDukrmB66As27fzyNludtcCbQa0zn6tPpvPhv9vtoevdn0vwOTmOOd03nm1zoaP0O -------------------------------------------------------------------------------- /public/images/book/flex/multiple-flex-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/multiple-flex-small.png -------------------------------------------------------------------------------- /public/images/book/flex/multiple-flex-small.xml: -------------------------------------------------------------------------------- 1 | 3ZXBboMwDIafhusERGWcx7ruslMPO6eJIVEDQSEU2NMvkFBArFontYeVC/FnO7H/WIqHkrzdKVyyD0lBeKFPWw+9emEY+lFkfj3pLAkCf2NJpjh1bAJ7/gUO+o7WnEK1CNRSCs3LJSSyKIDoBcNKyWYZlkqxPLXEGazAnmCxpp+cambpxvcn/g48Y3rs2DkOmBwzJevCHeeFKB0+687xuJWLrximspkhtPVQoqTUdpW3CYhe21E1m/d2wXsuW0Ghr0lANuGERQ1jxUNduhulaBjXsC8x6e3G3LeHXpjOhbECs1yf6Io4gdLQzpCrYAcyB606E9JM0qLIKcJmsp4hdteZnXOnnszCtfVzi+GqRVJV/SA9meHRfdV/7JjiigF1Rmr2cOMbbHqbC5FIIdWwFaIY4pQYXmkljzDzRCSGQ3pnAW+hX3RRv1SY8v6veN1So9+0DMIbiPn8wMPo9ET+lYLeYjjjBx/OIL7jdBpzemkG3+w5R9tv -------------------------------------------------------------------------------- /public/images/book/flex/vertical-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/vertical-large.png -------------------------------------------------------------------------------- /public/images/book/flex/vertical-large.xml: -------------------------------------------------------------------------------- 1 | 1ZU9c4MwDIZ/DTvgko+1NE2XThk6O7bAvhrEGSdAf30NmABHe0176RAGznolWdKDAY/EWb3XtBCvyEF5oc9rjzx5YbiNfHtvhaYXomjdC6mWvJeCUTjID3Ciy0tPkkM5CzSIyshiLjLMc2BmplGtsZqHJajmVQuawkI4MKqW6pvkRrgpfH/UX0CmwlUOB8eRsvdU4yl35byQJN3VuzM6bOXiS0E5VhOJ7DwSa0TTr7I6BtWSHaj1ec/feC9ta8jNNQmkTzhTdYKh464v0wwoKiENHArKWruyT9sjj8JkylqBXS4ruibOoA3UE8l1sAfMwOjGhlQjWrJyRMQE68PaidQ9zvSSO85kF26sr0cMFyMKoBz0bwfltBTAnZFgbtypDaLWlkrFqFB3WxFOYZMwq5dG4ztMPCu2gWPyz9w2N8C2WmA7Im/uGFozZ/MTQxLcAOJ6ATGxr/Zdnz2HkWyv5PiHs2jN8QvY+SY/GbL7BA== -------------------------------------------------------------------------------- /public/images/book/flex/vertical-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/vertical-small.png -------------------------------------------------------------------------------- /public/images/book/flex/vertical-small.xml: -------------------------------------------------------------------------------- 1 | 1ZVNc4MgEIZ/DXcjY5Jea9P00lMOPRNZhQm6DpKo/fVFxa+xnaZteogHh32XZXcfQAkN02qvWS5ekYMivscrQp+I7z8Enn03Qt0JQbDphERL3kmrUTjId3Cii0vOkkMxm2gQlZH5XIwwyyAyM41pjeV8WoxqnjVnCSyEQ8TUUn2T3AjXheeN+gvIRLjMfu84suiUaDxnLh3xadw+nTtl/VJufiEYx3Ii0R2hoUY03SitQlAN2Z5aF/f8hXcoW0NmrgmgXcCFqTP0Fbd1mbpHUQpp4JCzqLFLu9uEPgqTKmut7HCZ0RVxAW2gmkiugj1gCkbXdko5oqVrR0RMsA4ic9uZDLFjT3bg2vq8RX/RogDGQf+0Uc4KAdwZMWbGndpV0NhSqRAV6nYpyhls48jqhdF4golnHW3hGP8zt+0NsK0X2I7I6zuGVs/ZfMdwuNJ/gbhZQIzt1b7rs+cwXs3xF2fRmuMXsPVNfjJ09wE= -------------------------------------------------------------------------------- /public/images/book/flex/vertical-solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/vertical-solution.png -------------------------------------------------------------------------------- /public/images/book/flex/vertical-solution.xml: -------------------------------------------------------------------------------- 1 | 3VZNj9sgEP01ltpDK9tsUve4yWa3l0qV9tAzMWMbBRsLkzjpr+8Qg7+TzapRD5tLPI8BhjfDGzyyzo8vipbZT8lAeKHPjh558sIw9JdL/DPIqUEC4i8aJFWcWawDXvkfsKBv0T1nUA0ctZRC83IIxrIoINYDjCol66FbIsVw15KmMAFeYyqm6G/OdNagC9/v8B/A00y7E9uBLY13qZL7wm7nhSQ5/5rhnLqlrH+VUSbrHkQ2HlkrKXXzlR/XIAy3jrVm3vOF0TZsBYW+ZQJpJhyo2IOL+ByXPjkq6oxreC1pbOwa8+2RVaZzgVaAn5VWcgdrKaQ6+xMfs09IO+Low6OtEi6E8yxkgQuupgHbMxxAaTj2IHuAF5A5aHVCl7bcmhl1lyiytFjWS1ILUlscabtUxxB+WJLmCfs2ISzBZIFC7FNcVaZAv2JRajzO5/dSyWiVAbNGgovYexEsRtwhy4xClMRz/C/jCLbJfam11ziMbqQ6ugPT4YTpDCj76Ez/D2aXE2a3kp36vCYCjh+I1NOQu7c4btX8X0h2neyatELBHk2zQmsrZLybU1annwZB/2dudnzyjcWwZdnFpNKZTGVBxaZDz0m4rMyzIxktzXqmAGxolxNhAriahrrfOqc8O0yBoJofhq13jnu7wy/JMZI2y8E4XZXcqxisV7/5jSd+H9bHl3GD0FSloCcLITH01HMrjUN1Q4Bun3FTfiuuoT9+NBF0ddhyfltpuovUleb5xdETAHPHOD6HpiKAV09fewHYvj7T6qngaYFmjHWEMk5WbpNHO5Bzxs51Oyc0fUGJnG3DCq7U+h1kJIhG+Qj8h4mSPMwKybt1BM3u9dfktntik81f -------------------------------------------------------------------------------- /public/images/book/flex/vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/flex/vertical.png -------------------------------------------------------------------------------- /public/images/book/flex/vertical.xml: -------------------------------------------------------------------------------- 1 | 7VdNU4MwFPw1HHWACPaqVevFUw+eU/KATAPBkBbqrzchoRBbndqpHhx6ad6+r83msnhoXrQLgav8hRNgXuiT1kMPXhiGfhyrP43sDBIE0Y1BMkGJxQZgSd/Bgr5FN5RA7RRKzpmklQsmvCwhkQ6GheCNW5Zy5m6tcAYHwDLB7BB9pUTmBo18f8CfgWa57G9sEyucrDPBN6Vd54Uo7X4mXeB+lK2vc0x4M4LQo4fmgnNpTkU7B6a17VUzfU9fZPe0BZTylAZkGraYbaBnHDPVek/oVh0zfeyhlKuhirPcWZnitw3vE1d194h3qiCIqnZIfp6yEp8RRc+MPoCPkJh4TbwmXhOvidfEa+J1Eq+jE4/U1RUuz+ef1HVnz4YVZp67g56/4HoLQlLl0UYr6GXv8K1i7nVCZ0vY5FTCssKJjhvlilVRLgumomBfP/Zl1qrpO0E7gqxPWwAvQIqdKmkGA4pi6xvzkfncg9ia3mzfOzg/dbDm77gRDA+MoH3Pa2WxpWb9wxsTXOdAbKDFtyY/iHRMGZtzxkU3ChEMszTRrycFX8MoEyczWKW/LODsAvrF/1i/nSvTX8h5+//lDOLf01OFwzdklxt9qKPHDw== -------------------------------------------------------------------------------- /public/images/book/inline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/inline.png -------------------------------------------------------------------------------- /public/images/book/inlineBlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/inlineBlock.png -------------------------------------------------------------------------------- /public/images/book/marginchild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/marginchild.png -------------------------------------------------------------------------------- /public/images/book/marginchild.xml: -------------------------------------------------------------------------------- 1 | 7VjPc6MgFP5rPHZGQGn3uJvtj0tPOfRMhKhTFRdJNP3rFwNEWdlJ0rXT7Yy5BL734D0+eR9ogFZl9yhInT1zyooAhrQL0M8AQoBwpP565KCRW4w1kIqcGqcBWOdvzIChQXc5ZY3jKDkvZF67YMKriiXSwYgQvHXdtrxwo9YkZRNgnZBiir7kVGYaxXE44E8sTzMTOQ6NYUOS11TwXWXCBRBtjz9tLomdyvg3GaG8HUHoPkArwbnUrbJbsaKn1rKmxz38xXpKW7BKXjIg1gP2pNgxmzEu1NAfNN+rZto3LdTUpPoT23IVSK1DHgx1+NeOW8NNc3yw35UDiOtuMNpZSiLSvLqRvNZeKDx6FdrnQU/uBlSwLw8FezK+chF2G9o0o2nGdshGeDJY8l3yXfK9IJDjBx39gG2WS7auSdL3W3W8KKdMloXqgZP/WOGM6O2ZkKwbQUbxHhkvmRQH5dIOUg6wUeBsJOPg1oDEHB/paeygoaphZNQvqegaSZ1XPuFF8vn+feZ9lrPv5iXK50c5X7VfouDN7dPW+tn6RzPUP5zU/0rVYZ/8lQunpMkYNZ2+ls01GcT/yIqx2juoIelbPCUJekjCM3AEwIQktRDKRKMnhpiUPRvVpql9W0Zdm+u+2UjW288Q+QHcgTvokHfn2WGRh7w5Nhi46oQZy8CZ6/YX0KUlymfL8QcUE4ou1edojvKZvvP+pwKNsKvQsYemmRRadYePDkfb6MMOuv8N -------------------------------------------------------------------------------- /public/images/book/marginpadding.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/marginpadding.gif -------------------------------------------------------------------------------- /public/images/book/marginsibling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/marginsibling.png -------------------------------------------------------------------------------- /public/images/book/marginsibling.xml: -------------------------------------------------------------------------------- 1 | 7VhNs5sgFP01LjsjEMzLsrWvr5uusuiaCFGmKgySaPrriwEjRtsmrXmvmSabwOFePs6cc0UDFBfNiyIy+yIoywMY0iZAHwMIAYoW5q9FDhZZRpEFUsWpC+qBNf/OHBg6dMcpqwaBWohcczkEE1GWLNEDjCgl6mHYVuTDVSVJ2QhYJyQfo1851ZlFIxz2+GfG08ytjEM3sCHJt1SJXemWCyDaHn92uCDdVC6+yggVtQeh5wDFSghtW0UTs7yltmPN5n36yehp24qV+pIEZBP2JN+xbsdRblI/UL43zbRtdtBGnSNm4om4/zy1kqTssIKolJfvNkJrUQToveEahbLx5vSjL1/8qB196ORaZ1yztSRJ26+NJU1Qpovc9IBpbkWpnccAPuX7KnHC2TOlWeNBTjUvTBRMq4MJqXs7gMipOPOsABYOJM6C6Sm316FpOClOyxKOZBmbI7S7vfLklFQZozegwY12PnZlDmHb9UmCEyRFM3CEb2xdJ10tpNUt9HT79sb65XRTgY9VbrbKv1yuDmdV6bflC85gzehOyxdYPr1e/Vo+rh6Pq8efiBbhS708x1Xk6V68jJZvdxdZXeRlHM+jqTk1dO3zYYX/nq3uJff+NDVJ061EBcCIJnMUylRlJ4YRKVo+yk0lp1Rk3q1l26w0a8dfv1CB1Rl7YMKSCzxmD13Pnun2Xw2OY96XGfT8Aw== -------------------------------------------------------------------------------- /public/images/book/play/autocomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/play/autocomplete.png -------------------------------------------------------------------------------- /public/images/book/play/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/play/error.png -------------------------------------------------------------------------------- /public/images/book/play/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/book/play/full.png -------------------------------------------------------------------------------- /public/images/course.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/public/images/course.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TypeStyle 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /public/play/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TypeStyle 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /scripts/publish.ts: -------------------------------------------------------------------------------- 1 | console.log("--running publish--"); 2 | 3 | const ghpages = require('gh-pages'); 4 | const path = require('path'); 5 | const date = new Date(); 6 | 7 | ghpages.publish(path.resolve(__dirname + '/../public'), { 8 | message: `[ci skip] deployment (${date.getUTCFullYear()}-${date.getUTCMonth()+1}-${date.getUTCDate()}-${date.getUTCHours()}-${date.getUTCMinutes()})`, 9 | 10 | /** User */ 11 | user: { 12 | name: 'basarat', 13 | email: 'basarat@example.com' 14 | } 15 | }, (err) => { 16 | if (err) { 17 | console.log('--publish failed!--', err) 18 | return; 19 | } 20 | console.log("--publish done--"); 21 | }); 22 | 23 | -------------------------------------------------------------------------------- /src/app.tsx: -------------------------------------------------------------------------------- 1 | /** Setup es6 */ 2 | import 'babel-polyfill'; 3 | 4 | import { setupPage, normalize } from 'csstips'; 5 | import * as csstips from 'csstips'; 6 | normalize(); 7 | setupPage('#app-root'); 8 | 9 | import * as React from 'react'; 10 | import * as ReactDOM from 'react-dom'; 11 | import * as cp from './components'; 12 | import { renderRoutes } from './routing/router'; 13 | import { style, forceRenderStyles, cssRaw } from 'typestyle'; 14 | import './augmentTypes'; 15 | 16 | namespace SearchInputStyles { 17 | /** 18 | * Style algolia-autocomplete 19 | */ 20 | cssRaw(` 21 | .algolia-autocomplete { 22 | width: 100% 23 | } 24 | `); 25 | /** make the selected style match our theme */ 26 | cssRaw(` 27 | .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple, .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content { 28 | background-color: rgba(81,81,81,.1) !important; 29 | } 30 | `); 31 | 32 | /** Carbon Ads */ 33 | cssRaw(` 34 | #carbonads * { 35 | margin: initial; 36 | padding: initial; 37 | } 38 | #carbonads { 39 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 40 | Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, 41 | sans-serif; 42 | } 43 | #carbonads { 44 | display: flex; 45 | max-width: 330px; 46 | background-color: hsl(0, 0%, 98%); 47 | box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, 0.1); 48 | z-index: 100; 49 | } 50 | #carbonads a { 51 | color: inherit; 52 | text-decoration: none; 53 | } 54 | #carbonads a:hover { 55 | color: inherit; 56 | } 57 | #carbonads span { 58 | position: relative; 59 | display: block; 60 | overflow: hidden; 61 | } 62 | #carbonads .carbon-wrap { 63 | display: flex; 64 | } 65 | #carbonads .carbon-img { 66 | display: block; 67 | margin: 0; 68 | line-height: 1; 69 | } 70 | #carbonads .carbon-img img { 71 | display: block; 72 | } 73 | #carbonads .carbon-text { 74 | font-size: 13px; 75 | padding: 10px; 76 | margin-bottom: 16px; 77 | line-height: 1.5; 78 | text-align: left; 79 | } 80 | #carbonads .carbon-poweredby { 81 | display: block; 82 | padding: 6px 8px; 83 | background: #f1f1f2; 84 | text-align: center; 85 | text-transform: uppercase; 86 | letter-spacing: 0.5px; 87 | font-weight: 600; 88 | font-size: 8px; 89 | line-height: 1; 90 | border-top-left-radius: 3px; 91 | position: absolute; 92 | bottom: 0; 93 | right: 0; 94 | } 95 | `) 96 | 97 | 98 | export const inputClass = style( 99 | { 100 | width: '100%', 101 | padding: '10px', 102 | fontSize: '20px', 103 | color: '#555', 104 | $nest: { 105 | '&:focus': { 106 | border: '2px solid #999', 107 | outline: 'none', 108 | borderRadius: '3px', 109 | } 110 | } 111 | } 112 | ); 113 | } 114 | 115 | export const Header = () => { 116 | return ( 117 |
124 |
125 |

# TypeStyle

126 |

Making CSS TypeSafe

127 |
128 |
129 | ); 130 | } 131 | 132 | const anchorClass = style({ 133 | color: '#333', whiteSpace: 'nowrap', textDecoration: 'none', 134 | $nest: { 135 | '&:hover': { textDecoration: 'underline' }, 136 | '&:visited': { color: '#333' } 137 | } 138 | }) 139 | 140 | class App extends React.Component<{}, {}> { 141 | refs: { 142 | ads: any; 143 | } 144 | componentDidMount() { 145 | const addNode = ReactDOM.findDOMNode(this.refs.ads); 146 | const s = document.createElement("script"); 147 | s.id = "_carbonads_js"; 148 | s.src = "//cdn.carbonads.com/carbon.js?serve=CEAIP23L&placement=typestylegithubio"; 149 | addNode.appendChild(s); 150 | } 151 | 152 | render() { 153 | return ( 154 |
155 | 156 | 157 | {/** The github links */} 158 | 159 | 160 | 161 | 162 | 163 |
164 | Powered by your github 🌟s. 168 | Don't forget to share 🌹 169 | 170 |
171 |
172 | 173 | {/** Input for doc search */} 174 | 175 | 176 | 177 | 178 | 179 | {renderRoutes()} 180 | 181 | 182 | 183 |
184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | ); 193 | } 194 | } 195 | 196 | ReactDOM.render(, document.getElementById('app-root')); 197 | 198 | /** Loaded in our index.html */ 199 | declare var docsearch: any; 200 | docsearch({ 201 | apiKey: '88a637601106d67ed27a4d8c10915fde', 202 | indexName: 'typestyle', 203 | inputSelector: '#doc-search-input', 204 | debug: false // Set debug to true if you want to inspect the dropdown 205 | }); 206 | 207 | /** kickoff styles */ 208 | forceRenderStyles(); 209 | -------------------------------------------------------------------------------- /src/app_spec.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typestyle/typestyle.github.io/b759bc74c95573ea6e6435765e0024396d444a5e/src/app_spec.ts -------------------------------------------------------------------------------- /src/augmentTypes.ts: -------------------------------------------------------------------------------- 1 | import * as types from "typestyle/lib/types"; 2 | 3 | declare module "typestyle/lib/types" { 4 | interface CSSProperties { 5 | /** Nothing needed right now */ 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/bookSection.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * components used to setup the demo page 3 | */ 4 | import { style, classes } from 'typestyle'; 5 | import * as csstips from 'csstips'; 6 | import * as React from 'react'; 7 | import * as gls from './gls'; 8 | import { colors, fontSizes, spacing } from './styles'; 9 | import * as txt from './txt'; 10 | 11 | namespace BookSectionStyles { 12 | export const anchor = style({ 13 | color: colors.text, 14 | textDecoration: 'none', 15 | $nest: { 16 | '&:hover': { 17 | color: colors.text, 18 | textDecoration: 'underline' 19 | } 20 | } 21 | }); 22 | 23 | export const tocAnchor = style( 24 | csstips.padding(5, 5, 5, 10), 25 | { 26 | background: '#eee', 27 | color: colors.text, 28 | textDecoration: 'none', 29 | $nest: { 30 | '&:hover': { 31 | color: colors.text, 32 | background: '#ddd', 33 | } 34 | } 35 | }); 36 | 37 | export const anchorLookingLikeButton = style({ 38 | cursor: 'pointer', 39 | height: 'auto', 40 | padding: "12px 30px 11px", 41 | border: `1px solid ${colors.header}`, 42 | borderRadius: '3px', 43 | color: colors.white, 44 | backgroundColor: colors.header, 45 | fontSize: fontSizes.buttonText, 46 | textDecoration: "none", 47 | lineHeight: "1em", 48 | outline: 'none', 49 | transition: 'color .2s, background-color .2s', 50 | display: 'inline-block', 51 | $nest: { 52 | '&:hover': { 53 | backgroundColor: colors.headerHover, 54 | }, 55 | '&:active': { 56 | backgroundColor: colors.headerHover, 57 | }, 58 | '&:focus': { 59 | outline: 'thin dotted', 60 | outlineColor: colors.header 61 | } 62 | } 63 | }); 64 | } 65 | 66 | export type TOCItem = { display: string, link: string, prelude: string } 67 | 68 | type BookSectionProps = { 69 | title: string, 70 | link: string, 71 | toc: TOCItem[], 72 | children?: React.ReactNode 73 | } 74 | export const BookSection = ({ title, link, toc, children }: BookSectionProps) => { 75 | const currentSectionIndex = toc.findIndex(t => t.link === link); 76 | const currentToc = toc.find(t => t.link === link); 77 | const previousIfAny = toc[currentSectionIndex - 1]; 78 | const nextIfAny = toc[currentSectionIndex + 1]; 79 | 80 | const scrollToTop = () => window.scrollTo(0, 0); 81 | 82 | return 83 | Table of Contents 84 | 85 | {toc.map((t, index) => { 86 | return 94 | {t.display} 95 | 96 | })} 97 | 98 | 99 | {/** The id is used to track the content that should be made available for search */} 100 | 104 | 105 | {/** Next / previous */} 106 |
107 | 108 | {previousIfAny && 109 | Previous 110 | } 111 | 112 | {nextIfAny && nextIfAny.prelude} 113 | 114 | {nextIfAny && 115 | Next 116 | } 117 | 118 |
119 | } -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './gls'; 2 | export * from './bookSection'; 3 | export * from './txt'; 4 | export * from './markdown'; 5 | export * from './styles'; 6 | -------------------------------------------------------------------------------- /src/components/markdown.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as marked from "marked"; 3 | import { style, cssRaw, classes } from 'typestyle'; 4 | import * as csstips from 'csstips'; 5 | import { colors, spacing, fontSizes } from './styles'; 6 | import * as escape from 'escape-html'; 7 | import { getPlaygroundLink } from '../play/srcLoader'; 8 | 9 | /** 10 | * Using codemirror for syntax highlighting 11 | **/ 12 | import * as CodeMirror from 'codemirror'; 13 | cssRaw(require('codemirror/lib/codemirror.css')); 14 | cssRaw(require('codemirror/theme/monokai.css')); 15 | import 'codemirror/addon/runmode/runmode'; 16 | /** JSX */ 17 | import 'codemirror/mode/jsx/jsx'; 18 | import 'codemirror/mode/javascript/javascript'; 19 | import 'codemirror/mode/xml/xml'; 20 | /** CSS */ 21 | import 'codemirror/mode/css/css'; 22 | /** HTML */ 23 | import 'codemirror/mode/htmlmixed/htmlmixed'; 24 | /** Our function */ 25 | function highlightCodeWithMode(args: { code: string, mode: string }) { 26 | // console.log({ code }); // DEBUG 27 | const collection = []; 28 | (CodeMirror as any).runMode(args.code, args.mode, 29 | (text, category) => { 30 | text = escape(text); 31 | collection.push(category ? `${text}` : text) 32 | } 33 | ); 34 | return `
${collection.join('')}
` 35 | } 36 | 37 | namespace PlayButtonStyles { 38 | export const anchorLookingLikeButton = style({ 39 | cursor: 'pointer', 40 | height: 'auto', 41 | padding: "12px 30px 11px", 42 | border: `1px solid ${colors.header}`, 43 | borderRadius: '3px', 44 | color: `${colors.white} !important`, 45 | backgroundColor: colors.header, 46 | fontSize: fontSizes.buttonText, 47 | textDecoration: "none", 48 | lineHeight: "1em", 49 | outline: 'none', 50 | transition: 'color .2s, background-color .2s', 51 | display: 'inline-block', 52 | $nest: { 53 | '&:hover': { 54 | backgroundColor: colors.headerHover, 55 | }, 56 | '&:active': { 57 | backgroundColor: colors.headerHover, 58 | }, 59 | '&:focus': { 60 | outline: 'thin dotted', 61 | outlineColor: colors.header 62 | } 63 | } 64 | }); 65 | } 66 | 67 | /** 68 | * CSS customizations 69 | */ 70 | namespace MarkDownStyles { 71 | export const rootClass = 'typestyle-markdown'; 72 | 73 | cssRaw(` 74 | .${rootClass} { 75 | color: ${colors.text} 76 | } 77 | 78 | .${rootClass} p { 79 | margin: 0px; 80 | line-height: 24px; 81 | } 82 | 83 | .${rootClass} h2 { 84 | margin: 0px; 85 | } 86 | .${rootClass} h3 { 87 | margin: 0px; 88 | } 89 | 90 | /** List styling */ 91 | .${rootClass} ul { 92 | margin: 0px; 93 | margin-bottom: 20px !important; 94 | line-height: ${spacing.lineHeight}; 95 | padding-left: 27px; 96 | } 97 | .${rootClass} ul>* { 98 | margin-bottom: 5px !important; 99 | } 100 | .${rootClass} ul>*:last-child { 101 | margin-bottom: 0px !important; 102 | } 103 | .${rootClass} li>* { 104 | margin-top: 5px !important; 105 | margin-bottom: 5px !important; 106 | } 107 | .${rootClass} li>*:last-child { 108 | margin-bottom: 0px !important; 109 | } 110 | 111 | .${rootClass} ol { 112 | margin: 0px; 113 | margin-bottom: 20px !important; 114 | line-height: ${spacing.lineHeight}; 115 | padding-left: 27px; 116 | } 117 | .${rootClass} ol>* { 118 | margin-bottom: 5px !important; 119 | } 120 | .${rootClass} ol>*:last-child { 121 | margin-bottom: 0px !important; 122 | } 123 | 124 | .${rootClass} a { 125 | color: grey; 126 | } 127 | 128 | .${rootClass} a:hover { 129 | color: black; 130 | } 131 | 132 | /** Inline code */ 133 | .${rootClass} code { 134 | padding-left: 5px; 135 | padding-right: 5px; 136 | background: #eee; 137 | font-family: consolas, menlo, monospace; 138 | } 139 | 140 | /** Block code */ 141 | .${rootClass} pre>code { 142 | display: block; 143 | padding: 10px; 144 | background: #f4f4f4; 145 | overflow: auto; 146 | font-family: consolas, menlo, monospace; 147 | border-left: 2px solid #ddd; 148 | line-height: 24px; 149 | } 150 | 151 | /** Blockquote */ 152 | .${rootClass} blockquote { 153 | margin: 0; 154 | padding: 5px 20px; 155 | color: #6b6b6b; 156 | background-color: #f6f6f6; 157 | border-top: 1px solid #e5e5e5; 158 | border-bottom: 1px solid #e5e5e5; 159 | border-right: 1px solid #e5e5e5; 160 | border-left: 4px solid #e5e5e5; 161 | } 162 | 163 | /** Images */ 164 | .${rootClass} img { 165 | /** Ensures a nice display on mobile devices */ 166 | max-width: 100%; 167 | } 168 | `); 169 | } 170 | 171 | 172 | 173 | interface Props { markdown: string } 174 | 175 | /** 176 | * Renders markdown 177 | */ 178 | export class MarkDown extends React.Component { 179 | constructor(props: Props) { 180 | super(props); 181 | } 182 | 183 | render() { 184 | const rendered = toHtml(this.props.markdown); 185 | 186 | return ( 187 |
188 | ); 189 | } 190 | } 191 | 192 | /** Converts an html string to markdown */ 193 | export function toHtml(markdown: string) { 194 | /** Custom rendering */ 195 | const renderer = new marked.Renderer(); 196 | 197 | /** 198 | * Target blank external links 199 | * https://github.com/chjj/marked/pull/451 200 | **/ 201 | renderer.link = function(href, title, text) { 202 | var external, newWindow, out; 203 | external = /^https?:\/\/.+$/.test(href); 204 | newWindow = external || title === 'newWindow'; 205 | out = ""; 213 | return output; 214 | }; 215 | 216 | return ( 217 | marked(markdown, { 218 | gfm: true, 219 | renderer: renderer, 220 | highlight: (code, lang) => { 221 | if (lang === 'ts' || lang === 'js' || lang === 'typescript' || lang === 'javascript') { 222 | return highlightCodeWithMode({ code, mode: 'jsx' }) 223 | } 224 | if (lang === 'html') { 225 | return highlightCodeWithMode({ code, mode: 'text/html' }) 226 | } 227 | if (lang === 'css') { 228 | return highlightCodeWithMode({ code, mode: 'css' }) 229 | } 230 | if (lang === 'play') { 231 | // Remove any imports 232 | const codeForPlayground = code.split(/\n|\r\n/g).filter(l => !l.startsWith('import')).join('\n').trim(); 233 | 234 | /** Makes it easier to write :) */ 235 | code = code.trim(); 236 | 237 | return ` 238 | ${highlightCodeWithMode({ code, mode: 'jsx' })} 239 | 240 | Open in Playground 241 | `.trim() 242 | } 243 | 244 | return code; 245 | } 246 | }) 247 | // don't want a trailing newline 248 | .trim() 249 | ); 250 | } -------------------------------------------------------------------------------- /src/components/styles.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @module style constants 3 | */ 4 | export const colors = { 5 | white: 'white', 6 | 7 | header: 'black', 8 | headerHover: '#444', 9 | 10 | text: '#555' 11 | } 12 | export const fontSizes = { 13 | buttonText: '15px', 14 | } 15 | export const breakpoints = { 16 | phone: 480 17 | } 18 | export const spacing = { 19 | lineHeight: '24px' 20 | } -------------------------------------------------------------------------------- /src/components/txt.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {style} from 'typestyle'; 3 | import * as styles from './styles'; 4 | 5 | export const P = ({children}: {children?:any}) => { 6 | return

11 | {children} 12 |

; 13 | } 14 | 15 | export const H1 = ({children, id}: {children?:any, id: string}) => { 16 | return

21 | {children} 22 |

; 23 | } -------------------------------------------------------------------------------- /src/converter.tsx: -------------------------------------------------------------------------------- 1 | /** Setup es6 */ 2 | import 'babel-polyfill'; 3 | 4 | import { convertCss } from 'css-to-typestyle'; 5 | import { setupPage, normalize } from 'csstips'; 6 | import * as csstips from 'csstips'; 7 | import { rem, color } from 'csx'; 8 | import { style, cssRule, media } from 'typestyle'; 9 | normalize(); 10 | setupPage('#root'); 11 | 12 | /** 13 | * Clear default margins from all things 14 | * I wouldn't do this in a production site. But doing it to make teaching easier 15 | */ 16 | cssRule('h1,h2,h3,h4,h5,h6,h7,h8,p', { 17 | margin: 0 18 | }); 19 | 20 | import * as React from 'react'; 21 | import * as ReactDOM from 'react-dom'; 22 | import * as cp from './components'; 23 | import { observer } from 'mobx-react'; 24 | import * as gls from './components/gls'; 25 | 26 | @observer 27 | export class HeaderSmall extends React.Component<{}, {}> { 28 | render() { 29 | return ( 30 |
31 | 32 | 36 |

# TypeStyle 🌹

37 |
38 | 39 |
40 | 41 | 45 |

Help

46 |
47 | 48 | 49 | 50 | 51 | 52 |
53 | ); 54 | } 55 | } 56 | 57 | const baseBgColor = color('#343436'); 58 | 59 | const styledEntry = style({ 60 | width: '100%', 61 | height: '100%', 62 | backgroundColor: '#272822', 63 | color: '#f8f8f2', 64 | fontSize: 16, 65 | fontFamily: 'consolas, menlo, monospace', 66 | padding: rem(.7) 67 | }); 68 | 69 | const buttonClass = style({ 70 | borderRadius: rem(.5), 71 | borderColor: baseBgColor.toHexString(), 72 | backgroundColor: baseBgColor.darken(.2).toHexString(), 73 | color: baseBgColor.invert().lighten(.2).toHexString(), 74 | cursor: 'pointer', 75 | outline: 'none', 76 | $nest: { 77 | '&:hover': { 78 | backgroundColor: baseBgColor.darken(.1).toHexString() 79 | } 80 | } 81 | }); 82 | 83 | 84 | /** 85 | * Provides a nice demo / test component 86 | */ 87 | @observer 88 | export class Demo extends React.Component<{}, { cssContents?: string, tsContents?: string }> { 89 | constructor(props) { 90 | super(props); 91 | this.state = { 92 | cssContents: '', 93 | tsContents: '' 94 | }; 95 | } 96 | onConvertToTypeStyle = () => { 97 | const cssToConvert = this.state.cssContents; 98 | convertCss(cssToConvert).then((tsContents) => { 99 | this.setState({ tsContents }); 100 | }); 101 | } 102 | onCssContentChange = (event) => { 103 | this.setState({cssContents: event.target.value}); 104 | } 105 | render() { 106 | return 107 |
108 | 113 |
114 |
115 | 116 |
117 |
118 | 119 |
120 |
121 | } 122 | } 123 | 124 | ReactDOM.render(
125 | 126 | 127 |
, document.getElementById('root')); -------------------------------------------------------------------------------- /src/docs/advanced.md: -------------------------------------------------------------------------------- 1 | * [Concept: Deduping](/#/advanced/concept-deduping) 2 | * [Concept: Ordering pseudo classes](/#/advanced/concept-ordering-pseudo-classes) 3 | * [Concept: Ordering media queries](/#/advanced/concept-ordering-media-queries) 4 | * [Concept: Ensuring a unique selector](/#/advanced/concept-ensuring-a-unique-selector) 5 | * [content](/#/advanced/-content-) 6 | * [Google Fonts](/#/advanced/google-fonts) 7 | * [$debugName](/#/advanced/-debugname-) 8 | * [Multiple instances](/#/core/multiple-instances) 9 | 10 | ## Concept: Deduping 11 | It is safe to call `style` with the same object strucure again and again (e.g. from within a react render function). `style` doesn't generate new CSS if it's not required, this is shown below: 12 | 13 | ```play 14 | import {style} from 'typestyle'; 15 | 16 | const a = style({color:'red'}); 17 | const b = style({color:'red'}); 18 | 19 |
a:{a},b:{b}. Same? {(a===b).toString()}
; 20 | ``` 21 | 22 | We even automatically try to generate the smallest deduped CSS possible e.g. the following 23 | ```ts 24 | style({ color: 'red', $nest: { '&>*': { color: 'red' } } }); 25 | ``` 26 | Generates the CSS: 27 | ```css 28 | .f1nv0def,.f1nv0def>*{color:red} 29 | ``` 30 | You can see that since the structure `{color:red}` was common, so the selectors `.f1nv0def,.f1nv0def>*` are concatenated. 31 | 32 | This gives the following gains: 33 | 34 | * No needless CSS updates + reflows. 35 | * True freedom to use `className` like you would use `style` in your framework of choice. 36 | * No style bloat: Automatically smaller stylesheets based on your object reuse. 37 | 38 | ## Concept: Ordering pseudo classes 39 | You normally don't need to worry about ordering your pseudo classes. For cases when you do, you should use `&&`, then `&&&` so on, to increase specificity (in CSS `.foo` loses to `.foo.foo`). This also shows that you explicitly want the ordering to matter for any later code review. 40 | 41 | ```play 42 | /** BAD */ 43 | const hoverBackground = 'green'; 44 | const focusBackground = 'red'; 45 | const buttonClass = style({ 46 | $nest: { 47 | '&:focus': { background: focusBackground }, 48 | '&:hover': { background: hoverBackground }, 49 | }, 50 | }); 51 | 52 | /** Force `:focus` to come to the bottom of the generated CSS due to deduping */ 53 | const moveDown = style({ 54 | background: focusBackground 55 | }); 56 | 57 | /** Now `hover` will not work if `focus`ed anymore! */ 58 |
59 | 60 | 61 |
62 | ``` 63 | 64 | Sample fix: 65 | 66 | ```play 67 | /** GOOD */ 68 | const hoverBackground = 'green'; 69 | const focusBackground = 'red'; 70 | const buttonClass = style({ 71 | $nest: { 72 | '&:focus': { background: focusBackground }, 73 | /** Use && to increase specifity and show that order matters */ 74 | '&&:hover': { background: hoverBackground }, 75 | }, 76 | }); 77 | 78 | /** Force `:focus` to come to the bottom of the generated CSS due to deduping */ 79 | const moveDown = style({ 80 | background: focusBackground 81 | }); 82 | 83 | /** Now `hover` will still work even if `focus`ed! */ 84 |
85 | 86 | 87 |
88 | ``` 89 | 90 | A common pattern is sytling anchors using [LVHA-order](https://developer.mozilla.org/en-US/docs/Web/CSS/:active): 91 | 92 | ```play 93 | const anchorClass = style({ 94 | $nest: { 95 | '&:link': { color: 'blue' }, 96 | '&&:visited': { color: 'purple' }, 97 | '&&&:hover': { fontWeight: 'bold' }, 98 | '&&&&:active': { color: 'lime' }, 99 | }, 100 | }); 101 | 102 | TypeStyle; 106 | ``` 107 | 108 | ## Concept: Ordering media queries 109 | Conventional css authors will write media queries with an *override* mindset i.e 110 | 111 | ```css 112 | .some { 113 | /** Common */ 114 | font-size: 50px; 115 | /** Default */ 116 | color: red; 117 | } 118 | /** Override: change for bigger screens */ 119 | @media (min-width: 500px) { 120 | .some { 121 | color: green; 122 | } 123 | } 124 | ``` 125 | 126 | Due to style deduping you should not depend on style ordering, with TypeStyle you should write with a *mutally exclusive* mindset 127 | 128 | ```play 129 | import { style, media } from 'typestyle'; 130 | 131 | const some = style( 132 | /** Common */ 133 | {fontSize: '50px'}, 134 | /** Default */ 135 | media({minWidth: 0, maxWidth: 499}, {color:'red'}), 136 | /** Change for bigger screens */ 137 | media({minWidth: 500}, {color:'green'}), 138 | ); 139 | 140 |
Hello world
; 141 | ``` 142 | 143 | ## Concept: Ensuring a unique selector 144 | For certain browser scenarios you need to have a unique selector with its own body. The only use case we have experienced for now are browser specific pseudo selectors. For example for `placeholder` styling, having multiple browser specific selectors does not work (even though its valid CSS): 145 | 146 | ```css 147 | /** Does not work */ 148 | .f13byak::-webkit-input-placeholder, /* WebKit fails here */ 149 | .f13byak::-moz-placeholder, /* Firefox fails here */ 150 | .f13byak::-ms-input-placeholder { /* IE fails here */ 151 | color: rgb(255, 0, 0), 152 | } 153 | 154 | /** Does work */ 155 | .f13byak::-webkit-input-placeholder { 156 | color: rgb(255, 0, 0), 157 | } 158 | .f13byak::-moz-placeholder { 159 | color: rgb(255, 0, 0), 160 | } 161 | .f13byak::-ms-input-placeholder { 162 | color: rgb(255, 0, 0), 163 | } 164 | ``` 165 | 166 | You can ensure that a selector gets its own body by adding `$unique : true`. This ensures *that particular object* is not tested for deduping and always emitted even if a similar structure exists: 167 | 168 | ```play 169 | const className = style({ 170 | $nest: { 171 | '&::-webkit-input-placeholder': { 172 | $unique: true, 173 | color: `rgb(255, 0, 0)`, 174 | }, 175 | '&::-moz-placeholder': { 176 | $unique: true, 177 | color: `rgb(255, 0, 0)`, 178 | }, 179 | '&::-ms-input-placeholder': { 180 | $unique: true, 181 | color: `rgb(255, 0, 0)`, 182 | } 183 | } 184 | }); 185 | 186 | 187 | ``` 188 | 189 | ## `content` 190 | 191 | When assigning a string to the content property it requires double or single quotes in CSS. So you should use whatever string you want e.g. if you want to prefix with `'Hello'` you can wrap that whole content easily with backticks. 192 | 193 | ```play 194 | const hello = style({ 195 | $nest: { 196 | '&:before': { 197 | content: `'Hello '` 198 | } 199 | } 200 | }); 201 |
is it me you are looking for?
202 | ``` 203 | We don't do automatic quoting as you are free to use other things in CSS content that aren't quoted e.g. `attr`: 204 | 205 | ```play 206 | const before = style({ 207 | $nest: { 208 | '&:before': { 209 | content: `attr(data-before)` 210 | } 211 | } 212 | }); 213 |
214 | is it me you are looking for? 215 |
216 | ``` 217 | 218 | ## Google Fonts 219 | If you want to use google fonts e.g. [Roboto](http://www.google.com/fonts#UsePlace:use/Collection:Roboto), you can just use the `@import` syntax they give you with `cssRaw` 220 | 221 | > Note: Be sure to have this call *before* any other calls to `cssRaw` because [@import only works if its at the top of the file](https://www.w3.org/TR/css-cascade-3/#at-import) 222 | 223 | ```ts 224 | /** Import the file */ 225 | cssRaw(` 226 | @import url('https://fonts.googleapis.com/css?family=Roboto'); 227 | `); 228 | 229 | ``` 230 | and then you can use the font like you normally would e.g. 231 | 232 | ```ts 233 | /** Elsewhere */ 234 | const className = style({fontFamily:'Roboto, sans-serif'}); 235 |
236 |

Demo

237 |

Demo

238 |

Demo

239 |

Demo

240 |
Demo
241 |
; 242 | ``` 243 | 244 | ## `$debugName` 245 | If you want meaningful word associated with the generated className for debugging you can use the `$debugName` property e.g.: 246 | 247 | ```play 248 | const className = style({ 249 | $debugName: 'bigRed', 250 | color: 'red', 251 | fontSize: '50px', 252 | }); 253 |
254 | The className: 255 |
256 | {className} 257 |
; 258 | ``` 259 | 260 | Note that this name is purely for debugging convenience and has no impact on any of the other logic (e.g. deduping). 261 | 262 | ## Multiple instances 263 | The core `style` function is simply a handle to a single instance of `TypeStyle.style`. 264 | 265 | You can create multiple instances of TypeStyle using the `createTypeStyle` function. There are two ways to use this function: 266 | 267 | * You can use this to create a TypeStyle instance if you just want to *collect* the generated CSS. 268 | 269 | ```js 270 | const instance = typestyle.createTypeStyle(); 271 | instance.style({ color : 'red' }); // Will not write a stylesheet anywhere. 272 | const styles = instance.getStyles(); // Write these styles somewhere. You can write them to a tag or a file or wherever. 273 | ``` 274 | 275 | * You can pass in a style tag and this typestyle instance will write the styles to that tag. 276 | 277 | ```js 278 | const tag = document.createElement('style'); 279 | document.head.appendChild(tag); 280 | const instance = typestyle.createTypeStyle(tag); 281 | instance.style({ color : 'red' }); // Will write to the tag you created 282 | ``` 283 | -------------------------------------------------------------------------------- /src/docs/box.md: -------------------------------------------------------------------------------- 1 | How you manage the space around and inside your components is vital to a great design. First a quick recap of some core CSS concepts that we will use here. 2 | 3 | ### Never let margin bleed 4 | You really only need to know the difference between `margin` (something that's outside) and `padding` (something that is inside). Here's a picture: 5 | 6 | ![](/images/book/marginpadding.gif) 7 | 8 | 9 | One more thing about margin : *it collapses*. This means that if two items are next to each other with a margin of `30px` and `20px`, instead of of being separated by `50px` they will be separated by `30px`. This shown below: 10 | 11 | ![](/images/book/marginsibling.png) 12 | 13 | Not only that, if an element is unfortunate to be at the border of its parent, its margin will collapse with its parent. These facts are shown below: 14 | 15 | ![](/images/book/marginchild.png) 16 | 17 | This makes it very difficult to create a maintainable layout system if your components have external margins 18 | 19 | * Don't have an external `margin` on components. 20 | * `margin` is something that a parent should push down to put a visual seperation between its children. More on this later. 21 | 22 | ### Avoid inline 23 | Having an element as `display: inline` means that it completely ignores its height. Here is a visual difference where an element has been given a height but `inline` ignored it and `inline-block` got it. 24 | 25 | ![](/images/book/inline.png) 26 | 27 | ![](/images/book/inlineBlock.png) 28 | 29 | Also you cannot CSS3 transform inline elements. So use a `span` but if there is anything fancy you need the `span` to do, be sure to `inline-block` it. 30 | 31 | > TIP: `csstips` has a mixin `csstips.inlineBlock` for your inline desires. 32 | 33 | ## `csstips.padding` 34 | 35 | `csstips.padding` is a nice mixin to create padding for managing the space inside a component. e.g. Here is some pretty text you can hover over, 36 | 37 | ```play 38 | import {style} from 'typestyle'; 39 | import * as csx from 'csx'; 40 | import * as csstips from 'csstips'; 41 | 42 | const bg = csx.black; 43 | const color = csx.white; 44 | const prettyBox = style( 45 | csstips.padding(10), 46 | csstips.inlineBlock, 47 | { 48 | color:color.darken(.2).toHexString(), 49 | cursor:'pointer', 50 | backgroundColor:bg.toHexString(), 51 | transition: 'color .2s, background-color .2s', 52 | $nest: { 53 | '&:hover':{ 54 | color: color.toHexString(), 55 | backgroundColor:bg.lighten(.2).toHexString(), 56 | } 57 | } 58 | } 59 | ); 60 | 61 |
Hello World
62 | ``` 63 | 64 | This is what a padding should be used for. Maintaining a nice boundary seperation *inside* a component. 65 | 66 | ## Spacing children 67 | 68 | > If a function only does mutation inside is it still functional? 69 | 70 | Remeber the lesson of margins: You cannot have margins bleed. However you can use them to create a nice visual seperation between *your* children (subitems of the component). We provide utilities for just that `csstips.horizontallySpaced` and `csstips.verticallySpaced` 71 | 72 | E.g. a nice vertical layout: 73 | 74 | ```play 75 | import {style} from 'typestyle'; 76 | import * as csstips from 'csstips'; 77 | 78 | const DemoItem = () =>

85 | Demo Item 86 |

; 87 | 88 |
89 | 90 | 91 | 92 |
93 | ``` 94 | 95 | The reason for letting such spacing come down from the parent *instead* of having it on each `DemoItem` is common in application layouts, where you have buttons / page sections popping in and popping out due to some logic regarding page state and user permissions. Having this visual seperation come down using *margins* from the parent results in a more maintainable layout (as the components can be blissfully unaware of their siblings). 96 | 97 | Spacing items like this where there is no *margin bleed* at the borders **composes** nicely too e.g. here are two columns: 98 | 99 | ```play 100 | import {style} from 'typestyle'; 101 | import * as csx from 'csx'; 102 | import * as csstips from 'csstips'; 103 | 104 | const DemoItem = () =>

111 | Demo Item 112 |

; 113 | 114 | /* Sample showing DemoItems composing */ 115 | const DemoCollection = () =>
116 | 117 | 118 | 119 |
; 120 | 121 | /* Sample showing DemoCollections composing */ 122 |
123 | 124 | 125 |
; 126 | ``` 127 | 128 | You can see that, for the user, its impossible to tell the fact that there are two sets of three `DemoItem`s instead of one set of six `DemoItem`s. 129 | 130 | At some level up the heirarchy you would need to create a visual seperation from the border (e.g. at the page level) and there you should use `csstips.padding`. This is shown below where we have a nice root `Page` component: 131 | 132 | ```play 133 | import {style} from 'typestyle'; 134 | import * as csx from 'csx'; 135 | import * as csstips from 'csstips'; 136 | 137 | const DemoItem = () =>

144 | Demo Item 145 |

; 146 | 147 | const DemoCollection = () =>
148 | 149 | 150 | 151 |
; 152 | 153 | /** A nice Page component with padding */ 154 | const Page = (props) =>
155 | {props.children} 156 |
; 157 | 158 | 159 | 160 | 161 | ; 162 | ``` 163 | 164 | -------------------------------------------------------------------------------- /src/docs/colors.md: -------------------------------------------------------------------------------- 1 | Color management in a large project is always a challenge. With this in mind, TypeStyle (csx) comes with SASS/LESS/Stylus inspired color functions to make styling simple and reusable. 2 | 3 | > Values from these `csx` utilities need to be `toString`ed before being used in TypeStyle or any other library. This means that they can be used with or without TypeStyle 🌹 4 | 5 | Here are some of the things you can do: 6 | 7 | --- 8 | ## Create New Colors 9 | 10 | ### color(value: string): ColorHelper 11 | Creates a new color from a string 12 | 13 | - Can be a six character hex code, a three character hex code, or a CSS color function 14 | 15 | ```typescript 16 | import { color } from 'csx'; 17 | 18 | var red2 = color('#FF0000'); 19 | var red3 = color('#F00'); 20 | var red4 = color('rgb(255,0,0)'); 21 | var red5 = color('rgba(255,0,0,1)'); 22 | var red6 = color('hsl(0,100%,50%)'); 23 | var red7 = color('hsl(0,100%,50%,1)'); 24 | ``` 25 | 26 | ### hsl(hue: number, saturation: number | string: lightness: number | string): ColorHelper 27 | Creates a color from hue, saturation, and lightness 28 | 29 | - Hue is a number between 0 and 360. 30 | - Saturation and Lightness can be expressed as a string (e.g. '10%') or as a number between 0 and 1 (e.g. 0.1) 31 | 32 | ```typescript 33 | import { hsl } from 'csx'; 34 | 35 | const color1 = hsl(250, .5, .5); 36 | const color1 = hsl(250, '50%', '50%'); 37 | ``` 38 | 39 | ### hsla(hue: number, saturation: number | string: lightness: number | string, alpha: number | string): ColorHelper 40 | Creates a color from hue, saturation, lightness, and alpha 41 | 42 | - Hue is a number between 0 and 360. 43 | - Saturation and Lightness can be expressed as a string (e.g. '10%') or as a number between 0 and 1 (e.g. 0.1) 44 | - Alpha is expressed as a string (e.g. '10%') or as a number between 0 and 1 (e.g. 0.1) 45 | 46 | ```typescript 47 | import { hsla } from 'csx'; 48 | 49 | const color1 = hsla(250, .5, .5, .5); 50 | const color1 = hsla(250, '50%', '50%', '50%'); 51 | ``` 52 | 53 | ### rgb(red: number, green: number: blue: number): ColorHelper 54 | Creates a color from red, green, and blue values 55 | 56 | - Red, Green, and Blue are expressed as numbers between 0 and 255 57 | 58 | ```typescript 59 | import { rgb } from 'csx'; 60 | 61 | const red = rgb(255, 0, 0); 62 | const green = rgb(0, 255, 0); 63 | const blue = rgb(0, 0, 255); 64 | ``` 65 | 66 | ### rgba(red: number, green: number: blue: number, alpha: number | string): ColorHelper 67 | Creates a color from red, green, blue, and alpha values 68 | 69 | - Red, Green, and Blue are expressed as numbers between 0 and 255 70 | - Alpha is expressed as a string (e.g. '10%') or as a number between 0 and 1 (e.g. 0.1) 71 | 72 | ```typescript 73 | import { rgba } from 'csx'; 74 | 75 | const red = rgba(255, 0, 0, 1); 76 | const green = rgba(0, 255, 0, '100%'); 77 | const blue = rgba(0, 0, 255, 1); 78 | ``` 79 | 80 | --- 81 | ## Convert to String 82 | 83 | ### toHexString(): string 84 | Returns a string with the RGB Hex code (e.g. black = #000000). Useful when supporting a legacy browser. 85 | ```typescript 86 | import { color } from 'csx'; 87 | 88 | color('#FFF').toHexString(); // #FFFFFF 89 | ``` 90 | 91 | ### toString(): string 92 | Returns a string representing the color in its current color space. Hex color codes are automatically output as rgb() 93 | ```typescript 94 | import { hsla, rgba } from 'csx'; 95 | 96 | rgba(0, 0, 0, .5).toString(); // rgba(0, 0, 0, 50%) 97 | hsla(0, 0, 0, .5).toString(); // hsla(0, 0, 0, 50%) 98 | ``` 99 | 100 | --- 101 | ## Changing Color Space / Format 102 | 103 | ### toHSL(): string 104 | Converts to the **H**ue **S**aturation **L**ightness color space 105 | ```typescript 106 | import { color } from 'csx'; 107 | 108 | // outputs hsl(0,100%,50%) 109 | const red = color('rgb(255,0,0)').toHSL().toString(); 110 | ``` 111 | 112 | ### toHSLA(): string 113 | Converts to the **H**ue **S**aturation **L**ightness color space with an **A**lpha channel 114 | ```typescript 115 | import { color, rgb } from 'csx'; 116 | 117 | // outputs hsla(0,100%,50%,1) 118 | const red = rgb(255, 0, 0).toHSLA().toString(); 119 | ``` 120 | 121 | ### toRGB(): string 122 | Converts to the **R**ed **G**reen **B**lue color space 123 | ```typescript 124 | import { color } from 'csx'; 125 | 126 | // outputs rgb(255,0,0) 127 | const red = color('hsl(0,100%,50%)').toRGB().toString(); 128 | ``` 129 | 130 | ### toRGBA(): string 131 | Converts to the **R**ed **G**reen **B**lue color space with an **A**lpha channel 132 | ```typescript 133 | import { color } from 'csx'; 134 | 135 | // outputs rgba(255,0,0,1) 136 | const red = hsl(0, 1, .5).toRGBA().toString(); 137 | ``` 138 | 139 | --- 140 | ## Create a New Colors From Other Colors 141 | 142 | ### darken(amount: number | string, relative?: boolean): ColorHelper 143 | Creates a darker color 144 | - amount can be a string (e.g. '10%') or a number between 0 and 1 (e.g. 0.1) 145 | 146 | ```typescript 147 | import { color } from 'csx'; 148 | 149 | const red = color('#FF0000'); 150 | const red1 = red.darken('10%'); 151 | const red2 = red.darken(.1); 152 | ``` 153 | 154 | ### desaturate(amount: number | string, relative?: boolean): ColorHelper 155 | Creates a less saturated color 156 | 157 | - amount can be a string (e.g. '10%') or a number between 0 and 1 (e.g. 0.1) 158 | 159 | ```typescript 160 | import { color } from 'csx'; 161 | 162 | const red = color('#FF0000'); 163 | const red1 = red.desaturate('10%'); 164 | const red2 = red.desaturate(.1); 165 | ``` 166 | 167 | ### fade(alpha: number | string): ColorHelper 168 | Creates the same color with the specified opacity/alpha 169 | 170 | - amount can be a string (e.g. '10%') or a number between 0 and 1 (e.g. 0.1) 171 | 172 | ```typescript 173 | import { color } from 'csx'; 174 | 175 | const red = color('#FF0000'); 176 | const red1 = red.fade('50%'); 177 | const red2 = red.fade(.5); 178 | ``` 179 | 180 | ### fadeIn(amount: number | string, relative?: boolean): ColorHelper 181 | Creates a more opaque color 182 | 183 | - amount can be a string (e.g. '10%') or a number between 0 and 1 (e.g. 0.1) 184 | 185 | ```typescript 186 | import { color } from 'csx'; 187 | 188 | const red = color('#FF0000'); 189 | const red1 = red.fadeIn('10%'); 190 | const red2 = red.fadeIn(.1); 191 | ``` 192 | 193 | ### fadeOut(amount: number | string, relative?: boolean): ColorHelper 194 | Creates a more transparent color 195 | 196 | - amount can be a string (e.g. '10%') or a number between 0 and 1 (e.g. 0.1) 197 | 198 | ```typescript 199 | import { color } from 'csx'; 200 | 201 | const red = color('#FF0000'); 202 | const red1 = red.fadeOut('10%'); 203 | const red2 = red.fadeOut(.1); 204 | ``` 205 | 206 | ### grayscale(): ColorHelper 207 | Creates a grayscale version of the color 208 | 209 | ```typescript 210 | import { color } from 'csx'; 211 | 212 | const red = color('#FF0000'); 213 | const gray = red.grayscale(); 214 | ``` 215 | 216 | ### invert(): ColorHelper 217 | Creates the inverse of a color 218 | 219 | ```typescript 220 | import { color } from 'csx'; 221 | 222 | const green = color('#FFF').invert(); 223 | ``` 224 | 225 | ### lighten(amount: number | string): ColorHelper 226 | Creates a lighter color 227 | 228 | - amount can be a string (e.g. '10%') or a number between 0 and 1 (e.g. 0.1) 229 | 230 | ```typescript 231 | import { color } from 'csx'; 232 | 233 | const red = color('#FF0000'); 234 | const red1 = red.lighten('10%'); 235 | const red2 = red.lighten(.1); 236 | ``` 237 | 238 | ### mix(color2: CSSColor, weight?: string | number): ColorHelper 239 | Creates a new color from an existing color and a second color 240 | 241 | - color2 can be any type that resolves to a CSSColor (string, ColorHelper, etc.) 242 | - weight is expressed as a string (e.g. '50%') or a number between 0 and 1 (e.g. 0.5) 243 | 244 | ```typescript 245 | import { rgb } from 'csx'; 246 | 247 | const red = rgb(255, 0, 0); 248 | const blue = rgb(0, 0, 255); 249 | const purple = red.mix(blue, 0.5); 250 | ``` 251 | 252 | ### saturate(amount: number | string, relative?: boolean): ColorHelper 253 | Creates a more saturation color 254 | 255 | - amount can be a string (e.g. '10%') or a number between 0 and 1 (e.g. 0.1) 256 | 257 | ```typescript 258 | import { color } from 'csx'; 259 | 260 | const red = color('#FF0000'); 261 | const red1 = red.saturate('10%'); 262 | const red2 = red.saturate(.1); 263 | ``` 264 | 265 | ### shade(weight: number): ColorHelper 266 | It darkens the color by mixing black into it. It is the same as black.mix(color, weight). 267 | 268 | - weight is expressed as a number between 0 and 1 (e.g. 0.5) 269 | 270 | ```typescript 271 | import { color } from 'csx'; 272 | 273 | const red = color('#FF0000'); 274 | const darkerRed = red.shade(0.5); 275 | ``` 276 | 277 | ### spin(degrees: number): ColorHelper 278 | Shifts the hue around the color wheel by a certain number of positive or negative degrees 279 | 280 | - degrees is expressed as a number between -360 to 360. Values above or below that range will be wrapped around (e.g. 480 is the same as 120, -480 is the same as -120). 281 | 282 | ```typescript 283 | import { color, spin } from 'csx'; 284 | 285 | const red = color('#FF0000'); 286 | const green = red.spin(120); 287 | const blue = green.spin(120); 288 | const redAgain = blue.spin(120); 289 | ``` 290 | 291 | ### tint(weight: number): ColorHelper 292 | It lightens the color by mixing white into it. It is the same as white.mix(color, weight). 293 | 294 | - weight is expressed as a number between 0 and 1 (e.g. 0.5) 295 | 296 | ```typescript 297 | import { color } from 'csx'; 298 | 299 | const red = color('#FF0000'); 300 | const lighterRed = red.tint(0.5); 301 | ``` 302 | 303 | 304 | 305 | --- 306 | ## Inspect Colors 307 | 308 | ### red(): number 309 | Returns the value of the red component of the color 310 | ```typescript 311 | import { rgb } from 'csx'; 312 | 313 | const value = rgb(255, 0, 0).red(); // 255 314 | ``` 315 | 316 | ### green(): number 317 | Returns the value of the green component of the color 318 | ```typescript 319 | import { rgb } from 'csx'; 320 | 321 | const value = rgb(0, 255, 0).green(); // 255 322 | ``` 323 | 324 | ### blue(): number 325 | Returns the value of the blue component of the color 326 | ```typescript 327 | import { rgb } from 'csx'; 328 | 329 | const value = rgb(0, 0, 255).blue(); //255 330 | ``` 331 | 332 | ### hue(): number 333 | Returns the hue of the color 334 | ```typescript 335 | import { hsl } from 'csx'; 336 | 337 | const value = hsl(120, .5, .5).hue(); // 120 338 | ``` 339 | 340 | ### saturation(): number 341 | Returns the saturation of the color 342 | ```typescript 343 | import { hsl } from 'csx'; 344 | 345 | const value = hsl(120, .5, .5).saturation(); // 0.5 346 | ``` 347 | 348 | ### lightness(): number 349 | Returns the lightness/luminosity of the color 350 | ```typescript 351 | import { hsl } from 'csx'; 352 | 353 | const value = hsl(120, .5, .5).lightness(); // O.5 354 | ``` 355 | 356 | ### alpha() / opacity(): number 357 | Returns the alpha/opacity of the color 358 | ```typescript 359 | import { hsl, rgb } from 'csx'; 360 | 361 | const value1 = hsl(120, .5, .5).opacity(); // 1 362 | const value2 = rgb(128, 255, 0).alpha(); // 1 363 | ``` 364 | -------------------------------------------------------------------------------- /src/docs/flex.md: -------------------------------------------------------------------------------- 1 | Having well defined semantics of *how the layout will happen* is important for a maintainable layout system. The more variables you throw into the semantics the less reliable the layout becomes because *you need to compile the final result in your brain* in order to maintain the layout. We will cover the *concept* of a flexible layout system inspired by a *subset* of CSS flexbox. 2 | 3 | ## Root 4 | We will look at `flex`ible children and `content` children. These concepts exist inside a *`root`*. The root is simply the *container* that is used as a point of reference for these children. 5 | 6 | > A general purpose `csstips.flexRoot` exists. 7 | 8 | ## Flex 9 | > A *flex* container `csstips.flex` has the same size as its parent. 10 | 11 | Consider the following layout: 12 | 13 | ![](/images/book/flex/flex-small.png) 14 | 15 | Here the content *takes up all the available space offered by the parent*. If the parent offers more space, the child takes it gladly. 16 | 17 | ![](/images/book/flex/flex-large.png) 18 | 19 | The space taken by the child (content) is what is available to its children. No more, no less. 20 | 21 | Such a child is called *flex* (`csstips.flex`). 22 | 23 | ## Content 24 | > A *content* (`csstips.content`) child determines its size based on the size of its content 25 | 26 | In the previous example the child *flexed* into the parent. The only other concept we need for a child is that of *content*. **A *content* child determines its size based on the size of its content**. That is all the space it takes up in the parent. This is shown below where if the parent is too big the rest of the space is unused: 27 | 28 | ![](/images/book/flex/content-unused.png) 29 | 30 | If the parent is too small the content will overflow: 31 | 32 | ![](/images/book/flex/content-overflow.png) 33 | 34 | > If you want the parent flex container to scroll for content just mixin `csstips.scroll`. 35 | 36 | ## Root (redux) 37 | 38 | In CSS flexbox the concept of `root` does not exist without combining it with the concept of *flex direction*. 39 | 40 | > The general purpose `csstips.flexRoot` is just an alias (more semantic name) for `csstips.horizontal`. 41 | 42 | A root has a default *main axis* of `horizontal`. This is axis in which the children are layed out. In the *cross axis* the children are by default forced to `flex`. 43 | 44 | So there are really two roots: 45 | * `csstips.horizontal`: Lays out children horizontally based on `content` and `flexes` them vertically. 46 | * `csstips.vertical`: Lays out children vertically based on `content` and `flexes` them horizontally. 47 | 48 | Here is an example of a `csstips.horizontal` layout with `content` children. 49 | 50 | ![](/images/book/flex/horizontal.png) 51 | 52 | Here is an example of a `csstips.vertical` layout with `content` children. 53 | 54 | ![](/images/book/flex/vertical.png) 55 | 56 | # Examples 57 | We've seen four types of containers : `csstips.vertical`, `csstips.horizontal`, `csstips.flex`, `csstips.content`. The next step is to practice with real world examples 58 | 59 | ## Vertical Example 60 | Consider the following layout: 61 | 62 | ![](/images/book/flex/vertical-small.png) 63 | 64 | Up front we know that the root is `csstips.vertical`. Assume that we want the `body` section to *flex* i.e. as the root becomes larger it expands to consume the remaining space, while `header` and `footer` remain sized based on their content: 65 | 66 | ![](/images/book/flex/vertical-large.png) 67 | 68 | In our lingo the `root` here is `csstips.vertical` that has three children: 69 | 70 | * header: `csstips.content` 71 | * body: `csstips.flex` 72 | * footer: `csstips.content` 73 | 74 | Visually: 75 | 76 | ![](/images/book/flex/vertical-solution.png) 77 | 78 | > As mentioned before the children are going to automatically flex in the cross dimension (in this case horizontal). 79 | 80 | ## Multiple Flex Children 81 | The `flex` children actually share the *remainder* of the space left in the `root` after all the `content` children take up the space they need. This is shown below: 82 | 83 | ![](/images/book/flex/multiple-flex-small.png) 84 | 85 | If the parent becomes bigger the `flex` children share all the space that remains after the `content` children take their share: 86 | 87 | ![](/images/book/flex/multiple-flex-large.png) 88 | 89 | Actually a flex child can decide a *flex scaling factor* (`csstips.flex1`,`csstips.flex2` ... `csstips.flex12`) to divide up the remaining space. E.g. a 1:2 ratio can easily be achieved: 90 | 91 | ![](/images/book/flex/multiple-flex-scaling.png) 92 | 93 | The remainder space is divided into `3` (`1 + 2`) equal parts with `1` part going to a `A` and `2` parts going to `B`. 94 | 95 | ## Horizontal Example 96 | 97 | Consider the layout: 98 | 99 | ![](/images/book/flex/horizontal-small.png) 100 | 101 | Where we want to `body` to grow: 102 | 103 | ![](/images/book/flex/horizontal-large.png) 104 | 105 | We can see intutively that it is a *horizontal* container with *content* sidebars and *flex* body. 106 | 107 | * root: `csstips.horizontal` 108 | * sidebar: `csstips.content` 109 | * body: `csstips.flex` 110 | * sidebar: `csstips.content` 111 | 112 | This example should have been fairly obvious and was designed to give you a hands on experience 🌹. Visually: 113 | 114 | ![](/images/book/flex/horizontal-solution.png) 115 | 116 | ## Arbitrary Layout 117 | Consider this layout: 118 | 119 | ``` 120 | ---------------------------------------------------- 121 | | HEADER | 122 | ---------------------------------------------------- 123 | | | | | 124 | | SIDEBAR | BODY | SIDEBAR | 125 | | | | | 126 | ---------------------------------------------------- 127 | | FOOTER | 128 | ---------------------------------------------------- 129 | ``` 130 | This is actually a layout used by lots of applications. If you think about it, its just a nesting of concepts you already know `csstips.vertical`,`csstips.horizontal`, `csstips.flex`, `csstips.content`. 131 | 132 | In fact its a combination of the first example layout (`csstips.vertical`): 133 | 134 | ``` 135 | ------------------------------------ 136 | | HEADER | 137 | ------------------------------------ 138 | | | 139 | | BODY1 | 140 | | | 141 | ------------------------------------ 142 | | FOOTER | 143 | ------------------------------------ 144 | ``` 145 | 146 | Where the `body1` is itself a `csstips.horizontal` containing the `sidebar`s and `body`: 147 | 148 | ``` 149 | ----------------------------------------------- 150 | | HEADER | 151 | ----------------------------------------------- 152 | | | | | 153 | | SIDEBAR | BODY | SIDEBAR | 154 | | | | | 155 | ----------------------------------------------- 156 | | FOOTER | 157 | ----------------------------------------------- 158 | ``` 159 | Easy right! Here is a demo for you to play with: 160 | 161 | ```play 162 | const bg = (color) => ({backgroundColor:color}); 163 | 164 |
165 |
166 | Header 167 |
168 |
169 |
170 | Sidebar 171 |
172 |
173 | Body 174 |
175 |
176 | Sidebar 177 |
178 |
179 |
180 | Footer 181 |
182 |
183 | ``` 184 | 185 | [](TODO: # Scrolling) 186 | 187 | [](TODO: # Scrolling a sub child) 188 | 189 | [](TODO Components: We've covered enough of layout to allow you to create basic layouts quite easily) 190 | 191 | [](TODO: Overlays) 192 | 193 | [](TODO: Menu) 194 | -------------------------------------------------------------------------------- /src/docs/intro.md: -------------------------------------------------------------------------------- 1 | Maintainable CSS is nearly impossible. But writing maintainable JavaScript is a fairly solved problem. Lets combine the two to make CSS maintainability an issue of the past. 2 | 3 | ![](/images/autocomplete.gif) 4 | 5 | The DOM `style` attribute is not enough to cater for things like pseudo states and media queries. We provide a simple *zero config* `style` function that gives you all the power of CSS with all the safety and maintainability of TypeScript / JavaScript. 6 | 7 | # Utilities 8 | 9 | TypeStyle core is actually **very** small (~6k gz) and you are free to use just that. However this guide and TypeStyle is designed to be approachable by both beginner and expert CSS devs. So we provide utilities that are mentioned next. 10 | 11 | # About `csx` 12 | Provided as a separate library (`npm install csx`): 13 | 14 | * helps you manage CSS properties e.g. **colors** and create typed CSS variables. 15 | 16 | # About `csstips` 17 | 18 | Provided as a separate library (`npm install csstips`). Provides utilities for pagesetup / CSS normalization along with mixins and mixin creators. 19 | 20 | * it essentially documents CSS tips that people might not be familiar with. 21 | * gives names to these CSS tips. 22 | 23 | # Installation 24 | 25 | Available easily via npm: 26 | 27 | ``` 28 | npm install typestyle --save 29 | ``` 30 | 31 | # Video Course 32 | We also have a free [video course based on the same content as this guide if you prefer to watch instead of reading][course]. 33 | 34 | [![](https://raw.githubusercontent.com/typestyle/typestyle.github.io/source/public/images/course.png)][course] 35 | 36 | [course]:https://egghead.io/courses/maintainable-css-using-typestyle 37 | -------------------------------------------------------------------------------- /src/docs/page.md: -------------------------------------------------------------------------------- 1 | There are very few simple things about the default layout of an HTML page that need to be setup in order to prepare it for the application development era: 2 | 3 | * Full Sized Body 4 | * Box Model 5 | 6 | ## Full Sized Body 7 | You really want the root of your page to be something that takes up all the available space on screen and provides it as a drawing canvas for children. This can be done easily: 8 | 9 | ```css 10 | html, body { 11 | height: 100%; 12 | width: 100%; 13 | padding: 0px; 14 | margin: 0px; 15 | } 16 | ``` 17 | 18 | ## Box Model 19 | You really want the `width`/`height` of an element to represent the `border+padding+content`. This is shown below: 20 | 21 | ![](/images/book/borderbox.png) 22 | 23 | It sounds something basic but the first HTML spec got wrong. Its easy to fix though: 24 | 25 | ```css 26 | html { 27 | box-sizing: border-box; 28 | } 29 | *, *:before, *:after { 30 | box-sizing: inherit; 31 | } 32 | ``` 33 | 34 | ## Root 35 | Finally with modern web frameworks you normally render to some container div. e.g. `root` in the following example: 36 | 37 | ```html 38 | 39 | 40 |
41 | 42 | 43 | 44 | ``` 45 | 46 | You probably want `#root` to be the same size as the body. Easily done by making its width / height `100%`. 47 | 48 | ## `setupPage` 49 | Combine all that we've talked about and we have the following page setup. 50 | 51 | ```html 52 | 53 | 54 |
55 | 56 | 57 | 58 | ``` 59 | 60 | ```ts 61 | import {setupPage} from "csstips"; 62 | setupPage('#root'); 63 | ``` 64 | 65 | * sets up box model 66 | * sets up `html/body` size 67 | * sets up root component size 68 | 69 | Easy as :) 70 | 71 | ## `normalize` 72 | Browsers have different default styles for a few a the native tags e.g. the `template` tag *does not have `display: none` in IE but has it in all other browers. The list of such quirks is small but not something that you should bother with. This is why [normalize.css] exists. It is used by all the popular css frameworks including bootstrap. So a good idea to include in your own designs as well. Super simple with `normalize` ;) 73 | 74 | ```ts 75 | import {normalize} from "csstips"; 76 | normalize(); 77 | 78 | // Yay. Browser quirks ironed out 79 | ``` 80 | 81 | ## Recommended page setup 82 | 83 | Hence the recommended page setup 84 | 85 | ```html 86 | 87 | 88 | 89 | 90 | 91 |
92 | 93 | 94 | 95 | ``` 96 | 97 | ```ts 98 | import {normalize, setupPage} from "csstips"; 99 | 100 | normalize(); 101 | setupPage('#root'); 102 | 103 | // All your designs will now be more consistent across browsers 104 | ``` 105 | 106 | Notes on some non `css` stuff in this page setup: 107 | * `` ensures that mobile browsers use their width to render the page. Otherwise mobiles render the page at desktop resolutions, forcing the user to zoom in to read text and then you get horizontal scrollbars. Its lovingly called the *responsive meta tag*. 108 | * Having the `script` after the root `div` ensures that it can be used from our JavaScript without needing something like `DOMContentLoaded` 109 | 110 | [normalize.css]:https://github.com/necolas/normalize.css 111 | -------------------------------------------------------------------------------- /src/docs/play.md: -------------------------------------------------------------------------------- 1 | We found the JavaScript playgrounds out there missing key features for a great TypeScript experience. 2 | 3 | > ProTip: You *can* use typestyle in playgrounds as we also provide a `umd` build: e.g. https://unpkg.com/typestyle/umd/typestyle.js example http://codepen.io/basarat/pen/GNeyzX 4 | 5 | Introducing the TypeStyle playground : [https://typestyle.github.io/play/](https://typestyle.github.io/play/). It supports *full* demo development with: 6 | 7 | * JavaScript in the form of TypeScript 8 | * HTML in the form of React/JSX 9 | * CSS in the form of TypeStyle 10 | 11 | Here is the hello world: 12 | 13 | ```play 14 |
15 | Hello world 16 |
17 | ``` 18 | 19 | ## Features 20 | 21 | ### Autocomplete 22 | 23 | `ctrl + space` 24 | 25 | ![](/images/book/play/autocomplete.png) 26 | 27 | ### Errors 28 | 29 | We do continuous linting (TypeScript is really just the world's most powerful JavaScript linter). The first error is shown inline so you don't need to use your mouse much. 30 | 31 | ![](/images/book/play/error.png) 32 | 33 | ## Sharing 34 | 35 | The url is always kept in sync with the code so you can share it around at any point you want. 36 | 37 | ## Compilation Context 38 | 39 | We put the following variables into the compilation context (and runtime) for you so that you don't need to do anything special 40 | 41 | * `typestyle`: The complete `"typestyle"` module 42 | * `style`, `keyframes`, `classes` from the `"typestyle"` module 43 | * `csstips` : The complete `"csstips"` module 44 | * `csx`: The complete `"typestyle/lib/csx"` module 45 | 46 | 47 | Additionally we have `React` in context as well as its used internally by any JSX (e.g. `
` becomes `React.createElement('div')`). You can even create your own React components 48 | 49 | ```play 50 | const HelloWorld = () =>
Hello World
; 51 | 52 | ``` 53 | 54 | ## Output 55 | 56 | The last JSX expression that gets evaluated in the code gets rendered to the output window. You are free to write any other TypeScript code before that if you want. 57 | 58 | ```play 59 | let message = "Hello World!"; 60 |

61 | {message} 62 |

63 | ``` 64 | 65 | ![](/images/book/play/full.png) 66 | 67 | ## Root 68 | 69 | > We cover more of `csx` / `csstips` and the concepts of mixins and flexbox and all that jazz in other sections of the book. This is just a quick look to demonstrate the playground. 70 | 71 | The output is rendered in a container that has `position: relative`. So you can attach to it with handy dandy `csx.fillParent` e.g. 72 | 73 | ```play 74 | const bg = (backgroundColor) => ({backgroundColor}); 75 | 76 |
77 | I fill the root 78 |
79 | ``` 80 | 81 | And if you want to play with flexbox just use `csstips.vertical` (or `csstips.horizontal`) wherever it makes sense e.g. here we show two flex children: 82 | 83 | ```play 84 | const bg = (backgroundColor) => ({backgroundColor}); 85 | 86 |
87 |
88 |
89 |
90 | ``` 91 | 92 | And another with three children showing a common header / body / footer layout: 93 | 94 | ```play 95 | const bg = (backgroundColor) => ({backgroundColor}); 96 | 97 |
98 |
99 | Header 100 |
101 |
102 | Body 103 |
104 |
105 | Footer 106 |
107 |
108 | ``` 109 | -------------------------------------------------------------------------------- /src/docs/raw.md: -------------------------------------------------------------------------------- 1 | * [cssRule](/#/raw/-cssrule-) 2 | * [cssRaw](/#/raw/-cssraw-) 3 | * [Advantages of these functions over loaders](/#/raw/advantages-of-these-functions-over-loaders) 4 | 5 | We understand that just classes are not sufficient for a full CSS in JS story as the generated dom is not always coming from your library, or in your control. 6 | 7 | For those scenarios we have two functions 8 | 9 | ## `cssRule` 10 | 11 | You can even use any raw CSS selector as well using `cssRule` e.g. 12 | 13 | * To setup a application style layout: 14 | 15 | ```ts 16 | /** Use full window size for application */ 17 | cssRule('html, body', { 18 | height: '100%', 19 | width: '100%', 20 | padding: 0, 21 | margin: 0 22 | }); 23 | ``` 24 | 25 | * Page level media queries: 26 | 27 | ```ts 28 | /** Save ink with a white background */ 29 | cssRule('@media print', { 30 | body: { 31 | background: 'white' 32 | } 33 | }); 34 | ``` 35 | 36 | 37 | ## `fontFace` 38 | 39 | Specifying fonts has a special function for readability: 40 | 41 | ```ts 42 | import { fontFace } from 'typestyle'; 43 | 44 | fontFace({ 45 | fontFamily: 'Roboto', 46 | fontStyle: 'normal', 47 | fontWeight: 400, 48 | src: /* url here */'', 49 | unicodeRange: 'U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F' 50 | }); 51 | ``` 52 | 53 | * Fonts can also be specified using the above ```cssRule``` using the @font-face rule. 54 | 55 | ```ts 56 | cssRule('@font-face', { 57 | fontFamily: '"Bitstream Vera Serif Bold"', 58 | src: 'url("https://mdn.mozillademos.org/files/2468/VeraSeBd.ttf")' 59 | }); 60 | ``` 61 | 62 | ## `cssRaw` 63 | 64 | Sometimes someone just hands you are raw CSS file and you can't be bothered to make it into objects. An excellent use case is using something like `normalize.css`. 65 | 66 | ```ts 67 | import {cssRaw} from "typestyle" 68 | 69 | /** Sample rule from normalize.css */ 70 | cssRaw(` 71 | /** 72 | * Correct the font size and margin on h1 elements within section and 73 | * article contexts in Chrome, Firefox, and Safari. 74 | */ 75 | h1 { 76 | font-size: 2em; 77 | margin: 0.67em 0; 78 | } 79 | `) 80 | ``` 81 | 82 | > Protip `csstips.normalize()` uses this function internally to actually add `normalize.css`. More on this later. 83 | 84 | ## Advantages of these functions over loaders 85 | 86 | * Works seemlessly in a nodejs enviroment (example use cases are nodejs testing / server side rendering) whereas `require('./someCss.css')` does not without additional setup. 87 | * You still get to use JS and share variables with JS (using template strings!). 88 | * With `cssRule` you can also use all our (and your!) fancy mixins. 89 | 90 | Great. That covers the key TypeStyle API surface. -------------------------------------------------------------------------------- /src/docs/reviews.md: -------------------------------------------------------------------------------- 1 | 2 | * [I haven't been this giggly-happy about a library in a long time!!!](https://twitter.com/andrestaltz/status/788665551325454337) 3 | * [A type-safe CSS-in-JS library like no other - TypeStyle](https://twitter.com/areknawo/status/1135643283688964096) / [Blog post](https://areknawo.com/a-different-approach-to-css-in-js/) 4 | * [Rad idea!](https://twitter.com/iammerrick/status/788784672314576897) 5 | * [What a fantastic #css resource](https://twitter.com/zbrianw/status/792177209490313217) 6 | * [This is the CSS framework I have wanted to exist for the past 5 years.](https://twitter.com/jeffwhelpley/status/802275885353054208) 7 | * [Relief! CSS w/ Type Safety!](https://twitter.com/wwwalkerrun/status/802329604471959552) 8 | * [This is seriously cool stuff.](https://twitter.com/jonbrennecke/status/802351164247265280) 9 | * [YES! That's freaking awesome!](https://twitter.com/brechtbilliet/status/802385818295746560) 10 | * [This is an example of both types and linting being enforced by the same systems as the rest of your code](https://twitter.com/rauchg/status/802706919823581185) 11 | * [Nice approach to have your #css in order and using #typescript](https://twitter.com/chris_noring/status/802824657401475073) 12 | * [Try out #TypeStyle which uses #Typescript to generate your styles!](https://twitter.com/designpuddle/status/802658082924937216) 13 | * [The IDE abilities of TypeScript and TypeStyle are appealing for a larger component based design system](https://twitter.com/twnsndco/status/804081260633214977) 14 | * [I just discovered typestyle. It's awesome. 👍](https://twitter.com/arnarbirgisson/status/805805142289293314) 15 | * [The future of CSS for TypeScript powered applications #TypeStyle](https://twitter.com/piotrekwitek/status/817655604206596096) 16 | * [I really love #TypeStyle. So agnostic, so small, so out of your way. Css in JS that just works.](https://twitter.com/nickbalestra/status/818393513847037953) 17 | * [#typescript + #reactjs + #typestyle is just a thing of beauty, it's like developer ambrosia](https://twitter.com/deis/status/823404962323505152) 18 | * [I just came across TypeStyle ... I can't praise the library enough! Ultimate typesafe CSS solution](https://twitter.com/erikcupal/status/823700902578163712) 19 | * [Holy cow! TypeStyle combines #TypeScript with #CSS for maintainable styles! 😍](https://twitter.com/timo_ernst/status/823262762596007937) 20 | * [Started building a UI library for our platform @CyclePlatform with #typestyle. Absolute pleasure so far, thanks for the great work.](https://twitter.com/alexmattoni/status/839961368375590912) 21 | * [TypeStyle by @basarat is a fantastic #CSS in #javascript lib. Effectively CSS modules wrapped in JS/TS and excellent DX.](https://twitter.com/SteveALee/status/858255868139319296) 22 | * [TypeStyle by @notoriousb1t and @basarat is the perfect CSS-in-TS library. Small, elegant and strongly typed ;)](https://twitter.com/ale_burato/status/899165879849684992) 23 | * [TypeStyle looks like the typed CSS-in-JS solution I was looking for. If you can spare 30 minutes, do yourself a favor and watch the videos. Great work!](https://twitter.com/frontsideair/status/1019491595224276992) 24 | * [Last time I used CSS modules I got frustrated by the lack of type safety - lately I've been using (and loving) TypeStyle](https://twitter.com/muelvenables/status/1119369238286725120) 25 | 26 | * [Add yours 🌹!](https://twitter.com/intent/tweet?text=Super%20simple%20maintainable%20%23CSS%20with%20%23TypeStyle%3A%20https%3A%2F%2Ftypestyle.github.io%0A%0A%23JavaScript%20%23TypeScript%20%40basarat%20%F0%9F%8C%B9) 27 | 28 | # Samples 29 | 30 | TypeStyle is simply just a `style` function with a handful of *simple* utilities that we cover nicely in the rest of the book. So don't need another hello world here. That said, the best samples are real world usages. Here are a few: 31 | 32 | * [http://matrixmultiplication.xyz/](http://matrixmultiplication.xyz/) Built with TypeStyle / CycleJS / TypeScript 33 | * [This website, with docs](https://github.com/typestyle/typestyle.github.io) as well as [the playground](https://typestyle.github.io/play) are all built 100% with typestyle. 34 | * [alm.tools](http://alm.tools) and IDE designed to provide the best developer experience for TypeScript. 35 | * [https://wmaurer.github.io/cyclejs-fractals/](https://wmaurer.github.io/cyclejs-fractals) Built with TypeStyle / CycleJS / TypeScript 36 | -------------------------------------------------------------------------------- /src/docs/server.md: -------------------------------------------------------------------------------- 1 | Sever side rendering + static page generations is supported with a super easy function. It may not be a feature you need so you can safely skip reading this section. 2 | 3 | ## `getStyles` 4 | It allows you to get the styles as CSS which you can return as a part of your server response. e.g. 5 | 6 | ```ts 7 | /** Import */ 8 | import {style, getStyles} from "typestyle"; 9 | 10 | /** convert a style object to a CSS class name */ 11 | const className = style({color: 'red'}); 12 | 13 | /** Render to CSS style tag */ 14 | const styleTag = `` 15 | /** ^ send this as a part of your HTML response */ 16 | ``` 17 | 18 | Here is a more *fancy* example function that uses React: 19 | 20 | ```ts 21 | export const renderPage = ({ html, css }: { html: string, css: string }) => ` 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 |
${html}
32 | 33 | 34 | 35 | `; 36 | 37 | import {getStyles} from "typestyle"; 38 | import {App} from "./yourApp"; 39 | 40 | const response = renderPage({ 41 | html: ReactDOMServer.renderToString(), 42 | css: getStyles() 43 | }); 44 | 45 | /** ^ send this as your HTML response */ 46 | ``` 47 | 48 | Then in the frontend you simply use the same style tag after rendering the html: 49 | 50 | ```ts 51 | import {setStylesTarget} from "typestyle"; 52 | ReactDOM.render(, document.getElementById('root')); 53 | setStylesTarget(document.getElementById('styles-target')); 54 | ``` -------------------------------------------------------------------------------- /src/docs/utilities.md: -------------------------------------------------------------------------------- 1 | TypeStyle (csx) includes utilities that make using units and css functions feel natural and flow in JavaScript. 2 | 3 | > Values from these `csx` utilities need to be `toString`ed before being used in TypeStyle or any other library. This means that they can be used with or without TypeStyle 🌹 4 | 5 | ## Unit utilities 6 | 7 | ### em(value: number): string 8 | Returns the number followed by the em unit 9 | 10 | ```typescript 11 | import { em } from 'csx'; 12 | 13 | // outputs '0.5em' 14 | const width = em(.5); 15 | ``` 16 | 17 | ### percent(value: number): string 18 | Returns the number followed by the % unit 19 | 20 | ```typescript 21 | import { percent } from 'csx'; 22 | 23 | // outputs '50%' 24 | const width = percent(50); 25 | ``` 26 | 27 | ### px(value: number): string 28 | Returns the number followed by the px unit 29 | 30 | ```typescript 31 | import { px } from 'csx'; 32 | 33 | // outputs '42px' 34 | const width = px(42); 35 | ``` 36 | 37 | ### rad(value: number): string 38 | Returns the number followed by the rad unit 39 | 40 | ```typescript 41 | import { rad } from 'csx'; 42 | 43 | // outputs '20rad' 44 | const rotation = rad(20); 45 | ``` 46 | 47 | ### rem(value: number): string 48 | Returns the number followed by the rem unit 49 | 50 | ```typescript 51 | import { rem } from 'csx'; 52 | 53 | // outputs '1.5rem' 54 | const width = rem(1.5); 55 | ``` 56 | 57 | ### viewHeight(value: number): string 58 | Returns the number followed by the vh (view height) unit 59 | 60 | ```typescript 61 | import { viewHeight } from 'csx'; 62 | 63 | // outputs '25vh' 64 | const height = viewHeight(100/4); 65 | ``` 66 | 67 | ### viewWidth(value: number): string 68 | Returns the number followed by the vw (view width) unit 69 | 70 | ```typescript 71 | import { viewWidth } from 'csx'; 72 | 73 | // outputs '85vw' 74 | const width = viewWidth(85); 75 | ``` 76 | 77 | ### turn(value: number): string 78 | Returns the number followed by the turn unit 79 | 80 | ```typescript 81 | import { turn } from 'csx'; 82 | 83 | // outputs '1turn' 84 | const rotation = turn(1); 85 | ``` 86 | 87 | 88 | ## Function utilities 89 | 90 | ### important(value: string): string 91 | 92 | Returns the string followed by !important 93 | 94 | ```typescript 95 | import { important } from 'csx'; 96 | 97 | // outputs 'red !important' 98 | const content = important('red'); 99 | ``` 100 | 101 | ### quote(value: string): string 102 | 103 | Returns the string wrapped by quote(). Single quotes in the value are escaped 104 | 105 | ```typescript 106 | import { quote } from 'csx'; 107 | 108 | // outputs "quote('&')" 109 | const content = quote('&'); 110 | 111 | // outputs "quote('It\'s the cat\'s pajamas')" 112 | const content1 = quote("It's the cat's pajamas"); 113 | ``` 114 | 115 | ### url(value: string): string 116 | 117 | Returns the url wrapped by url() 118 | 119 | ```typescript 120 | import { url } from 'csx'; 121 | 122 | // outputs "url(images/my-background.png)" 123 | const imageLocation = url('images/my-background.png'); 124 | ``` -------------------------------------------------------------------------------- /src/docs/why.md: -------------------------------------------------------------------------------- 1 | The biggest reason is developer happiness from not having to manage too many files that are intrinsically linked to each other and not having to use different tools to get the *single* job done 🌹. 2 | 3 | But of course, there are lots of other reasons as well and we present a few below. 4 | 5 | # Comparison to other options 6 | 7 | There are a lot of other CSS in JS frameworks out there. In the past we used and experimented with quite a few. Some quick reasons why we wrote our own. 8 | 9 | * We are focused on Autocomplete / *Compile* time error analysis 10 | * None of them had this out of the box. 11 | * Not all APIs are statically analyzable e.g. if the API is powered by template strings, the CSS portion is essentially not analyzed at all. 12 | * Some forced you to use a custom AST transforms 13 | * Would be fine if custom ASTs came with IDE support + static analysis. It doesn't. 14 | * Many others are framework specific e.g. React specific 15 | * Some force you to rethink / wrap your component. Didn't want that. 16 | * They make upgrading your frontend framework harder as you need for them to update their wrapper first. 17 | * Many others try to solve problem with JS events instead of generating CSS. 18 | * This can result in issues when a JS event is absent, e.g. [a stuck `:hover`](https://goo.gl/e5tUyt) 19 | * Can be significantly slower in real world usage as CSS does a faster job of changing quick styles than events. 20 | * The number of issues reported on libraries that use JS events is generally too high for comfort. 21 | * Generally framework specific and that has problems we've mentioned before. 22 | * They also tend to change `style` instead of writing CSS which makes using devtools with immediate feedback (e.g. chrome dev tools) harder. 23 | * CSS Modules : Not CSS in JS. Just solves namespacing. 24 | * Most the other CSS managment problems still exist. We are essentially CSS modules, if CSS modules were written in JS. 25 | * Super small core size (~6k gz). 26 | * We are just putting a type system + encapsulation / maintainability (no globals) on CSS. 27 | 28 | Of course we would not exist without their previous and continued hard work and public idea exchange 🌹 29 | 30 | ## Concept: Hashed className 31 | 32 | CSS class names (e.g. `.btn`) suffer from global conflicts. 33 | 34 | * This makes integrating with third party libraries hard. Each library must follow a convention to prevent mistakes e.g. `bootstrap-btn` but *most libraries* don't and camp on global names. 35 | * This makes even writing maintainable CSS *in your project* hard and you need to treat it as *global* e.g. if you want to create a class for buttons in two components e.g. date picker and a file dropzone you must have `.mine-datepicker-button` and `.mine-dropzone-button` to prevent mistaken conflicts. With JS/TS you would probably have seperate `.js` files (e.g. `datePicker.js` and `dropzone.js`) and each can have a local variable `buttonClass` without any conflicts. You don't even need to export these to the outside world. 36 | 37 | However due to limitations of CSS we still need to generate a CSS file to allow you to use features like hover states and media queries. Hence we generate a className based on the content for you: 38 | 39 | ```play 40 | import {style} from 'typestyle'; 41 | 42 | const myColorClass = style({color:'red'}); 43 | 44 |
The generated class name: {myColorClass}
; 45 | ``` 46 | 47 | This gives you: 48 | 49 | * Isolation: No globals! 50 | 51 | ## Concept: Dead CSS 52 | 53 | Determining the impact of a CSS className on your codebase is one of the hardest problems facing frontend maintainability. 54 | 55 | Having the styles managed in JS (especially with TypeScript) gives you the following immediate gains: 56 | 57 | * You can immediately see all the places where a class is used (e.g find references in TypeScript). This also gives you the *impact* footprint of a CSS class. 58 | * Refactor class names easily, especially true with TypeScript (e.g. from `fooClass` to `barClass`. You no longer need to be afraid to touch your CSS class names). 59 | * Remove CSS classes that are no longer used easily (e.g. switch on `noUnusedLocals` in TypeScript). 60 | * Delete a TS file containing CSS classNames. If it's used, you get a nice compiler error which you can fix easily (same way you fix / remove unused JS code). Next go out and party 🎉. 61 | * Based on how *all module loaders work* (including webpack/tsify/rollup) if a file isn't *required*, it doesn't become a part of the bundle. So their CSS also goes away *automatically*. 62 | * With fancy tree shaking module loaders (like rollup/webpack2) if a variable isn't used, it's removed from the bundle. So even without `noUnusedLocals`, the CSS bound to these variables (e.g. `const fooUnused = style({color:'red'})`) goes away. 63 | 64 | ## Vendor Prefixing 65 | 66 | Note: We don't do *automatic* vendor prefixing for a few reasons: 67 | 68 | * Code bloat, runtime performance, you might want more control (we don't make choices that you might need to undo). 69 | * Vendor prefixing has no future: https://webkit.org/blog/6131/updating-our-prefixing-policy/ 70 | 71 | > Protip: Big fan of flexbox? Use `csstips` as it provides the necessary vendor prefixes so you don't need to worry about them. 72 | 73 | ## More boring reasons 74 | 75 | Beyond that here is a boring list of additional reasons to use TypeStyle. 76 | 77 | * No context switching your brain (think it's worth mentioning again). 78 | * Built in dependency system same as the rest of your JS. No special code for CSS needed. 79 | * Ship CSS in the same channel that you ship JS, with no special configuration being required by your library user. 80 | * Minification (Minify JS with existing tools). The CSS we generate is already nearly whitespace free. 81 | * Shared constants and reusable styles (Using variables and objects) 82 | * Extensible (Just use JavaScript with all its power) 83 | * Your components are still free to have class names that you can give to external people to further style your stuff (better still take `className` as a property and let them use *typestyle* too!). 84 | * Develop components alongside the style (No more hunting CSS files for estranged `ul > li > a`) 85 | * Create isomorphic applications (easy export to a CSS file is supported) 86 | * All the power of CSS without compromise e.g. pseudo states (e.g. `&:hover`) 87 | * Better than CSS, management of media queries i.e. *nested class driven* media quries 88 | * Fallback CSS properties using arrays (`{ backgroundColor: ['red', 'linear-gradient(to right, red 0%, blue 100%)'] }`) 89 | * Super small core size (~1k). 90 | * Extremely small and powerful API that works with any ecosystem. 91 | * Provides great TypeScript developer autocomplete experience. 92 | * No custom AST transform or module loader support needed. 93 | * Works with any framework (react, angular2, cyclejs, whatever, doesn't matter). 94 | * Zero config. Just use. 95 | 96 | > Note: Many of these are truly the advantages of using FreeStyle. The additional features by typestyle are *autoinjection*, *`types`* (for autocomplete and errors), `csx` (CSSFunctions and Typed CSS value creators) and *csstips* (a great set of CSS mixins / page setup helpers to give a smooth learning curve for even new CSS devs). 97 | -------------------------------------------------------------------------------- /src/play.tsx: -------------------------------------------------------------------------------- 1 | /** Setup es6 */ 2 | import 'babel-polyfill'; 3 | 4 | import { setupPage, normalize } from 'csstips'; 5 | import * as csstips from 'csstips'; 6 | import { style, cssRule, media } from 'typestyle'; 7 | normalize(); 8 | setupPage('#root'); 9 | 10 | /** 11 | * Clear default margins from all things 12 | * I wouldn't do this in a production site. But doing it to make teaching easier 13 | */ 14 | cssRule('h1,h2,h3,h4,h5,h6,h7,h8,p', { 15 | margin: '0px' 16 | }); 17 | 18 | import * as React from 'react'; 19 | import * as ReactDOM from 'react-dom'; 20 | import * as cp from './components'; 21 | import { CodeEditor } from './play/codeEditor/codeEditor'; 22 | import { observable, action, computed } from 'mobx'; 23 | import { observer } from 'mobx-react'; 24 | import { debounce } from './utils'; 25 | import * as ts from 'byots'; 26 | import * as ps from './play/projectService'; 27 | import { demoState } from './play/playState'; 28 | import { CodeOutput } from './play/codeOutput/codeOutput'; 29 | import * as gls from './components/gls'; 30 | 31 | @observer 32 | export class HeaderSmall extends React.Component<{}, {}> { 33 | render() { 34 | return ( 35 |
36 | 37 | 41 |

# TypeStyle 🌹

42 |
43 | 44 |
45 | 46 | 50 |

Help

51 |
52 | 53 | 54 | 55 | 56 | 57 |
58 | ); 59 | } 60 | } 61 | 62 | 63 | /** 64 | * Provides a nice demo / test component 65 | */ 66 | @observer 67 | export class Demo extends React.Component<{}, {}> { 68 | render() { 69 | const spacing = 10; 70 | 71 | return 72 | {/** code */} 73 | 74 | demoState.setCode(value))} 78 | onCodeEdit={(codeEdit) => demoState.onCodeEdit(codeEdit)} /> 79 | 80 | 81 | {/** output */} 82 | 83 | 84 | 85 | 86 | } 87 | } 88 | 89 | ReactDOM.render(
90 | 91 | 92 |
, document.getElementById('root')); -------------------------------------------------------------------------------- /src/play/codeEditor/addons/autocomplete/autocomplete.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Class names pulled from show-hint.css 3 | * commented out sections present before the *customizations* comment are sections we commented out of show-hint 4 | */ 5 | 6 | .CodeMirror-hints { 7 | /** Straight from show-hint.css */ 8 | position: absolute; 9 | z-index: 10; 10 | overflow: hidden; 11 | list-style: none; 12 | 13 | margin: 0; 14 | padding: 2px; 15 | 16 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 17 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 18 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 19 | /* border-radius: 3px; */ 20 | /* border: 1px solid silver; */ 21 | 22 | background: white; 23 | font-size: 90%; 24 | 25 | 26 | max-height: 20em; 27 | overflow-y: auto; 28 | 29 | /** customizations */ 30 | 31 | /** theoretically `display:table` is needed ... but as soon as we add it height no longer works (css limitation) 32 | * But since table-row table-cell children work even without this ... lets do that :) 33 | */ 34 | /** display: table; */ 35 | background: #272822; 36 | border: 1px solid #333; 37 | 38 | /** Better font */ 39 | font-family: consolas, menlo, monospace; 40 | 41 | /** Don't want scroll bars showing up on page */ 42 | max-height: 60%; 43 | } 44 | 45 | .CodeMirror-hint { 46 | margin: 0; 47 | padding: 0 4px; 48 | /* border-radius: 2px;*/ 49 | max-width: 19em; 50 | white-space: pre; 51 | color: black; 52 | cursor: pointer; 53 | 54 | /** customizations */ 55 | display: table-row; 56 | transition: color .2s cubic-bezier(0.23, 1, 0.32, 1); 57 | color: white; 58 | } 59 | 60 | li.CodeMirror-hint-active { 61 | background: #08f; 62 | color: white; 63 | 64 | /** customizations */ 65 | 66 | } 67 | 68 | 69 | /** 70 | * Everything below is custom 🌹 71 | * custom classes in our hints 72 | */ 73 | 74 | .cm-hint.left-left { 75 | padding: 2px 5px; 76 | display: table-cell; 77 | transition: color .2s cubic-bezier(0.23, 1, 0.32, 1); 78 | font-size: 8px; 79 | 80 | /* copy from font awesome */ 81 | font: normal normal normal 10px/1 FontAwesome; 82 | text-rendering: auto; 83 | -webkit-font-smoothing: antialiased; 84 | -moz-osx-font-smoothing: grayscale; 85 | } 86 | .CodeMirror-hint-active .cm-hint.left-left { 87 | color: white !important; 88 | } 89 | 90 | .cm-hint.left { 91 | color: #666; 92 | 93 | display: table-cell; 94 | padding-right: 4px; 95 | padding-left: 4px; 96 | 97 | transition: color .2s cubic-bezier(0.23, 1, 0.32, 1); 98 | 99 | /* Because of using table - row on parent spacing between items needs to come from here as well */ 100 | padding-top: 2px; 101 | padding-bottom: 2px; 102 | } 103 | .CodeMirror-hint-active .cm-hint.left { 104 | color: white !important; 105 | } 106 | 107 | .cm-hint.main { 108 | display: table-cell; 109 | 110 | padding-right: 4px; 111 | padding-left: 4px; 112 | } 113 | .CodeMirror-hint-active .cm-hint.main { 114 | } 115 | 116 | .cm-hint.right { 117 | display: table-cell; 118 | 119 | padding-right: 4px; 120 | padding-left: 4px; 121 | 122 | max-width: 200px; 123 | 124 | font-size: 12px; 125 | color: #D1D1D1; 126 | 127 | overflow: hidden; 128 | text-overflow: ellipsis; 129 | } 130 | .CodeMirror-hint-active .cm-hint.right { 131 | } 132 | 133 | 134 | /** 135 | * Code Mirror Documentation classes based on tern.css 136 | */ 137 | /* original */ 138 | .CodeMirror-Tern-tooltip { 139 | border: 1px solid silver; 140 | border-radius: 3px; 141 | color: #444; 142 | padding: 2px 5px; 143 | font-size: 90%; 144 | font-family: monospace; 145 | background-color: white; 146 | white-space: pre-wrap; 147 | 148 | max-width: 40em; 149 | position: absolute; 150 | z-index: 10; 151 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 152 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 153 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 154 | 155 | transition: opacity 1s; 156 | -moz-transition: opacity 1s; 157 | -webkit-transition: opacity 1s; 158 | -o-transition: opacity 1s; 159 | -ms-transition: opacity 1s; 160 | } 161 | .CodeMirror-Tern-hint-doc { 162 | max-width: 25em; 163 | margin-top: -3px; 164 | } 165 | /* extensions */ 166 | .CodeMirror-Tern-tooltip { 167 | background-color: #333; 168 | color: #DDD; 169 | border: 2px solid white; 170 | } -------------------------------------------------------------------------------- /src/play/codeEditor/addons/autocomplete/autocomplete.ts: -------------------------------------------------------------------------------- 1 | import * as ps from '../../../projectService'; 2 | import * as templates from './templates'; 3 | import CodeMirror = require('codemirror'); 4 | require('codemirror/addon/hint/show-hint'); 5 | require('codemirror/addon/hint/javascript-hint'); 6 | 7 | import { ExtendedCodeMirrorHint, render, isCompletionActive } from "./autocompleteShared"; 8 | import { debounce, createMap, throttle } from '../../../../utils'; 9 | import { cssRaw } from 'typestyle'; 10 | 11 | cssRaw(require('./autocomplete.css')); 12 | 13 | 14 | /** Enable showhint for this code mirror */ 15 | export function setupOptions(cmOptions: any, filePath: string) { 16 | cmOptions.showHint = true; 17 | cmOptions.hintOptions = { 18 | completeOnSingleClick: true, // User can click on the item to select it :) 19 | completeSingle: false, // Don't compelete the last item 20 | hint: new AutoCompleter(filePath).hint, 21 | }; 22 | 23 | // Debugging 24 | cmOptions.hintOptions.closeOnUnfocus = false; // DEBUG 25 | } 26 | 27 | /** Mostly make completions more aggressive */ 28 | export function setupCodeMirror(cm: CodeMirror.EditorFromTextArea) { 29 | let timeout: any; 30 | 31 | // Don't be aggresive on these ending characters 32 | let ignoreEnds = createMap([ 33 | ';', ',', 34 | ')', 35 | '`', '"', "'", 36 | "{", "}", "[", "]", 37 | " " 38 | ]); 39 | 40 | (cm as any).on("inputRead", function(ed: any, change: CodeMirror.EditorChange) { 41 | let editor: CodeMirror.EditorFromTextArea = ed; 42 | 43 | /** Very important to clear any pending request */ 44 | if (timeout) { 45 | clearTimeout(timeout); 46 | } 47 | 48 | /** only on user input (e.g. exclude `cut`) */ 49 | if (change.origin !== '+input') { 50 | return; 51 | } 52 | 53 | if (change && change.text && ignoreEnds[change.text.join('')]) { 54 | return; 55 | } 56 | 57 | timeout = setTimeout(function() { 58 | // if a completion is already active ... then cm will call us anyways :) 59 | if (isCompletionActive(editor)) { 60 | // For some reason it doesn't for `.` 61 | // Suspect its because `token` changes and therefore show-hint hides it ¯\_(ツ)_/¯ 62 | let cur = editor.getDoc().getCursor(); 63 | if (editor.getTokenAt(cur).string !== '.') { 64 | return; 65 | } 66 | } 67 | (CodeMirror as any).showHint(cm as any); 68 | }, 150); 69 | }); 70 | 71 | /** We don't get input reads on `(` for some reason */ 72 | (cm as any).on('keyup', function(ed: any, evt: KeyboardEvent) { 73 | if (evt.which === 57 && !isCompletionActive(ed)) { 74 | (CodeMirror as any).showHint(cm as any); 75 | } 76 | }); 77 | } 78 | 79 | export class AutoCompleter { 80 | /** if not the last request ... don't show results */ 81 | lastRequest: number; 82 | 83 | constructor(public filePath: string) { 84 | // Make hint async 85 | (this.hint as any).async = true; 86 | } 87 | 88 | hint = throttle((editor: CodeMirror.EditorFromTextArea, cb: (hints: CM.Hints) => void, options): void => { 89 | 90 | // options is just a copy of the `hintOptions` with defaults added 91 | // So do something fancy with the Editor 92 | // console.log(ed,options); 93 | // console.log(options); 94 | 95 | let cur = editor.getDoc().getCursor(); 96 | let token = editor.getTokenAt(cur); 97 | let prefix = token.string; 98 | let position = editor.getDoc().indexFromPos(cur); 99 | 100 | /** For various reasons if we don't want to return completions */ 101 | let noCompletions: CM.Hints = null; 102 | 103 | 104 | /** Purely designed to attach the render function + any information the render function needs */ 105 | function completionToCodeMirrorHint(completion: Completion, queryString: string): ExtendedCodeMirrorHint { 106 | let result: ExtendedCodeMirrorHint = { 107 | text: completion.name, 108 | render: render, 109 | comment: completion.comment, 110 | 111 | // Information the render function needs 112 | original: completion, 113 | queryString 114 | } 115 | return result; 116 | } 117 | 118 | // This code is based on http://codemirror.net/addon/tern/tern.js `hint` function 119 | function setupCompletionDocs(obj: T): T { 120 | var cls = "CodeMirror-Tern-"; // this was a global var that I pull *in* 121 | 122 | var tooltip = null; 123 | CodeMirror.on(obj, "close", function() { remove(tooltip); }); 124 | CodeMirror.on(obj, "update", function() { remove(tooltip); }); 125 | CodeMirror.on(obj, "select", function(cur: ExtendedCodeMirrorHint, node) { 126 | remove(tooltip); 127 | var content = cur.comment; 128 | if (content) { 129 | tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset, 130 | node.getBoundingClientRect().top + window.pageYOffset, content); 131 | tooltip.className += " " + cls + "hint-doc"; 132 | } 133 | }); 134 | return obj; 135 | 136 | // pulled in these functions as they were it is 137 | function remove(node) { 138 | var p = node && node.parentNode; 139 | if (p) p.removeChild(node); 140 | } 141 | function makeTooltip(x, y, content) { 142 | var node = (elt as any)("div", cls + "tooltip", content); 143 | node.style.left = x + "px"; 144 | node.style.top = y + "px"; 145 | document.body.appendChild(node); 146 | return node; 147 | } 148 | function elt(tagname, cls /*, ... elts*/) { 149 | var e = document.createElement(tagname); 150 | if (cls) e.className = cls; 151 | for (var i = 2; i < arguments.length; ++i) { 152 | var elt = arguments[i]; 153 | if (typeof elt == "string") elt = document.createTextNode(elt); 154 | e.appendChild(elt); 155 | } 156 | return e; 157 | } 158 | } 159 | 160 | // if in active project or a supported config file 161 | 162 | this.lastRequest = position; 163 | const res = ps.getCompletionsAtPosition({ filePath: this.filePath, position, prefix }) 164 | if (this.lastRequest !== position || !editor.hasFocus()) { 165 | cb(null); 166 | return; 167 | } 168 | 169 | let from = { line: cur.line, ch: token.start }; 170 | let to = { line: cur.line, ch: token.start + prefix.length }; 171 | 172 | // Don't eat these characters ! 173 | // (our projectService completions come back without these) 174 | if (token.string == '.' || token.string == '{') { 175 | from = to; 176 | } 177 | 178 | let completionInfo = { 179 | from, 180 | to, 181 | list: res.completions.filter(x => !x.snippet).map(c => completionToCodeMirrorHint(c, token.string)) 182 | }; 183 | 184 | // Function completion snippets 185 | const functionCompletionSnippets = res.completions.filter(x => !!x.snippet).map(x => { 186 | const template = new templates.Template({ 187 | name: x.snippet.name, 188 | description: x.snippet.description, 189 | template: x.snippet.template, 190 | functionCompletion: prefix && prefix == '(', 191 | }); 192 | return template 193 | }); 194 | const functionCompletionSnippetsRendered = templates.renderTemplates(editor, functionCompletionSnippets); 195 | 196 | setupCompletionDocs(completionInfo); 197 | 198 | cb(completionInfo); 199 | 200 | return; 201 | }, 500); 202 | 203 | } -------------------------------------------------------------------------------- /src/play/codeEditor/addons/autocomplete/autocompleteShared.tsx: -------------------------------------------------------------------------------- 1 | import { createMap } from '../../../../utils'; 2 | import { match, filter as fuzzyFilter } from "fuzzaldrin"; 3 | import * as CodeMirror from 'codemirror'; 4 | 5 | /** 6 | * Based on https://github.com/atom/fuzzy-finder/blob/51f1f2415ecbfab785596825a011c1d2fa2658d3/lib/fuzzy-finder-view.coffee#L56-L74 7 | */ 8 | export function renderMatchedSegments(result: string, query: string): JSX.Element[] { 9 | // A data structure which is efficient to render 10 | type MatchedSegments = { str: string, matched: boolean }[]; 11 | 12 | // local function that creates the *matched segment* data structure 13 | function getMatchedSegments(result: string, query: string) { 14 | let matches = match(result, query); 15 | let matchMap = createMap(matches); 16 | // collapse contiguous sections into a single `` 17 | let currentUnmatchedCharacters = []; 18 | let currentMatchedCharacters = []; 19 | let combined: MatchedSegments = []; 20 | function closeOffUnmatched() { 21 | if (currentUnmatchedCharacters.length) { 22 | combined.push({ str: currentUnmatchedCharacters.join(''), matched: false }); 23 | currentUnmatchedCharacters = []; 24 | } 25 | } 26 | function closeOffMatched() { 27 | if (currentMatchedCharacters.length) { 28 | combined.push({ str: currentMatchedCharacters.join(''), matched: true }); 29 | currentMatchedCharacters = []; 30 | } 31 | } 32 | result.split('').forEach((c, i) => { 33 | let isMatched = matchMap[i]; 34 | if (isMatched) { 35 | if (currentMatchedCharacters.length) { 36 | currentMatchedCharacters.push(c); 37 | } 38 | else { 39 | currentMatchedCharacters = [c] 40 | // close off any unmatched characters 41 | closeOffUnmatched(); 42 | } 43 | } 44 | else { 45 | if (currentUnmatchedCharacters.length) { 46 | currentUnmatchedCharacters.push(c); 47 | } 48 | else { 49 | currentUnmatchedCharacters = [c] 50 | // close off any matched characters 51 | closeOffMatched(); 52 | } 53 | } 54 | }); 55 | closeOffMatched(); 56 | closeOffUnmatched(); 57 | return combined; 58 | } 59 | 60 | /** 61 | * Rendering the matched segment data structure is trivial 62 | */ 63 | let matched = getMatchedSegments(result, query); 64 | let matchedStyle = {fontWeight:'bold', color:'#66d9ef'}; 65 | return matched.map((item, i) => { 66 | return {item.str}; 67 | }); 68 | } 69 | 70 | /** 71 | * Exists to allow us to pass throught the `original` information around. 72 | * Code mirror insists on using `CodeMirror.Hint` so we use that 73 | * But then we put the good stuff in `original` and use it in `render` and `complete` and `move` etc 74 | */ 75 | export interface ExtendedCodeMirrorHint extends CM.Hint { 76 | original?: Completion; 77 | /** For rending matched segments */ 78 | queryString?: string; 79 | comment?: string; 80 | template?: any; 81 | data?: CM.Hints; 82 | } 83 | 84 | /** 85 | * For highlighting matched segments 86 | */ 87 | import * as ReactServer from "react-dom/server"; 88 | import * as React from "react"; 89 | 90 | /** 91 | * Key strokes can have different effect based on this state 92 | * So moved this check out into a utility 93 | */ 94 | export function isCompletionActive(ed: CodeMirror.Editor): boolean { 95 | return !!(ed as any).state.completionActive; 96 | } 97 | 98 | /** 99 | * A common shared render function 100 | */ 101 | 102 | /** 103 | * General utility for consistent coloring 104 | */ 105 | export function kindToColor(kind: string, lighten = false) { 106 | let add = lighten ? 50 : 0; 107 | let opacity = lighten ? 0.2 : 1; 108 | switch (kind) { 109 | case ts.ScriptElementKind.keyword: 110 | case 'snippet': 111 | // redish 112 | return `rgba(${0xf9 + add},${0x26 + add},${0x72 + add},${opacity})`; 113 | case ts.ScriptElementKind.scriptElement: 114 | case ts.ScriptElementKind.moduleElement: 115 | case ts.ScriptElementKind.classElement: 116 | case ts.ScriptElementKind.localClassElement: 117 | case ts.ScriptElementKind.interfaceElement: 118 | case ts.ScriptElementKind.typeElement: 119 | case ts.ScriptElementKind.enumElement: 120 | case ts.ScriptElementKind.alias: 121 | case ts.ScriptElementKind.typeParameterElement: 122 | case ts.ScriptElementKind.primitiveType: 123 | // yelloish 124 | // #e6db74 125 | return `rgba(${0xe6 + add},${0xdb + add},${0x74 + add},${opacity})`; 126 | case ts.ScriptElementKind.variableElement: 127 | case ts.ScriptElementKind.localVariableElement: 128 | case ts.ScriptElementKind.memberVariableElement: 129 | case ts.ScriptElementKind.letElement: 130 | case ts.ScriptElementKind.constElement: 131 | case ts.ScriptElementKind.label: 132 | case ts.ScriptElementKind.parameterElement: 133 | case ts.ScriptElementKind.indexSignatureElement: 134 | // blueish 135 | // #66d9ef 136 | return `rgba(${0x66 + add},${0xd9 + add},${0xef + add},${opacity})`; 137 | case ts.ScriptElementKind.functionElement: 138 | case ts.ScriptElementKind.localFunctionElement: 139 | case ts.ScriptElementKind.memberFunctionElement: 140 | case ts.ScriptElementKind.memberGetAccessorElement: 141 | case ts.ScriptElementKind.memberSetAccessorElement: 142 | case ts.ScriptElementKind.callSignatureElement: 143 | case ts.ScriptElementKind.constructorImplementationElement: 144 | case 'path': 145 | // greenish 146 | // #a6e22e 147 | return `rgba(${0xa6 + add},${0xe2 + add},${0x2e + add},${opacity})`; 148 | default: 149 | return `rgba(${0xaa + add},${0xaa + add},${0xaa + add},${opacity})`; 150 | } 151 | } 152 | 153 | /** 154 | * For consitent icon lookup against kind 155 | */ 156 | import {FAIconName,toFontAwesomeCharCode} from "../fontAwesomeToCharCode"; 157 | export function kindToIcon(kind: string):string { 158 | switch (kind) { 159 | case 'snippet': 160 | return toFontAwesomeCharCode(FAIconName.exchange); 161 | case 'path': 162 | return toFontAwesomeCharCode(FAIconName.fileText); 163 | case ts.ScriptElementKind.keyword: 164 | return toFontAwesomeCharCode(FAIconName.key); 165 | case ts.ScriptElementKind.classElement: 166 | return toFontAwesomeCharCode(FAIconName.copyright); 167 | case ts.ScriptElementKind.interfaceElement: 168 | return toFontAwesomeCharCode(FAIconName.infoCircle); 169 | case ts.ScriptElementKind.scriptElement: 170 | case ts.ScriptElementKind.moduleElement: 171 | case ts.ScriptElementKind.localClassElement: 172 | case ts.ScriptElementKind.typeElement: 173 | case ts.ScriptElementKind.enumElement: 174 | case ts.ScriptElementKind.alias: 175 | case ts.ScriptElementKind.typeParameterElement: 176 | case ts.ScriptElementKind.primitiveType: 177 | return toFontAwesomeCharCode(FAIconName.archive); 178 | case ts.ScriptElementKind.variableElement: 179 | case ts.ScriptElementKind.localVariableElement: 180 | case ts.ScriptElementKind.memberVariableElement: 181 | case ts.ScriptElementKind.letElement: 182 | case ts.ScriptElementKind.constElement: 183 | case ts.ScriptElementKind.label: 184 | case ts.ScriptElementKind.parameterElement: 185 | case ts.ScriptElementKind.indexSignatureElement: 186 | return toFontAwesomeCharCode(FAIconName.at); 187 | case ts.ScriptElementKind.functionElement: 188 | case ts.ScriptElementKind.localFunctionElement: 189 | case ts.ScriptElementKind.memberFunctionElement: 190 | case ts.ScriptElementKind.memberGetAccessorElement: 191 | case ts.ScriptElementKind.memberSetAccessorElement: 192 | case ts.ScriptElementKind.callSignatureElement: 193 | case ts.ScriptElementKind.constructorImplementationElement: 194 | return toFontAwesomeCharCode(FAIconName.circleArrowRight); 195 | default: 196 | return toFontAwesomeCharCode(FAIconName.info); 197 | } 198 | } 199 | 200 | namespace AutocompleteStyles { 201 | /** 202 | * We have a rows of hints with each hint being 203 | * `leftLeft` `left` `main` `right` 204 | * icon for type for content for docs 205 | */ 206 | export const leftLeftClassName = 'cm-hint left-left'; 207 | export const leftClassName = 'cm-hint left'; 208 | export const mainClassName = 'cm-hint main'; 209 | export const rightClassName = 'cm-hint right'; 210 | } 211 | export function render(elt: HTMLLIElement, data: CM.Hints, cur: ExtendedCodeMirrorHint) { 212 | let original: Completion = cur.original; 213 | let color = kindToColor(original.kind); 214 | let colorBackground = kindToColor(original.kind, true); 215 | let icon = kindToIcon(original.kind); 216 | const text = cur.queryString ? ReactServer.renderToString({renderMatchedSegments(original.name,cur.queryString)}) : original.name; 217 | 218 | elt.innerHTML = ` 219 | ${icon} 220 | ${original.kind} 221 | ${text} 222 | ${original.display} 223 | `.replace(/\s+/g,' '); 224 | } -------------------------------------------------------------------------------- /src/play/codeEditor/addons/autocomplete/codemirror-showhint.d.ts: -------------------------------------------------------------------------------- 1 | // See docs https://codemirror.net/doc/manual.html#addon_show-hint 2 | 3 | declare module CM { 4 | type Position = {line: number, ch: number}; 5 | var commands: any; 6 | 7 | /** Provides a framework for showing autocompletion hints. Defines editor.showHint, which takes an optional 8 | options object, and pops up a widget that allows the user to select a completion. Finding hints is done with 9 | a hinting functions (the hint option), which is a function that take an editor instance and options object, 10 | and return a {list, from, to} object, where list is an array of strings or objects (the completions), and 11 | from and to give the start and end of the token that is being completed as {line, ch} objects. An optional 12 | selectedHint property (an integer) can be added to the completion object to control the initially selected hint. */ 13 | function showHint(cm: any, hinter?: (doc: any) => Hints, options?: ShowHintOptions): void; 14 | 15 | interface Hints { 16 | from?: Position; 17 | to?: Position; 18 | list: (Hint | string)[]; 19 | } 20 | 21 | /** Interface used by showHint.js Codemirror add-on 22 | When completions aren't simple strings, they should be objects with the following properties: */ 23 | interface Hint { 24 | /** text is what is used by default to apply the completion */ 25 | text: string; 26 | className?: string; 27 | displayText?: string; 28 | from?: Position; 29 | /** Called if a completion is picked. If provided *you* are responsible for applying the completion */ 30 | hint?: (cm: any, data: Hints, cur: Hint) => void; 31 | render?: (element: HTMLLIElement, data: Hints, cur: Hint) => void; 32 | to?: Position; 33 | } 34 | 35 | interface Editor { 36 | /** An extension of the existing CodeMirror typings for the Editor.on("keyup", func) syntax */ 37 | on(eventName: string, handler: (doc: any, event: any) => void): void; 38 | off(eventName: string, handler: (doc: any, event: any) => void): void; 39 | } 40 | 41 | /** Extend CodeMirror.Doc with a state object, so that the Doc.state.completionActive property is reachable*/ 42 | interface Doc { 43 | state: any; 44 | showHint: (options: ShowHintOptions) => void; 45 | } 46 | 47 | interface ShowHintOptions { 48 | completeSingle: boolean; 49 | hint: (doc: any) => Hints; 50 | } 51 | 52 | /** The Handle used to interact with the autocomplete dialog box.*/ 53 | interface Handle { 54 | moveFocus(n: number, avoidWrap: boolean): void; 55 | setFocus(n: number): void; 56 | menuSize(): number; 57 | length: number; 58 | close(): void; 59 | pick(): void; 60 | data: any; 61 | } 62 | 63 | interface EditorConfiguration { 64 | showHint?: boolean; 65 | hintOptions?: ShowHintOptions; 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/play/codeEditor/addons/autocomplete/snippets.ts: -------------------------------------------------------------------------------- 1 | /** Based on https://github.com/angelozerr/CodeMirror-XQuery/blob/master/codemirror-javascript/addon/hint/javascript/javascript-templates.js#L1 */ 2 | 3 | export interface TemplateConfig { 4 | name: string; 5 | description: string; 6 | 7 | /** A string version of the template. We parse this to TokenStream */ 8 | template: string; 9 | 10 | /** 11 | * Function completion means that we do not each the preceeding `(` on completing 12 | * By default its false 13 | */ 14 | functionCompletion?: boolean; 15 | } 16 | 17 | export interface TemplatesForContext { 18 | context: string; 19 | templates: TemplateConfig[]; 20 | } 21 | 22 | export const defaultSnippets: TemplatesForContext[] = [ 23 | { 24 | context: "typescript", 25 | templates: [ 26 | { 27 | "name": "for", 28 | "description": "iterate over array", 29 | "template": "for (let ${2:index} = 0; ${index} < ${1:array}.length; ${index}++) {\n\t${cursor}\n}" 30 | }, 31 | { 32 | "name": "fort", 33 | "description": "iterate over array with temporary variable", 34 | "template": "for (let ${index} = 0; ${index} < ${array}.length; ${index}++) {\n\tconst ${array_element} = ${array}[${index}];\n\t${cursor}\n}" 35 | }, 36 | { 37 | "name": "forin", 38 | "description": "iterate using for .. in", 39 | "template": "for (const ${2:key} in ${1:iterable}) {\n\t${cursor}\n}" 40 | }, 41 | { 42 | "name": "forof", 43 | "description": "iterate using for .. of", 44 | "template": "for (const ${2:item} of ${1:iterable}) {\n\t${cursor}\n}" 45 | }, 46 | { 47 | "name": "import", 48 | "description": "ES6 style import", 49 | "template": "import ${2:name} from '${1:path}';${cursor}" 50 | }, 51 | { 52 | "name": "importr", 53 | "description": "CommonJs Style Import", 54 | "template": "import ${1:name} = require('${2:path}');${cursor}" 55 | }, 56 | { 57 | "name": "interface", 58 | "description": "Create a new interface", 59 | "template": "interface ${1:Name} {\n\t${cursor}\n}" 60 | }, 61 | { 62 | "name": "class", 63 | "description": "Create a new class", 64 | "template": "class ${1:Name} {\n\t${cursor}\n}" 65 | }, 66 | { 67 | "name": "namespace", 68 | "description": "Create a new namespace", 69 | "template": "namespace ${1:Name} {\n\t${cursor}\n}" 70 | }, 71 | { "name": "do", "description": "do while statement", "template": "do {\n\t${cursor}\n} while (${condition});" }, 72 | { "name": "switch", "description": "switch case statement", "template": "switch (${key}) {\n\tcase ${value}:\n\t\t${cursor}\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n}" }, 73 | { "name": "if", "description": "if statement", "template": "if (${condition}) {\n\t${cursor}\n}" }, 74 | { "name": "ifelse", "description": "if else statement", "template": "if (${condition}) {\n\t${cursor}\n} else {\n\t\n}" }, 75 | { "name": "elseif", "description": "else if block", "template": "else if (${condition}) {\n\t${cursor}\n}" }, 76 | { "name": "else", "description": "else block", "template": "else {\n\t${cursor}\n}" }, 77 | { "name": "try", "description": "try catch block", "template": "try {\n\t${cursor}\n} catch (e) {\n\t// ${todo}: handle exception\n}" }, 78 | { "name": "catch", "description": "catch block", "template": "catch (e) {\n\t${cursor}// ${todo}: handle exception\n}" }, 79 | { "name": "function", "description": "function", "template": "function ${name}(${}) {\n\t${cursor}\n}" }, 80 | { "name": "functiona", "description": "anonymous function", "template": "function (${}) {\n\t${cursor}\n}" }, 81 | { "name": "new", "description": "create new object", "template": "var ${name} = new ${type}(${arguments});" }, 82 | { "name": "lazy", "description": "lazy creation", "template": "if (${name:var} == null) {\n\t${name} = new ${type}(${arguments});\n\t${cursor}\n}\n\nreturn ${name};" }, 83 | { "name": "@author", "description": "author name", "template": "@author ${user}" }, 84 | { "name": "while", "description": "while loop with condition", "template": "while (${condition}) {\n\t${cursor}\n}" } 85 | ] 86 | } 87 | ]; -------------------------------------------------------------------------------- /src/play/codeEditor/addons/autocomplete/templates.css: -------------------------------------------------------------------------------- 1 | /** Other instances of the placeholder being edited right now */ 2 | .CodeMirror-templates-variable-selected { 3 | background-color: #666; 4 | /*outline: solid #FFF 1px;*/ 5 | } 6 | 7 | /** Other place holders that user can cycle to */ 8 | .CodeMirror-templates-variable { 9 | background-color: #111; 10 | /*outline: solid #FFF 1px;*/ 11 | } 12 | 13 | .CodeMirror-templates-variable-start { 14 | } 15 | 16 | .CodeMirror-templates-variable-end { 17 | } -------------------------------------------------------------------------------- /src/play/codeEditor/addons/lint.css: -------------------------------------------------------------------------------- 1 | /** Just the Overrides of original lint.css (still included) */ 2 | 3 | .CodeMirror-lint-tooltip { 4 | background-color: #333; 5 | color: #DDD; 6 | border: 2px solid white; 7 | padding: 3px 6px; 8 | 9 | /* just more important than hover tooltip */ 10 | z-index: 1000; 11 | } 12 | 13 | .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { 14 | /* clear what CM has */ 15 | background-image: none; 16 | } 17 | .CodeMirror-lint-message-error { 18 | /* clear what CM has */ 19 | padding-left: 0px; 20 | } 21 | /* copy from font awesome */ 22 | .CodeMirror-lint-marker-error:before, .CodeMirror-lint-message-error:before{ 23 | content: "\f057"; 24 | font: normal normal normal 10px/1 FontAwesome; 25 | font-size: inherit; 26 | text-rendering: auto; 27 | -webkit-font-smoothing: antialiased; 28 | -moz-osx-font-smoothing: grayscale; 29 | 30 | /* our error color scheme */ 31 | color: #f92672; 32 | } 33 | .CodeMirror-lint-marker-error:before { 34 | font-size: .7rem; 35 | } 36 | .CodeMirror-lint-message-error:before { 37 | padding-right: 4px; 38 | } 39 | /* change the style of multiple in gutter */ 40 | .CodeMirror-lint-marker-multiple:before{ 41 | content: "\f05a"; 42 | } 43 | 44 | /* Change the underlines for error */ 45 | .CodeMirror-lint-mark-error { 46 | background-image: url(/assets/error.svg); 47 | } 48 | 49 | /** Don't let the line widget break our flexbox */ 50 | .CodeMirror-linewidget { 51 | width: 100% !important; 52 | } -------------------------------------------------------------------------------- /src/play/codeEditor/addons/lint.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a straight copy of lint.js from codemirror source 3 | * Changes prefixed with `BAS` 4 | * - comment out the call to popOver lint (as user can use gutter anyways) and inline error hover conflicts with type hover 5 | * - change the multiple error to add class to *same* dom node instead of adding a sub child as its easier to style 6 | */ 7 | 8 | export let imp = require('codemirror'); 9 | let CodeMirror = imp; // This is to overcome an unreported bug in the compiler 10 | 11 | var GUTTER_ID = "CodeMirror-lint-markers"; 12 | 13 | function showTooltip(e, content) { 14 | var tt = document.createElement("div"); 15 | tt.className = "CodeMirror-lint-tooltip"; 16 | tt.appendChild(content.cloneNode(true)); 17 | document.body.appendChild(tt); 18 | 19 | function position(e) { 20 | if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); 21 | tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; 22 | tt.style.left = (e.clientX + 5) + "px"; 23 | } 24 | CodeMirror.on(document, "mousemove", position); 25 | position(e); 26 | if (tt.style.opacity != null) tt.style.opacity = '1'; 27 | return tt; 28 | } 29 | function rm(elt) { 30 | if (elt.parentNode) elt.parentNode.removeChild(elt); 31 | } 32 | function hideTooltip(tt) { 33 | if (!tt.parentNode) return; 34 | if (tt.style.opacity == null) rm(tt); 35 | tt.style.opacity = 0; 36 | setTimeout(function() { rm(tt); }, 600); 37 | } 38 | 39 | function showTooltipFor(e, content, node) { 40 | var tooltip = showTooltip(e, content); 41 | function hide() { 42 | CodeMirror.off(node, "mouseout", hide); 43 | if (tooltip) { hideTooltip(tooltip); tooltip = null; } 44 | } 45 | var poll = setInterval(function() { 46 | if (tooltip) for (var n = node;; n = n.parentNode) { 47 | if (n && n.nodeType == 11) n = n.host; 48 | if (n == document.body) return; 49 | if (!n) { hide(); break; } 50 | } 51 | if (!tooltip) return clearInterval(poll); 52 | }, 400); 53 | CodeMirror.on(node, "mouseout", hide); 54 | } 55 | 56 | function LintState(cm, options, hasGutter) { 57 | this.marked = []; 58 | this.options = options; 59 | this.timeout = null; 60 | this.hasGutter = hasGutter; 61 | this.onMouseOver = function(e) { onMouseOver(cm, e); }; 62 | this.waitingFor = 0 63 | } 64 | 65 | function parseOptions(_cm, options) { 66 | if (options instanceof Function) return {getAnnotations: options}; 67 | if (!options || options === true) options = {}; 68 | return options; 69 | } 70 | 71 | function clearMarks(cm) { 72 | var state = cm.state.lint; 73 | if (state.hasGutter) cm.clearGutter(GUTTER_ID); 74 | for (var i = 0; i < state.marked.length; ++i) 75 | state.marked[i].clear(); 76 | state.marked.length = 0; 77 | } 78 | 79 | function makeMarker(labels, severity, multiple, tooltips) { 80 | var marker = document.createElement("div"), inner:any = marker; 81 | marker.className = "CodeMirror-lint-marker-" + severity; 82 | if (multiple) { 83 | // BAS 84 | marker.className = marker.className + " " + "CodeMirror-lint-marker-multiple"; 85 | // ORIGINAL: 86 | // inner = marker.appendChild(document.createElement("div")); 87 | // inner.className = "CodeMirror-lint-marker-multiple"; 88 | } 89 | 90 | if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { 91 | showTooltipFor(e, labels, inner); 92 | }); 93 | 94 | return marker; 95 | } 96 | 97 | function getMaxSeverity(a, b) { 98 | if (a == "error") return a; 99 | else return b; 100 | } 101 | 102 | function groupByLine(annotations) { 103 | var lines = []; 104 | for (var i = 0; i < annotations.length; ++i) { 105 | var ann = annotations[i], line = ann.from.line; 106 | (lines[line] || (lines[line] = [])).push(ann); 107 | } 108 | return lines; 109 | } 110 | 111 | function annotationTooltip(ann) { 112 | var severity = ann.severity; 113 | if (!severity) severity = "error"; 114 | var tip = document.createElement("div"); 115 | tip.className = "CodeMirror-lint-message-" + severity; 116 | tip.appendChild(document.createTextNode(ann.message)); 117 | return tip; 118 | } 119 | 120 | function lintAsync(cm, getAnnotations, passOptions) { 121 | var state = cm.state.lint 122 | var id = ++state.waitingFor 123 | function abort() { 124 | id = -1 125 | cm.off("change", abort) 126 | } 127 | cm.on("change", abort) 128 | getAnnotations(cm.getValue(), function(annotations, arg2) { 129 | cm.off("change", abort) 130 | if (state.waitingFor != id) return 131 | if (arg2 && annotations instanceof CodeMirror) annotations = arg2 132 | updateLinting(cm, annotations) 133 | }, passOptions, cm); 134 | } 135 | 136 | function startLinting(cm) { 137 | var state = cm.state.lint, options = state.options; 138 | var passOptions = options.options || options; // Support deprecated passing of `options` property in options 139 | var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); 140 | if (!getAnnotations) return; 141 | if (options.async || getAnnotations.async) { 142 | lintAsync(cm, getAnnotations, passOptions) 143 | } else { 144 | updateLinting(cm, getAnnotations(cm.getValue(), passOptions, cm)); 145 | } 146 | } 147 | 148 | function updateLinting(cm, annotationsNotSorted) { 149 | clearMarks(cm); 150 | var state = cm.state.lint, options = state.options; 151 | 152 | var annotations = groupByLine(annotationsNotSorted); 153 | 154 | for (var line = 0; line < annotations.length; ++line) { 155 | var anns = annotations[line]; 156 | if (!anns) continue; 157 | 158 | var maxSeverity = null; 159 | var tipLabel = state.hasGutter && document.createDocumentFragment(); 160 | 161 | for (var i = 0; i < anns.length; ++i) { 162 | var ann = anns[i]; 163 | var severity = ann.severity; 164 | if (!severity) severity = "error"; 165 | maxSeverity = getMaxSeverity(maxSeverity, severity); 166 | 167 | if (options.formatAnnotation) ann = options.formatAnnotation(ann); 168 | if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); 169 | 170 | if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { 171 | className: "CodeMirror-lint-mark-" + severity, 172 | __annotation: ann 173 | })); 174 | } 175 | 176 | if (state.hasGutter) 177 | cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1, 178 | state.options.tooltips)); 179 | } 180 | if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); 181 | } 182 | 183 | function onChange(cm) { 184 | var state = cm.state.lint; 185 | if (!state) return; 186 | clearTimeout(state.timeout); 187 | state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); 188 | } 189 | 190 | function popupSpanTooltip(ann, e) { 191 | // BAS early return 🌹 192 | return; 193 | 194 | // var target = e.target || e.srcElement; 195 | // showTooltipFor(e, annotationTooltip(ann), target); 196 | } 197 | 198 | function onMouseOver(cm, e) { 199 | var target = e.target || e.srcElement; 200 | if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; 201 | var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; 202 | var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); 203 | for (var i = 0; i < spans.length; ++i) { 204 | var ann = spans[i].__annotation; 205 | if (ann) return popupSpanTooltip(ann, e); 206 | } 207 | } 208 | 209 | CodeMirror.defineOption("lint", false, function(cm, val, old) { 210 | if (old && old != (CodeMirror as any).Init) { 211 | clearMarks(cm); 212 | if (cm.state.lint.options.lintOnChange !== false) 213 | cm.off("change", onChange); 214 | CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); 215 | clearTimeout(cm.state.lint.timeout); 216 | delete cm.state.lint; 217 | } 218 | 219 | if (val) { 220 | var gutters = cm.getOption("gutters"), hasLintGutter = false; 221 | for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; 222 | var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); 223 | if (state.options.lintOnChange !== false) 224 | cm.on("change", onChange); 225 | if (state.options.tooltips != false) 226 | CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); 227 | 228 | startLinting(cm); 229 | } 230 | }); 231 | 232 | CodeMirror.defineExtension("performLint", function() { 233 | if (this.state.lint) startLinting(this); 234 | }); -------------------------------------------------------------------------------- /src/play/codeEditor/addons/linter.ts: -------------------------------------------------------------------------------- 1 | import { cssRaw } from 'typestyle'; 2 | import CM = require('codemirror'); 3 | import { demoState } from '../../playState'; 4 | import * as escape from 'escape-html'; 5 | let CodeMirror = CM; 6 | 7 | // Docs https://codemirror.net/doc/manual.html#addon_lint 8 | cssRaw(require('codemirror/addon/lint/lint.css')); 9 | import lint = require('./lint'); 10 | var _import = lint; 11 | cssRaw(require('./lint.css')); 12 | 13 | /** Enable linter for this code mirror */ 14 | export function setupOptions(options: any, filePath: string) { 15 | options.lint = new Linter(filePath).lint; 16 | } 17 | 18 | interface LintError { 19 | message: string, 20 | /** 'error' (default) | 'warning' */ 21 | severity?: string, 22 | from: CM.Position, 23 | to: CM.Position 24 | } 25 | 26 | function codeErrorToLintError(codeError: CodeError): LintError { 27 | return { 28 | message: codeError.message, 29 | severity: 'error', 30 | from: CodeMirror.Pos(codeError.from.line, codeError.from.ch), 31 | to: CodeMirror.Pos(codeError.to.line, codeError.to.ch) 32 | }; 33 | } 34 | 35 | class Linter { 36 | constructor(public filePath: string) { 37 | } 38 | 39 | lint = (doc: string, options: any, cm: CodeMirror.Editor) => { 40 | let rawErrors = demoState.currentErrors; 41 | let errors: LintError[] = rawErrors.map(codeErrorToLintError); 42 | // this.updateInlineWidgets(cm, 43 | // /** only first */ 44 | // rawErrors.filter((x, i) => !i) 45 | // ); // If you want to enable inline error linting 46 | return errors; 47 | } 48 | 49 | // based on view-source:https://codemirror.net/demo/widget.html 50 | widgets = [] 51 | updateInlineWidgets(editor: any, codeErrors: CodeError[]) { 52 | let widgets = this.widgets; 53 | editor.operation(function() { 54 | for (var i = 0; i < widgets.length; ++i) { 55 | editor.removeLineWidget(widgets[i]); 56 | } 57 | widgets.length = 0; 58 | 59 | for (var i = 0; i < codeErrors.length; ++i) { 60 | var err = codeErrors[i]; 61 | 62 | var msg = document.createElement("div"); 63 | 64 | msg.style.display = 'inline-block'; 65 | 66 | msg.innerHTML = `
67 | 🐛 ${escape(err.message)} 68 |
`; 69 | 70 | widgets.push(editor.addLineWidget(err.from.line, msg, { coverGutter: false, noHScroll: true })); 71 | } 72 | }); 73 | 74 | var info = editor.getScrollInfo(); 75 | var after = editor.charCoords({ line: editor.getDoc().getCursor().line + 1, ch: 0 }, "local").top; 76 | if (info.top + info.clientHeight < after) { 77 | editor.scrollTo(null, after - info.clientHeight + 3); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/play/codeEditor/addons/textHover.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-hover-tooltip { 2 | background-color: infobackground; 3 | border: 1px solid black; 4 | border-radius: 4px 4px 4px 4px; 5 | color: infotext; 6 | font-family: monospace; 7 | font-size: 10pt; 8 | overflow: hidden; 9 | padding: 2px 5px; 10 | position: fixed; 11 | z-index: 100; 12 | max-width: 600px; 13 | opacity: 0; 14 | transition: opacity .4s; 15 | -moz-transition: opacity .4s; 16 | -webkit-transition: opacity .4s; 17 | -o-transition: opacity .4s; 18 | -ms-transition: opacity .4s; 19 | } 20 | 21 | /* If one wants the highlight what token was actually detected */ 22 | /*.CodeMirror-hover { 23 | outline: 2px solid grey; 24 | }*/ 25 | 26 | /** 27 | * Overrides from original 28 | */ 29 | .CodeMirror-hover-tooltip { 30 | background-color: #333; 31 | color: #DDD; 32 | border: 2px solid white; 33 | padding: 3px 6px; 34 | } -------------------------------------------------------------------------------- /src/play/codeEditor/addons/textHover.ts: -------------------------------------------------------------------------------- 1 | // src : https://github.com/angelozerr/CodeMirror-XQuery/blob/master/codemirror-extension/addon/hover/text-hover.js 2 | // Changes prefixed with `BAS` 3 | import {cssRaw} from 'typestyle'; 4 | cssRaw(require('./textHover.css')); 5 | // BAS 6 | /** We have single promise queue shared among *all code mirror instances* for all the queries sent */ 7 | let PromiseQueue = []; 8 | let hidePreviousToolTip = null; 9 | 10 | export var CodeMirror = require('codemirror'); 11 | (function() { 12 | "use strict"; 13 | 14 | var HOVER_CLASS = " CodeMirror-hover"; 15 | 16 | function showTooltip(e, content) { 17 | var tt = document.createElement("div"); 18 | tt.className = "CodeMirror-hover-tooltip"; 19 | if (typeof content == "string") { 20 | content = document.createTextNode(content); 21 | } 22 | tt.appendChild(content); 23 | document.body.appendChild(tt); 24 | 25 | function position(e) { 26 | if (!tt.parentNode) 27 | return CodeMirror.off(document, "mousemove", position); 28 | tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; 29 | tt.style.left = (e.clientX + 5) + "px"; 30 | } 31 | CodeMirror.on(document, "mousemove", position); 32 | position(e); 33 | if (tt.style.opacity != null) 34 | tt.style.opacity = "1"; 35 | return tt; 36 | } 37 | function rm(elt) { 38 | if (elt.parentNode) 39 | elt.parentNode.removeChild(elt); 40 | } 41 | function hideTooltip(tt) { 42 | if (!tt.parentNode) 43 | return; 44 | if (tt.style.opacity == null) 45 | rm(tt); 46 | tt.style.opacity = 0; 47 | setTimeout(function() { 48 | rm(tt); 49 | }, 600); 50 | } 51 | 52 | function showTooltipFor(e, content, node, state, cm) { 53 | var tooltip = showTooltip(e, content); 54 | function hide() { 55 | CodeMirror.off(node, "mouseout", hide); 56 | CodeMirror.off(node, "click", hide); 57 | node.className = node.className.replace(HOVER_CLASS, ""); 58 | if (tooltip) { 59 | hideTooltip(tooltip); 60 | tooltip = null; 61 | } 62 | cm.removeKeyMap(state.keyMap); 63 | } 64 | var poll = setInterval(function() { 65 | if (tooltip) 66 | for ( var n = node;; n = n.parentNode) { 67 | if (n == document.body) 68 | return; 69 | if (!n) { 70 | hide(); 71 | break; 72 | } 73 | } 74 | if (!tooltip) 75 | return clearInterval(poll); 76 | }, 400); 77 | CodeMirror.on(node, "mouseout", hide); 78 | CodeMirror.on(node, "click", hide); 79 | state.keyMap = {Esc: hide}; 80 | cm.addKeyMap(state.keyMap); 81 | //BAS 82 | hidePreviousToolTip = hide; 83 | } 84 | 85 | function TextHoverState(cm, options) { 86 | this.options = options; 87 | this.timeout = null; 88 | if (options.delay) { 89 | this.onMouseOver = function(e) { 90 | onMouseOverWithDelay(cm, e); 91 | }; 92 | } else { 93 | this.onMouseOver = function(e) { 94 | onMouseOver(cm, e); 95 | }; 96 | } 97 | this.keyMap = null; 98 | } 99 | 100 | function parseOptions(cm, options) { 101 | if (options instanceof Function) 102 | return { 103 | getTextHover : options 104 | }; 105 | if (!options || options === true) 106 | options = {}; 107 | if (!options.getTextHover) 108 | options.getTextHover = cm.getHelper(CodeMirror.Pos(0, 0), "textHover"); 109 | if (!options.getTextHover) 110 | throw new Error("Required option 'getTextHover' missing (text-hover addon)"); 111 | return options; 112 | } 113 | 114 | function onMouseOverWithDelay(cm, e) { 115 | var state = cm.state.textHover, delay = state.options.delay; 116 | clearTimeout(state.timeout); 117 | if (e.srcElement) { 118 | // hack for IE, because e.srcElement failed when it is used in the tiemout function 119 | var newE = {srcElement: e.srcElement, clientX : e.clientX, clientY: e.clientY}; 120 | e = newE; 121 | } 122 | state.timeout = setTimeout(function() {onMouseOver(cm, e);}, delay); 123 | } 124 | 125 | function onMouseOver(cm, e) { 126 | var node = e.target || e.srcElement; 127 | if (node) { 128 | var state = cm.state.textHover, data = getTokenAndPosAt(cm, e); 129 | var result = Promise.resolve(state.options.getTextHover(cm, data, e)); 130 | 131 | // Add to queue 132 | PromiseQueue.push(result); 133 | 134 | // BAS hide any previous 135 | if (hidePreviousToolTip) { 136 | hidePreviousToolTip(); 137 | hidePreviousToolTip = null; 138 | } 139 | 140 | result.then((content) => { 141 | // If not the last in the queue ... the results don't matter 142 | if (PromiseQueue[PromiseQueue.length - 1] !== result){ 143 | PromiseQueue = PromiseQueue.filter(p => p != result); 144 | return; 145 | } 146 | PromiseQueue = PromiseQueue.filter(p => p != result); 147 | 148 | if (content) { 149 | node.className += HOVER_CLASS; 150 | if (typeof content == 'function') { 151 | content(showTooltipFor, data, e, node, state, cm); 152 | } 153 | else { 154 | showTooltipFor(e, content, node, state, cm); 155 | } 156 | } 157 | }); 158 | } 159 | } 160 | 161 | function optionHandler(cm, val, old) { 162 | if (old && old != CodeMirror.Init) { 163 | CodeMirror.off(cm.getWrapperElement(), "mouseover", 164 | cm.state.textHover.onMouseOver); 165 | delete cm.state.textHover; 166 | } 167 | 168 | if (val) { 169 | var state = cm.state.textHover = new TextHoverState(cm, parseOptions(cm, 170 | val)); 171 | CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); 172 | } 173 | } 174 | 175 | // When the mouseover fires, the cursor might not actually be over 176 | // the character itself yet. These pairs of x,y offsets are used to 177 | // probe a few nearby points when no suitable marked range is found. 178 | var nearby = [ 0, 0, 0, 5, 0, -5, 5, 0, -5, 0 ]; 179 | 180 | function getTokenAndPosAt(cm, e) { 181 | var node = e.target || e.srcElement, text = node.innerText 182 | || node.textContent; 183 | for ( var i = 0; i < nearby.length; i += 2) { 184 | var pos = cm.coordsChar({ 185 | left : e.clientX + nearby[i], 186 | top : e.clientY + nearby[i + 1] 187 | }); 188 | var token = cm.getTokenAt(pos); 189 | if (token && token.string === text) { 190 | return { 191 | token : token, 192 | pos : pos 193 | }; 194 | } 195 | } 196 | } 197 | 198 | CodeMirror.defineOption("textHover", false, optionHandler); // deprecated 199 | 200 | })(); -------------------------------------------------------------------------------- /src/play/codeEditor/codeEditor.tsx: -------------------------------------------------------------------------------- 1 | // Code 2 | import CodeMirror = require('codemirror'); 3 | import * as React from "react"; 4 | import * as ReactDOM from "react-dom"; 5 | import { style, classes, cssRaw } from "typestyle"; 6 | import * as ts from 'byots'; 7 | import * as utils from '../../utils'; 8 | import * as ps from '../projectService'; 9 | import escape = require("escape-html"); 10 | import { toHtml } from '../../components/markdown'; 11 | import * as csx from 'csstips'; 12 | 13 | const headerHeight = '78px'; 14 | 15 | // CSS 16 | cssRaw(require('codemirror/lib/codemirror.css')); 17 | cssRaw(require('codemirror/theme/monokai.css')); 18 | cssRaw(require('codemirror/addon/fold/foldgutter.css')); 19 | // Css overrides 20 | cssRaw(` 21 | /* Make code mirror flex boxy */ 22 | .CodeMirror { 23 | position: absolute; 24 | left: 0px; 25 | right: 0px; 26 | top: 0px; 27 | bottom: 0px; 28 | height: 100%; 29 | } 30 | 31 | /** Bigger, better font */ 32 | .CodeMirror { 33 | font-size: 16px; 34 | font-family: consolas, menlo, monospace; 35 | } 36 | 37 | /* Make code mirror selections a bit more popping */ 38 | .cm-s-monokai div.CodeMirror-selected { 39 | background: #58574B; 40 | } 41 | 42 | /* matchbrackets addon */ 43 | .cm-s-monokai .CodeMirror-matchingbracket { 44 | color: #4f0 !important; 45 | background-color: #32332b; 46 | text-decoration: none; 47 | } 48 | .cm-s-monokai .CodeMirror-nonmatchingbracket { 49 | background-color: #32332b; 50 | } 51 | 52 | /* cm-s-monokai has nothing for qualifier */ 53 | .cm-s-monokai span.cm-qualifier { color: rgb(0, 208, 255); } 54 | 55 | /* make overrite (insert) mode visually different */ 56 | .CodeMirror-overwrite .CodeMirror-cursor{ 57 | border-left:2px solid red !important; 58 | } 59 | 60 | /* match-highlight from demo : https://codemirror.net/demo/matchhighlighter.html */ 61 | .cm-matchhighlight { 62 | text-decoration: underline; 63 | } 64 | 65 | /* tag matching is a bit too bold. Dull it down a bit */ 66 | .CodeMirror-matchingtag { 67 | background: rgba(0, 150, 150, .3); 68 | } 69 | 70 | /* Code folding style from graphiql */ 71 | .CodeMirror-foldmarker { 72 | border-radius: 4px; 73 | background: #08f; 74 | background: -webkit-linear-gradient(#43A8FF, #0F83E8); 75 | background: linear-gradient(#43A8FF, #0F83E8); 76 | 77 | color: white; 78 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1); 79 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1); 80 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1); 81 | font-family: arial; 82 | line-height: 0; 83 | padding: 0px 4px 1px; 84 | font-size: 12px; 85 | margin: 0 3px; 86 | text-shadow: 0 -1px rgba(0, 0, 0, 0.1); 87 | } 88 | `) 89 | 90 | /** 91 | * addons 92 | */ 93 | // keymap 94 | require('codemirror/keymap/sublime'); 95 | // comments (single / multiline) 96 | require('codemirror/addon/comment/comment'); 97 | // code folding 98 | require('codemirror/addon/fold/foldcode'); 99 | require('codemirror/addon/fold/foldgutter'); 100 | require('codemirror/addon/fold/brace-fold'); 101 | require('codemirror/addon/fold/xml-fold'); 102 | require('codemirror/addon/fold/markdown-fold'); 103 | require('codemirror/addon/fold/comment-fold'); 104 | // require('codemirror/addon/fold/foldgutter.css'); 105 | // Highlight active line 106 | require('codemirror/addon/selection/active-line'); 107 | // Highlight matching brackets 108 | require('codemirror/addon/edit/matchbrackets'); 109 | // Auto close brackets and strings 110 | require('codemirror/addon/edit/closebrackets'); 111 | // Auto match tags (great for TSX!) 112 | require('codemirror/addon/edit/matchtags'); 113 | // Auto highlight same words selected 114 | require('codemirror/addon/search/match-highlighter'); 115 | 116 | const mode = 'jsx'; 117 | require('codemirror/mode/jsx/jsx'); 118 | require('codemirror/mode/javascript/javascript'); 119 | require('codemirror/mode/xml/xml'); 120 | 121 | /** Our addons */ 122 | import autocomplete = require('./addons/autocomplete/autocomplete'); 123 | import linter = require('./addons/linter'); 124 | import textHover = require('./addons/textHover'); 125 | const ensureImport = textHover; 126 | 127 | 128 | /** 129 | * Fixup keymaps 130 | */ 131 | 132 | /** Load CM and keymaps */ 133 | require('codemirror/keymap/sublime') 134 | 135 | /** Straight out of codemirror.js */ 136 | export var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); 137 | export var mac = ios || /Mac/.test(navigator.platform); 138 | export var windows = /win/i.test(navigator.platform); 139 | /** Nice display name for the mod by user platform */ 140 | export var modName = mac ? '⌘' : 'Ctrl'; 141 | let mod = mac ? 'Cmd' : 'Ctrl'; 142 | 143 | // The key is like sublime -> default -> basic 144 | let keyMap = (CodeMirror as any).keyMap; 145 | let basicMap = keyMap.basic; 146 | let defaultMap = keyMap.default; 147 | let sublimeMap = keyMap.sublime; 148 | 149 | /** Want to use to to copy url */ 150 | delete sublimeMap[`${mod}-L`]; 151 | 152 | interface Props { 153 | onFocusChange?: (focused: boolean) => any; 154 | readOnly?: boolean | "nocursor"; 155 | preview?: ts.TextSpan; 156 | value: string; 157 | onChange: (value: string) => any; 158 | onCodeEdit: (codeEdit: CodeEdit) => any; 159 | filePath: string; 160 | } 161 | 162 | export class CodeEditor extends React.Component{ 163 | constructor(props) { 164 | super(props); 165 | 166 | this.state = { 167 | isFocused: false 168 | }; 169 | } 170 | 171 | codeMirror: CodeMirror.EditorFromTextArea; 172 | refs: { 173 | [string: string]: any; 174 | textarea: any; 175 | } 176 | 177 | /** Ready after the doc is loaded */ 178 | ready = false; 179 | afterReadyQueue: { (): void }[] = []; 180 | /** If already ready it execs ... otherwise waits */ 181 | afterReady = (cb: () => void) => { 182 | if (this.ready) cb(); 183 | else { 184 | this.afterReadyQueue.push(cb); 185 | } 186 | } 187 | 188 | componentDidMount() { 189 | 190 | var options: CodeMirror.EditorConfiguration = { 191 | mode: mode, 192 | 193 | lineNumbers: true, 194 | keyMap: 'sublime', 195 | theme: 'monokai', 196 | indentUnit: 2, 197 | 198 | // Soft tabs (tabs to spaces): 199 | // https://github.com/codemirror/CodeMirror/issues/988#issuecomment-37692827 200 | extraKeys: { 201 | Tab: function(cm) { 202 | if (cm.doc.somethingSelected()) { 203 | return CodeMirror.Pass; 204 | } 205 | var spacesPerTab = cm.getOption("indentUnit"); 206 | var spacesToInsert = spacesPerTab - (cm.doc.getCursor("start").ch % spacesPerTab); 207 | var spaces = Array(spacesToInsert + 1).join(" "); 208 | cm.replaceSelection(spaces, "end", "+input"); 209 | }, 210 | 'Ctrl-Space': "autocomplete", 211 | 'Cmd-S': () => { 212 | /** We have no use for save */ 213 | }, 214 | 'Ctrl-S': () => { 215 | /** We have no use for save */ 216 | } 217 | }, 218 | 219 | foldGutter: true, 220 | gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], 221 | 222 | // Active line addon 223 | styleActiveLine: true, 224 | 225 | // Match bracket addon 226 | matchBrackets: true, 227 | 228 | // match-highlighter 229 | highlightSelectionMatches: { showToken: /\w/ }, 230 | 231 | // Auto close brackets and strings 232 | autoCloseBrackets: true, 233 | 234 | // Match tags (great for tsx!) 235 | // Doesn't work right now. 236 | // It needs `tag` token to work (see code in `xml-fold.js` i.e. `/\btag\b/``) 237 | matchTags: { bothTags: true }, 238 | 239 | /** Overcomes horizontal scrolling for now */ 240 | lineWrapping: true, 241 | 242 | // Text hover 243 | textHover: { 244 | delay: 50, 245 | getTextHover: (cm, data, e: MouseEvent) => { 246 | if (data && data.pos) { 247 | return this.getQuickInfo(data.pos); 248 | } 249 | }, 250 | } 251 | } as any; 252 | 253 | // setup hint / autocomplete options 254 | autocomplete.setupOptions(options, this.props.filePath); 255 | 256 | // lint 257 | linter.setupOptions(options, this.props.filePath); 258 | 259 | var textareaNode = ReactDOM.findDOMNode(this.refs.textarea); 260 | this.codeMirror = CodeMirror.fromTextArea(textareaNode as HTMLTextAreaElement, options); 261 | this.codeMirror.on('focus', this.focusChanged.bind(this, true)); 262 | this.codeMirror.on('blur', this.focusChanged.bind(this, false)); 263 | 264 | // also lint on errors changing 265 | this.codeMirror.on('change', utils.debounce(() => { 266 | this.codeMirror && (this.codeMirror as any).performLint(); 267 | }, 2000)); 268 | 269 | // Make hint / autocomplete more aggresive 270 | autocomplete.setupCodeMirror(this.codeMirror); 271 | 272 | this.codeMirror.on('change', this.codemirrorValueChanged); 273 | this._currentCodemirrorValue = this.props.value || ''; 274 | this.codeMirror.setValue(this._currentCodemirrorValue); 275 | 276 | (this.codeMirror.getDoc() as any).on('beforeChange', (doc: CodeMirror.Doc, change: CodeMirror.EditorChange) => { 277 | 278 | // This is just the user pressing backspace on an empty file. 279 | // If we let it go through then the classifier cache will crash. 280 | // So abort 281 | if (change.from.line === change.to.line && change.from.ch === change.to.ch && change.text.length === 1 && change.text[0].length === 0) { 282 | return; 283 | } 284 | 285 | let codeEdit: CodeEdit = { 286 | from: { line: change.from.line, ch: change.from.ch }, 287 | to: { line: change.to.line, ch: change.to.ch }, 288 | newText: change.text.join('\n'), 289 | sourceId: '' 290 | }; 291 | 292 | this.props.onCodeEdit(codeEdit); 293 | }); 294 | 295 | setTimeout(() => this.codeMirror.refresh(), 200);// Needed to resize gutters correctly 296 | setTimeout(() => this.codeMirror.refresh(), 1000);// Needed to resize gutters correctly 297 | } 298 | 299 | componentWillUnmount() { 300 | // todo: is there a lighter-weight way to remove the cm instance? 301 | if (this.codeMirror) { 302 | this.codeMirror.toTextArea(); 303 | /** 304 | * Very hacky way to unlink docs from CM 305 | * If we don't do this then the doc stays in memory and so does cm :-/ 306 | */ 307 | (this.codeMirror.getDoc() as any).cm = null; 308 | } 309 | } 310 | 311 | componentWillReceiveProps(nextProps: Props) { 312 | if (this.codeMirror && nextProps.value !== undefined && this._currentCodemirrorValue != nextProps.value.toString()) { 313 | this.codeMirror.setValue(nextProps.value); 314 | } 315 | } 316 | 317 | _currentCodemirrorValue: string; 318 | codemirrorValueChanged = (doc, change) => { 319 | const newValue = doc.getValue(); 320 | this._currentCodemirrorValue = newValue; 321 | this.props.onChange && this.props.onChange(newValue); 322 | } 323 | 324 | getCodeMirror() { 325 | return this.codeMirror; 326 | } 327 | 328 | focusChanged = (focused) => { 329 | this.setState({ 330 | isFocused: focused 331 | }); 332 | this.props.onFocusChange && this.props.onFocusChange(focused); 333 | } 334 | 335 | getValue() { 336 | return this.codeMirror.getDoc().getValue(); 337 | } 338 | 339 | getQuickInfo = (pos: CodeMirror.Position): Promise => { 340 | return ps.quickInfo({ filePath: this.props.filePath, position: this.codeMirror.getDoc().indexFromPos(pos) }) 341 | .then(resp => { 342 | if (!resp.valid) return; 343 | 344 | var message = ''; 345 | if (resp.errors.length) { 346 | message = message + `🐛 ${resp.errors.map(e => escape(e.message)).join('
')}

` 347 | } 348 | 349 | if (resp.info) { 350 | message = message + `${escape(resp.info.name)}`; 351 | if (resp.info.comment) { 352 | message = message + `
${toHtml(resp.info.comment)}`; 353 | } 354 | } 355 | 356 | let div = document.createElement('div'); 357 | div.innerHTML = message; 358 | return div; 359 | }); 360 | }; 361 | 362 | render() { 363 | var className = 'ReactCodeMirror'; 364 | if (this.state.isFocused) { 365 | className += ' ReactCodeMirror--focused'; 366 | } 367 | return ( 368 |
369 |