├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── bin └── publish-website ├── client ├── img │ └── arrow.png ├── index.html ├── scripts │ └── index.js ├── styles │ └── index.styl ├── vendor │ ├── prism.css │ └── prism.js └── webpack.config.js ├── gulpfile.js ├── lib └── ReactFitText.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/ 3 | .DS_Store 4 | bin 5 | client 6 | gulpfile.js 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2014 Sergio Rafael Gianazza 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Fit text 2 | 3 | This component is a port of the famous [fit text](http://fittextjs.com/) plugin for jQuery to use with React. 4 | 5 | This component is 100% jQuery Free. 6 | 7 | Homepage: [http://softwarepsychonaut.com/react-fittext](http://softwarepsychonaut.com/react-fittext) 8 | 9 | ## Usage 10 | 11 | Simply wrap your text component inside 12 | 13 | ```javascript 14 | // In your React component 15 | 16 | var ReactFitText = require('react-fittext'); 17 | ... 18 | 19 | 20 |

Testing React Fittext

21 |
22 | ``` 23 | 24 | and you're good to go! 25 | 26 | If you want to see a live demo of this component, you can [check the homepage](http://softwarepsychonaut.com/react-fittext). 27 | 28 | ### Parameters 29 | There are few options you can send to the component to modify it default behaviour: 30 | 31 | * __compressor__: you can tweak this variable to increase / decrease the font-size. Default is 1. 32 | * __minFontSize__: the minimun font size (in px) this component should use. 33 | * __maxFontSize__: the maximum font size (in px) this component should use. 34 | 35 | ## Issues / PR 36 | 37 | If you found any issue with this component, please [report it](https://github.com/gianu/react-fittext/issues). 38 | 39 | If you want to improve the code, feel free to create a PR! 40 | 41 | ## Thanks 42 | * [KyleAMathews](https://github.com/KyleAMathews) 43 | * [revolunet](https://github.com/revolunet) 44 | * [poteto](https://github.com/poteto) 45 | -------------------------------------------------------------------------------- /bin/publish-website: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o nounset 4 | set -o errexit 5 | 6 | git_status="$(git status -z 2> /dev/null)" 7 | 8 | if [[ ! -z ${git_status} ]]; then 9 | echo "You still have changes in your current branch. Commit everything before publishing." 10 | exit 1 11 | fi 12 | 13 | git fetch 2> /dev/null 14 | git checkout gh-pages 15 | git pull 16 | git merge master 17 | rm -rf assets 18 | gulp prepare-publish 19 | mv public/* . 20 | git add -A 21 | git commit -m "Site update" 22 | git push origin gh-pages 23 | git checkout - 24 | 25 | echo "Done publishing!" 26 | -------------------------------------------------------------------------------- /client/img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianu/react-fittext/c478e3f8daca3252b7eb827a51c726c2a04b25c7/client/img/arrow.png -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ReactFitText Test Page 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |

14 | This component is a port of FitText. 15 |

16 |
17 |

FitText makes font-sizes flexible. Use this plugin on your fluid or responsive layout to achieve scalable headlines that fill the width of a parent element.

18 |

This port is 100% jQuery free, you only need React to use it.

19 |
20 |
21 |

Usage

22 |

If you want to use this component on your React project you only need to add react-fittext to your dependencies

23 |
24 |         npm install react-fittext --save
25 |       
26 |

There is an excelent use example in this very same page. To use React-Fittext on the title and subtitle of this page you only need to do this:

27 |

28 |     
29 |
30 |

React-FitText is brought to you by gianu (@sgianazza)

31 |

The Original jQuery Plugin is brought to you by Paravel

32 |
33 |
34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /client/scripts/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | require("../styles/index.styl"); 3 | 4 | var React = require("react"); 5 | var ReactDOM = require("react-dom"); 6 | var createClass = require("create-react-class"); 7 | var ReactFitText = require('../../lib/ReactFitText'); 8 | 9 | var Body = createClass({ 10 | render: function() { 11 | return
12 | 13 |

React FitText

14 |
15 | 16 |

17 | A React component for inflating web type 18 | 19 | Fork it on Github 20 | 21 |

22 |
23 |
; 24 | } 25 | }); 26 | 27 | 28 | ReactDOM.render(, document.querySelector('.js-fittext')); 29 | -------------------------------------------------------------------------------- /client/styles/index.styl: -------------------------------------------------------------------------------- 1 | @import 'nib' 2 | 3 | body 4 | top 0 5 | margin 0 6 | padding 0 7 | background #c44032 8 | color #f4f1de 9 | font-size 100% 10 | font-family "Montserrat", 'Arial', 'san-serif' 11 | 12 | header 13 | margin 3.5% 0 4% 14 | 15 | h1 16 | text-align center 17 | text-indent -.12em 18 | color #faf1de 19 | font-family "Arvo", 'sans-serif' 20 | font-weight 900 21 | display inline-block 22 | margin 0 auto 23 | line-height 1 24 | width 100% 25 | text-shadow #863027 .01em .01em 0, #863027 .02em .02em 0, #863027 .03em .03em 0, #863027 .04em .04em 0, #863027 .05em .05em 0, #863027 .06em .06em 0, #863027 .07em .07em 0, #863027 .08em .08em 0, #863027 .09em .09em 0, #863027 .1em .1em 0 26 | 27 | a 28 | color #f4f1de 29 | 30 | p 31 | font-size 1.2em 32 | 33 | footer 34 | border-top .12em solid #f4f1de 35 | padding-top 2% 36 | margin-top 3% 37 | display block 38 | font-size .8em 39 | 40 | .container 41 | width 75% 42 | margin 0 auto 43 | 44 | .download 45 | font-family "Arvo", 'sans-serif' 46 | text-align left 47 | margin-bottom 0px 48 | 49 | .download a 50 | text-decoration none 51 | 52 | .download__link 53 | padding 0.3em .8em 54 | background url('../img/arrow.png') no-repeat 55 | background-size 100% 56 | margin-left .4em 57 | transition margin .15s ease-out 58 | text-decoration none 59 | &:hover 60 | color #f4f1de 61 | margin-left .6em 62 | margin-right -.2em 63 | -------------------------------------------------------------------------------- /client/vendor/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+bash&plugins=line-numbers+file-highlight */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | text-shadow: 0 1px white; 12 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | line-height: 1.5; 19 | 20 | -moz-tab-size: 4; 21 | -o-tab-size: 4; 22 | tab-size: 4; 23 | 24 | -webkit-hyphens: none; 25 | -moz-hyphens: none; 26 | -ms-hyphens: none; 27 | hyphens: none; 28 | } 29 | 30 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 31 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 32 | text-shadow: none; 33 | background: #b3d4fc; 34 | } 35 | 36 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 37 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 38 | text-shadow: none; 39 | background: #b3d4fc; 40 | } 41 | 42 | @media print { 43 | code[class*="language-"], 44 | pre[class*="language-"] { 45 | text-shadow: none; 46 | } 47 | } 48 | 49 | /* Code blocks */ 50 | pre[class*="language-"] { 51 | padding: 1em; 52 | margin: .5em 0; 53 | overflow: auto; 54 | } 55 | 56 | :not(pre) > code[class*="language-"], 57 | pre[class*="language-"] { 58 | background: #f5f2f0; 59 | } 60 | 61 | /* Inline code */ 62 | :not(pre) > code[class*="language-"] { 63 | padding: .1em; 64 | border-radius: .3em; 65 | } 66 | 67 | .token.comment, 68 | .token.prolog, 69 | .token.doctype, 70 | .token.cdata { 71 | color: slategray; 72 | } 73 | 74 | .token.punctuation { 75 | color: #999; 76 | } 77 | 78 | .namespace { 79 | opacity: .7; 80 | } 81 | 82 | .token.property, 83 | .token.tag, 84 | .token.boolean, 85 | .token.number, 86 | .token.constant, 87 | .token.symbol, 88 | .token.deleted { 89 | color: #905; 90 | } 91 | 92 | .token.selector, 93 | .token.attr-name, 94 | .token.string, 95 | .token.char, 96 | .token.builtin, 97 | .token.inserted { 98 | color: #690; 99 | } 100 | 101 | .token.operator, 102 | .token.entity, 103 | .token.url, 104 | .language-css .token.string, 105 | .style .token.string { 106 | color: #a67f59; 107 | background: hsla(0, 0%, 100%, .5); 108 | } 109 | 110 | .token.atrule, 111 | .token.attr-value, 112 | .token.keyword { 113 | color: #07a; 114 | } 115 | 116 | .token.function { 117 | color: #DD4A68; 118 | } 119 | 120 | .token.regex, 121 | .token.important, 122 | .token.variable { 123 | color: #e90; 124 | } 125 | 126 | .token.important { 127 | font-weight: bold; 128 | } 129 | 130 | .token.entity { 131 | cursor: help; 132 | } 133 | 134 | pre.line-numbers { 135 | position: relative; 136 | padding-left: 3.8em; 137 | counter-reset: linenumber; 138 | } 139 | 140 | pre.line-numbers > code { 141 | position: relative; 142 | } 143 | 144 | .line-numbers .line-numbers-rows { 145 | position: absolute; 146 | pointer-events: none; 147 | top: 0; 148 | font-size: 100%; 149 | left: -3.8em; 150 | width: 3em; /* works for line-numbers below 1000 lines */ 151 | letter-spacing: -1px; 152 | border-right: 1px solid #999; 153 | 154 | -webkit-user-select: none; 155 | -moz-user-select: none; 156 | -ms-user-select: none; 157 | user-select: none; 158 | 159 | } 160 | 161 | .line-numbers-rows > span { 162 | pointer-events: none; 163 | display: block; 164 | counter-increment: linenumber; 165 | } 166 | 167 | .line-numbers-rows > span:before { 168 | content: counter(linenumber); 169 | color: #999; 170 | display: block; 171 | padding-right: 0.8em; 172 | text-align: right; 173 | } 174 | -------------------------------------------------------------------------------- /client/vendor/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+bash&plugins=line-numbers+file-highlight */ 2 | self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){g.lastIndex=0;var m=g.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),O=[p,1];b&&O.push(b);var N=new a(l,u?t.tokenize(m,u):m,h);O.push(N),w&&O.push(w),Array.prototype.splice.apply(r,O)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; 3 | Prism.languages.markup={comment://g,prolog:/<\?.+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/\&#?[\da-z]{1,8};/gi},Prism.hooks.add("wrap",function(t){"entity"===t.type&&(t.attributes.title=t.content.replace(/&/,"&"))});; 4 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*{))/gi,inside:{punctuation:/[;:]/g}},url:/url\((["']?).*?\1\)/gi,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/g,property:/(\b|\B)[\w-]+(?=\s*:)/gi,string:/("|')(\\?.)*?\1/g,important:/\B!important\b/gi,punctuation:/[\{\};:]/g,"function":/[-a-z0-9]+(?=\()/gi},Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/[\w\W]*?<\/style>/gi,inside:{tag:{pattern:/|<\/style>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css},alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').+?\1/gi,inside:{"attr-name":{pattern:/^\s*style/gi,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/gi,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag));; 5 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//g,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*?(\r?\n|$)/g,lookbehind:!0}],string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; 6 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/g,"function":/(?!\d)[a-z0-9_$]+(?=\()/gi}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript},alias:"language-javascript"}});; 7 | Prism.languages.bash=Prism.languages.extend("clike",{comment:{pattern:/(^|[^"{\\])(#.*?(\r?\n|$))/g,lookbehind:!0},string:{pattern:/("|')(\\?[\s\S])*?\1/g,inside:{property:/\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^\}]+\})/g}},keyword:/\b(if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/g}),Prism.languages.insertBefore("bash","keyword",{property:/\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^}]+\})/g}),Prism.languages.insertBefore("bash","comment",{important:/(^#!\s*\/bin\/bash)|(^#!\s*\/bin\/sh)/g});; 8 | Prism.hooks.add("after-highlight",function(e){var n=e.element.parentNode;if(n&&/pre/i.test(n.nodeName)&&-1!==n.className.indexOf("line-numbers")){var t,a=1+e.code.split("\n").length;lines=new Array(a),lines=lines.join(""),t=document.createElement("span"),t.className="line-numbers-rows",t.innerHTML=lines,n.hasAttribute("data-start")&&(n.style.counterReset="linenumber "+(parseInt(n.getAttribute("data-start"),10)-1)),e.element.appendChild(t)}});; 9 | (function(){if(!self.Prism||!self.document||!document.querySelector){return}var a={js:"javascript",html:"markup",svg:"markup",xml:"markup",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell"};Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(c){var e=c.getAttribute("data-src");var g=(e.match(/\.(\w+)$/)||[,""])[1];var f=a[g]||g;var b=document.createElement("code");b.className="language-"+f;c.textContent="";b.textContent="Loading…";c.appendChild(b);var d=new XMLHttpRequest();d.open("GET",e,true);d.onreadystatechange=function(){if(d.readyState==4){if(d.status<400&&d.responseText){b.textContent=d.responseText;Prism.highlightElement(b)}else{if(d.status>=400){b.textContent="✖ Error "+d.status+" while fetching file: "+d.statusText}else{b.textContent="✖ Error: File does not exist or is empty"}}}};d.send(null)})})();; 10 | -------------------------------------------------------------------------------- /client/webpack.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var Path = require("path"), 3 | webpack = require("webpack"), 4 | HtmlWebpackPlugin = require("html-webpack-plugin"), 5 | nib = require("nib"), 6 | webpackConfig, 7 | 8 | IS_PRODUCTION = "production" === process.env.NODE_ENV, 9 | JSX_WITH_HOT_LOEADERS = ["react-hot-loader/webpack", "babel-loader?optional=runtime"], 10 | CSS_LOADER = "style-loader!css-loader?root=../", 11 | 12 | webpackConfig = module.exports = { 13 | entry: { 14 | app: [ 15 | "./client/scripts/index.js" 16 | ] 17 | }, 18 | output: { 19 | path: Path.resolve(__dirname, "../public/assets"), 20 | publicPath: "assets/", 21 | filename: (IS_PRODUCTION ? "[hash].js" : "bundle.js") 22 | }, 23 | module: { 24 | loaders: [ 25 | { test: /\.js(x?)$/, include: [/lib/, /client/], exclude: [/node_modules/], loaders: JSX_WITH_HOT_LOEADERS }, 26 | { test: /\.jpg$/, loader: "file-loader" }, 27 | { test: /\.png$/, loader: "url-loader?prefix=/public/&limit=10000&mimetype=image/png"}, 28 | { test: /\.css$/, loader: CSS_LOADER }, 29 | { test: /\.styl$/, loader: 'style-loader!css-loader!stylus-loader'}, 30 | ] 31 | }, 32 | plugins: [ 33 | new HtmlWebpackPlugin({ 34 | template: "./client/index.html", 35 | filename: "../index.html" 36 | }) 37 | ], 38 | stylus: { 39 | use: [nib()] 40 | } 41 | }; 42 | 43 | if (IS_PRODUCTION) { 44 | webpackConfig.plugins.push( 45 | new webpack.optimize.DedupePlugin() 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var react = require('gulp-react'); 3 | var webpack = require('webpack'); 4 | var WebpackDevServer = require('webpack-dev-server'); 5 | var runSequence = require('run-sequence'); 6 | var gutil = require('gulp-util'); 7 | 8 | gulp.task('build', function(callback) { 9 | return gulp.src(__dirname + '/src/*.js') 10 | .pipe(react({harmony: true})) 11 | .pipe(gulp.dest(__dirname + '/lib')); 12 | }); 13 | 14 | gulp.task('copy-examples', function() { 15 | gulp.src([__dirname + '/client/scripts/index.js']) 16 | .pipe(gulp.dest(__dirname + '/public/assets/examples')); 17 | }); 18 | 19 | gulp.task('copy-vendor', function() { 20 | gulp.src([__dirname + '/client/vendor/**/*']) 21 | .pipe(gulp.dest(__dirname + '/public/assets')); 22 | }); 23 | 24 | gulp.task('webpack:build', function(callback) { 25 | var myConfig = Object.create(require('./client/webpack.config')); 26 | myConfig.plugins = myConfig.plugins.concat( 27 | new webpack.DefinePlugin({ 28 | "process.env": { 29 | "NODE_ENV": JSON.stringify("production") 30 | } 31 | }), 32 | new webpack.optimize.DedupePlugin() 33 | ); 34 | 35 | webpack(myConfig, function(err, stats) { 36 | if (err) { throw new gutil.PluginError('webpack:build', err); } 37 | gutil.log("[webpack:build]", stats.toString({ 38 | colors: true 39 | })); 40 | 41 | callback(); 42 | }); 43 | }); 44 | 45 | gulp.task('webpack:dev-server', function(callback) { 46 | var webpackConfig = require('./client/webpack.config'); 47 | var compiler = webpack(webpackConfig); 48 | 49 | new WebpackDevServer(compiler, { 50 | contentBase: './public', 51 | inline: true, 52 | hot: true 53 | }).listen(8080, "localhost", function(err) { 54 | if (err) throw new gutil.PluginError("webpack:dev-server", err); 55 | 56 | gutil.log('[webpack:dev-server]', 'http://localhost:8080/webpack-dev-server/index.html'); 57 | }); 58 | }); 59 | 60 | gulp.task('dev', function(callback) { 61 | runSequence('webpack:build', 'copy-vendor', 'copy-examples', 'webpack:dev-server', callback); 62 | }); 63 | 64 | gulp.task('prepare-publish', function(callback){ 65 | runSequence('webpack:build', 'copy-vendor', 'copy-examples', callback); 66 | }); 67 | -------------------------------------------------------------------------------- /lib/ReactFitText.js: -------------------------------------------------------------------------------- 1 | /* 2 | * React FitText 3 | * https://github.com/gianu/react-fittext 4 | * 5 | * A port of the jQuery plugin: http://github.com/davatron5000/FitText.js 6 | * 7 | * Released under the MIT license 8 | * http://gianu.mit-license.org 9 | */ 10 | 'use strict'; 11 | 12 | var React = require('react'); 13 | var ReactDOM = require('react-dom'); 14 | var ReactPropTypes = require('prop-types'); 15 | var createClass = require('create-react-class'); 16 | 17 | // Map from node to options 18 | const nodes = new Map(); 19 | 20 | function updateElementStyle(element, options, width) { 21 | element.style.fontSize = `${Math.min(Math.max(width / (options.compressor * 10), options.minFontSize), options.maxFontSize)}px`; 22 | } 23 | 24 | let updateQueued = false; 25 | 26 | function onBodyResize() { 27 | updateQueued = true; 28 | const widths = []; 29 | nodes.forEach((options, element) => { 30 | widths.push(element.offsetWidth); 31 | }); 32 | let i = 0; 33 | nodes.forEach((options, element) => { 34 | updateElementStyle(element, options, widths[i]); 35 | i += 1; 36 | }); 37 | } 38 | 39 | window.addEventListener("resize", onBodyResize); 40 | window.addEventListener("load", onBodyResize); 41 | 42 | module.exports = createClass({ 43 | displayName: 'ReactFitText', 44 | 45 | propTypes: { 46 | children: ReactPropTypes.element.isRequired, 47 | compressor: ReactPropTypes.number, 48 | minFontSize: ReactPropTypes.number, 49 | maxFontSize: ReactPropTypes.number, 50 | }, 51 | 52 | getDefaultProps: function() { 53 | return { 54 | compressor: 1.0, 55 | minFontSize: Number.NEGATIVE_INFINITY, 56 | maxFontSize: Number.POSITIVE_INFINITY 57 | }; 58 | }, 59 | 60 | componentWillMount: function() { 61 | if (!updateQueued) { 62 | window.requestAnimationFrame(onBodyResize); 63 | } 64 | }, 65 | 66 | componentWillUnmount: function() { 67 | if (this._childRef) { 68 | nodes.delete(this._childRef); 69 | } 70 | }, 71 | 72 | componentDidUpdate: function() { 73 | onBodyResize(); 74 | }, 75 | 76 | _renderChildren: function(){ 77 | var _this = this; 78 | 79 | return React.Children.map(this.props.children, function (child) { 80 | return React.cloneElement(child, { ref: function ref(c) { 81 | if (c) { 82 | nodes.set(c, _this.props); 83 | } 84 | _this._childRef = c; 85 | } }); 86 | }); 87 | }, 88 | render: function() { 89 | return this._renderChildren()[0]; 90 | } 91 | }); 92 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-fittext", 3 | "version": "1.0.0", 4 | "description": "A React plugin to fit the text in the screen", 5 | "main": "lib/ReactFitText.js", 6 | "scripts": { 7 | "build_client": "webpack -p --config ./client/webpack.config.js", 8 | "dev_install": "npm install && npm dedupe", 9 | "dev": "webpack --config ./client/webpack.config.js && webpack-dev-server --config ./client/webpack.config.js --inline --hot --content-base ./public" 10 | }, 11 | "keywords": [ 12 | "react", 13 | "fittext", 14 | "responsive", 15 | "react-component" 16 | ], 17 | "author": { 18 | "name": "Sergio R. Gianazza", 19 | "email": "sgianazza@gmail.com", 20 | "url": "http://www.softwarepsychonaut.com" 21 | }, 22 | "license": { 23 | "type": "MIT", 24 | "url": "http://www.mit-license.org" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/gianu/react-fittext/issues" 28 | }, 29 | "homepage": "http://softwarepsychonaut.com/react-fittext", 30 | "devDependencies": { 31 | "babel": "^5.8.23", 32 | "babel-core": "^5.8.25", 33 | "babel-loader": "^5.3.2", 34 | "babel-runtime": "^5.8.24", 35 | "css-loader": "^0.9.0", 36 | "expose-loader": "^0.5.3", 37 | "file-loader": "^0.8.1", 38 | "gulp": "~3.9.1", 39 | "gulp-jshint": "^2.1.0", 40 | "gulp-react": "^3.1.0", 41 | "gulp-util": "^3.0.1", 42 | "html-webpack-plugin": "^1.6.1", 43 | "nib": "^1.1.0", 44 | "react": "^16.5.2", 45 | "react-dom": "^16.5.2", 46 | "react-hot-loader": "3.1.1", 47 | "run-sequence": "^2.2.1", 48 | "style-loader": "^0.12.4", 49 | "stylus": "^0.54.5", 50 | "stylus-loader": "^1.3.0", 51 | "url-loader": "^0.5.5", 52 | "webpack": "^1.4.7", 53 | "webpack-dev-server": "^1.6.5" 54 | }, 55 | "peerDependencies": { 56 | "react": ">=15.0.0", 57 | "react-dom": ">=15.0.0" 58 | }, 59 | "dependencies": { 60 | "create-react-class": ">=15.5.3", 61 | "prop-types": ">=15.5.10" 62 | } 63 | } 64 | --------------------------------------------------------------------------------