├── .gitignore
├── README.md
├── examples
├── README.md
├── components
│ ├── buttons
│ │ └── buttons.js
│ ├── checkbox-groups
│ │ └── checkbox-groups.js
│ ├── checkboxes
│ │ └── checkboxes.js
│ ├── dropdowns
│ │ └── dropdowns.js
│ ├── icons
│ │ ├── icons.jsx
│ │ └── icons.styl
│ ├── images
│ │ └── images.js
│ ├── inputs
│ │ └── inputs.js
│ ├── links
│ │ └── links.js
│ ├── menus
│ │ └── menus.js
│ ├── popups
│ │ ├── popups.jsx
│ │ └── popups.styl
│ ├── progressbars
│ │ └── progressbars.js
│ ├── radio-groups
│ │ └── radio-groups.js
│ ├── radios
│ │ └── radios.js
│ ├── react
│ │ └── react.bemhtml.js
│ ├── selects
│ │ ├── selects.jsx
│ │ └── selects.styl
│ ├── spins
│ │ └── spins.js
│ └── textareas
│ │ └── textareas.js
├── design
│ ├── attach
│ │ ├── attach.css
│ │ └── attach.deps.js
│ ├── button
│ │ ├── button.css
│ │ └── button.deps.js
│ ├── checkbox-group
│ │ ├── checkbox-group.css
│ │ └── checkbox-group.deps.js
│ ├── checkbox
│ │ ├── checkbox.css
│ │ └── checkbox.deps.js
│ ├── dropdown
│ │ ├── dropdown.css
│ │ └── dropdown.deps.js
│ ├── input
│ │ ├── input.css
│ │ └── input.deps.js
│ ├── link
│ │ ├── link.css
│ │ └── link.deps.js
│ ├── menu-item
│ │ ├── menu-item.css
│ │ └── menu-item.deps.js
│ ├── menu
│ │ ├── menu.css
│ │ └── menu.deps.js
│ ├── modal
│ │ ├── modal.css
│ │ └── modal.deps.js
│ ├── popup
│ │ ├── popup.css
│ │ └── popup.deps.js
│ ├── progressbar
│ │ ├── progressbar.css
│ │ └── progressbar.deps.js
│ ├── radio-group
│ │ ├── radio-group.css
│ │ └── radio-group.deps.js
│ ├── radio
│ │ ├── radio.css
│ │ └── radio.deps.js
│ ├── select
│ │ ├── select.css
│ │ └── select.deps.js
│ ├── spin
│ │ ├── spin.css
│ │ └── spin.deps.js
│ └── textarea
│ │ ├── textarea.css
│ │ └── textarea.deps.js
├── index.jsx
├── package.json
├── server.js
└── webpack.config.js
├── package.json
├── src
├── components
│ ├── attach
│ │ └── attach.js
│ ├── button
│ │ └── button.js
│ ├── checkbox-group
│ │ ├── checkbox-group.bemhtml.js
│ │ └── checkbox-group.js
│ ├── checkbox
│ │ ├── __control
│ │ │ └── checkbox__control.bemhtml.js
│ │ ├── _type
│ │ │ └── checkbox_type_button.bemhtml.js
│ │ ├── checkbox.deps.js
│ │ └── checkbox.js
│ ├── control
│ │ └── control.js
│ ├── dropdown
│ │ ├── _switcher
│ │ │ └── dropdown_switcher_link.bemhtml.js
│ │ ├── dropdown.deps.js
│ │ └── dropdown.js
│ ├── icon
│ │ └── icon.js
│ ├── image
│ │ └── image.js
│ ├── input
│ │ ├── __control
│ │ │ └── input__control.bemhtml.js
│ │ ├── _has-clear
│ │ │ └── input_has-clear.bemhtml.js
│ │ ├── input.deps.js
│ │ └── input.js
│ ├── link
│ │ ├── link.bemhtml.js
│ │ └── link.js
│ ├── menu-item
│ │ └── menu-item.js
│ ├── menu
│ │ ├── __group
│ │ │ └── menu__group.js
│ │ ├── menu.bemhtml.js
│ │ └── menu.js
│ ├── modal
│ │ └── modal.js
│ ├── popup
│ │ ├── calc-drawing-params.js
│ │ └── popup.js
│ ├── progressbar
│ │ └── progressbar.js
│ ├── radio-group
│ │ ├── radio-group.bemhtml.js
│ │ └── radio-group.js
│ ├── radio
│ │ ├── __control
│ │ │ └── radio__control.bemhtml.js
│ │ ├── _type
│ │ │ └── radio_type_button.bemhtml.js
│ │ ├── radio.deps.js
│ │ └── radio.js
│ ├── select
│ │ ├── __button
│ │ │ └── select__button.bemhtml.js
│ │ ├── __menu
│ │ │ └── select__menu.bemhtml.js
│ │ ├── __popup
│ │ │ └── select__popup.bemhtml.js
│ │ ├── select.deps.js
│ │ └── select.js
│ ├── spin
│ │ └── spin.js
│ └── textarea
│ │ ├── textarea.bemhtml.js
│ │ └── textarea.js
├── core
│ └── bem
│ │ └── bem.js
├── index.js
├── lib
│ └── window.js
└── provider
│ └── provider.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 | npm-debug.log
4 | dist
5 | examples/index.html
6 | src/provider/templates.js
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-bl
2 |
3 | Powerful React components which uses XJST. Wtf? [Russian](https://github.com/bem/bem-forum-content-ru/issues/961)
4 |
5 | > npm i && cd examples && npm i && npm start
6 |
7 | ### License
8 |
9 | MIT
10 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # react-components examples
2 |
3 | > npm i && npm start && open http://localhost:8080
4 |
--------------------------------------------------------------------------------
/examples/components/buttons/buttons.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Button = require('../../../src').Button;
6 | require('bem-loader!../../design/button/button.css');
7 |
8 | module.exports = class Buttons extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Buttons
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/components/checkbox-groups/checkbox-groups.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const CheckboxGroup = require('../../../src').CheckboxGroup;
6 | require('bem-loader!../../design/checkbox-group/checkbox-group.css');
7 |
8 | module.exports = class CheckboxGroups extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Checkbox groups
14 |
15 |
24 |
25 |
33 |
34 |
35 |
45 |
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/examples/components/checkboxes/checkboxes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Checkbox = require('../../../src').Checkbox;
6 | require('bem-loader!../../design/checkbox/checkbox.css');
7 |
8 | module.exports = class Checkboxes extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Checkboxes
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/components/dropdowns/dropdowns.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Dropdown = require('../../../src').Dropdown;
6 | require('bem-loader!../../design/popup/popup.css');
7 |
8 | module.exports = class Dropdowns extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Dropdowns
14 |
15 |
21 |
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/components/icons/icons.jsx:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Icon = require('../../../src').Icon;
6 | require('./icons.styl');
7 |
8 | module.exports = class Icons extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Icons
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/components/icons/icons.styl:
--------------------------------------------------------------------------------
1 | .icon_theme_islands {
2 | background-size: contain;
3 | width: 50px;
4 | height: 50px;
5 | display: inline-block;
6 | }
7 |
--------------------------------------------------------------------------------
/examples/components/images/images.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Image = require('../../../src').Image;
6 |
7 | module.exports = class Images extends React.Component {
8 |
9 | render() {
10 | return (
11 |
12 |
Images
13 |
14 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/components/inputs/inputs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Input = require('../../../src').Input;
6 | require('bem-loader!../../design/input/input.css');
7 |
8 | module.exports = class Inputs extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Inputs
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/components/links/links.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Link = require('../../../src').Link;
6 | require('bem-loader!../../design/link/link.css');
7 |
8 | module.exports = class Buttons extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Links
14 |
15 | Link S
16 |
17 | Link S
18 |
19 | Link S
20 |
21 | Link M
22 |
23 | Link M
24 |
25 | Link M
26 |
27 | Link L
28 |
29 | Link L
30 |
31 | Link L
32 |
33 | Link XL
34 |
35 | Link XL
36 |
37 | Link XL
38 |
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/components/menus/menus.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Menu = require('../../../src').Menu;
6 | const MenuItem = require('../../../src').MenuItem;
7 | require('bem-loader!../../design/menu/menu.css');
8 | require('bem-loader!../../design/menu-item/menu-item.css');
9 |
10 | module.exports = class Menus extends React.Component {
11 |
12 | render() {
13 | return (
14 |
15 |
Menus
16 |
17 |
18 |
19 | text 1
20 |
21 |
22 | text 2
23 |
24 |
25 |
26 | text 3
27 |
28 |
29 | text 4
30 |
31 |
32 |
33 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/components/popups/popups.jsx:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const ReactDOM = require('react-dom');
3 |
4 | const Popup = require('../../../src').Popup;
5 | require('bem-loader!../../design/popup/popup.css');
6 | require('./popups.styl');
7 | const Link = require('../../../src/').Link;
8 |
9 | const DIRECTIONS = [
10 | 'bottom-left', 'bottom-center', 'bottom-right',
11 | 'top-left', 'top-center', 'top-right',
12 | 'right-top', 'right-center', 'right-bottom',
13 | 'left-top', 'left-center', 'left-bottom',
14 | ];
15 |
16 | const POPUPS = DIRECTIONS.map(d => ({ directions: [d], id: d, }));
17 | POPUPS.push({
18 | directions: null,
19 | id: 'popup-at-50x50',
20 | position: {
21 | top: 50,
22 | left: 50,
23 | },
24 | });
25 |
26 | module.exports = class Popups extends React.Component {
27 |
28 | constructor(props) {
29 | super(props);
30 |
31 | this.state = POPUPS.reduce((acc, cur) => {
32 | acc[cur.id] = false;
33 | return acc;
34 | }, {});
35 | }
36 |
37 | componentDidMount() {
38 | POPUPS.forEach(p => {
39 | if (p.directions) {
40 | this.refs[p.id].setTarget(ReactDOM.findDOMNode(this.refs['target_' + p.id]));
41 | } else if(p.position) {
42 | this.refs[p.id].setPosition(p.position.top, p.position.left);
43 | }
44 | });
45 | }
46 |
47 | renderPopupWithTarget(id, directions, isAutoclosable=false) {
48 | return (
49 |
{ this.setState({ [id]: !this.state[id] }); } }
54 | >
55 | { id }
56 |
57 |
{ this.setState({ [id]: false }); }
61 | : () => {}
62 | }
63 | className='test-popup'
64 | ref={ id }
65 | directions={ directions }
66 | theme='islands'
67 | visible={ this.state[id] }
68 | size='s'
69 | >
70 | { id }
71 |
72 |
);
73 | }
74 |
75 | render() {
76 | return (
77 |
78 |
Popups
79 |
80 |
81 |
82 |
83 | { this.renderPopupWithTarget('top-left', ['top-left']) }
84 |
85 |
86 | { this.renderPopupWithTarget('top-center', ['top-center'], true) }
87 |
88 |
89 | { this.renderPopupWithTarget('top-right', ['top-right']) }
90 |
91 |
92 |
93 |
94 |
95 | { this.renderPopupWithTarget('left-top', ['left-top']) }
96 |
97 |
98 | { this.renderPopupWithTarget('popup-at-50x50', null) }
99 |
100 |
101 | { this.renderPopupWithTarget('right-top', ['right-top']) }
102 |
103 |
104 |
105 |
106 | { this.renderPopupWithTarget('left-center', ['left-center']) }
107 |
108 |
109 | { this.renderPopupWithTarget('right-center', ['right-center']) }
110 |
111 |
112 |
113 |
114 | { this.renderPopupWithTarget('left-bottom', ['left-bottom']) }
115 |
116 |
117 | { this.renderPopupWithTarget('right-bottom', ['right-bottom']) }
118 |
119 |
120 |
121 |
122 |
123 | { this.renderPopupWithTarget('bottom-left', ['bottom-left']) }
124 |
125 |
126 | { this.renderPopupWithTarget('bottom-center', ['bottom-center']) }
127 |
128 |
129 | { this.renderPopupWithTarget('bottom-right', ['bottom-right']) }
130 | <
131 | td className="directions__cell">
132 |
133 |
134 |
135 | );
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/examples/components/popups/popups.styl:
--------------------------------------------------------------------------------
1 | .directions {
2 | padding: 70px;
3 | }
4 |
5 | .directions__cell {
6 | padding: 8px;
7 | }
8 |
9 | .directions__cell_border_yes {
10 | border: 1px solid #ccc;
11 | }
12 |
13 | .directions__cell_align_left {
14 | text-align: left;
15 | }
16 |
17 | .directions__cell_align_center {
18 | text-align: center;
19 | }
20 |
21 | .directions__cell_align_right {
22 | text-align: right;
23 | }
24 |
25 | .popup_visible {
26 | display: block;
27 | }
28 |
29 | .test-popup {
30 | padding: 16px;
31 | }
32 |
--------------------------------------------------------------------------------
/examples/components/progressbars/progressbars.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Progressbar = require('../../../src').Progressbar;
6 | require('bem-loader!../../design/progressbar/progressbar.css');
7 |
8 | module.exports = class Progressbars extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Progressbars
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/components/radio-groups/radio-groups.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const RadioGroup = require('../../../src').RadioGroup;
6 | require('bem-loader!../../design/radio-group/radio-group.css');
7 |
8 | module.exports = class RadioGroups extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Radio groups
14 |
15 |
24 |
25 |
33 |
34 |
35 |
44 |
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/examples/components/radios/radios.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Radio = require('../../../src').Radio;
6 | require('bem-loader!../../design/radio/radio.css');
7 |
8 | module.exports = class Radios extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Radios
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/components/react/react.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('react')(
2 | attrs()(function() {
3 | return {
4 | id: 'root'
5 | };
6 | })
7 | );
8 |
--------------------------------------------------------------------------------
/examples/components/selects/selects.jsx:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const ReactDOM = require('react-dom');
3 |
4 | const Select = require('../../../src').Select;
5 | const Icon = require('../../../src').Icon;
6 |
7 | require('bem-loader!../../design/select/select.css');
8 | require('./selects.styl');
9 |
10 | const OPTIONS_1 = [
11 | {
12 | value: 1,
13 | text: 'Green',
14 | },
15 | {
16 | value: 2,
17 | text: 'Gold',
18 | },
19 | {
20 | value: 3,
21 | text: 'Rose',
22 | }
23 | ];
24 |
25 | const OPTIONS_2 = [
26 | {
27 | value: 1,
28 | text: 'Ivan',
29 | },
30 | {
31 | value: 2,
32 | text: 'Anton',
33 | },
34 | {
35 | value: 3,
36 | text: 'Nikita',
37 | }
38 | ];
39 |
40 |
41 | module.exports = class Selects extends React.Component {
42 |
43 | constructor(props) {
44 | super(props);
45 | }
46 |
47 | render() {
48 | return (
49 |
50 |
Selects
51 | Radio mode
52 |
59 | Check mode
60 |
67 |
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/examples/components/selects/selects.styl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/components/spins/spins.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Spin = require('../../../src').Spin;
6 | require('bem-loader!../../design/spin/spin.css');
7 |
8 | module.exports = class Spins extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Spins
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/components/textareas/textareas.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Textarea = require('../../../src').Textarea;
6 | require('bem-loader!../../design/textarea/textarea.css');
7 |
8 | module.exports = class Textareas extends React.Component {
9 |
10 | render() {
11 | return (
12 |
13 |
Textareas
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/design/attach/attach.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/attach/attach.css
--------------------------------------------------------------------------------
/examples/design/attach/attach.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/button/button.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/button/button.css
--------------------------------------------------------------------------------
/examples/design/button/button.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/checkbox-group/checkbox-group.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/checkbox-group/checkbox-group.css
--------------------------------------------------------------------------------
/examples/design/checkbox-group/checkbox-group.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/checkbox/checkbox.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/checkbox/checkbox.css
--------------------------------------------------------------------------------
/examples/design/checkbox/checkbox.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/dropdown/dropdown.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/dropdown/dropdown.css
--------------------------------------------------------------------------------
/examples/design/dropdown/dropdown.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/input/input.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/input/input.css
--------------------------------------------------------------------------------
/examples/design/input/input.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands', 'has-clear': true } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/link/link.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/link/link.css
--------------------------------------------------------------------------------
/examples/design/link/link.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/menu-item/menu-item.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/menu-item/menu-item.css
--------------------------------------------------------------------------------
/examples/design/menu-item/menu-item.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/menu/menu.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/menu/menu.css
--------------------------------------------------------------------------------
/examples/design/menu/menu.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/modal/modal.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/modal/modal.css
--------------------------------------------------------------------------------
/examples/design/modal/modal.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/popup/popup.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/popup/popup.css
--------------------------------------------------------------------------------
/examples/design/popup/popup.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/progressbar/progressbar.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/progressbar/progressbar.css
--------------------------------------------------------------------------------
/examples/design/progressbar/progressbar.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/radio-group/radio-group.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/radio-group/radio-group.css
--------------------------------------------------------------------------------
/examples/design/radio-group/radio-group.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/radio/radio.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/radio/radio.css
--------------------------------------------------------------------------------
/examples/design/radio/radio.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/select/select.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/select/select.css
--------------------------------------------------------------------------------
/examples/design/select/select.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/spin/spin.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/spin/spin.css
--------------------------------------------------------------------------------
/examples/design/spin/spin.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/design/textarea/textarea.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awinogradov/react-bl/f554aefde301c6c73440eef7d5cf91ee6bb296a7/examples/design/textarea/textarea.css
--------------------------------------------------------------------------------
/examples/design/textarea/textarea.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { theme: 'islands' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/examples/index.jsx:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const ReactDOM = require('react-dom');
3 |
4 | const Buttons = require('./components/buttons/buttons');
5 | const Checkboxes = require('./components/checkboxes/checkboxes');
6 | const CheckboxGroups = require('./components/checkbox-groups/checkbox-groups');
7 | const Dropdowns = require('./components/dropdowns/dropdowns');
8 | const Icons = require('./components/icons/icons.jsx');
9 | const Images = require('./components/images/images');
10 | const Inputs = require('./components/inputs/inputs');
11 | const Links = require('./components/links/links');
12 | const Menus = require('./components/menus/menus');
13 | const Progressbars = require('./components/progressbars/progressbars');
14 | const RadioGroups = require('./components/radio-groups/radio-groups');
15 | const Radios = require('./components/radios/radios');
16 | const Spins = require('./components/spins/spins');
17 | const Textareas = require('./components/textareas/textareas');
18 | const Popups = require('./components/popups/popups.jsx');
19 | const Selects = require('./components/selects/selects.jsx');
20 |
21 | ReactDOM.render(
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ,
40 | document.getElementById('root')
41 | );
42 |
43 | // TODO: refactor next like ./components/buttons
44 |
45 | // import { Select } from './react-components/select/select';
46 | // import { Attach } from './react-components/attach/attach';
47 |
48 | // ReactDom.render(
49 | // /
50 | //
51 | // Select
52 | //
53 | //
54 | //
55 | //
56 | //
57 | //
58 | //
59 | //
60 | //
61 | //
62 | //
63 | //
64 | //
65 | //
66 | //
67 | //
68 |
69 | //
70 | // Attach
71 | //
72 | //
73 | //
74 | //
75 | //
76 | //
77 | //
78 | //
79 | //
80 | //
81 | //
82 | //
83 | //
84 | //
85 | //
86 | //
87 | //
88 | //
94 | //
95 | // ,
96 | // document.getElementById('root')
97 | // );
98 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-components-examples",
3 | "version": "0.0.1",
4 | "main": "index.js",
5 | "scripts": {
6 | "postinstall": "cd ./node_modules/bem-xjst && npm run make",
7 | "build": "STANDALONE=1 webpack",
8 | "dev": "node server.js",
9 | "open": "open http://localhost:8080",
10 | "start": "npm run build && npm run dev && npm run open"
11 | },
12 | "dependencies": {
13 | "bem-components": "git+ssh://git@github.com/awinogradov/bem-components.git#v2",
14 | "bem-core": "git+ssh://git@github.com/bem/bem-core.git#v2",
15 | "bem-xjst": "git+ssh://git@github.com:awinogradov/bem-xjst.git#feature/bemreact",
16 | "react-xjst": "git+ssh://git@github.com/bem-contrib/react-xjst.git"
17 | },
18 | "author": "Anton Winogradov ",
19 | "license": "MIT",
20 | "devDependencies": {
21 | "babel-core": "^6.5.1",
22 | "babel-loader": "^6.2.2",
23 | "babel-preset-es2015": "^6.5.0",
24 | "babel-preset-react": "^6.5.0",
25 | "bem-loader": "^0.4.0",
26 | "browserify": "^13.0.0",
27 | "css-loader": "^0.23.1",
28 | "file-loader": "^0.8.5",
29 | "fs-extra": "^0.26.7",
30 | "style-loader": "^0.13.0",
31 | "stylus": "^0.54.2",
32 | "stylus-loader": "^2.0.0",
33 | "url-loader": "^0.5.7",
34 | "webpack": "^1.12.13",
35 | "webpack-dev-server": "^1.14.1"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/examples/server.js:
--------------------------------------------------------------------------------
1 | const WebpackDevServer = require('webpack-dev-server');
2 | const webpack = require('webpack');
3 | const webpackConfig = require('./webpack.config');
4 |
5 | const compiler = webpack(webpackConfig);
6 |
7 | new WebpackDevServer(compiler, {
8 | contentBase: './dist',
9 | hot: true,
10 | quiet: false,
11 | noInfo: false,
12 | filename: 'index.jsx',
13 | watchOptions: {
14 | aggregateTimeout: 300,
15 | poll: true
16 | },
17 | stats: { colors: true },
18 | }).listen(8080, 'localhost', function(err) {
19 | if (err) {
20 | console.log(err);
21 | } else {
22 | console.log('Webpack dev server listening at localhost:8080');
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/examples/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const bemLoader = require('bem-loader');
3 | const CollectBemAssetsPlugin = bemLoader.CollectBemAssetsPlugin;
4 |
5 | const fs = require('fs-extra');
6 | const bemxjst = require('bem-xjst');
7 | const bemhtml = bemxjst.bemhtml;
8 |
9 | const templates = bemhtml.compile([
10 | fs.readFileSync('./node_modules/bem-core/common.blocks/ua/ua.bemhtml'),
11 | fs.readFileSync('./node_modules/bem-core/common.blocks/page/page.bemhtml'),
12 | fs.readFileSync('./node_modules/bem-core/common.blocks/page/__js/page__js.bemhtml'),
13 | fs.readFileSync('./node_modules/bem-core/common.blocks/page/__css/page__css.bemhtml'),
14 | fs.readFileSync('./components/react/react.bemhtml.js')
15 | ].join(''));
16 |
17 | fs.outputFile(
18 | './dist/index.html',
19 | templates.apply({
20 | block: 'page',
21 | scripts: { elem: 'js', url: './main.js' },
22 | content: { block: 'react' }
23 | })
24 | );
25 |
26 | module.exports = {
27 | entry: './index.jsx',
28 | module: {
29 | loaders: [
30 | {
31 | test: /\.styl$/,
32 | loader: [
33 | 'style-loader',
34 | 'css-loader',
35 | 'stylus-loader'
36 | ].join('!')
37 | },
38 | {
39 | test: /\.(ttf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
40 | loader: 'url-loader?mimetype=application/octet-stream'
41 | },
42 | {
43 | test: /\.(gif)$/i,
44 | loaders: [
45 | 'url-loader?mimetype=image/gif'
46 | ]
47 | },
48 | {
49 | test: /\.(svg)$/i,
50 | loaders: [
51 | 'url-loader?mimetype=image/svg+xml'
52 | ]
53 | },
54 | {
55 | test: /\.jsx?$/,
56 | include: path.resolve(__dirname),
57 | loader: 'babel',
58 | query: {
59 | presets: ['react', 'es2015']
60 | }
61 | }
62 | ]
63 | },
64 | devtool: 'source-map',
65 | output: {
66 | path: path.join(__dirname, 'dist'),
67 | filename: '[name].js',
68 | },
69 | plugins: [
70 | new CollectBemAssetsPlugin({
71 | done: function(data) {
72 | bemLoader.setStylesData(data['styl']);
73 | },
74 | techs: ['styl'],
75 | levels: [
76 | 'bem-components/design/common.blocks',
77 | 'bem-components/design/desktop.blocks',
78 | 'bem-components/common.blocks',
79 | ].map(function(short) {
80 | return path.resolve(process.cwd(), `./node_modules/${short}`);
81 | }).concat([
82 | './components',
83 | './design'
84 | ])
85 | })
86 | ]
87 | };
88 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-bl",
3 | "version": "0.0.1",
4 | "main": "./src/",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "build": "STANDALONE=1 webpack",
8 | "predeploy": "cd examples && npm i && npm run build",
9 | "deploy": "gh-pages -d examples/dist"
10 | },
11 | "dependencies": {
12 | "bem-cn": "^1.2.2",
13 | "bem-components": "^3.0.0",
14 | "bem-core": "^3.0.1",
15 | "bem-naming": "^1.0.1",
16 | "param-case": "^1.1.2",
17 | "react-xjst": "github:bem-contrib/react-xjst#f2dc651",
18 | "xjst-vidom": "github:guria/xjst-vidom#1a7cb7b"
19 | },
20 | "author": "Anton Winogradov ",
21 | "license": "MIT",
22 | "devDependencies": {
23 | "babel-core": "^6.5.1",
24 | "babel-loader": "^6.2.2",
25 | "babel-preset-es2015": "^6.5.0",
26 | "babel-preset-react": "^6.5.0",
27 | "bem-loader": "^0.5.0",
28 | "browserify": "^13.0.0",
29 | "file-loader": "^0.8.5",
30 | "fs-extra": "^0.26.7",
31 | "gh-pages": "^0.11.0",
32 | "react": "^15.1.0",
33 | "react-dom": "^15.1.0",
34 | "url-loader": "^0.5.7",
35 | "webpack": "^1.13.0"
36 | },
37 | "peerDependencies": {
38 | "react": "^15.1.0",
39 | "react-dom": "^15.1.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/attach/attach.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 | const provide = require('../../provider/provider');
5 |
6 | module.exports = class Attach extends React.Component {
7 |
8 | constructor(props, context) {
9 | super(props, context);
10 | this.state = {
11 | focused: false
12 | };
13 | }
14 |
15 | render() {
16 | return provide({
17 | block: 'attach',
18 | attrs: {
19 | onFocus: () => (this.setState({ focused: true })),
20 | onBlur: () => (this.setState({ focused: false }))
21 | },
22 | mods: {
23 | size: this.props.size,
24 | theme: this.props.theme,
25 | focused: this.state.focused,
26 | disabled: this.props.disabled
27 | },
28 | name: this.props.name,
29 | button: this.props.button,
30 | noFileText: this.props.noFileText
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/button/button.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Control = require('../control/control');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Button extends Control {
9 |
10 | constructor(props) {
11 | super(props);
12 |
13 | this._focusedByPointer = false;
14 | this._isPointerPressInProgress = false;
15 | }
16 |
17 | _focus() {
18 | if(this._isPointerPressInProgress) return;
19 |
20 | if(this._focusedByPointer) {
21 | this.setStateAndMod({focused: true});
22 | } else {
23 | this.setStateAndMod({'focused-hard': true});
24 | }
25 | }
26 |
27 | _onFocus(e) {
28 | this._focus();
29 |
30 | this.props.onFocus && this.props.onFocus(e, this.state);
31 | }
32 |
33 | _onBlur(e) {
34 | this.setStateAndMod({pressed: false, 'focused-hard': false});
35 |
36 | super._onBlur(e);
37 | }
38 |
39 | _onClick(e) {
40 | if(!this.props.disabled) {
41 | this.props.onClick && this.props.onClick(e, this.state);
42 | }
43 | }
44 |
45 | _onMouseLeave(e) {
46 | if(this._isPointerPressInProgress) {
47 | this.setStateAndMod({pressed: false});
48 | }
49 |
50 | super._onMouseLeave(e);
51 | }
52 |
53 | _onMouseDown(e) {
54 | this._isPointerPressInProgress = true;
55 |
56 | if(!this.props.disabled) {
57 | this._focusedByPointer = true;
58 | this.setStateAndMod({pressed: true});
59 |
60 | this.props.onMouseDown && this.props.onMouseDown(e, this.state);
61 | }
62 | }
63 |
64 | _onMouseUp(e) {
65 | this._isPointerPressInProgress = false;
66 |
67 | this._focusedByPointer = true;
68 | this._focus();
69 | this._focusedByPointer = false;
70 |
71 | this.setStateAndMod({pressed: false});
72 |
73 | this.props.onMouseUp && this.props.onMouseUp(e, this.state);
74 | }
75 |
76 | render() {
77 | return provide({
78 | block: this.bem.block,
79 | attrs: Object.assign({
80 | onMouseEnter: this._onMouseEnter.bind(this),
81 | onMouseLeave: this._onMouseLeave.bind(this),
82 | onFocus: this._onFocus.bind(this),
83 | onBlur: this._onBlur.bind(this),
84 | onClick: this._onClick.bind(this),
85 | onMouseDown: this._onMouseDown.bind(this),
86 | onMouseUp: this._onMouseUp.bind(this),
87 | 'aria-disabled': this.props.disabled
88 | }, this.props.attrs),
89 | mods: {
90 | size: this.props.size,
91 | theme: this.props.theme,
92 | view: this.props.view,
93 | hovered: this.state.hovered,
94 | focused: this.state.focused,
95 | 'focused-hard': this.state['focused-hard'],
96 | pressed: this.state.pressed,
97 | togglable: this.props.togglable,
98 | disabled: this.props.disabled
99 | },
100 | mix: this.props.mix,
101 | title: this.props.title,
102 | text: this.props.text,
103 | content: this.props.children
104 | });
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/components/checkbox-group/checkbox-group.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('checkbox-group')(
2 | content()(function() {
3 | 'use strict';
4 |
5 | var mods = this.mods,
6 | ctx = this.ctx,
7 | val = ctx.val,
8 | isValDef = typeof val !== 'undefined';
9 |
10 | const React = require('react');
11 | const Checkbox = require('../components/checkbox/checkbox');
12 |
13 | if(isValDef && !Array.isArray(val)) throw Error('checkbox-group: val must be an array');
14 |
15 | return (ctx.options || []).map(function(option, i) {
16 | return [
17 | !!i && !mods.type && { tag : 'br' },
18 | React.createElement(Checkbox, {
19 | theme: mods.theme,
20 | size: mods.size,
21 | type: mods.type,
22 | checked: isValDef && val.indexOf(option.val) > -1,
23 | disabled: option.disabled || mods.disabled,
24 | name: ctx.name,
25 | val: option.val,
26 | text: option.text,
27 | title: option.title,
28 | icon: option.icon
29 | })
30 | ];
31 | });
32 | })
33 | );
34 |
--------------------------------------------------------------------------------
/src/components/checkbox-group/checkbox-group.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../core/bem/bem');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class CheckboxGroup extends BEM {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.block,
13 | attrs: {
14 |
15 | },
16 | mods: {
17 | type: this.props.type,
18 | size: this.props.size,
19 | theme: this.props.theme,
20 | hovered: this.state.hovered,
21 | focused: this.state.focused,
22 | disabled: this.props.disabled
23 | },
24 | name: this.props.name,
25 | val: this.props.val,
26 | options: this.props.options
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/checkbox/__control/checkbox__control.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('checkbox').elem('control')(
2 | attrs()(function() {
3 | 'use strict';
4 |
5 | const originalAttrs = applyNext();
6 |
7 | originalAttrs.autoComplete = originalAttrs.autocomplete;
8 | delete originalAttrs.autocomplete;
9 | originalAttrs.defaultValue = originalAttrs.value;
10 | delete originalAttrs.value;
11 | originalAttrs.defaultChecked = originalAttrs.checked;
12 | delete originalAttrs.checked;
13 |
14 | return originalAttrs;
15 | })
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/checkbox/_type/checkbox_type_button.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('checkbox').mod('type', 'button')(
2 | content()(function() {
3 | 'use strict';
4 |
5 | const React = require('react');
6 | const Button = require('../components/button/button');
7 |
8 | const ctx = this.ctx,
9 | mods = this.mods;
10 |
11 | const originalContent = applyNext();
12 |
13 | // Replace XJST Button
14 | // FIXME: support icon!
15 | originalContent[0] = React.createElement(Button, {
16 | theme: mods.theme,
17 | size: mods.size,
18 | togglable: 'check',
19 | text: ctx.text,
20 | attrs: { // FIXME: make plain props
21 | role: 'checkbox',
22 | 'aria-pressed': undefined,
23 | 'aria-checked': String(!!mods.checked)
24 | },
25 | title: ctx.title
26 | });
27 |
28 | return originalContent;
29 | })
30 | );
31 |
--------------------------------------------------------------------------------
/src/components/checkbox/checkbox.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { type: 'button' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/src/components/checkbox/checkbox.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Control = require('../control/control');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Checkbox extends Control {
9 |
10 | _onChange() {
11 | this.setStateAndMod({checked: !this.state.checked});
12 |
13 | this.props.onChange && this.props.onChange(e, this.state);
14 | }
15 |
16 | componentWillMount() {
17 | this.state.checked = this.props.checked;
18 | }
19 |
20 | render() {
21 | return provide({
22 | block: this.bem.block,
23 | attrs: {
24 | onMouseEnter: this._onMouseEnter.bind(this),
25 | onMouseLeave: this._onMouseLeave.bind(this),
26 | onFocus: this._onFocus.bind(this),
27 | onBlur: this._onBlur.bind(this),
28 | onChange: this._onChange.bind(this)
29 | },
30 | mods: {
31 | size: this.props.size,
32 | theme: this.props.theme,
33 | type: this.props.type,
34 | checked: this.state.checked,
35 | hovered: this.state.hovered,
36 | focused: this.state.focused,
37 | disabled: this.props.disabled
38 | },
39 | id: this.props.id,
40 | name: this.props.name,
41 | tabIndex: this.props.tabIndex,
42 | title: this.props.title,
43 | icon: this.props.icon,
44 | text: this.props.text,
45 | val: this.props.val
46 | });
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/control/control.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 | const Component = React.Component;
5 | const PropTypes = React.PropTypes;
6 |
7 | const BEM = require('../../core/bem/bem');
8 |
9 | module.exports = class Control extends BEM {
10 |
11 | _onFocus(e) {
12 | this.setStateAndMod({focused: true});
13 |
14 | this.props.onFocus && this.props.onFocus(e, this.state);
15 | }
16 |
17 | _onMouseEnter(e) {
18 | this.setStateAndMod({hovered: true});
19 |
20 | this.props.onMouseEnter && this.props.onMouseEnter(e, this.state);
21 | }
22 |
23 | _onMouseLeave(e) {
24 | this.setStateAndMod({hovered: false});
25 |
26 | this.props.onMouseLeave && this.props.onMouseLeave(e, this.state);
27 | }
28 |
29 | _onBlur(e) {
30 | this.setStateAndMod({focused: false, hovered: false});
31 |
32 | this.props.onBlur && this.props.onBlur(e, this.state);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/dropdown/_switcher/dropdown_switcher_link.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('dropdown').mod('switcher', 'link').elem('switcher').def()(function() {
2 | 'use strict';
3 |
4 | // FIXME: it's should work fine!
5 | return applyNext();
6 |
7 | const React = require('react');
8 | const Link = require('../components/link/link');
9 | const dropdown = this._dropdown;
10 | const switcher = dropdown.switcher;
11 |
12 | if(Array.isArray(switcher)) return switcher;
13 |
14 | let dropdownMods = this.mods;
15 | return this.isSimple(switcher) ?
16 | applyCtx(React.createElement(Link, {
17 | theme: dropdownMods.theme,
18 | pseudo: true,
19 | attrs: {
20 | 'aria-haspopup': true,
21 | 'aria-controls': this._popupId,
22 | 'aria-expanded': false
23 | },
24 | disabled: dropdownMods.disabled,
25 | mix: apply('mix')
26 | }, switcher)) :
27 | applyCtx(switcher);
28 | });
29 |
--------------------------------------------------------------------------------
/src/components/dropdown/dropdown.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | {
4 | mods: { switcher: ['link', 'button'] },
5 | elems: ['switcher']
6 | }
7 | ]
8 | });
9 |
--------------------------------------------------------------------------------
/src/components/dropdown/dropdown.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../core/bem/bem');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Dropdown extends BEM {
9 |
10 | _onSwitcherClick() {
11 | this.setStateAndMod({opened: true});
12 | this.props.onSwitcherClick && this.props.onSwitcherClick();
13 | }
14 |
15 | render() {
16 | return provide({
17 | block: this.bem.block,
18 | attrs: {
19 | 'aria-expanded': this.props.opened,
20 | onClick: this._onSwitcherClick
21 | },
22 | mods: {
23 | size: this.props.size,
24 | theme: this.props.theme,
25 | switcher: this.props.type,
26 | opened: this.props.opened,
27 | disabled: this.props.disabled
28 | },
29 | switcher: this.props.switcher,
30 | popup: this.props.popup
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/icon/icon.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../core/bem/bem');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Icon extends BEM {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.block,
13 | mods: {
14 | size: this.props.size,
15 | theme: this.props.theme
16 | },
17 | url: this.props.url,
18 | content: this.props.children
19 | });
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/image/image.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../core/bem/bem');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Image extends BEM {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.block,
13 | mods: {
14 | size: this.props.size,
15 | theme: this.props.theme
16 | },
17 | url: this.props.url,
18 | title: this.props.title,
19 | alt: this.props.alt,
20 | width: this.props.width,
21 | height: this.props.height,
22 | content: this.props.children
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/input/__control/input__control.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('input').elem('control')(
2 | attrs()(function() {
3 | 'use strict';
4 |
5 | const originalAttrs = applyNext();
6 |
7 | originalAttrs.defaultValue = originalAttrs.value;
8 | delete originalAttrs.value;
9 | originalAttrs.tabIndex = originalAttrs.tabindex,
10 | delete originalAttrs.tabindex;
11 | originalAttrs.placeHolder = originalAttrs.placeholder
12 | delete originalAttrs.placeholder;
13 | originalAttrs.autoComplete = originalAttrs.autocomplete;
14 | delete originalAttrs.autocomplete;
15 |
16 | return originalAttrs;
17 | })
18 | );
19 |
--------------------------------------------------------------------------------
/src/components/input/_has-clear/input_has-clear.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('input').mod('has-clear', true).elem('box')(
2 | content()(function() {
3 | 'use strict';
4 |
5 | const originalContent = applyNext();
6 |
7 | // extend input__clear by React bindings
8 | originalContent[1].attrs = originalContent[1].attrs || {};
9 | originalContent[1].attrs.onClick = this._input.attrs.onClearClick;
10 | return originalContent;
11 | })
12 | );
13 |
--------------------------------------------------------------------------------
/src/components/input/input.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | {
4 | mods: { 'has-clear': true },
5 | elems: ['clear']
6 | }
7 | ]
8 | });
9 |
--------------------------------------------------------------------------------
/src/components/input/input.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Control = require('../control/control');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Input extends Control {
9 |
10 | _onChange() {
11 | this.props.onChange && this.props.onChange(e, this.state);
12 | }
13 |
14 | _onClearClick() {
15 | console.log('hi there');
16 | }
17 |
18 | componentWillMount() {
19 | this.state.val = this.props.val;
20 | }
21 |
22 | render() {
23 | return provide({
24 | block: this.bem.block,
25 | attrs: {
26 | onMouseEnter: this._onMouseEnter.bind(this),
27 | onMouseLeave: this._onMouseLeave.bind(this),
28 | onFocus: this._onFocus.bind(this),
29 | onBlur: this._onBlur.bind(this),
30 | onChange: this._onChange.bind(this),
31 | onClearClick: this._onClearClick.bind(this)
32 | },
33 | mods: {
34 | size: this.props.size,
35 | theme: this.props.theme,
36 | hovered: this.state.hovered,
37 | focused: this.state.focused,
38 | 'has-clear': this.props.hasClear,
39 | disabled: this.props.disabled
40 | },
41 | id: this.props.id,
42 | tabIndex: this.props.tabIndex,
43 | name: this.props.name,
44 | placeholder: this.props.placeholder,
45 | val: this.state.val
46 | });
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/link/link.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('link')(
2 | attrs()(function() {
3 | 'use strict';
4 |
5 | const originalAttrs = applyNext();
6 |
7 | originalAttrs.tabIndex = originalAttrs.tabindex;
8 | delete originalAttrs.tabindex;
9 |
10 | return originalAttrs;
11 | })
12 | );
13 |
--------------------------------------------------------------------------------
/src/components/link/link.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Control = require('../control/control');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Link extends Control {
9 |
10 | constructor(props, context) {
11 | super(props, context);
12 | }
13 |
14 | _onClick(e) {
15 | if(this.props.disabled || this.props.pseudo) {
16 | e.preventDefault();
17 | }
18 |
19 | this.props.onClick && this.props.onClick(e, this.state);
20 | }
21 |
22 | render() {
23 | return provide({
24 | block: this.bem.block,
25 | attrs: Object.assign({
26 | onMouseEnter: this._onMouseEnter.bind(this),
27 | onMouseLeave: this._onMouseLeave.bind(this),
28 | onFocus: this._onFocus.bind(this),
29 | onBlur: this._onBlur.bind(this),
30 | onClick: this._onClick.bind(this),
31 | tabIndex: this.props.tabIndex,
32 | 'aria-disabled': this.props.disabled,
33 | target: this.props.target
34 | }, this.props.attrs),
35 | mods: {
36 | size: this.props.size,
37 | theme: this.props.theme,
38 | pseudo: this.props.pseudo,
39 | focused: this.state.focused,
40 | hovered: this.state.hovered,
41 | disabled: this.props.disabled
42 | },
43 | mix: this.props.mix,
44 | url: this.props.url,
45 | content: this.props.children
46 | });
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/menu-item/menu-item.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../core/bem/bem');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class MenuItem extends BEM {
9 |
10 | _onMouseEnter(e) {
11 | this.setStateAndMod({hovered: true});
12 |
13 | this.props.onMouseEnter && this.props.onMouseEnter(e, this.state);
14 | }
15 |
16 | _onMouseLeave(e) {
17 | this.setStateAndMod({hovered: false});
18 |
19 | this.props.onMouseLeave && this.props.onMouseLeave(e, this.state);
20 | }
21 |
22 | _onClick(e) {
23 | if(!this.props.disabled) {
24 | this.props.onClick && this.props.onClick(e, this.state.val);
25 | }
26 | }
27 |
28 | componentWillMount() {
29 | this.state.val = this.props.val;
30 | }
31 |
32 | render() {
33 | return provide({
34 | block: this.bem.block,
35 | attrs: {
36 | onMouseEnter: this._onMouseEnter.bind(this),
37 | onMouseLeave: this._onMouseLeave.bind(this),
38 | onClick: this._onClick.bind(this),
39 | 'aria-disabled': this.props.disabled,
40 | 'aria-checked': this.props.checked
41 | },
42 | mods: {
43 | type: this.props.link,
44 | size: this.props.size,
45 | theme: this.props.theme,
46 | hovered: this.state.hovered,
47 | focused: this.state.focused,
48 | checked: this.props.checked,
49 | disabled: this.props.disabled
50 | },
51 | val: this.state.val,
52 | content: this.props.children
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/menu/__group/menu__group.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../../core/bem/bem');
6 | const provide = require('../../../provider/provider');
7 |
8 | module.exports = class MenuElemGroup extends BEM {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.block,
13 | elem: 'group',
14 | title: this.props.title,
15 | content: this.props.children
16 | });
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/menu/menu.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('menu')(
2 | attrs()(function() {
3 | 'use strict';
4 |
5 | const originalAttrs = applyNext();
6 |
7 | originalAttrs.tabIndex = originalAttrs.tabindex;
8 | delete originalAttrs.tabindex;
9 |
10 | return originalAttrs;
11 | })
12 | );
13 |
--------------------------------------------------------------------------------
/src/components/menu/menu.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Control = require('../control/control');
6 | const provide = require('../../provider/provider');
7 |
8 | class Menu extends Control {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.block,
13 | attrs: {
14 | onMouseEnter: this._onMouseEnter.bind(this),
15 | onMouseLeave: this._onMouseLeave.bind(this),
16 | // TODO
17 | onKeyDown: this.handleKeyDown.bind(this),
18 | onFocus: this._onFocus.bind(this),
19 | onBlur: this._onBlur.bind(this)
20 | },
21 | mods: {
22 | mode: this.props.mode,
23 | size: this.props.size,
24 | theme: this.props.theme,
25 | hovered: this.state.hovered,
26 | focused: this.state.focused,
27 | disabled: this.props.disabled
28 | },
29 | mix: this.props.mix,
30 | val: this.state.value,
31 | content: this.props.children
32 | });
33 | }
34 |
35 | handleKeyDown(e) {
36 | if (this.props.onKeyDown) {
37 | this.props.onKeyDown(e);
38 | }
39 | }
40 | }
41 |
42 | Menu.Group = require('./__group/menu__group');
43 |
44 | module.exports = Menu;
45 |
--------------------------------------------------------------------------------
/src/components/modal/modal.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
--------------------------------------------------------------------------------
/src/components/popup/calc-drawing-params.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /**
3 | * based on https://github.com/bem/bem-components/blob/v2/common.blocks/popup/_target/popup_target.js
4 | */
5 |
6 | const VIEWPORT_ACCURACY_FACTOR = 0.99;
7 |
8 | const DEFAULT_DIRECTIONS = [
9 | 'bottom-left', 'bottom-center', 'bottom-right',
10 | 'top-left', 'top-center', 'top-right',
11 | 'right-top', 'right-center', 'right-bottom',
12 | 'left-top', 'left-center', 'left-bottom',
13 | ];
14 |
15 | /**
16 | * @typedef {Object} DrawingParams
17 | * @params {String} direction
18 | * @params {Number} left
19 | * @params {Number} top
20 | */
21 |
22 | /**
23 | * @typedef {Object} PopupDimension
24 | * @param {Number} width
25 | * @param {Number} height
26 | * @param {Number} area
27 | */
28 |
29 | /**
30 | * @typedef {Object} Rect
31 | * @param {Number} top
32 | * @param {Number} left
33 | * @param {Number} [bottom]
34 | * @param {Number} [right]
35 | * @param {Number} [width]
36 | * @param {Number} [height]
37 | */
38 |
39 | /**
40 | * @param {PopupHash} popup
41 | * @returns {DrawingParams}
42 | */
43 | function calcBestDrawingParams(popup) {
44 | if (!popup.isTargetAnchor && popup.targetPosition) {
45 | return {
46 | left: popup.targetPosition.left,
47 | top: popup.targetPosition.top,
48 | direction: null,
49 | };
50 | }
51 |
52 | let popupDimensions = calcPopupDimensions(popup);
53 | let targetDimensions = calcTargetDimensions(popup);
54 | let viewportDimensions = calcViewportDimensions();
55 | let directions = !!popup.directions
56 | ? popup.directions
57 | : DEFAULT_DIRECTIONS;
58 | let i = 0;
59 | let direction;
60 | let position;
61 | let viewportFactor;
62 | let bestDirection;
63 | let bestPosition;
64 | let bestViewportFactor;
65 |
66 | while (directions[i]) {
67 | direction = directions[i++];
68 | position = calcPos(direction, targetDimensions, popupDimensions, popup);
69 | viewportFactor = calcViewportFactor(position, viewportDimensions, popupDimensions, popup);
70 |
71 | if (i === 1 ||
72 | viewportFactor > bestViewportFactor ||
73 | !bestViewportFactor
74 | && popup.bestDirection
75 | ) {
76 | bestDirection = direction;
77 | bestViewportFactor = viewportFactor;
78 | bestPosition = position;
79 | }
80 |
81 | if (bestViewportFactor > VIEWPORT_ACCURACY_FACTOR) {
82 | break;
83 | }
84 | }
85 |
86 | return {
87 | direction: bestDirection,
88 | left: bestPosition.left,
89 | top: bestPosition.top,
90 | };
91 | }
92 |
93 | module.exports = {
94 | calcBestDrawingParams,
95 | DEFAULT_DIRECTIONS,
96 | };
97 |
98 | /**
99 | * @param {PopupHash} popup
100 | * @returns {PopupDimension}
101 | */
102 | function calcPopupDimensions(popup) {
103 | let display = popup.domElementPopup.style.display;
104 |
105 | // to get the sizes
106 | if (!display || display === 'none') {
107 | popup.domElementPopup.style.display = 'block';
108 | }
109 |
110 | let popupWidth = popup.domElementPopup.offsetWidth;
111 | let popupHeight = popup.domElementPopup.offsetHeight;
112 |
113 | popup.domElementPopup.style.display = display;
114 |
115 | return {
116 | width: popupWidth,
117 | height: popupHeight,
118 | area: popupWidth * popupHeight,
119 | };
120 | }
121 |
122 | /**
123 | * @param {PopupHash} popup
124 | * @returns {Rect}
125 | */
126 | function calcTargetDimensions(popup) {
127 | let anchor = popup.targetAnchor;
128 | let anchorRect = popup.targetAnchor.getBoundingClientRect();
129 | let body = document.body;
130 |
131 | return {
132 | left: anchorRect.left + body.scrollLeft,
133 | top: anchorRect.top + body.scrollTop,
134 | width: anchorRect.width,
135 | height: anchorRect.height,
136 | };
137 | }
138 |
139 | /**
140 | * @returns {Rect}
141 | */
142 | function calcViewportDimensions() {
143 | let winTop = window.pageYOffset;
144 | let winLeft = window.pageXOffset;
145 | let winWidth = window.innerWidth;
146 | let winHeight = window.innerHeight;
147 |
148 | return {
149 | top: winTop,
150 | left: winLeft,
151 | bottom: winTop + winHeight,
152 | right: winLeft + winWidth,
153 | };
154 | }
155 |
156 | /**
157 | * @param {Point} position
158 | * @param {Rect} viewportDimensions
159 | * @param {PopupDimension} popupDimensions
160 | * @param {PopupHash} popup
161 | * @returns {Number}
162 | */
163 | function calcViewportFactor(position, viewportDimensions, popupDimensions, popup) {
164 | var viewportOffset = popup.offset.viewport;
165 | var intersectionLeft = Math.max(position.left, viewportDimensions.left + viewportOffset);
166 | var intersectionRight = Math.min(position.left
167 | + popupDimensions.width, viewportDimensions.right
168 | - viewportOffset);
169 | var intersectionTop = Math.max(position.top, viewportDimensions.top + viewportOffset);
170 | var intersectionBottom = Math.min(position.top + popupDimensions.height,
171 | viewportDimensions.bottom - viewportOffset);
172 |
173 | return ((intersectionLeft < intersectionRight) && (intersectionTop < intersectionBottom))
174 | ? ((intersectionRight - intersectionLeft) * (intersectionBottom - intersectionTop)
175 | / popupDimensions.area)
176 | : 0;
177 | }
178 |
179 | /**
180 | * @param {String} direction
181 | * @param {Rect} targetDimensions
182 | * @param {PopupDimension} popupDimensions
183 | * @param {PopupHash} popup
184 | * @returns {Point}
185 | */
186 | function calcPos(direction, targetDimensions, popupDimensions, popup) {
187 | let result = {};
188 | let mainOffset = popup.offset.main || 0;
189 | let secondaryOffset = popup.offset.second || 0;
190 |
191 | let mainDirection = getMainDirection(direction);
192 | let secondaryDirection = getSecondaryDirection(direction);
193 |
194 | switch (mainDirection) {
195 | case 'bottom':
196 | result.top = targetDimensions.top + targetDimensions.height + mainOffset;
197 | break;
198 | case 'top':
199 | result.top = targetDimensions.top - popupDimensions.height - mainOffset;
200 | break;
201 | case 'left':
202 | result.left = targetDimensions.left - popupDimensions.width - mainOffset;
203 | break;
204 | case 'right':
205 | result.left = targetDimensions.left + targetDimensions.width + mainOffset;
206 | break;
207 | default:
208 | break;
209 | }
210 |
211 | switch (secondaryDirection) {
212 | case 'right':
213 | result.left = targetDimensions.left
214 | + targetDimensions.width
215 | - popupDimensions.width
216 | - secondaryOffset;
217 | break;
218 | case 'left':
219 | result.left = targetDimensions.left + secondaryOffset;
220 | break;
221 | case 'bottom':
222 | result.top = targetDimensions.top
223 | + targetDimensions.height
224 | - popupDimensions.height
225 | - secondaryOffset;
226 | break;
227 | case 'top':
228 | result.top = targetDimensions.top + secondaryOffset;
229 | break;
230 | case 'center':
231 | switch (mainDirection) {
232 | case 'top':
233 | case 'bottom':
234 | result.left = targetDimensions.left
235 | + targetDimensions.width / 2
236 | - popupDimensions.width / 2;
237 | break;
238 | case 'left':
239 | case 'right':
240 | result.top = targetDimensions.top
241 | + targetDimensions.height / 2
242 | - popupDimensions.height / 2;
243 | break;
244 | default:
245 | break;
246 | }
247 | break;
248 | default:
249 | break;
250 | }
251 |
252 | return result;
253 | }
254 |
255 | /**
256 | * @param {String} direction
257 | * @return {String|Boolean}
258 | */
259 | function getMainDirection(direction) {
260 | let deliveryPosition = direction.indexOf('-');
261 | return (deliveryPosition !== -1) && direction.substr(0, deliveryPosition);
262 | }
263 |
264 | /**
265 | * @param {String} direction
266 | * @return {String|Boolean}
267 | */
268 | function getSecondaryDirection(direction) {
269 | let deliveryPosition = direction.indexOf('-');
270 | return (deliveryPosition !== -1) && direction.substr(deliveryPosition + 1);
271 | }
272 |
--------------------------------------------------------------------------------
/src/components/popup/popup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // TODO
4 | // - support block props
5 | // - zIndexGroupLevel
6 | // - nested popups
7 | // - hide popup if target isn't visible
8 | // - handle window resize - do we really need it?
9 | // - tests
10 |
11 | const React = require('react');
12 | const ReactDOM = require('react-dom');
13 |
14 | const BEM = require('../../core/bem/bem');
15 | const provide = require('../../provider/provider');
16 |
17 | const ZINDEX_FACTOR = 1000;
18 | const visiblePopupsZIndexes = {};
19 |
20 | const calcBestDrawingParams = require('./calc-drawing-params').calcBestDrawingParams;
21 | const isEventOutsideClientBounds = require('../../lib/window').isEventOutsideClientBounds;
22 |
23 | module.exports = class Popup extends BEM {
24 |
25 | constructor(props) {
26 | super(props);
27 |
28 | // TODO
29 | // this._parentPopup = undefined;
30 | // this._zIndex = null;
31 | // this._zIndexGroupLevel = null;
32 |
33 | this.domElementPopup = null;
34 | this.anchor = null;
35 | this.position = null;
36 | this.clickEventBindTimeout = null;
37 | this.isWindowClickBinded = false;
38 |
39 | this.defaultProps = {
40 | visible: false,
41 | autoclosable: false,
42 | target: 'anchor',
43 | size: 's'
44 | };
45 |
46 | this.state = {
47 | direction: null,
48 | hovered: false,
49 | styles: {
50 | top: 0,
51 | left: 0
52 | }
53 | };
54 |
55 | this.handleMouseEnter = this.handleMouseEnter.bind(this);
56 | this.handleMouseLeave = this.handleMouseLeave.bind(this);
57 | this.handleWindowClick = this.handleWindowClick.bind(this);
58 |
59 | this.redraw = this.redraw.bind(this);
60 |
61 | this._popupContainerElement = document.createElement('div');
62 | document.body.appendChild(this._popupContainerElement);
63 | }
64 |
65 | componentWillReceiveProps(nextProps) {
66 | if (nextProps.visible) {
67 | this.redraw();
68 | }
69 | }
70 |
71 | componentDidMount() {
72 | this.props.autoclosable && this.ensureClickEvent();
73 | }
74 |
75 | componentWillUnmount() {
76 | ReactDOM.unmountComponentAtNode(this._popupContainerElement);
77 | document.body.removeChild(this._popupContainerElement);
78 |
79 | this.props.autoclosable && this.ensureClickEvent(true);
80 | }
81 |
82 | componentDidUpdate(prevProps, prevState) {
83 | if (this.props.autoclosable) {
84 | if (prevProps.onClickOutside !== this.props.onClickOutside) {
85 | this.ensureClickEvent();
86 | } else {
87 | if (prevProps.visible !== this.props.visible) {
88 | this.ensureClickEvent(!this.props.visible);
89 | }
90 | }
91 | }
92 | }
93 |
94 | render() {
95 | var popup = provide({
96 | block: 'popup',
97 | attrs: {
98 | className: this.props.className,
99 | 'aria-hidden': !this.state.visible,
100 | style: `top: ${this.state.styles.top}; left: ${this.state.styles.left}`,
101 | onMouseEnter: this.handleMouseEnter,
102 | onMouseLeave: this.handleMouseLeave,
103 | ref: (popup) => {
104 | this._popup = popup;
105 | }
106 | },
107 | mods: {
108 | theme: this.props.theme,
109 | autoclosable: !!this.props.autoclosable,
110 | target: this.props.target,
111 | size: this.props.size,
112 | visible: this.props.visible,
113 | invalid: this.props.invalid,
114 | type: (this.props.target === 'anchor') && (this.props.type === 'tooltip') && this.props.type,
115 | height: this.props.height,
116 | direction: this.state.direction,
117 | },
118 | directions: [this.state.direction],
119 | mainOffset: this.props.mainOffset,
120 | secondaryOffset: this.props.secondaryOffset,
121 | viewportOffset: this.props.viewportOffset,
122 | // zIndexGroupLevel: this.props.zIndexGroupLevel,
123 | content: [
124 | this.props.children,
125 | { /* */ }
126 | ],
127 | });
128 |
129 | ReactDOM.unstable_renderSubtreeIntoContainer(
130 | this,
131 | popup,
132 | this._popupContainerElement
133 | );
134 |
135 | return React.createElement('div');
136 | }
137 |
138 | handleMouseEnter() {
139 | this.setState({ hovered: true });
140 |
141 | if (this.props.onMouseEnter) {
142 | this.props.onMouseEnter();
143 | }
144 | }
145 |
146 | handleMouseLeave() {
147 | this.setState({ hovered: false });
148 |
149 | if (this.props.onMouseLeave) {
150 | this.props.onMouseLeave();
151 | }
152 | }
153 |
154 | handleWindowClick(e) {
155 | if (this.props.autoclosable && !!this.domElementPopup && isEventOutsideClientBounds(e, this.domElementPopup)) {
156 | if (this.props.onClickOutside) {
157 | this.props.onClickOutside(e);
158 | }
159 | }
160 | }
161 |
162 | ensureClickEvent(isDestroy) {
163 | let isNeedBindEvent = isDestroy !== undefined ? !isDestroy : this.props.visible;
164 |
165 | // We need timeouts to not to catch the event that causes
166 | // popup opening (because it propagates to the `window`).
167 | if (this.clickEventBindTimeout) {
168 | clearTimeout(this.clickEventBindTimeout);
169 | this.clickEventBindTimeout = null;
170 | }
171 |
172 | this.clickEventBindTimeout = setTimeout(() => {
173 | if (!this.isWindowClickBinded && isNeedBindEvent) {
174 | window.addEventListener('click', this.handleWindowClick);
175 | this.isWindowClickBinded = true;
176 | } else if (this.isWindowClickBinded && !isNeedBindEvent) {
177 | window.removeEventListener('click', this.handleWindowClick);
178 | this.isWindowClickBinded = false;
179 | }
180 | }, 0);
181 | }
182 |
183 | setTarget(target) {
184 | this.anchor = target;
185 | this.redraw();
186 | }
187 |
188 | redraw() {
189 | if (this.props.target === 'anchor' && !this.anchor || this.props.target === 'position' && !this.position) {
190 | throw 'Cannot show popup without target or position';
191 | }
192 |
193 | if (!this.domElementPopup) {
194 | this.domElementPopup = ReactDOM.findDOMNode(this._popup);
195 | }
196 |
197 | let drawingParams = calcBestDrawingParams({
198 | directions: this.props.directions,
199 | bestDirection: this.state.direction,
200 | isTargetAnchor: this.props.target === 'anchor',
201 | domElementPopup: this.domElementPopup,
202 | offset: {
203 | main: this.props.mainOffset,
204 | second: this.props.secondaryOffset,
205 | viewport: this.props.viewportOffset
206 | },
207 | targetPosition: this.position,
208 | targetAnchor: this.anchor
209 | });
210 |
211 | this.setState({
212 | direction: drawingParams.direction,
213 | styles: this.getDrawingCss(drawingParams)
214 | });
215 | }
216 |
217 | getDrawingCss(drawingParams) {
218 | return {
219 | left: drawingParams.left,
220 | top: drawingParams.top
221 | };
222 | }
223 |
224 | setPosition(left, top) {
225 | this.position = {
226 | left: left,
227 | top: top
228 | };
229 | this.redraw();
230 | }
231 |
232 | // TODO
233 | // _calcZIndexGroupLevel() {
234 | // let res = this.props.zIndexGroupLevel,
235 | // parentPopup = this._getParentPopup();
236 |
237 | // parentPopup && (res += parentPopup._zIndexGroupLevel);
238 |
239 | // return res;
240 | // }
241 |
242 | // _captureZIndex() {
243 | // let level = this._zIndexGroupLevel === null?
244 | // this._zIndexGroupLevel = this._calcZIndexGroupLevel() :
245 | // this._zIndexGroupLevel,
246 | // zIndexes = visiblePopupsZIndexes[level] || (visiblePopupsZIndexes[level] = [(level + 1) * ZINDEX_FACTOR]),
247 | // prevZIndex = this._zIndex;
248 |
249 | // this._zIndex = zIndexes[zIndexes.push(zIndexes[zIndexes.length - 1] + 1) - 1];
250 | // // this._zIndex !== prevZIndex && this.domElem.css('z-index', this._zIndex);
251 |
252 | // return this;
253 | // }
254 |
255 | // _releaseZIndex() {
256 | // let zIndexes = visiblePopupsZIndexes[this._zIndexGroupLevel];
257 | // zIndexes.splice(zIndexes.indexOf(this._zIndex), 1);
258 |
259 | // return this;
260 | // }
261 |
262 | // _recaptureZIndex() {
263 | // this._releaseZIndex();
264 | // this._zIndexGroupLevel = null;
265 |
266 | // return this._captureZIndex();
267 | // }
268 |
269 | // _getParentPopup() {
270 | // return this._parentPopup;
271 | // }
272 |
273 | }
274 |
--------------------------------------------------------------------------------
/src/components/progressbar/progressbar.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../core/bem/bem');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Progressbar extends BEM {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.block,
13 | attrs: {
14 | 'aria-valuenow': this.props.val + '%',
15 | style: {
16 | width: this.props.val + '%'
17 | }
18 | },
19 | mods: {
20 | theme: this.props.theme
21 | },
22 | val: this.props.val
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/radio-group/radio-group.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('radio-group')(
2 | content()(function() {
3 | 'use strict';
4 |
5 | var mods = this.mods,
6 | ctx = this.ctx,
7 | isValDef = typeof ctx.val !== 'undefined';
8 |
9 | const React = require('react');
10 | const Radio = require('../components/radio/radio');
11 |
12 | return (ctx.options || []).map(function(option, i) {
13 | return [
14 | !!i && !mods.type && { tag : 'br' },
15 | React.createElement(Radio, {
16 | theme: mods.theme,
17 | size: mods.size,
18 | type: mods.type,
19 | checked: isValDef && ctx.val === option.val,
20 | disabled: option.disabled || mods.disabled,
21 | name: ctx.name,
22 | val: option.val,
23 | text: option.text,
24 | title: option.title,
25 | icon: option.icon
26 | })
27 | ];
28 | });
29 | })
30 | );
31 |
--------------------------------------------------------------------------------
/src/components/radio-group/radio-group.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../core/bem/bem');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class RadioGroup extends BEM {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.block,
13 | attrs: {
14 |
15 | },
16 | mods: {
17 | type: this.props.type,
18 | size: this.props.size,
19 | theme: this.props.theme,
20 | hovered: this.state.hovered,
21 | focused: this.state.focused,
22 | disabled: this.props.disabled
23 | },
24 | name: this.props.name,
25 | val: this.props.val,
26 | options: this.props.options
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/radio/__control/radio__control.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('radio').elem('control')(
2 | attrs()(function() {
3 | 'use strict';
4 |
5 | const originalAttrs = applyNext();
6 |
7 | originalAttrs.autoComplete = originalAttrs.autocomplete;
8 | delete originalAttrs.autocomplete;
9 | originalAttrs.defaultValue = originalAttrs.value;
10 | delete originalAttrs.value;
11 | originalAttrs.defaultChecked = originalAttrs.checked;
12 | delete originalAttrs.checked;
13 |
14 | return originalAttrs;
15 | })
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/radio/_type/radio_type_button.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('radio').mod('type', 'button')(
2 | content()(function() {
3 | 'use strict';
4 |
5 | const React = require('react');
6 | const Button = require('../components/button/button');
7 |
8 | const ctx = this.ctx,
9 | mods = this.mods;
10 |
11 | const originalContent = applyNext();
12 |
13 | // Replace XJST Button
14 | // FIXME: support icon!
15 | originalContent[0] = React.createElement(Button, {
16 | theme: mods.theme,
17 | size: mods.size,
18 | togglable: mods.mode === 'radio-check'? 'check' : 'radio',
19 | text: ctx.text,
20 | attrs: {
21 | role: 'checkbox',
22 | 'aria-pressed': undefined,
23 | 'aria-checked': String(!!mods.checked)
24 | },
25 | title: ctx.title
26 | });
27 |
28 | return originalContent;
29 | })
30 | );
31 |
--------------------------------------------------------------------------------
/src/components/radio/radio.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { mods: { type: 'button' } }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/src/components/radio/radio.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Control = require('../control/control');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Radio extends Control {
9 |
10 | _onChange() {
11 | this.props.disabled || this.setStateAndMod({checked: true});
12 |
13 | this.props.onChange && this.props.onChange(e, this.state);
14 | }
15 |
16 | componentWillMount() {
17 | this.state.checked = this.props.checked;
18 | }
19 |
20 | render() {
21 | return provide({
22 | block: this.bem.block,
23 | attrs: {
24 | onMouseEnter: this._onMouseEnter.bind(this),
25 | onMouseLeave: this._onMouseLeave.bind(this),
26 | onFocus: this._onFocus.bind(this),
27 | onBlur: this._onBlur.bind(this),
28 | onChange: this._onChange.bind(this)
29 | },
30 | attrs: {
31 | onMouseEnter: this._onMouseEnter.bind(this),
32 | onMouseLeave: this._onMouseLeave.bind(this),
33 | onFocus: this._onFocus.bind(this),
34 | onBlur: this._onBlur.bind(this),
35 | onChange: this._onChange.bind(this)
36 | },
37 | mods: {
38 | size: this.props.size,
39 | theme: this.props.theme,
40 | type: this.props.type,
41 | checked: this.state.checked,
42 | hovered: this.state.hovered,
43 | focused: this.state.focused,
44 | disabled: this.props.disabled
45 | },
46 | val: this.props.val,
47 | name: this.props.name,
48 | tabIndex: this.props.tabIndex,
49 | title: this.props.title,
50 | icon: this.props.icon,
51 | text: this.props.text
52 | });
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/select/__button/select__button.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('select').elem('button')(
2 | def()(function() {
3 | var select = this._select,
4 | ctx = this.ctx,
5 | mods = this.mods;
6 |
7 | const React = require('react');
8 | const Button = require('../components/button/button');
9 | const selectComponent = select._select;
10 |
11 | return React.createElement(Button, {
12 | ref: (button) => {
13 | selectComponent._button = button;
14 | },
15 | mix: { block : this.block, elem : this.elem },
16 | size: mods.size,
17 | view: mods.view,
18 | // focused : mods.focused,
19 | disabled: mods.disabled,
20 | checked: mods.mode !== 'radio' && !!this._checkedOptions.length,
21 | onClick: this._select.bindings.onButtonClick,
22 | theme: mods.theme,
23 | // role : 'listbox',
24 | // 'aria-owns' : this._optionsIds.join(' '),
25 | // 'aria-multiselectable' : mods.mode === 'check'? 'true' : undefined,
26 | // 'aria-labelledby' : this._selectTextId
27 | },
28 | [
29 | { elem: 'text', content: select.text },
30 | { block : 'icon', mix : { block : 'select', elem : 'tick' } }
31 | ]
32 | );
33 | })
34 | );
35 |
--------------------------------------------------------------------------------
/src/components/select/__menu/select__menu.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('select').elem('menu')(
2 | def()(function() {
3 | const React = require('react');
4 | const Menu = require('../components/menu/menu');
5 | const MenuItem = require('../components/menu-item/menu-item');
6 | const _select = this.ctx._select;
7 | const selectComponent = _select._select;
8 |
9 | return React.createElement.apply(React, [
10 | Menu,
11 | {
12 | mix: { block : this.block, elem : this.elem },
13 | ref: menu => selectComponent._menu = menu,
14 | size: _select.mods.size,
15 | disabled: _select.mods.disabled,
16 | mode: _select.mods.mode,
17 | content: _select.options,
18 | checkedItems: _select.bindings.checkedItems,
19 | style: _select.bindings.popupMenuWidth,
20 | onKeyDown: _select.bindings.onKeyDown,
21 | theme: _select.mods.theme,
22 | }].concat(_select.options.map(option => React.createElement(
23 | MenuItem,
24 | {
25 | onClick: _select.bindings.onOptionCheck,
26 | theme: _select.mods.theme,
27 | val: option.value,
28 | }, option.content)
29 | ))
30 | );
31 | })
32 | );
33 |
--------------------------------------------------------------------------------
/src/components/select/__popup/select__popup.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('popup')
2 | .match(function () { return !!this._select })
3 | .def()(function() {
4 | const React = require('react');
5 | const Popup = require('../components/popup/popup');
6 | const selectComponent = this._select;
7 |
8 | return React.createElement(Popup, {
9 | ref: popup => {
10 | selectComponent._select._popup = popup;
11 | },
12 | directions: this.ctx.directions,
13 | target: 'anchor',
14 | theme: this.mods.theme,
15 | autoclosable: true,
16 | visible: selectComponent.bindings.isOpened,
17 | onClickOutside: selectComponent.bindings.onClickOutside
18 | }, { block: selectComponent.block, mods: selectComponent.mods, elem: 'menu', _select: selectComponent });
19 | });
20 |
--------------------------------------------------------------------------------
/src/components/select/select.deps.js:
--------------------------------------------------------------------------------
1 | ({
2 | shouldDeps: [
3 | { elem: 'popup' }
4 | ]
5 | });
6 |
--------------------------------------------------------------------------------
/src/components/select/select.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const ReactDOM = require ('react-dom');
4 | const BEM = require('../../core/bem/bem');
5 | const provide = require('../../provider/provider');
6 |
7 | module.exports = class Select extends BEM {
8 |
9 | constructor(props) {
10 | super(props);
11 |
12 | this.handleOptionCheck = this.handleOptionCheck.bind(this);
13 | this.handleClickOutside = this.handleClickOutside.bind(this);
14 | this.handleKeyDown = this.handleKeyDown.bind(this);
15 | this.updatePopupMenuStyles = this.updatePopupMenuStyles.bind(this);
16 | this.handleButtonClick = this.handleButtonClick.bind(this);
17 |
18 | this.timeoutId = null;
19 |
20 | this.state = {
21 | opened: !!this.props.opened,
22 | checkedItems: this.props.checkedItems || [],
23 | popupMenuStyles: {}
24 | };
25 | }
26 |
27 | componentDidMount() {
28 | this._popup.setTarget(ReactDOM.findDOMNode(this._button));
29 | this.updatePopupMenuStyles();
30 | }
31 |
32 | render() {
33 | return provide({
34 | block: 'select',
35 | options: this.renderOptionsList(this.props.options),
36 | text: this.renderButtonContent(),
37 | name: this.props.name,
38 | val: this.state.checkedItems,
39 | mods: {
40 | mode: this.props.mode,
41 | size: this.props.size,
42 | width: this.props.width,
43 | theme: this.props.theme,
44 | disabled: this.props.disabled,
45 | focused: this.state.focused,
46 | },
47 | bindings: {
48 | onButtonClick: this.handleButtonClick,
49 | onClickOutside: this.handleClickOutside,
50 | onOptionCheck: this.handleOptionCheck,
51 | // TODO
52 | onKeyDown: this.handleKeyDown,
53 | isOpened: this.state.opened,
54 | checkedItems: this.props.checkedItems || this.state.checkedItems,
55 | popupMenuWidth: this.state.popupMenuWidth
56 | },
57 | _select: this
58 | });
59 | }
60 |
61 | renderOptionsList(options) {
62 | return (
63 | options.map(option => {
64 | if (option.type === 'group' && !!option.content) {
65 | let content = this.renderOptionsList(option.content);
66 |
67 | return ({
68 | type: 'group',
69 | title: option.title,
70 | content
71 | });
72 | } else {
73 | return ({
74 | value: option.value,
75 | content: [option.icon, option.text]
76 | });
77 | }
78 | })
79 | );
80 | }
81 |
82 | renderButtonContent() {
83 | let checkedItems = this.props.options.filter(option => this.state.checkedItems.indexOf(option.value) !== -1);
84 |
85 | return checkedItems.map(item => item.text).join(', ') || this.props.text || '--------';
86 | }
87 |
88 | updatePopupMenuStyles() {
89 | let button = ReactDOM.findDOMNode(this._button);
90 | let buttonWidth = button.getBoundingClientRect().width;
91 | let popupMenuStyles = {};
92 |
93 | if (this.props.width === 'available') {
94 | popupMenuStyles = `minWidth: ${buttonWidth}; maxWidth: ${buttonWidth}`;
95 | } else {
96 | popupMenuStyles = `minWidth: ${buttonWidth}`;
97 | }
98 |
99 | this.setState({
100 | popupMenuStyles
101 | });
102 | }
103 |
104 | handleButtonClick() {
105 | let newOpenedState = this.props.opened !== undefined ? !this.props.opened : !this.state.opened;
106 |
107 | this.setState({
108 | opened: newOpenedState
109 | });
110 |
111 | if (this.props.onClick) {
112 | this.props.onClick();
113 | }
114 | }
115 |
116 | handleOptionCheck(e, checkedValue) {
117 | let isCheckedMode = this.props.mode === 'check';
118 | let checkedItems = this.state.checkedItems;
119 |
120 | if (isCheckedMode) {
121 | if (this.state.checkedItems.indexOf(checkedValue) !== -1) {
122 | checkedItems = checkedItems.filter(i => i !== checkedValue);
123 | } else {
124 | checkedItems.push(checkedValue);
125 | }
126 | } else {
127 | checkedItems = [ checkedValue ];
128 | }
129 |
130 | this.setState({
131 | checkedItems,
132 | opened: this.props.mode === 'check'
133 | });
134 |
135 | if (this.props.mode !== 'check') {
136 | ReactDOM.findDOMNode(this._button).focus();
137 | }
138 |
139 | if (this.props.onOptionCheck) {
140 | this.props.onOptionCheck(checkedItems);
141 | }
142 | }
143 |
144 | handleClickOutside() {
145 | this.setState({
146 | opened: false
147 | });
148 |
149 | if (this.props.onClickOutside) {
150 | this.props.onClickOutside();
151 | }
152 | }
153 |
154 | handleKeyDown(e) {
155 | // TODO
156 | }
157 | };
158 |
--------------------------------------------------------------------------------
/src/components/spin/spin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const BEM = require('../../core/bem/bem');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Spin extends BEM {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.class,
13 | mods: {
14 | size: this.props.size,
15 | theme: this.props.theme,
16 | visible: this.props.visible
17 | }
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/textarea/textarea.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('textarea')(
2 | attrs()(function() {
3 | 'use strict';
4 |
5 | const originalAttrs = applyNext();
6 |
7 | originalAttrs.defaultValue = this.ctx.val;
8 | originalAttrs.tabIndex = originalAttrs.tabindex,
9 | delete originalAttrs.tabindex;
10 | originalAttrs.placeHolder = originalAttrs.placeholder
11 | delete originalAttrs.placeholder;
12 | originalAttrs.autoComplete = originalAttrs.autocomplete;
13 | delete originalAttrs.autocomplete;
14 |
15 | return originalAttrs;
16 | }),
17 | content()(function() {
18 | return;
19 | })
20 | );
21 |
--------------------------------------------------------------------------------
/src/components/textarea/textarea.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 |
5 | const Input = require('../input/input');
6 | const provide = require('../../provider/provider');
7 |
8 | module.exports = class Textarea extends Input {
9 |
10 | render() {
11 | return provide({
12 | block: this.bem.block,
13 | attrs: {
14 | onMouseEnter: this._onMouseEnter.bind(this),
15 | onMouseLeave: this._onMouseLeave.bind(this),
16 | onFocus: this._onFocus.bind(this),
17 | onBlur: this._onBlur.bind(this),
18 | onChange: this._onChange.bind(this)
19 | },
20 | mods: {
21 | size: this.props.size,
22 | theme: this.props.theme,
23 | hovered: this.state.hovered,
24 | focused: this.state.focused,
25 | disabled: this.props.disabled
26 | },
27 | id: this.props.id,
28 | name: this.props.name,
29 | tabIndex: this.props.tabIndex,
30 | placeholder: this.props.placeholder,
31 | val: this.state.val
32 | });
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/core/bem/bem.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 | const Component = React.Component;
5 | const block = require('bem-cn');
6 | const paramCase = require('param-case');
7 | const naming = require('bem-naming');
8 |
9 | module.exports = class BEM extends Component {
10 |
11 | constructor(props) {
12 | super(props);
13 |
14 | this.state = {};
15 | this.bem = this.bem || {};
16 |
17 | this.bem.entity = this._camelToBem(this.constructor.name);
18 | this.bem.block = block(this.bem.entity.block);
19 | this.bem.elem = (name, mods) => (this._buildElemClass(name, mods));
20 | this.bem.mix = props.mix;
21 |
22 | this.bem._refs = {};
23 |
24 | this._initMod = this.bem.entity.modName ? {
25 | [this.bem.entity.modName]: this.bem.entity.modVal
26 | } : {};
27 |
28 | this.bem.class = this._buildClass();
29 | }
30 |
31 | setMod(mod) {
32 | this.bem.mods = Object.assign({}, this._initMod, this.bem.mods, mod);
33 | this.bem.class = this._buildClass();
34 | }
35 |
36 | setStateAndMod(object) {
37 | this.setMod(object);
38 | this.setState(object);
39 | }
40 |
41 | _camelToBem(str) {
42 | let entity = str.replace('Elem', '__');
43 | entity = entity.replace('Mod', '_');
44 | entity = entity.replace('Val', '_');
45 |
46 | entity = naming.parse(entity);
47 | entity.block = paramCase(entity.block);
48 | Object.keys(entity).forEach(key => {
49 | entity[key] = entity[key].toLowerCase();
50 | });
51 |
52 | return entity;
53 | }
54 |
55 |
56 | _buildClass() {
57 | return this
58 | .bem
59 | .block(
60 | this.bem.block,
61 | this.bem.mods
62 | )(
63 | this.bem.elem
64 | ).mix(
65 | this.bem.mix
66 | );
67 | }
68 |
69 | _buildElemClass(name, mods) {
70 | this.bem.ref = (elem) => {this.bem._refs[name] = elem};
71 | return this
72 | .bem
73 | .block(
74 | name,
75 | mods
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const Attach = require('./components/attach/attach');
2 | const Button = require('./components/button/button');
3 | const Checkbox = require('./components/checkbox/checkbox');
4 | const CheckboxGroup = require('./components/checkbox-group/checkbox-group');
5 | const Control = require('./components/control/control');
6 | const Dropdown = require('./components/dropdown/dropdown');
7 | const Icon = require('./components/icon/icon');
8 | const Image = require('./components/image/image');
9 | const Input = require('./components/input/input');
10 | const Link = require('./components/link/link');
11 | const Menu = require('./components/menu/menu');
12 | const MenuItem = require('./components/menu-item/menu-item');
13 | const Modal = require('./components/modal/modal');
14 | const Popup = require('./components/popup/popup');
15 | const Progressbar = require('./components/progressbar/progressbar');
16 | const Radio = require('./components/radio/radio');
17 | const RadioGroup = require('./components/radio-group/radio-group');
18 | const Select = require('./components/select/select');
19 | const Spin = require('./components/spin/spin');
20 | const Textarea = require('./components/textarea/textarea');
21 |
22 | module.exports = {
23 | Attach,
24 | Button,
25 | Checkbox,
26 | CheckboxGroup,
27 | Control,
28 | Dropdown,
29 | Icon,
30 | Image,
31 | Input,
32 | Link,
33 | Menu,
34 | MenuItem,
35 | Modal,
36 | Popup,
37 | Progressbar,
38 | Radio,
39 | RadioGroup,
40 | Select,
41 | Spin,
42 | Textarea
43 | };
44 |
--------------------------------------------------------------------------------
/src/lib/window.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /**
3 | * Check that mouse was outside `e`
4 | * @param {Object} e - MouseEvent
5 | * @param {React.Element} element - Element to check bounds
6 | * @returns {Boolean}
7 | */
8 | module.exports.isEventOusideBounds = function(e, element) {
9 | let rect = element.getBoundingClientRect();
10 | return ((e.pageX < rect.left || e.pageX > rect.right) ||
11 | (e.pageY < rect.top || e.pageY > rect.bottom));
12 | }
13 |
14 | /**
15 | * Check that mouse was outside `e`
16 | * @param {Object} e - MouseEvent
17 | * @param {React.Element} element - Element to check bounds
18 | * @returns {Boolean}
19 | */
20 | module.exports.isEventOutsideClientBounds = function(e, element) {
21 | let rect = element.getBoundingClientRect();
22 | return e.clientX < rect.left || e.clientX > rect.right
23 | || e.clientY < rect.top || e.clientY > rect.bottom;
24 | }
25 |
--------------------------------------------------------------------------------
/src/provider/provider.js:
--------------------------------------------------------------------------------
1 | const reactXjst = require('react-xjst');
2 | const React = require('react');
3 | const Runtime = require("xjst-vidom");
4 |
5 | let vidom = {}
6 | let api = new Runtime({});
7 | api.compile(require('./templates'));
8 | api.exportApply(vidom)
9 |
10 | module.exports = reactXjst(vidom, React);
11 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const bemLoader = require('bem-loader');
3 | const CollectBemAssetsPlugin = bemLoader.CollectBemAssetsPlugin;
4 | const fs = require('fs-extra');
5 | function wrapTemplates (code) {
6 | return `module.exports = function (${ require('xjst-vidom').prototype.locals.join(', ') }) {
7 | ${ code }
8 | }
9 | `;
10 | }
11 |
12 | module.exports = {
13 | entry: './src/index.js',
14 | devtool: 'source-map',
15 | output: {
16 | path: path.join(__dirname, 'dist'),
17 | filename: '[name].js',
18 | },
19 | plugins: [
20 | new CollectBemAssetsPlugin({
21 | done: function(data) {
22 | if (process.env.STANDALONE) {
23 | fs.outputFileSync(
24 | './src/provider/templates.js',
25 | wrapTemplates(bemLoader.generateBemHtml(data.bemhtml))
26 | );
27 | }
28 | },
29 | techs: ['bemhtml'],
30 | techExtensions: {
31 | bemhtml: ['bemhtml', 'bemhtml.js'],
32 | },
33 | levels: [
34 | 'bem-core/common.blocks',
35 | 'bem-core/desktop.blocks',
36 | 'bem-components/common.blocks',
37 | 'bem-components/desktop.blocks'
38 | ].map(function(short) {
39 | return path.resolve(process.cwd(), `./node_modules/${short}`);
40 | }).concat([
41 | './src/core',
42 | './src/components'
43 | ])
44 | })
45 | ]
46 | };
47 |
--------------------------------------------------------------------------------