├── .gitignore
├── CHANGELOG.md
├── Gruntfile.js
├── LICENSE.md
├── README.md
├── dist
├── react-input-placeholder.js
└── react-input-placeholder.min.js
├── package.json
└── src
├── react-input-placeholder.js
└── umd.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.DS_Store
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.2.0
2 | ### Bugfixes
3 | - [Fix prop warning](https://github.com/enigma-io/react-input-placeholder/pull/12)
4 | - [Remove createFactory calls (fixes JSX usage)](https://github.com/enigma-io/react-input-placeholder/pull/11)
5 | - [Fix IE8 incompatibility](https://github.com/enigma-io/react-input-placeholder/pull/4)
6 |
7 | **If you are not using JSX, you will need to wrap the use of this module in `createFactory`.**
8 |
9 | ## 1.1.0
10 | ### Bugfixes
11 | - [Make module compatible with newer versions of React](https://github.com/enigma-io/react-input-placeholder/pull/7)
12 | - [Check for document before using it](https://github.com/enigma-io/react-input-placeholder/pull/6)
13 |
14 | ### Deprecations
15 | - Support for React versions `< 0.13.0` will be removed in `2.0.0`
16 |
17 | ## 1.0.0
18 | - Initial commit
19 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | grunt.initConfig({
3 | pkg: grunt.file.readJSON('package.json'),
4 | browserify: {
5 | dist: {
6 | files: {
7 | 'dist/react-input-placeholder.js': ['src/umd.js']
8 | },
9 | }
10 | },
11 | uglify: {
12 | options: {
13 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd") %>) - <%= pkg.author %> */\n'
14 | },
15 | dist: {
16 | files: {
17 | 'dist/react-input-placeholder.min.js': ['dist/react-input-placeholder.js']
18 | }
19 | }
20 | },
21 | jshint: {
22 | options: {
23 | camelcase: true,
24 | nonew: true,
25 | plusplus: true,
26 | quotmark: true,
27 | bitwise: true,
28 | forin: true,
29 | curly: true,
30 | eqeqeq: true,
31 | immed: true,
32 | latedef: true,
33 | newcap: true,
34 | noarg: true,
35 | undef: true,
36 | unused: true,
37 | regexp: true,
38 | trailing: true,
39 | node: true,
40 | browser: true,
41 | laxbreak: true
42 | },
43 | gruntfile: {
44 | files: {
45 | src: ['Gruntfile.js']
46 | }
47 | },
48 | dev: {
49 | files: {
50 | src: ['src/**/*.js']
51 | },
52 | options: {
53 | debug: true,
54 | devel: true
55 | }
56 | },
57 | dist: {
58 | files: {
59 | src: ['src/**/*.js']
60 | }
61 | }
62 | }
63 | });
64 |
65 | grunt.loadNpmTasks('grunt-contrib-jshint');
66 | grunt.loadNpmTasks('grunt-browserify');
67 | grunt.loadNpmTasks('grunt-contrib-uglify');
68 |
69 | grunt.registerTask('lint', ['jshint']);
70 | grunt.registerTask('dist', ['browserify', 'uglify']);
71 | grunt.registerTask('default', ['jshint', 'dist']);
72 | };
73 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Enigma Technologies, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | React Input and Textarea with Placeholder Shim
2 | =======================
3 |
4 | `PlaceholderShim` provides `Input` and `Textarea`, small wrappers around `React.createElement('input')` and `React.createElement('textarea')` respectively that shims in `placeholder` functionality for browsers that don't natively support it. Currently only tested with IE9.
5 |
6 | Demo: https://jsfiddle.net/69z2wepo/16498/
7 |
8 | ## Getting Started
9 | ### Browserify
10 |
11 | Install: `npm install react-input-placeholder`
12 |
13 | Require:
14 |
15 | ```js
16 | Input = require('react-input-placeholder')(React).Input;
17 | Textarea = require('react-input-placeholder')(React).Textarea;
18 | ```
19 |
20 | ### No module
21 |
22 | The compiled bundle sits in the `dist/` folder.
23 |
24 | ```js
25 |
26 |
30 | ```
31 |
32 | ## Usage
33 |
34 | You can use `Input` or `Textarea` exactly the same way you'd use `React.createElement('input')` or `` in JSX. All attributes will be passed on, and all event callbacks will be called. However, please note that the placeholder shim only works on [controlled](http://facebook.github.io/react/docs/forms.html#controlled-components) inputs (i.e., you must provide a `value` or `valueLink` prop).
35 |
36 | When the placeholder text is visible, the `placeholder` CSS class will be added to the `input` element so you can style it, e.g.
37 |
38 | ```css
39 | input.placeholder,
40 | textarea.placeholder {
41 | color: gray;
42 | font-style: italic;
43 | }
44 | ```
45 |
46 | ### Before
47 |
48 | Placeholder doesn't show on IE9.
49 |
50 | ```html
51 |
52 | ```
53 |
54 | ### After
55 |
56 | Works on IE9!
57 |
58 | ```html
59 |
60 | ```
61 |
62 | ## Building yourself
63 |
64 | ```shell
65 | npm install
66 | grunt dist
67 | ```
68 |
69 | ## License
70 |
71 | MIT
72 |
--------------------------------------------------------------------------------
/dist/react-input-placeholder.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o (http://enigma.io) */
2 | !function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g (http://enigma.io)",
6 | "main": "src/react-input-placeholder.js",
7 | "repository": {
8 | "type": "git",
9 | "url": "git://github.com/enigma-io/react-input-placeholder.git"
10 | },
11 | "keywords": [
12 | "placeholder",
13 | "input",
14 | "react",
15 | "react-component"
16 | ],
17 | "contributors": [
18 | "Joe Natalzia",
19 | "Evan Jacobs "
20 | ],
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/enigma-io/react-input-placeholder/issues"
24 | },
25 | "browser": {
26 | "react": "react"
27 | },
28 | "devDependencies": {
29 | "grunt": "^0.4.1",
30 | "grunt-browserify": "^1.2.9",
31 | "grunt-contrib-jshint": "^0.7.0",
32 | "grunt-contrib-uglify": "^0.2.4",
33 | "react": "^0.13.3"
34 | },
35 | "scripts": {
36 | "build": "grunt",
37 | "test": "grunt lint"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/react-input-placeholder.js:
--------------------------------------------------------------------------------
1 | var isPlaceholderSupported = (typeof document !== 'undefined')
2 | && 'placeholder' in document.createElement('input');
3 |
4 | /**
5 | * Input is a wrapper around React.DOM.input with a `placeholder` shim for IE9.
6 | * NOTE: only supports "controlled" inputs (http://facebook.github.io/react/docs/forms.html#controlled-components)
7 | */
8 | var createShimmedElement = function(React, elementConstructor, name) {
9 | return React.createClass({
10 | displayName: name,
11 |
12 | componentWillMount: function() {
13 | this.needsPlaceholding = this.props.placeholder && !isPlaceholderSupported;
14 | },
15 |
16 | componentWillReceiveProps: function(props) {
17 | this.needsPlaceholding = props.placeholder && !isPlaceholderSupported;
18 | },
19 |
20 | // this component supports valueLink or value/onChange.
21 | // borrowed from LinkedValueMixin.js
22 | getValue: function() {
23 | if (this.props.valueLink) {
24 | return this.props.valueLink.value;
25 | }
26 |
27 | return this.props.value;
28 | },
29 |
30 | getOnChange: function() {
31 | if (this.props.valueLink) {
32 | return this._handleLinkedValueChange;
33 | }
34 |
35 | return this.props.onChange;
36 | },
37 |
38 | _handleLinkedValueChange: function(e) {
39 | this.props.valueLink.requestChange(e.target.value);
40 | },
41 |
42 | // keep track of focus
43 | onFocus: function(e) {
44 | this.hasFocus = true;
45 | this.setSelectionIfNeeded(e.target);
46 |
47 | if (this.props.onFocus) {
48 | return this.props.onFocus(e);
49 | }
50 | },
51 | onBlur: function(e) {
52 | this.hasFocus = false;
53 |
54 | if (this.props.onBlur) {
55 | return this.props.onBlur(e);
56 | }
57 | },
58 |
59 | setSelectionIfNeeded: function(node) {
60 | if ( this.needsPlaceholding
61 | && 'setSelectionRange' in node
62 | && this.hasFocus
63 | && this.isPlaceholding
64 | && (node.selectionStart !== 0 || node.selectionEnd !== 0)) {
65 | node.setSelectionRange(0, 0);
66 | } // if placeholder is visible, ensure cursor is at start of input
67 | },
68 |
69 | onChange: function(e) {
70 | var onChange = this.getOnChange();
71 | var value;
72 | var index;
73 |
74 | if (this.isPlaceholding) {
75 | // remove placeholder when text is added
76 | value = e.target.value;
77 | index = value.indexOf(this.props.placeholder);
78 |
79 | if (index !== -1) {
80 | e.target.value = value.slice(0, index);
81 | }
82 | }
83 |
84 | if (onChange) {
85 | return onChange(e);
86 | }
87 | },
88 |
89 | onSelect: function(e) {
90 | if (this.isPlaceholding) {
91 | this.setSelectionIfNeeded(e.target);
92 |
93 | return false;
94 | } else if (this.props.onSelect) {
95 | return this.props.onSelect(e);
96 | }
97 | },
98 |
99 | componentDidUpdate: function() {
100 | this.setSelectionIfNeeded(this.getDOMNode());
101 | },
102 |
103 | render: function() {
104 | var props = {};
105 | var value;
106 | var key;
107 |
108 | for (key in this.props) {
109 | if (this.props.hasOwnProperty(key)) {
110 | props[key] = this.props[key];
111 | }
112 | }
113 |
114 | if (this.needsPlaceholding) {
115 | // override valueLink and event handlers
116 | props.onFocus = this.onFocus;
117 | props.onBlur = this.onBlur;
118 | props.onChange = this.onChange;
119 | props.onSelect = this.onSelect;
120 | props.valueLink = undefined;
121 |
122 | value = this.getValue();
123 |
124 | if (!value) {
125 | this.isPlaceholding = true;
126 | value = this.props.placeholder;
127 | props.className += ' placeholder';
128 | } else {
129 | this.isPlaceholding = false;
130 | }
131 |
132 | props.value = value;
133 | }
134 |
135 | if (!('createElement' in React)) { /* start -- to be removed in 2.0.0 */
136 | return this.transferPropsTo(elementConstructor());
137 | } else { /* -- end */
138 | return React.createElement(elementConstructor, props, this.props.children);
139 | }
140 | }
141 | });
142 | };
143 |
144 | module.exports = function(React) {
145 | if (!('createElement' in React)) { /* start -- to be removed in 2.0.0 */
146 | return {
147 | Input: createShimmedElement(React, React.DOM.input, 'Input'),
148 | Textarea: createShimmedElement(React, React.DOM.textarea, 'Textarea')
149 | };
150 | } else { /* -- end */
151 | return {
152 | Input: createShimmedElement(React, 'input', 'Input'),
153 | Textarea: createShimmedElement(React, 'textarea', 'Textarea')
154 | };
155 | }
156 | };
157 |
--------------------------------------------------------------------------------
/src/umd.js:
--------------------------------------------------------------------------------
1 | /*global define*/
2 | var reactInputPlaceholder = require('./react-input-placeholder');
3 | if (typeof define === 'function' && define.amd) {
4 | define(['react'], function (React) {
5 | return reactInputPlaceholder(React);
6 | });
7 | } else {
8 | window.PlaceholderShim = reactInputPlaceholder(window.React);
9 | }
--------------------------------------------------------------------------------