├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitattributes
├── .gitignore
├── .sass-lint.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── create-flex-components.js
├── create-global-scoped-components.js
├── docs
├── containers
│ ├── accordion
│ │ └── index.js
│ ├── callout
│ │ └── index.js
│ ├── dropdown
│ │ └── index.js
│ ├── media-object
│ │ └── index.js
│ ├── off-canvas
│ │ └── index.js
│ ├── reveal
│ │ └── index.js
│ ├── table
│ │ └── index.js
│ └── tabs
│ │ └── index.js
├── controls
│ ├── button-group
│ │ └── index.js
│ ├── button
│ │ └── index.js
│ ├── close-button
│ │ └── index.js
│ └── switch
│ │ └── index.js
├── custom
│ └── toggle-switch
│ │ └── index.js
├── docs.scss
├── favicon.ico
├── general
│ ├── flex
│ │ └── index.js
│ ├── float
│ │ └── index.js
│ ├── forms
│ │ └── index.js
│ ├── grid-flex
│ │ └── index.js
│ ├── grid
│ │ └── index.js
│ └── visibility
│ │ └── index.js
├── index.html
├── index.js
├── media
│ ├── badge
│ │ └── index.js
│ ├── flex-video
│ │ └── index.js
│ ├── label
│ │ └── index.js
│ ├── progress-bar
│ │ └── index.js
│ ├── thumbnail
│ │ └── index.js
│ └── tooltip
│ │ └── index.js
├── navigation
│ ├── breadcrumb
│ │ └── index.js
│ ├── menu
│ │ └── index.js
│ ├── pagination
│ │ └── index.js
│ └── top-bar
│ │ └── index.js
├── typography
│ ├── base
│ │ └── index.js
│ ├── helpers
│ │ └── index.js
│ ├── print
│ │ └── index.js
│ └── text-alignment
│ │ └── index.js
└── webpack.config.js
├── examples
├── cdn-flex
│ ├── app.js
│ ├── favicon.ico
│ ├── index.html
│ └── webpack.config.js
├── cdn
│ ├── app.js
│ ├── favicon.ico
│ ├── index.html
│ └── webpack.config.js
├── css-modules-custom
│ ├── app.js
│ ├── favicon.ico
│ ├── index.html
│ ├── theme.json
│ └── webpack.config.js
├── css-modules
│ ├── app.js
│ ├── favicon.ico
│ ├── index.html
│ └── webpack.config.js
├── global-flex
│ ├── app.js
│ ├── app.scss
│ ├── favicon.ico
│ ├── index.html
│ └── webpack.config.js
└── global
│ ├── app.js
│ ├── app.scss
│ ├── favicon.ico
│ ├── index.html
│ └── webpack.config.js
├── package.json
├── server.js
└── src
├── _common.scss
├── _foundation.scss
├── _typography.scss
├── accordion
├── _custom.scss
├── _styles.scss
└── index.js
├── badge
├── _styles.scss
└── index.js
├── breadcrumb
├── _styles.scss
└── index.js
├── button-group
├── _styles.scss
└── index.js
├── button
├── _styles.scss
└── index.js
├── callout
├── _styles.scss
└── index.js
├── close-button
├── _styles.scss
└── index.js
├── collapse
├── _custom.scss
├── _styles.scss
└── index.js
├── dropdown
├── _styles.scss
└── index.js
├── fade
├── _custom.scss
├── _styles.scss
└── index.js
├── flex-mock
├── _styles.scss
└── index.js
├── flex-video
├── _styles.scss
└── index.js
├── flex
├── _styles.scss
└── index.js
├── flexbox.js
├── float
├── _styles.scss
└── index.js
├── forms
├── _custom.scss
├── _styles.scss
└── index.js
├── grid-flex
├── _styles.scss
└── index.js
├── grid
├── _styles.scss
└── index.js
├── index.js
├── label
├── _styles.scss
└── index.js
├── media-object
├── _styles.scss
└── index.js
├── menu-icon
├── _styles.scss
└── index.js
├── menu
├── _custom.scss
├── _styles.scss
└── index.js
├── off-canvas
├── _custom.scss
├── _styles.scss
└── index.js
├── pagination
├── _styles.scss
└── index.js
├── print
├── _styles.scss
└── index.js
├── progress-bar
├── _styles.scss
└── index.js
├── reveal
├── _styles.scss
└── index.js
├── switch
├── _styles.scss
└── index.js
├── table
├── _styles.scss
└── index.js
├── tabs
├── _styles.scss
└── index.js
├── text-alignment
├── _styles.scss
└── index.js
├── thumbnail
├── _styles.scss
└── index.js
├── title-bar
├── _styles.scss
└── index.js
├── toggle-switch
├── _custom.scss
├── _styles.scss
└── index.js
├── tooltip
├── _custom.scss
├── _styles.scss
└── index.js
├── top-bar
├── _custom.scss
├── _styles.scss
└── index.js
├── typography-helpers
├── _styles.scss
└── index.js
├── util
├── constants
│ └── index.js
├── create-wrapper-component
│ └── index.js
├── default-component
│ └── index.js
├── key-mirror-array
│ └── index.js
├── overlay-trigger
│ └── index.js
└── screen-size
│ └── index.js
└── visibility
├── _styles.scss
└── index.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015", "stage-0"],
3 | "plugins": "transform-runtime"
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # http://editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | # Change these settings to your own preference
10 | indent_style = space
11 | indent_size = 2
12 |
13 | # We recommend you to keep these unchanged
14 | end_of_line = lf
15 | charset = utf-8
16 | trim_trailing_whitespace = true
17 | insert_final_newline = true
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
21 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "airbnb",
4 | "rules": {
5 | "react/require-extension": 0,
6 | "react/jsx-filename-extension": 0
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=lf
2 |
3 | *.ico binary
4 | *.gif binary
5 | *.jpg binary
6 | *.jpeg binary
7 | *.png binary
8 | *.svg binary
9 | *.woff binary
10 | *.woff2 binary
11 | *.ttf binary
12 | *.eot binary
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
35 | # Build directories
36 | lib
37 |
--------------------------------------------------------------------------------
/.sass-lint.yml:
--------------------------------------------------------------------------------
1 | options:
2 | formatter: stylish
3 | files:
4 | include: '+(src|docs|examples)/**/*.s+(a|c)ss'
5 | rules:
6 | # Extends
7 | extends-before-mixins: 1
8 | extends-before-declarations: 1
9 | placeholder-in-extend: 1
10 |
11 | # Mixins
12 | mixins-before-declarations: 1
13 |
14 | # Line Spacing
15 | one-declaration-per-line: 1
16 | empty-line-between-blocks: 1
17 | single-line-per-selector: 1
18 |
19 | # Disallows
20 | no-color-keywords: 1
21 | no-color-literals: 1
22 | no-css-comments: 1
23 | no-debug: 1
24 | no-duplicate-properties: 1
25 | no-empty-rulesets: 1
26 | no-extends: 0
27 | no-ids: 1
28 | no-important: 1
29 | no-invalid-hex: 1
30 | no-mergeable-selectors: 1
31 | no-misspelled-properties: 1
32 | no-qualifying-elements: 1
33 | no-trailing-zero: 1
34 | no-transition-all: 1
35 | no-url-protocols: 1
36 | no-vendor-prefixes: 1
37 | no-warn: 1
38 | property-units: 1
39 |
40 | # Nesting
41 | force-attribute-nesting: 1
42 | force-element-nesting: 1
43 | force-pseudo-nesting: 1
44 |
45 | # Name Formats
46 | class-name-format: 1
47 | function-name-format: 1
48 | id-name-format: 1
49 | mixin-name-format: 1
50 | placeholder-name-format: 1
51 | variable-name-format: 1
52 |
53 | # Style Guide
54 | bem-depth: 1
55 | border-zero: 1
56 | brace-style: 1
57 | clean-import-paths: 1
58 | empty-args: 1
59 | hex-length: 1
60 | hex-notation: 1
61 | indentation: 1
62 | leading-zero: 1
63 | nesting-depth: 1
64 | property-sort-order:
65 | - 1
66 | -
67 | order: concentric
68 | quotes: 1
69 | shorthand-values: 1
70 | url-quotes: 1
71 | variable-for-property: 1
72 | zero-unit: 1
73 |
74 | # Inner Spacing
75 | space-after-comma: 1
76 | space-before-colon: 1
77 | space-after-colon: 1
78 | space-before-brace: 1
79 | space-before-bang: 1
80 | space-after-bang: 1
81 | space-between-parens: 1
82 | space-around-operator: 1
83 |
84 | # Final Items
85 | trailing-semicolon: 1
86 | final-newline: 1
87 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Antonio Ruberto
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-foundation-components
2 |
3 | Foundation Sites 6 components implemented in React and compatible with CSS Modules!
4 |
5 | ## Why?
6 |
7 | I like [React](https://facebook.github.io/react). I like [CSS Modules](https://github.com/css-modules/css-modules). I like [Foundation Sites](http://foundation.zurb.com/sites.html). This is an experiment to see if it's possible to combine all these tools in a modular way!
8 |
9 | ## Installation
10 |
11 | ```
12 | npm install --save react-foundation-components
13 | ```
14 |
15 | Each component uses local scoped CSS by requiring the subset of the Foundation stylesheets it needs.
16 |
17 | Documentation (in progress) or demos of the components are available at http://aruberto.github.io/react-foundation-components. Please look at https://github.com/aruberto/react-foundation-components/tree/master/docs for an example webpack setup and example use of each component.
18 |
19 | You can also look at https://github.com/aruberto/react-foundation-components/tree/master/examples/css-modules as an example project that uses CSS Modules with this library.
20 |
21 | If you want to use CSS modules and customize Foundation at the same time, look at https://github.com/aruberto/react-foundation-components/tree/master/examples/css-modules-custom which uses https://www.npmjs.com/package/jsontosass-loader to inject sass variables that override Foundation's default settings.
22 |
23 | Recommend importing on a per component basis instead of importing the main entry point of package. Importing main entry point will cause final bundle to include all CSS and JS whereas importing on a per component basis will cause your final bundle to only include the CSS and JS you actually need (this may change when tree shaking is introduced in webpack 2)!
24 |
25 | Favor
26 |
27 | ```
28 | import Button from 'react-foundation-components/lib/button';
29 | import { ShowForScreenSize, HideForScreenSize } from 'react-foundation-components/lib/visibility';
30 | ```
31 |
32 | over
33 |
34 | ```
35 | import { Button, ShowForScreenSize, HideForScreenSize } from 'react-foundation-components';
36 | ```
37 |
38 | If you can't use CSS Modules, a set of components that use Foundation's global scoped class names are also provided. These are located under react-foundation-components/lib/global. To import Button that uses global scoped class names:
39 |
40 | ```
41 | import Button from 'react-foundation-components/lib/global/button';
42 | import { ShowForScreenSize, HideForScreenSize } from 'react-foundation-components/lib/global/visibility';
43 | ```
44 |
45 | If you use the components under react-foundation-components/lib/global, you are responsible for loading Foundation CSS stylesheet. You can do this in a few ways:
46 | * include stylesheet such as https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.0/foundation.min.css in head of your html with CDN link
47 | * Import/Require react-foundation-components/lib/\_foundation.scss
48 | * Use https://www.npmjs.com/package/foundation-sites-loader
49 |
50 | Here are some example applications that use global scoped class name components:
51 | * Uses Foundation from CDN - https://github.com/aruberto/react-foundation-components/tree/master/examples/cdn
52 | * Uses Foundation (Flexbox Version) from CDN - https://github.com/aruberto/react-foundation-components/tree/master/examples/cdn-flex
53 | * Requires react-foundation-components/lib/\_foundation.scss with global-flexbox set to false - https://github.com/aruberto/react-foundation-components/tree/master/examples/global
54 | * Requires react-foundation-components/lib/\_foundation.scss with global-flexbox set to true - https://github.com/aruberto/react-foundation-components/tree/master/examples/global-flex
55 |
56 | ## Thanks
57 |
58 | A lot of the components are inspired by [React Bootstrap](https://github.com/react-bootstrap/react-bootstrap) and this project even makes heavy use their utility libraries like [react-overlays](https://github.com/react-bootstrap/react-overlays), [dom-helpers](https://github.com/react-bootstrap/dom-helpers), [prop-types-extra](https://github.com/react-bootstrap/prop-types-extra) and [uncontrollable](https://github.com/jquense/uncontrollable). So big thanks to all these project's contributers for all their amazing work!
59 |
--------------------------------------------------------------------------------
/create-flex-components.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console, strict */
2 | 'use strict';
3 |
4 | const path = require('path');
5 | const fs = require('fs-extra');
6 | const s = require('underscore.string');
7 |
8 | const createFlexSccsContent = (component) =>
9 | `$global-flexbox: true;
10 |
11 | @import '../${component}/styles';
12 | `;
13 | const FLEX_COMPONENTS = [
14 | 'button-group',
15 | 'forms',
16 | 'media-object',
17 | 'menu',
18 | 'title-bar',
19 | 'top-bar',
20 | ];
21 |
22 | FLEX_COMPONENTS.forEach((component) => {
23 | const directoryPath = path.join(__dirname, 'lib', component);
24 | const indexPath = path.join(directoryPath, 'index.js');
25 | const scssPath = path.join(directoryPath, '_styles.scss');
26 |
27 | try {
28 | fs.accessSync(indexPath, fs.F_OK);
29 | fs.accessSync(scssPath, fs.F_OK);
30 | } catch (e) {
31 | // Not component directory
32 | console.warn(`Skipping component ${component}!`);
33 | return;
34 | }
35 |
36 | const flexDirectoryPath = path.join(__dirname, 'lib', `${component}-flex`);
37 | const flexIndexPath = path.join(flexDirectoryPath, 'index.js');
38 | const flexScssPath = path.join(flexDirectoryPath, '_styles.scss');
39 | let flexIndexContent = s(fs.readFileSync(indexPath, 'utf8'));
40 |
41 | FLEX_COMPONENTS.forEach((dependency) => {
42 | flexIndexContent = flexIndexContent.replace(`'../${dependency}'`, `'../${dependency}-flex'`);
43 | });
44 |
45 | flexIndexContent = flexIndexContent.replace('\'../grid\'', '\'../grid-flex\'');
46 | flexIndexContent = flexIndexContent.replace('\'../flex-mock\'', '\'../flex\'');
47 | flexIndexContent = flexIndexContent.replace('IS_FLEX_MODE = false', 'IS_FLEX_MODE = true');
48 |
49 | fs.ensureDirSync(flexDirectoryPath);
50 | fs.writeFileSync(flexIndexPath, flexIndexContent.value(), 'utf8');
51 | fs.writeFileSync(flexScssPath, createFlexSccsContent(component), 'utf8');
52 | });
53 |
--------------------------------------------------------------------------------
/create-global-scoped-components.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const path = require('path');
4 | const fs = require('fs-extra');
5 | const through2 = require('through2');
6 | const s = require('underscore.string');
7 |
8 | const libPath = path.join(__dirname, 'lib');
9 | const globalPath = path.join(libPath, 'global');
10 | const packageIndexPath = path.join(libPath, 'index.js');
11 | const packageFlexboxIndexPath = path.join(libPath, 'flexbox.js');
12 | const globalPackageIndexPath = path.join(globalPath, 'index.js');
13 | const globalPackageFlexboxIndexPath = path.join(globalPath, 'flexbox.js');
14 |
15 | fs.ensureDirSync(path.dirname(globalPath));
16 | fs.copySync(packageIndexPath, globalPackageIndexPath);
17 | fs.copySync(packageFlexboxIndexPath, globalPackageFlexboxIndexPath);
18 |
19 | const filter = through2.obj(function componentDirectoryFilter(item, enc, next) {
20 | if (item.stats.isFile()) {
21 | const fileName = path.basename(item.path);
22 |
23 | if (fileName === 'index.js') {
24 | const directory = path.dirname(item.path);
25 | const scssPath = path.join(directory, '_styles.scss');
26 |
27 | try {
28 | fs.accessSync(item.path, fs.F_OK);
29 | fs.accessSync(scssPath, fs.F_OK);
30 |
31 | this.push(item);
32 | } catch (e) {
33 | // Not component directory
34 | }
35 | }
36 | }
37 |
38 | next();
39 | });
40 |
41 | fs.walk(libPath)
42 | .pipe(filter)
43 | .on('data', (item) => {
44 | const globalComponentPath =
45 | s(item.path)
46 | .reverse()
47 | .replace(
48 | s(`lib${path.sep}`).reverse().value(),
49 | s(`lib${path.sep}global${path.sep}`).reverse().value()
50 | )
51 | .reverse()
52 | .value();
53 |
54 | const content = fs.readFileSync(item.path, 'utf8')
55 | .replace(/require\('\.\/\_styles\.scss'\)/g, '{}')
56 | .replace(/require\('classnames\/bind'\)/, 'require(\'classnames\')')
57 | .replace(/require\('\.\.\/util\//g, 'require(\'../../util/')
58 |
59 | fs.ensureDirSync(path.dirname(globalComponentPath));
60 | fs.writeFileSync(globalComponentPath, content, 'utf8');
61 | });
62 |
--------------------------------------------------------------------------------
/docs/containers/callout/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Callout } from '../../../src/callout';
4 |
5 | const CalloutPage = () => (
6 |
7 |
8 | This is a callout.
9 | It has an easy to override visual style, and is appropriately subdued.
10 | It's dangerous to go alone, take this.
11 |
12 |
13 | This is a secondary panel.
14 | It has an easy to override visual style, and is appropriately subdued.
15 | It's dangerous to go alone, take this.
16 |
17 |
18 | This is a primary panel.
19 | It has an easy to override visual style, and is appropriately subdued.
20 | It's dangerous to go alone, take this.
21 |
22 |
23 | This is a success panel.
24 | It has an easy to override visual style, and is appropriately subdued.
25 | It's dangerous to go alone, take this.
26 |
27 |
28 | This is a warning panel.
29 | It has an easy to override visual style, and is appropriately subdued.
30 | It's dangerous to go alone, take this.
31 |
32 |
33 | This is a alert panel.
34 | It has an easy to override visual style, and is appropriately subdued.
35 | It's dangerous to go alone, take this.
36 |
37 |
38 | This is a primary panel.
39 | It has an easy to override visual style, and is appropriately subdued.
40 | It's dangerous to go alone, take this.
41 |
42 |
43 | This is a primary panel.
44 | It has an easy to override visual style, and is appropriately subdued.
45 | It's dangerous to go alone, take this.
46 |
47 |
48 | );
49 |
50 | export default CalloutPage;
51 |
--------------------------------------------------------------------------------
/docs/containers/dropdown/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Dropdown, LinkWithDropdown } from '../../../src/dropdown';
4 | import { Button } from '../../../src/button';
5 |
6 | const dropdownStyle = { position: 'relative', top: '20px', left: '50px' };
7 |
8 | const DropdownPage = () => (
9 |
10 | I'm a dropdown!
11 |
12 | I'm a tiny dropdown!
13 |
14 | I'm a small dropdown!
15 |
16 | I'm a large dropdown!
17 |
18 |
19 |
20 |
23 | Name
24 |
25 | Rank
26 |
27 | }
28 | >
29 | Toggle Dropdown
30 |
31 |
32 |
37 | Hoverable Dropdown
38 |
39 |
40 |
45 | Focusable Dropdown
46 |
47 |
48 |
52 | Toggle Dropdown
53 |
54 |
55 |
59 | Top Dropdown
60 |
61 |
62 |
66 | Left Dropdown
67 |
68 |
69 |
73 | Right Dropdown
74 |
75 |
76 |
80 | Toggle Dropdown With Id
81 |
82 |
83 | );
84 |
85 | export default DropdownPage;
86 |
--------------------------------------------------------------------------------
/docs/containers/media-object/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { MediaObjectSection, MediaObject } from '../../../src/media-object';
4 | import {
5 | MediaObjectSection as FlexMediaObjectSection,
6 | MediaObject as FlexMediaObject,
7 | } from '../../../lib/media-object-flex'; // eslint-disable-line import/no-unresolved
8 |
9 | const MediaObjectPage = () => (
10 |
11 |
12 |
13 |
17 |
18 |
19 | Dreams feel real while we're in them.
20 |
21 | I'm going to improvise. Listen, there's something you should know about me... about
22 | inception. An idea is like a virus, resilient, highly contagious. The smallest seed
23 | of an idea can grow. It can grow to define or destroy you.
24 |
25 |
26 |
27 |
28 |
29 |
30 |
34 |
35 |
36 | Why is it so important to dream?
37 |
38 | So, once we've made the plant, how do we go out? Hope you have something more elegant
39 | in mind than shooting me in the head? A kick. Whats a kick? This, Ariadne, would be a
40 | kick.
41 |
42 |
43 | What is the most resilient parasite? Bacteria? A virus? An intestinal worm? An idea.
44 | Resilient... highly contagious. Once an idea has taken hold of the brain it's almost
45 | impossible to eradicate. An idea that is fully formed - fully understood - that
46 | sticks; right in there somewhere.
47 |
48 |
49 |
50 |
54 |
55 |
56 |
57 |
58 |
59 |
63 |
64 |
65 | I Can Stack.
66 |
67 | Shrink the browser width to see me stack. I do tricks for dog treats, but I'm not a
68 | dog.
69 |
70 |
71 |
72 |
73 |
74 |
75 |
79 |
80 |
81 | Why is it so important to dream?
82 |
83 | So, once we've made the plant, how do we go out? Hope you have something more elegant
84 | in mind than shooting me in the head? A kick. Whats a kick? This, Ariadne, would be a
85 | kick.
86 |
87 |
88 | What is the most resilient parasite? Bacteria? A virus? An intestinal worm? An idea.
89 | Resilient... highly contagious. Once an idea has taken hold of the brain it's almost
90 | impossible to eradicate. An idea that is fully formed - fully understood - that
91 | sticks; right in there somewhere.
92 |
93 |
94 |
95 |
99 |
100 |
101 |
102 | );
103 |
104 | export default MediaObjectPage;
105 |
--------------------------------------------------------------------------------
/docs/containers/off-canvas/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import {
4 | OffCanvas,
5 | OffCanvasContent,
6 | OffCanvasContainer,
7 | } from '../../../src/off-canvas';
8 | import {
9 | TitleBar,
10 | TitleBarItem,
11 | TitleBarTitle,
12 | TitleBarMenuIcon,
13 | } from '../../../src/title-bar';
14 | import {
15 | TitleBar as FlexTitleBar,
16 | TitleBarItem as FlexTitleBarItem,
17 | TitleBarTitle as FlexTitleBarTitle,
18 | TitleBarMenuIcon as FlexTitleBarMenuIcon,
19 | } from '../../../lib/title-bar-flex'; // eslint-disable-line import/no-unresolved
20 | import { HideForScreenSize } from '../../../src/visibility';
21 |
22 | export default class OffCanvasPage extends Component {
23 | state = {
24 | open: null,
25 | };
26 |
27 | handleClose = () => this.setState({ open: null });
28 |
29 | handleToggle = (position) => {
30 | const { open: prevOpen } = this.state;
31 | const open = prevOpen === position ? null : position;
32 |
33 | this.setState({ open });
34 | };
35 |
36 | handleToggleLeft = () => this.handleToggle('left');
37 |
38 | handleToggleRight = () => this.handleToggle('right');
39 |
40 | render() {
41 | const { open } = this.state;
42 |
43 | return (
44 |
45 |
46 | Left Sidebar
47 |
48 |
49 | Right Sidebar
50 |
51 |
52 |
53 |
54 |
60 | Foundation
61 |
62 |
63 |
69 |
70 |
71 |
72 |
73 |
74 |
80 | Foundation
81 |
82 |
83 |
89 |
90 |
91 |
92 | Main Content
93 |
94 |
95 | );
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/docs/containers/reveal/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { Reveal } from '../../../src/reveal';
4 | import { Button } from '../../../src/button';
5 | import { CloseButton } from '../../../src/close-button';
6 |
7 | export default class RevealPage extends Component {
8 | state = {
9 | showBasic: false,
10 | showTiny: false,
11 | showSmall: false,
12 | showLarge: false,
13 | showFull: false,
14 | showNested1: false,
15 | showNested2: false,
16 | };
17 |
18 | handleShowHide = (type, show) => this.setState({ [`show${type}`]: show });
19 |
20 | handleShowBasic = () => this.handleShowHide('Basic', true);
21 |
22 | handleHideBasic = () => this.handleShowHide('Basic', false);
23 |
24 | handleShowTiny = () => this.handleShowHide('Tiny', true);
25 |
26 | handleHideTiny = () => this.handleShowHide('Tiny', false);
27 |
28 | handleShowSmall = () => this.handleShowHide('Small', true);
29 |
30 | handleHideSmall = () => this.handleShowHide('Small', false);
31 |
32 | handleShowLarge = () => this.handleShowHide('Large', true);
33 |
34 | handleHideLarge = () => this.handleShowHide('Large', false);
35 |
36 | handleShowFull = () => this.handleShowHide('Full', true);
37 |
38 | handleHideFull = () => this.handleShowHide('Full', false);
39 |
40 | handleShowNested1 = () => this.handleShowHide('Nested1', true);
41 |
42 | handleHideNested1 = () => this.handleShowHide('Nested1', false);
43 |
44 | handleShowNested2 = () => this.handleShowHide('Nested2', true);
45 |
46 | handleHideNested2 = () => this.handleShowHide('Nested2', false);
47 |
48 | handleShowNoOverlay = () => this.handleShowHide('NoOverlay', true);
49 |
50 | handleHideNoOverlay = () => this.handleShowHide('NoOverlay', false);
51 |
52 | handleShowNoCloseOnClick = () => this.handleShowHide('NoCloseOnClick', true);
53 |
54 | handleHideNoCloseOnClick = () => this.handleShowHide('NoCloseOnClick', false);
55 |
56 | render() {
57 | const {
58 | showBasic,
59 | showTiny,
60 | showSmall,
61 | showLarge,
62 | showFull,
63 | showNested1,
64 | showNested2,
65 | showNoOverlay,
66 | showNoCloseOnClick,
67 | } = this.state;
68 |
69 | return (
70 |
71 |
Click me for a Modal
72 |
73 |
74 | Awesome. I Have It.
75 | Your couch. It is mine.
76 | I'm a cool paragraph that lives inside of an even cooler modal. Wins!
77 |
78 |
79 |
Click me for a Tiny Modal
80 |
81 |
82 | OH I'M SO TIIINY
83 |
84 |
85 |
Click me for a Small Modal
86 |
87 |
88 | I may be small, but I've got a big heart!
89 |
90 |
91 |
Click me for a Large Modal
92 |
93 |
94 | I'm big, like bear!
95 |
96 |
97 |
Click me for a Full Screen Modal
98 |
99 |
100 | OH I'M SO FUUUUL
101 |
102 |
103 |
104 |
Click me for a Modal
105 |
106 |
107 | Awesome!
108 | I have another modal inside of me!
109 | Click me for another Modal
110 |
111 |
112 |
113 | ANOTHER MODAL!!!
114 |
115 |
116 |
Click me for an overlay-lacking Modal
117 |
118 |
119 | I feel so free!
120 |
121 |
122 |
Click me for a static overlay Modal
123 |
124 |
125 | Click outside : Nothing happens!!!
126 |
127 |
128 | );
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/docs/containers/table/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Table } from '../../../src/table';
4 |
5 | const TablePage = () => (
6 |
7 |
8 |
9 |
10 | Table Header
11 | Table Header
12 | Table Header
13 | Table Header
14 |
15 |
16 |
17 |
18 | Content Goes Here
19 | This is longer content Donec id elit non mi porta gravida at eget metus.
20 | Content Goes Here
21 | Content Goes Here
22 |
23 |
24 | Content Goes Here
25 |
26 | This is longer Content Goes Here Donec id elit non mi porta gravida at eget metus.
27 |
28 | Content Goes Here
29 | Content Goes Here
30 |
31 |
32 | Content Goes Here
33 |
34 | This is longer Content Goes Here Donec id elit non mi porta gravida at eget metus.
35 |
36 | Content Goes Here
37 | Content Goes Here
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Table Header
46 | Table Header
47 | Table Header
48 | Table Header
49 |
50 |
51 |
52 |
53 | Content Goes Here
54 | This is longer content Donec id elit non mi porta gravida at eget metus.
55 | Content Goes Here
56 | Content Goes Here
57 |
58 |
59 | Content Goes Here
60 |
61 | This is longer Content Goes Here Donec id elit non mi porta gravida at eget metus.
62 |
63 | Content Goes Here
64 | Content Goes Here
65 |
66 |
67 | Content Goes Here
68 |
69 | This is longer Content Goes Here Donec id elit non mi porta gravida at eget metus.
70 |
71 | Content Goes Here
72 | Content Goes Here
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | Cookies
81 | Taste
82 | Calories
83 | Overall
84 |
85 |
86 |
87 |
88 | Chocolate Chip
89 | Tastey
90 | 120cal
91 | 7.5/10
92 |
93 |
94 | Snickerdoodle
95 | Delicious
96 | 95cal
97 | 8/10
98 |
99 |
100 | Oatmeal Raisin
101 | Superb
102 | 10cal
103 | 11/10
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | This is the description!
112 | One
113 | Two
114 | Three
115 | Four
116 | Five
117 | Six
118 | Seven
119 | Eight
120 | Nine
121 | Ten
122 | Eleven
123 | Twelve
124 |
125 |
126 |
127 |
128 |
129 | These are all the words that people use to describe Foundation 6!
130 |
131 | Cool
132 | Swag
133 | Chill
134 | Killer
135 | Rad
136 | Baller
137 | OMG
138 | Sweet
139 | Awesome
140 | Beast
141 | Dope
142 | Tubular
143 |
144 |
145 | These are some words that people use to describe other web frameworks.
146 | Whatevs
147 | Ugh.
148 | LOL
149 | K
150 | Aight
151 | Eh.
152 | Grrr...
153 | Meh.
154 | TTYL
155 | Bleh.
156 | Really?
157 | Why?
158 |
159 |
160 | Here are some great super heros.
161 | Batman
162 | Superman
163 | Spiderman
164 | Wonder Woman
165 | Hulk
166 | Nicolas Cage
167 | Antman
168 | Aquaman
169 | Captain America
170 | Wolverine
171 | Thor
172 | Iron Man
173 |
174 |
175 |
176 |
177 | Here's a footer, just in case
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | );
195 |
196 | export default TablePage;
197 |
--------------------------------------------------------------------------------
/docs/containers/tabs/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { Tabs, Tab } from '../../../src/tabs';
4 |
5 | export default class TabPage extends Component {
6 | state = {
7 | activeKey: '1',
8 | };
9 |
10 | handleSelect = (activeKey) => this.setState({ activeKey });
11 |
12 | render() {
13 | const { activeKey } = this.state;
14 |
15 | return (
16 |
17 |
18 |
19 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
20 | incididunt ut labore et dolore magna aliqua.
21 |
22 |
23 | Vivamus hendrerit arcu sed erat molestie vehicula. Sed auctor neque eu tellus rhoncus ut
24 | eleifend nibh porttitor. Ut in nulla enim. Phasellus molestie magna non est bibendum non
25 | venenatis nisl tempor. Suspendisse dictum feugiat nisl ut dapibus.
26 |
27 |
28 |
32 |
33 |
34 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
35 | incididunt ut labore et dolore magna aliqua.
36 |
37 |
38 |
42 |
43 |
44 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
45 | incididunt ut labore et dolore magna aliqua.
46 |
47 |
48 |
49 |
50 |
51 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
52 | incididunt ut labore et dolore magna aliqua.
53 |
54 |
55 | Vivamus hendrerit arcu sed erat molestie vehicula. Sed auctor neque eu tellus rhoncus ut
56 | eleifend nibh porttitor. Ut in nulla enim. Phasellus molestie magna non est bibendum non
57 | venenatis nisl tempor. Suspendisse dictum feugiat nisl ut dapibus.
58 |
59 |
60 |
64 |
65 |
66 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
67 | incididunt ut labore et dolore magna aliqua.
68 |
69 |
70 |
74 |
75 |
76 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
77 | incididunt ut labore et dolore magna aliqua.
78 |
79 |
80 |
81 |
82 |
83 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
84 | incididunt ut labore et dolore magna aliqua.
85 |
86 |
87 | Vivamus hendrerit arcu sed erat molestie vehicula. Sed auctor neque eu tellus rhoncus ut
88 | eleifend nibh porttitor. Ut in nulla enim. Phasellus molestie magna non est bibendum non
89 | venenatis nisl tempor. Suspendisse dictum feugiat nisl ut dapibus.
90 |
91 |
92 |
96 |
97 |
98 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
99 | incididunt ut labore et dolore magna aliqua.
100 |
101 |
102 |
106 |
107 |
108 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
109 | incididunt ut labore et dolore magna aliqua.
110 |
111 |
112 |
113 | );
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/docs/controls/close-button/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { CloseButton } from '../../../src/close-button';
4 | import { Callout } from '../../../src/callout';
5 |
6 | const CloseButtonPage = () => (
7 |
8 |
Close Button
9 |
10 | The close button can be used anywhere you need something to go away on click.
11 |
12 |
13 |
Basics
14 |
Importing the CloseButton component:
15 |
16 |
17 | {
18 | `// Import with local scoped class names (via CSS Modules)
19 | import { CloseButton } from 'react-foundation-components/lib/close-button';
20 |
21 | or
22 |
23 | // Import with global scoped class names
24 | import { CloseButton } from 'react-foundation-components/lib/global/close-button';`
25 | }
26 |
27 |
28 |
29 | Clarify the button's purpose using the aria-label
prop.
30 |
31 |
32 |
33 | {
34 | `
35 |
36 | Look at this close button!
37 | `
38 | }
39 |
40 |
41 |
42 |
43 | Look at this close button!
44 |
45 |
46 | );
47 |
48 | export default CloseButtonPage;
49 |
--------------------------------------------------------------------------------
/docs/docs.scss:
--------------------------------------------------------------------------------
1 | $color-background: #f4f5f6 !default
2 |
3 | @import '../src/typography';
4 |
5 | code {
6 | margin: 0 .2rem 1rem;
7 | border-radius: .4rem;
8 | background: $color-background;
9 | padding: .2rem .5rem;
10 | white-space: nowrap;
11 | font-size: 86%;
12 | }
13 |
14 | pre {
15 | font-family: 'Menlo', 'Consolas', 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Monaco', monospace;
16 |
17 | & > code {
18 | display: block;
19 | border-radius: 0;
20 | padding: 1rem 1.5rem;
21 | white-space: pre;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aruberto/react-foundation-components/3db1a7a9bae46734089635bffb53d107b43d973b/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/general/float/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Float, ClearFix } from '../../../src/float';
4 | import { Callout } from '../../../src/callout';
5 | import { Button } from '../../../src/button';
6 |
7 | const FloatPage = () => (
8 |
9 |
Float
10 |
11 | Helpful positioning components.
12 |
13 |
Basics
14 |
Importing the Float components:
15 |
16 |
17 | {
18 | `// Import with local scoped class names (via CSS Modules)
19 | import { Float, ClearFix } from 'react-foundation-components/lib/float';
20 |
21 | or
22 |
23 | // Import with global scoped class names
24 | import { Float, ClearFix } from 'react-foundation-components/lib/global/float';`
25 | }
26 |
27 |
28 |
29 | Position content by placing it inside the Float component. Set the position
30 | prop to left, right or center to specify the position. Center will only work when Float
31 | has as absolute width. To clear floats, wrap them in the ClearFix component.
32 |
33 |
34 |
35 | {
36 | `
37 |
38 | Left
39 |
40 |
41 | Right
42 |
43 |
44 |
45 |
46 | `
47 | }
48 |
49 |
50 |
51 |
52 | Left
53 |
54 |
55 | Right
56 |
57 |
58 |
59 |
60 |
61 |
62 | );
63 |
64 | export default FloatPage;
65 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= htmlWebpackPlugin.options.title %>
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/media/badge/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Badge } from '../../../src/badge';
4 | import { ShowForScreenReader } from '../../../src/visibility';
5 |
6 | const BadgePage = () => (
7 |
8 |
Badge
9 |
10 | The badge is a basic component that displays a number.
11 |
12 |
13 |
Basics
14 |
Importing the Badge component:
15 |
16 |
17 | {
18 | `// Import with local scoped class names (via CSS Modules)
19 | import { Badge } from 'react-foundation-components/lib/badge';
20 |
21 | or
22 |
23 | // Import with global scoped class names
24 | import { Badge } from 'react-foundation-components/lib/global/badge';`
25 | }
26 |
27 |
28 |
29 |
30 | {
31 | '1 '
32 | }
33 |
34 |
35 |
36 | 1
37 |
38 |
39 |
Coloring
40 |
41 | Give a Badge additional meaning by setting the color
prop. Possible values
42 | are primary, secondary, success, alert and warning.
43 |
44 |
45 |
46 | {
47 | `1
48 | 2
49 | 3
50 | A
51 | B `
52 | }
53 |
54 |
55 |
56 | 1
57 |
58 | 2
59 |
60 | 3
61 |
62 | A
63 |
64 | B
65 |
66 |
67 |
Accessibility
68 |
69 | A Badge will typically be describing another element on the page. To bind the two elements
70 | together, give the badge an id
, and reference that id
in an
71 | aria-describedby
attribute on the main element. The Badge content itself
72 | might need more context for users that use screen readers. You can add extra text inside
73 | the badge by wrapping the text with the ShowForScreenReader component.
74 |
75 |
76 |
77 | {
78 | `Unread Messages
79 |
80 | 12
81 | Unread Messages
82 | `
83 | }
84 |
85 |
86 |
87 | Unread Messages
88 |
89 | 12
90 | Unread Messages
91 |
92 |
93 |
94 | );
95 |
96 | export default BadgePage;
97 |
--------------------------------------------------------------------------------
/docs/media/flex-video/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { FlexVideo } from '../../../src/flex-video';
4 |
5 | const FlexVideoPage = () => (
6 |
7 |
Flex Video
8 |
9 | Wrap embedded videos from YouTube, Vimeo, and others in a flex video container to ensure
10 | they maintain the correct aspect ratio regardless of screen size.
11 |
12 |
13 |
Basics
14 |
Importing the FlexVideo component:
15 |
16 |
17 | {
18 | `// Import with local scoped class names (via CSS Modules)
19 | import { FlexVideo } from 'react-foundation-components/lib/flex-video';
20 |
21 | or
22 |
23 | // Import with global scoped class names
24 | import { FlexVideo } from 'react-foundation-components/lib/global/flex-video';`
25 | }
26 |
27 |
28 |
29 | All the props you can set on iframe can also be set on the FlexVideo component.
30 |
31 |
32 |
33 | {
34 | ` `
41 | }
42 |
43 |
44 |
51 |
52 | The default ratio is 4:3. Set the widescreen
prop to change it to 16:9.
53 |
54 |
55 |
56 | {
57 | ` `
65 | }
66 |
67 |
68 |
76 |
77 | Embedded Vimeo videos are special snowflakes of their own. Set the vimeo
prop
78 | to display a Vimeo video.
79 |
80 |
81 |
82 | {
83 | ` `
92 | }
93 |
94 |
95 |
104 |
105 | );
106 |
107 | export default FlexVideoPage;
108 |
--------------------------------------------------------------------------------
/docs/media/label/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Label } from '../../../src/label';
4 |
5 | const LabelPage = () => (
6 |
7 |
Label
8 |
9 | Labels are useful to call out certain sections or to attach metadata.
10 |
11 |
12 |
Basics
13 |
Importing the Label component:
14 |
15 |
16 | {
17 | `// Import with local scoped class names (via CSS Modules)
18 | import { Label } from 'react-foundation-components/lib/label';
19 |
20 | or
21 |
22 | // Import with global scoped class names
23 | import { Label } from 'react-foundation-components/lib/global/label';`
24 | }
25 |
26 |
27 |
28 |
29 | {
30 | 'Default Label '
31 | }
32 |
33 |
34 |
35 | Default Label
36 |
37 |
38 |
Coloring
39 |
40 | Give a Label additional meaning by setting the color
prop. Possible values
41 | are primary, secondary, success, alert and warning.
42 |
43 |
44 |
45 | {
46 | `Primary Label
47 | Secondary Label
48 | Success Label
49 | Alert Label
50 | Warning Label `
51 | }
52 |
53 |
54 |
55 | Primary Label
56 |
57 | Secondary Label
58 |
59 | Success Label
60 |
61 | Alert Label
62 |
63 | Warning Label
64 |
65 |
66 |
Accessibility
67 |
68 | A Label will typically be describing another element on the page. To bind the two elements
69 | together, give the badge an id
, and reference that id
in an
70 | aria-describedby
attribute on the main element.
71 |
72 |
73 |
74 | {
75 | `
76 | Re: re: re: you won't believe what's in this email!
77 |
78 |
79 | High Priority `
80 | }
81 |
82 |
83 |
84 |
85 | Re: re: re: you won't believe what's in this email!
86 |
87 |
88 | High Priority
89 |
90 |
91 | If an element is described by multiple labels, place multiple IDs inside
92 | of the aria-describedby
prop.
93 |
94 |
95 |
96 | {
97 | `
98 | Re: re: re: you won't believe what's in this email!
99 |
100 | High Priority
101 | Unread `
102 | }
103 |
104 |
105 |
106 |
107 | Re: re: re: you won't believe what's in this email!
108 |
109 |
110 | High Priority
111 |
112 | Unread
113 |
114 |
115 | );
116 |
117 | export default LabelPage;
118 |
--------------------------------------------------------------------------------
/docs/media/progress-bar/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { ProgressBar } from '../../../src/progress-bar';
4 |
5 | function percentFormatter(percent) {
6 | const rounded = Math.round(percent * 100);
7 |
8 | return `${rounded}%`;
9 | }
10 |
11 | function complexFormatter(percent, value, min, max) {
12 | return `percent = ${percent}, value = ${value}, min = ${min}, max = ${max}`;
13 | }
14 |
15 | const ProgressBarPage = () => (
16 |
17 |
Progress Bar
18 |
19 | Show your progress.
20 |
21 |
22 |
Basics
23 |
Importing the ProgressBar component:
24 |
25 |
26 | {
27 | `// Import with local scoped class names (via CSS Modules)
28 | import { ProgressBar } from 'react-foundation-components/lib/progress-bar';
29 |
30 | or
31 |
32 | // Import with global scoped class names
33 | import { ProgressBar } from 'react-foundation-components/lib/global/progress-bar';`
34 | }
35 |
36 |
37 |
38 | Set the value
prop to specify the current progress value (defaults to 0).
39 | Set the min
and/or max
props to change the minimum/maximum
40 | possible values for progress bar (defaults to 0 and 100 respectively).
41 |
42 |
43 |
44 | {
45 | `
46 |
47 |
48 |
49 |
50 | `
51 | }
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
Coloring
62 |
63 | Give a Badge additional meaning by setting the color
prop. Possible values
64 | are primary, secondary, success, alert and warning.
65 |
66 |
67 |
68 | {
69 | `
70 |
71 |
72 |
73 | `
74 | }
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
With Text
84 |
85 | You can add text inside the meter of a progress bar by providing
86 | a labelFormatter
function. The function takes 4 arguments: (1) the calculated
87 | percent, (2) the curent value (adjusted to make sure it falls within the min and max), (3)
88 | the minimum value and (4) the maximum value.
89 |
90 |
91 |
92 | {
93 | `function complexFormatter(percent, value, min, max) {
94 | return \`percent = \${percent}, value = \${value}, min = \${min}, max = \${max}\`;
95 | }
96 |
97 | function percentFormatter(percent) {
98 | const rounded = Math.round(percent * 100);
99 |
100 | return \`\${rounded}%\`;
101 | }
102 |
103 |
104 | `
105 | }
106 |
107 |
108 |
109 |
110 |
111 | );
112 |
113 | export default ProgressBarPage;
114 |
--------------------------------------------------------------------------------
/docs/media/thumbnail/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Thumbnail } from '../../../src/thumbnail';
4 |
5 | const ThumbnailPage = () => (
6 |
7 |
Thumbnail
8 |
9 | A reduced size image.
10 |
11 |
12 |
Basics
13 |
Importing the Thumbnail component:
14 |
15 |
16 | {
17 | `// Import with local scoped class names (via CSS Modules)
18 | import { Thumbnail } from 'react-foundation-components/lib/thumbnail';
19 |
20 | or
21 |
22 | // Import with global scoped class names
23 | import { Thumbnail } from 'react-foundation-components/lib/global/thumbnail';`
24 | }
25 |
26 |
27 |
28 | Thumbnail behaves similar to an img tag.
29 |
30 |
31 |
32 | {
33 | `
37 |
41 | `
45 | }
46 |
47 |
48 |
49 |
53 |
54 |
58 |
59 |
63 |
64 |
65 | );
66 |
67 | export default ThumbnailPage;
68 |
--------------------------------------------------------------------------------
/docs/navigation/breadcrumb/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Breadcrumb, BreadcrumbItem } from '../../../src/breadcrumb';
4 |
5 | const BreadcrumbPage = () => (
6 |
7 |
8 | Home
9 | Features
10 | Gene Splicing
11 | Cloning
12 |
13 |
14 | );
15 |
16 | export default BreadcrumbPage;
17 |
--------------------------------------------------------------------------------
/docs/navigation/menu/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Menu, MenuItem } from '../../../src/menu';
4 | import {
5 | Menu as FlexMenu,
6 | MenuItem as FlexMenuItem,
7 | } from '../../../lib/menu-flex'; // eslint-disable-line import/no-unresolved
8 |
9 | const iconStyle = {
10 | fontStyle: 'normal',
11 | };
12 |
13 | const MenuPage = () => (
14 |
122 | );
123 |
124 | export default MenuPage;
125 |
--------------------------------------------------------------------------------
/docs/navigation/pagination/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { Pagination } from '../../../src/pagination';
4 |
5 | function pageContentFormatter(page, activePage) {
6 | if (page === activePage) {
7 | return You are on {page} ;
8 | }
9 |
10 | return Go to {page} ;
11 | }
12 |
13 | export default class PaginationPage extends Component {
14 | state = {
15 | activePage: 1,
16 | };
17 |
18 | handleSelect = (activePage) => this.setState({ activePage });
19 |
20 | render() {
21 | const { activePage } = this.state;
22 |
23 | return (
24 |
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/docs/typography/base/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const BaseTypographyPage = () => (
4 |
5 |
6 | This is a paragraph. Paragraphs are preset with a font size, line height and spacing to
7 | match the overall vertical rhythm. To show what a paragraph looks like this needs a little
8 | more content so, did you know that there are storms occurring on Jupiter that are larger
9 | than the Earth? Pretty cool. Wrap strong around type to make it bold! .
10 | You can also use em to italicize your words .
11 |
12 |
h1. This is a very large header.
13 |
h2. This is a large header.
14 |
h3. This is a medium header.
15 |
h4. This is a moderate header.
16 |
h5. This is a small header.
17 |
h6. This is a tiny header.
18 |
Foundation for Sites Version 6.0.4
19 |
20 | Links are very standard, and the color is preset to the Foundation primary color.
21 |
22 | Learn more about Foundation's global colors.
23 |
24 |
25 |
26 |
27 | List item with a much longer description or more content.
28 | List item
29 | List item
30 |
31 | Nested list item
32 | Nested list item
33 | Nested list item
34 |
35 |
36 | List item
37 | List item
38 | List item
39 |
40 |
41 | Cheese (essential)
42 | Pepperoni
43 | Bacon
44 |
45 | Normal bacon
46 | Canadian bacon
47 |
48 |
49 | Sausage
50 | Onions
51 | Mushrooms
52 |
53 |
54 | Time
55 |
56 | The indefinite continued progress of existence and events in the past, present, and
57 | future regarded as a whole.
58 |
59 | Space
60 | A continuous area or expanse that is free, available, or unoccupied.
61 |
62 | The dimensions of height, depth, and width within which all things exist and move.
63 |
64 |
65 |
66 | Those people who think they know everything are a great annoyance to those of us who do.
67 | Isaac Asimov
68 |
69 |
70 | In my dream last night, I saw J. R. R. Tolkien and
71 | George R. R. Martin hanging out on
72 | Sunset Blvd .
73 |
74 | Remember to escape angle brackets when printing HTML:
<div>
75 |
Press Cmd+Q (or Ctrl+Q on Windows) to play Half-Life 3.
76 |
77 |
78 | );
79 |
80 | export default BaseTypographyPage;
81 |
--------------------------------------------------------------------------------
/docs/typography/helpers/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Subheader, Lead, UnbulletedList, Statistic } from '../../../src/typography-helpers';
4 |
5 | const TypographyHelpersPage = () => (
6 |
7 |
unspecified subheader
8 |
h1 subheader
9 |
h2 subheader
10 |
h3 subheader
11 |
h4 subheader
12 |
h5 subheader
13 |
h6 subheader
14 |
15 |
What are your cats really dreaming about while they sleep?
16 |
17 |
18 |
19 | List item with a much longer description or more content.
20 | List item
21 | List item
22 |
23 | Nested list item
24 | Nested list item
25 | Nested list item
26 |
27 |
28 | List item
29 | List item
30 | List item
31 |
32 |
33 | Days without merge conflict
128
34 |
35 | );
36 |
37 | export default TypographyHelpersPage;
38 |
--------------------------------------------------------------------------------
/docs/typography/print/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { ShowForPrint, HideForPrint } from '../../../src/print';
4 |
5 | const PrintPage = () => (
6 |
7 | You can see me when printing!
8 |
9 | You can not see me when printing!
10 |
11 | );
12 |
13 | export default PrintPage;
14 |
--------------------------------------------------------------------------------
/docs/typography/text-alignment/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { TextAlignment } from '../../../src/text-alignment';
4 |
5 | const TextAlignmentPage = () => (
6 |
7 |
8 | This text is left-aligned. Set in the year 0 F.E. ("Foundation Era"), The
9 | Psychohistorians opens on Trantor, the capital of the 12,000-year-old Galactic Empire.
10 | Though the empire appears stable and powerful, it is slowly decaying in ways that parallel
11 | the decline of the Western Roman Empire.
12 |
13 |
14 |
15 | This text is right-aligned. Set in the year 0 F.E. ("Foundation Era"), The
16 | Psychohistorians opens on Trantor, the capital of the 12,000-year-old Galactic Empire.
17 | Though the empire appears stable and powerful, it is slowly decaying in ways that parallel
18 | the decline of the Western Roman Empire.
19 |
20 |
21 |
22 | This text is center-aligned. Set in the year 0 F.E. ("Foundation Era"), The
23 | Psychohistorians opens on Trantor, the capital of the 12,000-year-old Galactic Empire.
24 | Though the empire appears stable and powerful, it is slowly decaying in ways that parallel
25 | the decline of the Western Roman Empire.
26 |
27 |
28 |
29 | This text is justified. Set in the year 0 F.E. ("Foundation Era"), The
30 | Psychohistorians opens on Trantor, the capital of the 12,000-year-old Galactic Empire.
31 | Though the empire appears stable and powerful, it is slowly decaying in ways that parallel
32 | the decline of the Western Roman Empire.
33 |
34 |
35 |
36 | This text is right-aligned on large screen or larger. Set in the year 0 F.E.
37 | ("Foundation Era"), The Psychohistorians opens on Trantor, the capital of the
38 | 12,000-year-old Galactic Empire. Though the empire appears stable and powerful, it is
39 | slowly decaying in ways that parallel the decline of the Western Roman Empire.
40 |
41 |
42 | );
43 |
44 | export default TextAlignmentPage;
45 |
--------------------------------------------------------------------------------
/docs/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const autoprefixer = require('autoprefixer');
6 | const parseArgs = require('minimist');
7 |
8 | const argv = parseArgs(process.argv.slice(2), {
9 | default: {
10 | dev: false,
11 | verbose: false,
12 | },
13 | });
14 |
15 | const DEV = argv.dev;
16 | const VERBOSE = argv.verbose;
17 | const GLOBALS = {
18 | 'process.env.NODE_ENV': DEV ? JSON.stringify('development') : JSON.stringify('production'),
19 | };
20 | const PLUGINS = [
21 | new webpack.DefinePlugin(GLOBALS),
22 | new HtmlWebpackPlugin({
23 | title: 'React Foundation Components',
24 | template: path.join(__dirname, 'index.html'),
25 | favicon: path.join(__dirname, 'favicon.ico'),
26 | inject: 'body',
27 | }),
28 | ];
29 | const DEV_PLUGINS = [
30 | new webpack.HotModuleReplacementPlugin(),
31 | new webpack.NoErrorsPlugin(),
32 | ];
33 | const PROD_PLUGINS = [
34 | new ExtractTextPlugin('main-[contenthash].css'),
35 | new webpack.optimize.OccurenceOrderPlugin(),
36 | new webpack.optimize.DedupePlugin(),
37 | new webpack.optimize.UglifyJsPlugin({
38 | compress: {
39 | warnings: VERBOSE,
40 | },
41 | }),
42 | new webpack.optimize.AggressiveMergingPlugin(),
43 | ];
44 | const ENTRY_MIDDLEWARE = DEV ? ['webpack-hot-middleware/client'] : [];
45 | const BABEL_PLUGINS = [];
46 | const BABEL_DEV_PLUGINS = [
47 | [
48 | 'react-transform',
49 | {
50 | transforms: [
51 | {
52 | transform: 'react-transform-hmr',
53 | imports: ['react'],
54 | locals: ['module'],
55 | },
56 | {
57 | transform: 'react-transform-catch-errors',
58 | imports: ['react', 'redbox-react'],
59 | },
60 | ],
61 | },
62 | ],
63 | ];
64 | const BABEL_PROD_PLUGINS = [];
65 | const SASS_LOADERS = [
66 | 'style',
67 | `css?modules${DEV ? '&localIdentName=[path]---[local]' : ''}!postcss!sass`,
68 | ];
69 |
70 | module.exports = {
71 | entry: ENTRY_MIDDLEWARE.concat(path.join(__dirname)),
72 |
73 | output: {
74 | path: path.join(__dirname, 'lib'),
75 | filename: `[name]-[${DEV ? 'hash' : 'chunkhash'}].js`,
76 | publicPath: '/react-foundation-components',
77 | },
78 |
79 | plugins: PLUGINS.concat(DEV ? DEV_PLUGINS : PROD_PLUGINS),
80 |
81 | cache: DEV,
82 | debug: DEV,
83 | devtool: DEV ? 'cheap-module-eval-source-map' : false,
84 |
85 | stats: {
86 | colors: true,
87 | reasons: true,
88 | hash: VERBOSE,
89 | version: VERBOSE,
90 | timings: true,
91 | chunks: VERBOSE,
92 | chunkModules: VERBOSE,
93 | cached: VERBOSE,
94 | cachedAssets: VERBOSE,
95 | },
96 |
97 | babel: {
98 | plugins: BABEL_PLUGINS.concat(DEV ? BABEL_DEV_PLUGINS : BABEL_PROD_PLUGINS),
99 | cacheDirectory: argv.dev,
100 | },
101 |
102 | sassLoader: {
103 | precision: 8,
104 | },
105 |
106 | postcss: [
107 | autoprefixer({
108 | browsers: [
109 | 'Android >= 4',
110 | 'Chrome >= 20',
111 | 'Firefox >= 24',
112 | 'Explorer >= 9',
113 | 'Edge >= 1',
114 | 'iOS >= 6',
115 | 'Opera >= 12',
116 | 'Safari >= 6',
117 | ],
118 | }),
119 | ],
120 |
121 | imagemin: {
122 | minimize: !DEV,
123 | gifsicle: {
124 | interlaced: true,
125 | },
126 | jpegtran: {
127 | progressive: true,
128 | },
129 | optipng: {
130 | optimizationLevel: 7,
131 | },
132 | svgo: {
133 | plugins: [
134 | {
135 | removeTitle: true,
136 | },
137 | {
138 | convertPathData: false,
139 | },
140 | {
141 | removeViewBox: false,
142 | },
143 | ],
144 | },
145 | },
146 |
147 | url: {
148 | dataUrlLimit: 10000,
149 | },
150 |
151 | module: {
152 | loaders: [
153 | {
154 | test: /\.js$/,
155 | exclude: /node_modules/,
156 | loader: 'babel',
157 | },
158 | {
159 | test: /\.scss$/,
160 | loader: DEV ? SASS_LOADERS.join('!') : ExtractTextPlugin.extract.apply(null, SASS_LOADERS),
161 | },
162 | {
163 | test: /\.(gif|jpe?g|png|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
164 | loader: 'url!img',
165 | },
166 | {
167 | test: /\.(woff2?|ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
168 | loader: 'url',
169 | },
170 | ],
171 | },
172 | };
173 |
--------------------------------------------------------------------------------
/examples/cdn-flex/app.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unresolved */
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 |
6 | import { Button } from '../../lib/global/button';
7 | import { ButtonGroup } from '../../lib/global/button-group-flex';
8 |
9 | const App = () => (
10 |
11 |
Welcome to CDN CSS with Flexbox example!
12 |
13 | We've imported Foundation from CDN - https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.0/foundation-flex.min.css.
14 |
15 |
16 | Make sure to use the components under lib/global. These components use global scoped
17 | class names.
18 |
19 |
20 | Also make sure to use the Flexbox components. The non Flexbox components use class names
21 | that don't exist in the flex CDN stylesheet.
22 |
23 |
24 |
Button react-foundation-components/lib/global/button
25 | Click Me!
26 |
27 |
28 |
ButtonGroup react-foundation-components/lib/global/button-group-flex
29 |
This ButtonGroup uses Flexbox layout
30 |
31 | A
32 | B
33 | C
34 |
35 |
36 |
37 | );
38 |
39 | ReactDOM.render( , document.getElementById('app'));
40 |
--------------------------------------------------------------------------------
/examples/cdn-flex/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aruberto/react-foundation-components/3db1a7a9bae46734089635bffb53d107b43d973b/examples/cdn-flex/favicon.ico
--------------------------------------------------------------------------------
/examples/cdn-flex/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= htmlWebpackPlugin.options.title %>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/cdn-flex/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 |
5 | module.exports = {
6 | entry: path.join(__dirname, 'app.js'),
7 |
8 | output: {
9 | path: path.join(__dirname, 'lib'),
10 | filename: '[name]-[chunkhash].js',
11 | publicPath: '/',
12 | },
13 |
14 | plugins: [
15 | new webpack.DefinePlugin({
16 | 'process.env.NODE_ENV': JSON.stringify('production'),
17 | }),
18 | new HtmlWebpackPlugin({
19 | title: 'React Foundation Components CSS Modules Example',
20 | template: path.join(__dirname, 'index.html'),
21 | favicon: path.join(__dirname, 'favicon.ico'),
22 | inject: 'body',
23 | }),
24 | new webpack.optimize.OccurenceOrderPlugin(),
25 | new webpack.optimize.DedupePlugin(),
26 | new webpack.optimize.UglifyJsPlugin(),
27 | new webpack.optimize.AggressiveMergingPlugin(),
28 | ],
29 |
30 | module: {
31 | loaders: [
32 | {
33 | test: /\.js$/,
34 | exclude: /node_modules/,
35 | loader: 'babel',
36 | },
37 | ],
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/examples/cdn/app.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unresolved */
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 |
6 | import { Button } from '../../lib/global/button';
7 | import { ButtonGroup } from '../../lib/global/button-group';
8 |
9 | const App = () => (
10 |
11 |
Welcome to CDN CSS example!
12 |
13 | We've imported Foundation from CDN - https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.0/foundation.min.css.
14 |
15 |
16 | Make sure to use the components under lib/global. These components use global scoped
17 | class names.
18 |
19 |
20 | Also make sure to use the non Flexbox components. The Flexbox components use class names
21 | that don't exist in the non flex CDN stylesheet.
22 |
23 |
24 |
Button react-foundation-components/lib/global/button
25 | Click Me!
26 |
27 |
28 |
ButtonGroup react-foundation-components/lib/global/button-group
29 |
This ButtonGroup uses float layout
30 |
31 | A
32 | B
33 | C
34 |
35 |
36 |
37 | );
38 |
39 | ReactDOM.render( , document.getElementById('app'));
40 |
--------------------------------------------------------------------------------
/examples/cdn/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aruberto/react-foundation-components/3db1a7a9bae46734089635bffb53d107b43d973b/examples/cdn/favicon.ico
--------------------------------------------------------------------------------
/examples/cdn/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= htmlWebpackPlugin.options.title %>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/cdn/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 |
5 | module.exports = {
6 | entry: path.join(__dirname, 'app.js'),
7 |
8 | output: {
9 | path: path.join(__dirname, 'lib'),
10 | filename: '[name]-[chunkhash].js',
11 | publicPath: '/',
12 | },
13 |
14 | plugins: [
15 | new webpack.DefinePlugin({
16 | 'process.env.NODE_ENV': JSON.stringify('production'),
17 | }),
18 | new HtmlWebpackPlugin({
19 | title: 'React Foundation Components CSS Modules Example',
20 | template: path.join(__dirname, 'index.html'),
21 | favicon: path.join(__dirname, 'favicon.ico'),
22 | inject: 'body',
23 | }),
24 | new webpack.optimize.OccurenceOrderPlugin(),
25 | new webpack.optimize.DedupePlugin(),
26 | new webpack.optimize.UglifyJsPlugin(),
27 | new webpack.optimize.AggressiveMergingPlugin(),
28 | ],
29 |
30 | module: {
31 | loaders: [
32 | {
33 | test: /\.js$/,
34 | exclude: /node_modules/,
35 | loader: 'babel',
36 | },
37 | ],
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/examples/css-modules-custom/app.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unresolved */
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 |
6 | import '../../lib/_typography.scss';
7 |
8 | import { Button } from '../../lib/button';
9 | import { ButtonGroup } from '../../lib/button-group';
10 | import { ButtonGroup as FlexButtonGroup } from '../../lib/button-group-flex';
11 |
12 | const App = () => (
13 |
14 |
Welcome to CSS Modules Custom example!
15 |
16 | Importing a component will import the components required CSS. This means that your final
17 | CSS stylesheet generated by bundler will always contain minimum required amount of class
18 | names.
19 |
20 |
21 | We've also imported react-foundation-components/lib/_typography.scss
for
22 | general look and feel.
23 |
24 |
25 | We are also using https://www.npmjs.com/package/jsontosass-loader to prepend theme.json
26 | to all CSS modules imported. Here theme.json is changing the secondary color to purple.
27 |
28 |
29 |
Button react-foundation-components/lib/button
30 | Click Me!
31 | Purple is Secondary Color
32 |
33 |
34 |
ButtonGroup react-foundation-components/lib/button-group
35 |
36 | A
37 | B
38 | C
39 |
40 |
41 |
42 |
43 | Flexbox ButtonGroup react-foundation-components/lib/button-group-flex
44 |
45 |
46 | Because CSS Modules creates class names that are scoped locally to the component,
47 | we can use same Flexbox based and Float based components at same time! CSS Modules FTW!
48 |
49 |
50 | A
51 | B
52 | C
53 |
54 |
55 |
56 | );
57 |
58 | ReactDOM.render( , document.getElementById('app'));
59 |
--------------------------------------------------------------------------------
/examples/css-modules-custom/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aruberto/react-foundation-components/3db1a7a9bae46734089635bffb53d107b43d973b/examples/css-modules-custom/favicon.ico
--------------------------------------------------------------------------------
/examples/css-modules-custom/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= htmlWebpackPlugin.options.title %>
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/css-modules-custom/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "foundation-palette": {
3 | "primary": "#2199e8",
4 | "secondary": "#800080",
5 | "success": "#3adb76",
6 | "warning": "#ffae00",
7 | "alert": "#ec5840"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/css-modules-custom/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const autoprefixer = require('autoprefixer');
6 |
7 | const themePath = path.join(__dirname, 'theme.json');
8 |
9 | module.exports = {
10 | entry: path.join(__dirname, 'app.js'),
11 |
12 | output: {
13 | path: path.join(__dirname, 'lib'),
14 | filename: '[name]-[chunkhash].js',
15 | publicPath: '/',
16 | },
17 |
18 | plugins: [
19 | new webpack.DefinePlugin({
20 | 'process.env.NODE_ENV': JSON.stringify('production'),
21 | }),
22 | new HtmlWebpackPlugin({
23 | title: 'React Foundation Components CSS Modules Example',
24 | template: path.join(__dirname, 'index.html'),
25 | favicon: path.join(__dirname, 'favicon.ico'),
26 | inject: 'body',
27 | }),
28 | new ExtractTextPlugin('main-[contenthash].css'),
29 | new webpack.optimize.OccurenceOrderPlugin(),
30 | new webpack.optimize.DedupePlugin(),
31 | new webpack.optimize.UglifyJsPlugin(),
32 | new webpack.optimize.AggressiveMergingPlugin(),
33 | ],
34 |
35 | sassLoader: {
36 | precision: 8,
37 | },
38 |
39 | postcss: [
40 | autoprefixer({
41 | browsers: [
42 | 'Android >= 4',
43 | 'Chrome >= 20',
44 | 'Firefox >= 24',
45 | 'Explorer >= 9',
46 | 'Edge >= 1',
47 | 'iOS >= 6',
48 | 'Opera >= 12',
49 | 'Safari >= 6',
50 | ],
51 | }),
52 | ],
53 |
54 | module: {
55 | loaders: [
56 | {
57 | test: /\.js$/,
58 | exclude: /node_modules/,
59 | loader: 'babel',
60 | },
61 | {
62 | test: /\.scss$/,
63 | loader: ExtractTextPlugin.extract(
64 | 'style',
65 | `css?modules!postcss!sass!jsontosass?path=${themePath}`
66 | ),
67 | },
68 | ],
69 | },
70 | };
71 |
--------------------------------------------------------------------------------
/examples/css-modules/app.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unresolved */
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 |
6 | import '../../lib/_typography.scss';
7 |
8 | import { Button } from '../../lib/button';
9 | import { ButtonGroup } from '../../lib/button-group';
10 | import { ButtonGroup as FlexButtonGroup } from '../../lib/button-group-flex';
11 |
12 | const App = () => (
13 |
14 |
Welcome to CSS Modules example!
15 |
16 | Importing a component will import the components required CSS. This means that your final
17 | CSS stylesheet generated by bundler will always contain minimum required amount of class
18 | names.
19 |
20 |
21 | We've also imported react-foundation-components/lib/_typography.scss
for
22 | general look and feel.
23 |
24 |
25 |
Button react-foundation-components/lib/button
26 | Click Me!
27 |
28 |
29 |
ButtonGroup react-foundation-components/lib/button-group
30 |
31 | A
32 | B
33 | C
34 |
35 |
36 |
37 |
38 | Flexbox ButtonGroup react-foundation-components/lib/button-group-flex
39 |
40 |
41 | Because CSS Modules creates class names that are scoped locally to the component,
42 | we can use same Flexbox based and Float based components at same time! CSS Modules FTW!
43 |
44 |
45 | A
46 | B
47 | C
48 |
49 |
50 |
51 | );
52 |
53 | ReactDOM.render( , document.getElementById('app'));
54 |
--------------------------------------------------------------------------------
/examples/css-modules/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aruberto/react-foundation-components/3db1a7a9bae46734089635bffb53d107b43d973b/examples/css-modules/favicon.ico
--------------------------------------------------------------------------------
/examples/css-modules/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= htmlWebpackPlugin.options.title %>
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/css-modules/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const autoprefixer = require('autoprefixer');
6 |
7 | module.exports = {
8 | entry: path.join(__dirname, 'app.js'),
9 |
10 | output: {
11 | path: path.join(__dirname, 'lib'),
12 | filename: '[name]-[chunkhash].js',
13 | publicPath: '/',
14 | },
15 |
16 | plugins: [
17 | new webpack.DefinePlugin({
18 | 'process.env.NODE_ENV': JSON.stringify('production'),
19 | }),
20 | new HtmlWebpackPlugin({
21 | title: 'React Foundation Components CSS Modules Example',
22 | template: path.join(__dirname, 'index.html'),
23 | favicon: path.join(__dirname, 'favicon.ico'),
24 | inject: 'body',
25 | }),
26 | new ExtractTextPlugin('main-[contenthash].css'),
27 | new webpack.optimize.OccurenceOrderPlugin(),
28 | new webpack.optimize.DedupePlugin(),
29 | new webpack.optimize.UglifyJsPlugin(),
30 | new webpack.optimize.AggressiveMergingPlugin(),
31 | ],
32 |
33 | sassLoader: {
34 | precision: 8,
35 | },
36 |
37 | postcss: [
38 | autoprefixer({
39 | browsers: [
40 | 'Android >= 4',
41 | 'Chrome >= 20',
42 | 'Firefox >= 24',
43 | 'Explorer >= 9',
44 | 'Edge >= 1',
45 | 'iOS >= 6',
46 | 'Opera >= 12',
47 | 'Safari >= 6',
48 | ],
49 | }),
50 | ],
51 |
52 | module: {
53 | loaders: [
54 | {
55 | test: /\.js$/,
56 | exclude: /node_modules/,
57 | loader: 'babel',
58 | },
59 | {
60 | test: /\.scss$/,
61 | loader: ExtractTextPlugin.extract('style', 'css?modules!postcss!sass'),
62 | },
63 | ],
64 | },
65 | };
66 |
--------------------------------------------------------------------------------
/examples/global-flex/app.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unresolved */
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 |
6 | import './app.scss';
7 |
8 | import { Button } from '../../lib/global/button';
9 | import { ButtonGroup } from '../../lib/global/button-group-flex';
10 |
11 | const App = () => (
12 |
13 |
Welcome to Global CSS with Flexbox example!
14 |
15 | We've imported react-foundation-components/lib/_foundation.scss
with
16 | global-flexbox set to true to import all Foundation class names into CSS name space.
17 |
18 |
19 | Importing _foundation.scss
also gives us the ability to
20 | customize Foundation. Here we have changed the secondary color to purple.
21 |
22 |
23 | Make sure to use the components under lib/global. These components use global scoped
24 | class names.
25 |
26 |
27 | Also make sure to use the Flexbox components. The non Flexbox components use class names
28 | that don't exist when global-flexbox is set to true.
29 |
30 |
31 |
Button react-foundation-components/lib/global/button
32 | Click Me!
33 | Purple is Secondary Color
34 |
35 |
36 |
ButtonGroup react-foundation-components/lib/global/button-group-flex
37 |
This ButtonGroup uses Flexbox layout
38 |
39 | A
40 | B
41 | C
42 |
43 |
44 |
45 | );
46 |
47 | ReactDOM.render( , document.getElementById('app'));
48 |
--------------------------------------------------------------------------------
/examples/global-flex/app.scss:
--------------------------------------------------------------------------------
1 | $global-flexbox: true;
2 | $foundation-palette: (
3 | primary: #2199e8,
4 | secondary: #800080,
5 | success: #3adb76,
6 | warning: #ffae00,
7 | alert: #ec5840,
8 | );
9 |
10 | @import '../../lib/foundation';
11 |
--------------------------------------------------------------------------------
/examples/global-flex/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aruberto/react-foundation-components/3db1a7a9bae46734089635bffb53d107b43d973b/examples/global-flex/favicon.ico
--------------------------------------------------------------------------------
/examples/global-flex/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= htmlWebpackPlugin.options.title %>
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/global-flex/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const autoprefixer = require('autoprefixer');
6 |
7 | module.exports = {
8 | entry: path.join(__dirname, 'app.js'),
9 |
10 | output: {
11 | path: path.join(__dirname, 'lib'),
12 | filename: '[name]-[chunkhash].js',
13 | publicPath: '/',
14 | },
15 |
16 | plugins: [
17 | new webpack.DefinePlugin({
18 | 'process.env.NODE_ENV': JSON.stringify('production'),
19 | }),
20 | new HtmlWebpackPlugin({
21 | title: 'React Foundation Components CSS Modules Example',
22 | template: path.join(__dirname, 'index.html'),
23 | favicon: path.join(__dirname, 'favicon.ico'),
24 | inject: 'body',
25 | }),
26 | new ExtractTextPlugin('main-[contenthash].css'),
27 | new webpack.optimize.OccurenceOrderPlugin(),
28 | new webpack.optimize.DedupePlugin(),
29 | new webpack.optimize.UglifyJsPlugin(),
30 | new webpack.optimize.AggressiveMergingPlugin(),
31 | ],
32 |
33 | sassLoader: {
34 | precision: 8,
35 | },
36 |
37 | postcss: [
38 | autoprefixer({
39 | browsers: [
40 | 'Android >= 4',
41 | 'Chrome >= 20',
42 | 'Firefox >= 24',
43 | 'Explorer >= 9',
44 | 'Edge >= 1',
45 | 'iOS >= 6',
46 | 'Opera >= 12',
47 | 'Safari >= 6',
48 | ],
49 | }),
50 | ],
51 |
52 | module: {
53 | loaders: [
54 | {
55 | test: /\.js$/,
56 | exclude: /node_modules/,
57 | loader: 'babel',
58 | },
59 | {
60 | test: /\.scss$/,
61 | loader: ExtractTextPlugin.extract('style', 'css!postcss!sass'),
62 | },
63 | ],
64 | },
65 | };
66 |
--------------------------------------------------------------------------------
/examples/global/app.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unresolved */
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 |
6 | import './app.scss';
7 |
8 | import { Button } from '../../lib/global/button';
9 | import { ButtonGroup } from '../../lib/global/button-group';
10 |
11 | const App = () => (
12 |
13 |
Welcome to Global CSS example!
14 |
15 | We've imported react-foundation-components/lib/_foundation.scss
with
16 | global-flexbox set to false to import all Foundation class names into CSS name space.
17 |
18 |
19 | Importing _foundation.scss
also gives us the ability to
20 | customize Foundation. Here we have changed the secondary color to purple.
21 |
22 |
23 | Make sure to use the components under lib/global. These components use global scoped
24 | class names.
25 |
26 |
27 | Also make sure to use the non Flexbox components. The Flexbox components use class names
28 | that don't exist when global-flexbox is set to false.
29 |
30 |
31 |
Button react-foundation-components/lib/global/button
32 | Click Me!
33 | Purple is Secondary Color
34 |
35 |
36 |
ButtonGroup react-foundation-components/lib/global/button-group
37 |
This ButtonGroup uses float layout
38 |
39 | A
40 | B
41 | C
42 |
43 |
44 |
45 | );
46 |
47 | ReactDOM.render( , document.getElementById('app'));
48 |
--------------------------------------------------------------------------------
/examples/global/app.scss:
--------------------------------------------------------------------------------
1 | $foundation-palette: (
2 | primary: #2199e8,
3 | secondary: #800080,
4 | success: #3adb76,
5 | warning: #ffae00,
6 | alert: #ec5840,
7 | );
8 |
9 | @import '../../lib/foundation';
10 |
--------------------------------------------------------------------------------
/examples/global/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aruberto/react-foundation-components/3db1a7a9bae46734089635bffb53d107b43d973b/examples/global/favicon.ico
--------------------------------------------------------------------------------
/examples/global/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= htmlWebpackPlugin.options.title %>
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/global/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const autoprefixer = require('autoprefixer');
6 |
7 | module.exports = {
8 | entry: path.join(__dirname, 'app.js'),
9 |
10 | output: {
11 | path: path.join(__dirname, 'lib'),
12 | filename: '[name]-[chunkhash].js',
13 | publicPath: '/',
14 | },
15 |
16 | plugins: [
17 | new webpack.DefinePlugin({
18 | 'process.env.NODE_ENV': JSON.stringify('production'),
19 | }),
20 | new HtmlWebpackPlugin({
21 | title: 'React Foundation Components CSS Modules Example',
22 | template: path.join(__dirname, 'index.html'),
23 | favicon: path.join(__dirname, 'favicon.ico'),
24 | inject: 'body',
25 | }),
26 | new ExtractTextPlugin('main-[contenthash].css'),
27 | new webpack.optimize.OccurenceOrderPlugin(),
28 | new webpack.optimize.DedupePlugin(),
29 | new webpack.optimize.UglifyJsPlugin(),
30 | new webpack.optimize.AggressiveMergingPlugin(),
31 | ],
32 |
33 | sassLoader: {
34 | precision: 8,
35 | },
36 |
37 | postcss: [
38 | autoprefixer({
39 | browsers: [
40 | 'Android >= 4',
41 | 'Chrome >= 20',
42 | 'Firefox >= 24',
43 | 'Explorer >= 9',
44 | 'Edge >= 1',
45 | 'iOS >= 6',
46 | 'Opera >= 12',
47 | 'Safari >= 6',
48 | ],
49 | }),
50 | ],
51 |
52 | module: {
53 | loaders: [
54 | {
55 | test: /\.js$/,
56 | exclude: /node_modules/,
57 | loader: 'babel',
58 | },
59 | {
60 | test: /\.scss$/,
61 | loader: ExtractTextPlugin.extract('style', 'css!postcss!sass'),
62 | },
63 | ],
64 | },
65 | };
66 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-foundation-components",
3 | "version": "0.14.0",
4 | "description": "Foundation Sites components built with the power of React and CSS Modules",
5 | "main": "lib/index.js",
6 | "files": [
7 | "lib"
8 | ],
9 | "scripts": {
10 | "clean": "rimraf lib docs/lib",
11 | "lint": "eslint src && eslint docs && eslint examples && sass-lint --verbose",
12 | "build": "npm run clean && babel --quiet src --out-dir lib && cpy \"**/*.scss\" ../lib --parents --cwd=src && node create-flex-components.js && node create-global-scoped-components.js",
13 | "build-docs": "npm run clean && npm run build && webpack --config docs/webpack.config.js",
14 | "start-docs": "npm run build && node server.js docs/webpack.config.js --dev",
15 | "start-examples-css-modules": "npm run build && node server.js examples/css-modules/webpack.config.js",
16 | "start-examples-css-modules-custom": "npm run build && node server.js examples/css-modules-custom/webpack.config.js",
17 | "start-examples-global": "npm run build && node server.js examples/global/webpack.config.js",
18 | "start-examples-global-flex": "npm run build && node server.js examples/global-flex/webpack.config.js",
19 | "start-examples-cdn": "npm run build && node server.js examples/cdn/webpack.config.js",
20 | "start-examples-cdn-flex": "npm run build && node server.js examples/cdn-flex/webpack.config.js",
21 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
22 | "release-patch": "npm run build && release-it",
23 | "release-minor": "npm run build && release-it minor"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/aruberto/react-foundation-components.git"
28 | },
29 | "keywords": [
30 | "react",
31 | "foundation",
32 | "foundation-sites",
33 | "css-modules",
34 | "react-component"
35 | ],
36 | "author": "aruberto",
37 | "license": "MIT",
38 | "bugs": {
39 | "url": "https://github.com/aruberto/react-foundation-components/issues"
40 | },
41 | "homepage": "https://github.com/aruberto/react-foundation-components#readme",
42 | "peerDependencies": {
43 | "react": ">=16",
44 | "react-dom": ">=16"
45 | },
46 | "dependencies": {
47 | "babel-runtime": "^6.25.0",
48 | "classnames": "^2.2.5",
49 | "dom-helpers": "^2.4.0",
50 | "foundation-sites": "~6.2.4",
51 | "lodash": "^4.17.4",
52 | "prop-types": "^15.6.0",
53 | "react-overlays": "^0.7.0",
54 | "prop-types-extra": "1.0.1",
55 | "uncontrollable": "^5.1.0",
56 | "underscore.string": "^3.3.4"
57 | },
58 | "devDependencies": {
59 | "autoprefixer": "^6.7.7",
60 | "babel-cli": "^6.24.1",
61 | "babel-core": "^6.25.0",
62 | "babel-eslint": "^6.1.2",
63 | "babel-loader": "^6.4.1",
64 | "babel-plugin-react-transform": "^2.0.2",
65 | "babel-plugin-transform-runtime": "^6.23.0",
66 | "babel-preset-es2015": "^6.24.1",
67 | "babel-preset-react": "^6.24.1",
68 | "babel-preset-stage-0": "^6.24.1",
69 | "conventional-changelog-cli": "^1.3.2",
70 | "cpy-cli": "^1.0.1",
71 | "css-loader": "^0.25.0",
72 | "eslint": "^3.19.0",
73 | "eslint-config-airbnb": "^11.2.0",
74 | "eslint-plugin-import": "^1.16.0",
75 | "eslint-plugin-jsx-a11y": "^2.2.3",
76 | "eslint-plugin-react": "^6.10.3",
77 | "express": "^4.15.3",
78 | "extract-text-webpack-plugin": "^1.0.1",
79 | "file-loader": "^0.9.0",
80 | "fs-extra": "^0.30.0",
81 | "html-webpack-plugin": "^2.29.0",
82 | "img-loader": "^1.3.1",
83 | "jsontosass-loader": "^0.1.9",
84 | "minimist": "^1.2.0",
85 | "node-sass": "^4.5.3",
86 | "postcss-loader": "^0.13.0",
87 | "react": "^16.3.0",
88 | "react-dom": "^16.3.0",
89 | "react-router": "3.2.1",
90 | "react-transform-catch-errors": "^1.0.2",
91 | "react-transform-hmr": "^1.0.4",
92 | "redbox-react": "^1.4.3",
93 | "release-it": "^2.8.2",
94 | "rimraf": "^2.6.1",
95 | "sass-lint": "^1.10.2",
96 | "sass-loader": "^4.1.1",
97 | "style-loader": "^0.13.2",
98 | "through2": "^2.0.3",
99 | "url-loader": "^0.5.9",
100 | "webpack": "^1.15.0",
101 | "webpack-dev-middleware": "^1.11.0",
102 | "webpack-hot-middleware": "^2.18.2"
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const path = require('path');
4 | const webpack = require('webpack');
5 | const express = require('express');
6 | const webpackDevMiddleware = require('webpack-dev-middleware');
7 | const webpackHotMiddleware = require('webpack-hot-middleware');
8 |
9 | const config = require(path.join(__dirname, process.argv[2]));
10 |
11 | const app = express();
12 | const compiler = webpack(config);
13 |
14 | app.use(webpackDevMiddleware(compiler, {
15 | noInfo: true,
16 | stats: { colors: true },
17 | publicPath: config.output.publicPath,
18 | }));
19 |
20 | app.use(webpackHotMiddleware(compiler));
21 |
22 | app.listen(3000, '0.0.0.0', (err) => {
23 | if (err) {
24 | console.error(err);
25 | return;
26 | }
27 |
28 | console.info('Listening at http://0.0.0.0:3000');
29 | });
30 |
--------------------------------------------------------------------------------
/src/_common.scss:
--------------------------------------------------------------------------------
1 | $breakpoint-classes: (small medium large xlarge xxlarge);
2 |
3 | @import '~foundation-sites/scss/util/util';
4 | @import '~foundation-sites/scss/global';
5 | @import '~foundation-sites/scss/typography/base';
6 |
--------------------------------------------------------------------------------
/src/_foundation.scss:
--------------------------------------------------------------------------------
1 | @import './common';
2 |
3 | @import './typography';
4 | @import './typography-helpers/styles';
5 | @import './text-alignment/styles';
6 |
7 | @if $global-flexbox {
8 | @import './flex/styles';
9 | @import './grid-flex/styles';
10 | } @else {
11 | @import './grid/styles';
12 | }
13 |
14 | @import './forms/styles';
15 | @import './float/styles';
16 | @import './visibility/styles';
17 |
18 | @import './button/styles';
19 | @import './button-group/styles';
20 | @import './close-button/styles';
21 | @import './switch/styles';
22 |
23 | @import './menu/styles';
24 | @import './top-bar/styles';
25 |
26 | @import './accordion/styles';
27 | @import './callout/styles';
28 | @import './dropdown/styles';
29 | @import './media-object/styles';
30 | @import './menu-icon/styles';
31 | @import './off-canvas/styles';
32 | @import './reveal/styles';
33 | @import './table/styles';
34 | @import './tabs/styles';
35 | @import './title-bar/styles';
36 |
37 | @import './badge/styles';
38 | @import './flex-video/styles';
39 | @import './label/styles';
40 | @import './progress-bar/styles';
41 | @import './thumbnail/styles';
42 | @import './tooltip/styles';
43 |
44 | @import './toggle-switch/styles';
45 |
46 | @import './collapse/styles';
47 | @import './fade/styles';
48 |
--------------------------------------------------------------------------------
/src/_typography.scss:
--------------------------------------------------------------------------------
1 | @import './common';
2 | @import '~foundation-sites/scss/typography/print';
3 |
4 | @include foundation-global-styles
5 | @include foundation-typography-base
6 | @include foundation-print-styles;
7 |
--------------------------------------------------------------------------------
/src/accordion/_custom.scss:
--------------------------------------------------------------------------------
1 | .accordion-item {
2 | border-bottom: $accordion-content-border;
3 | }
4 |
5 | .accordion-title {
6 | border-bottom: 0;
7 | }
8 |
9 | .accordion-content {
10 | border-top: $accordion-content-border;
11 | border-bottom: 0;
12 | }
13 |
--------------------------------------------------------------------------------
/src/accordion/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/accordion';
3 |
4 | @include foundation-accordion;
5 |
6 | @import './custom';
7 |
--------------------------------------------------------------------------------
/src/accordion/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Children, cloneElement, isValidElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import uncontrollable from 'uncontrollable';
6 | import includes from 'lodash/includes';
7 | import noop from 'lodash/noop';
8 | import isBlank from 'underscore.string/isBlank';
9 |
10 | import { Collapse } from '../collapse';
11 | import styles from './_styles.scss';
12 |
13 | const cxStyles = cxBinder.bind(styles);
14 |
15 | const AccordionItemControlled = ({
16 | active,
17 | children,
18 | className,
19 | contentClassName,
20 | contentStyle,
21 | eventKey,
22 | id,
23 | onClick,
24 | onSelect,
25 | onToggle,
26 | title,
27 | titleClassName,
28 | titleStyle,
29 | ...restProps,
30 | }) => {
31 | const classNames = cx(className, cxStyles('accordion-item', { 'is-active': active }));
32 | const titleClassNames = cx(titleClassName, cxStyles('accordion-title'));
33 | const contentClassNames = cx(contentClassName, cxStyles('accordion-content'));
34 | let titleId = null;
35 | let contentId = null;
36 |
37 | if (!isBlank(id)) {
38 | titleId = `${id}Title`;
39 | contentId = `${id}Content`;
40 | }
41 |
42 | const onTitleClick = (...args) => {
43 | const [event] = args;
44 |
45 | event.preventDefault();
46 |
47 | if (onClick) {
48 | onClick(...args);
49 | }
50 |
51 | if (onToggle) {
52 | onToggle(!active, ...args);
53 | }
54 |
55 | if (onSelect) {
56 | onSelect(eventKey, ...args);
57 | }
58 | };
59 |
60 | return (
61 |
62 |
73 | {title}
74 |
75 |
76 |
77 |
85 | {children}
86 |
87 |
88 |
89 |
90 | );
91 | };
92 |
93 | AccordionItemControlled.propTypes = {
94 | active: PropTypes.bool,
95 | children: PropTypes.node,
96 | className: PropTypes.string,
97 | contentClassName: PropTypes.string,
98 | contentStyle: PropTypes.object,
99 | eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
100 | id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
101 | onClick: PropTypes.func,
102 | onSelect: PropTypes.func,
103 | onToggle: PropTypes.func,
104 | title: PropTypes.node,
105 | titleClassName: PropTypes.string,
106 | titleStyle: PropTypes.object,
107 | };
108 |
109 | export const AccordionItem = uncontrollable(AccordionItemControlled, { active: 'onToggle' });
110 | AccordionItem.displayName = 'AccordionItem';
111 |
112 | const AccordionControlled = ({
113 | allowAllClosed,
114 | children,
115 | className,
116 | multiExpand,
117 | activeKey: maybeActiveKey = multiExpand ? [] : null,
118 | onSelect,
119 | ...restProps,
120 | }) => {
121 | let activeKey = maybeActiveKey;
122 |
123 | if (!allowAllClosed && (multiExpand && activeKey.length === 0 || isBlank(activeKey))) {
124 | const childArray =
125 | Children.toArray(children)
126 | .filter((child) => isValidElement(child) && !isBlank(child.props.eventKey));
127 |
128 | if (childArray.length >= 1) {
129 | const firstKey = childArray[0].props.eventKey;
130 | activeKey = multiExpand ? [firstKey] : firstKey;
131 | }
132 | }
133 |
134 | const onChildSelect = (eventKey, ...args) => {
135 | if (multiExpand) {
136 | if (includes(activeKey, eventKey)) {
137 | const filtered = activeKey.filter((item) => item !== eventKey);
138 |
139 | if (!allowAllClosed && filtered.length === 0) {
140 | onSelect([eventKey], ...args);
141 | } else {
142 | onSelect(filtered, ...args);
143 | }
144 | } else {
145 | onSelect([...activeKey, eventKey], ...args);
146 | }
147 | } else {
148 | if (allowAllClosed && activeKey === eventKey) {
149 | onSelect(null, ...args);
150 | } else {
151 | onSelect(eventKey, ...args);
152 | }
153 | }
154 | };
155 |
156 | const classNames = cx(className, cxStyles('accordion'));
157 | const clonedChildren = Children.map(children, (child) => {
158 | if (isValidElement(child)) {
159 | return cloneElement(child, {
160 | active:
161 | multiExpand
162 | ? includes(activeKey, child.props.eventKey)
163 | : activeKey === child.props.eventKey,
164 | onSelect: onChildSelect,
165 | onToggle: noop,
166 | });
167 | }
168 |
169 | return child;
170 | });
171 |
172 | return ;
173 | };
174 |
175 | AccordionControlled.propTypes = {
176 | activeKey: PropTypes.oneOfType([
177 | PropTypes.string,
178 | PropTypes.number,
179 | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
180 | ]),
181 | allowAllClosed: PropTypes.bool,
182 | children: PropTypes.node,
183 | className: PropTypes.string,
184 | defaultActiveKey: PropTypes.oneOfType([
185 | PropTypes.string,
186 | PropTypes.number,
187 | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
188 | ]),
189 | multiExpand: PropTypes.bool,
190 | onSelect: PropTypes.func,
191 | };
192 |
193 | export const Accordion = uncontrollable(AccordionControlled, { activeKey: 'onSelect' });
194 | Accordion.displayName = 'Accordion';
195 |
196 | Accordion.Item = AccordionItem;
197 |
198 | export default Accordion;
199 |
--------------------------------------------------------------------------------
/src/badge/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/badge';
3 |
4 | @include foundation-badge;
5 |
--------------------------------------------------------------------------------
/src/badge/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import includes from 'lodash/includes';
3 |
4 | import { COMPONENT_COLORS, COMPONENT_ALTERNATIVE_COLORS } from '../util/constants';
5 | import createWrapperComponent from '../util/create-wrapper-component';
6 | import styles from './_styles.scss';
7 |
8 | export const Badge = createWrapperComponent({
9 | displayName: 'Badge',
10 | styles,
11 | propTypes: {
12 | color: PropTypes.oneOf(COMPONENT_COLORS),
13 | },
14 | mapProps: ({ color, ...props }) => ({
15 | props,
16 | classNames: ['badge', { [color]: includes(COMPONENT_ALTERNATIVE_COLORS, color) }],
17 | }),
18 | });
19 |
20 | export default Badge;
21 |
--------------------------------------------------------------------------------
/src/breadcrumb/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/breadcrumbs';
3 |
4 | @include foundation-breadcrumbs;
5 |
--------------------------------------------------------------------------------
/src/breadcrumb/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 |
6 | import styles from './_styles.scss';
7 |
8 | const cxStyles = cxBinder.bind(styles);
9 |
10 | export const Breadcrumb = ({
11 | className,
12 | ...restProps,
13 | }) => {
14 | const classNames = cx(className, cxStyles('breadcrumbs'));
15 |
16 | return ;
17 | };
18 |
19 | Breadcrumb.propTypes = {
20 | className: PropTypes.string,
21 | };
22 |
23 | export const BreadcrumbItem = ({
24 | className,
25 | disabled,
26 | ...restProps,
27 | }) => {
28 | const classNames = cx(className, cxStyles({ disabled }));
29 |
30 | return ;
31 | };
32 |
33 | BreadcrumbItem.propTypes = {
34 | className: PropTypes.string,
35 | disabled: PropTypes.bool,
36 | };
37 |
38 | Breadcrumb.Item = BreadcrumbItem;
39 |
40 | export default Breadcrumb;
41 |
--------------------------------------------------------------------------------
/src/button-group/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/button';
3 | @import '~foundation-sites/scss/components/button-group';
4 |
5 | @include foundation-button-group;
6 |
--------------------------------------------------------------------------------
/src/button-group/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Children, cloneElement, isValidElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import {
8 | COMPONENT_SIZES,
9 | COMPONENT_COLORS,
10 | BUTTON_GROUP_STACK_SCREEN_SIZES,
11 | } from '../util/constants';
12 | import styles from './_styles.scss';
13 |
14 | const cxStyles = cxBinder.bind(styles);
15 |
16 | export const ButtonGroup = ({
17 | children,
18 | className,
19 | color,
20 | expanded,
21 | size,
22 | stack,
23 | ...restProps,
24 | }) => {
25 | const classNames =
26 | cx(
27 | className,
28 | cxStyles(
29 | 'button-group',
30 | {
31 | [color]: includes(COMPONENT_COLORS, color),
32 | expanded,
33 | [size]: includes(COMPONENT_SIZES, size),
34 | stacked: stack && !includes(BUTTON_GROUP_STACK_SCREEN_SIZES, stack),
35 | [`stacked-for-${stack}`]: includes(BUTTON_GROUP_STACK_SCREEN_SIZES, stack),
36 | }
37 | )
38 | );
39 | const clonedChildren =
40 | Children.map(children, (child) => {
41 | if (isValidElement(child)) {
42 | return cloneElement(child, { className: cx(child.props.className, cxStyles('button')) });
43 | }
44 |
45 | return child;
46 | });
47 |
48 | return {clonedChildren}
;
49 | };
50 |
51 | ButtonGroup.propTypes = {
52 | children: PropTypes.node,
53 | className: PropTypes.string,
54 | color: PropTypes.oneOf(COMPONENT_COLORS),
55 | expanded: PropTypes.bool,
56 | size: PropTypes.oneOf(COMPONENT_SIZES),
57 | stack: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(BUTTON_GROUP_STACK_SCREEN_SIZES)]),
58 | };
59 |
60 | export default ButtonGroup;
61 |
--------------------------------------------------------------------------------
/src/button/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/button';
3 |
4 | @include foundation-button;
5 |
--------------------------------------------------------------------------------
/src/button/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import { COMPONENT_SIZES, COMPONENT_COLORS } from '../util/constants';
8 | import styles from './_styles.scss';
9 |
10 | const cxStyles = cxBinder.bind(styles);
11 |
12 | export const Button = ({
13 | className,
14 | color,
15 | disabled,
16 | dropdown,
17 | dropdownArrowOnly,
18 | expanded,
19 | hollow,
20 | href,
21 | size,
22 | target,
23 | ...restProps,
24 | }) => {
25 | const classNames =
26 | cx(
27 | className,
28 | cxStyles(
29 | 'button',
30 | {
31 | [color]: includes(COMPONENT_COLORS, color),
32 | disabled,
33 | dropdown,
34 | 'arrow-only': dropdown && dropdownArrowOnly,
35 | expanded,
36 | hollow,
37 | [size]: includes(COMPONENT_SIZES, size),
38 | }
39 | )
40 | );
41 |
42 | if (href || target) {
43 | return (
44 |
52 | );
53 | }
54 |
55 | return ;
56 | };
57 |
58 | Button.propTypes = {
59 | className: PropTypes.string,
60 | color: PropTypes.oneOf(COMPONENT_COLORS),
61 | disabled: PropTypes.bool,
62 | dropdown: PropTypes.bool,
63 | dropdownArrowOnly: PropTypes.bool,
64 | expanded: PropTypes.bool,
65 | hollow: PropTypes.bool,
66 | href: PropTypes.string,
67 | size: PropTypes.oneOf(COMPONENT_SIZES),
68 | target: PropTypes.string,
69 | };
70 | Button.defaultProps = {
71 | type: 'button',
72 | };
73 |
74 | export default Button;
75 |
--------------------------------------------------------------------------------
/src/callout/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/callout';
3 |
4 | @include foundation-callout;
5 |
--------------------------------------------------------------------------------
/src/callout/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import { CALLOUT_SIZES, COMPONENT_COLORS } from '../util/constants';
8 | import styles from './_styles.scss';
9 |
10 | const cxStyles = cxBinder.bind(styles);
11 |
12 | export const Callout = ({
13 | className,
14 | color,
15 | size,
16 | ...restProps,
17 | }) => {
18 | const classNames =
19 | cx(
20 | className,
21 | cxStyles(
22 | 'callout',
23 | {
24 | [color]: includes(COMPONENT_COLORS, color),
25 | [size]: includes(CALLOUT_SIZES, size),
26 | }
27 | )
28 | );
29 |
30 | return
;
31 | };
32 |
33 | Callout.propTypes = {
34 | className: PropTypes.string,
35 | color: PropTypes.oneOf(COMPONENT_COLORS),
36 | size: PropTypes.oneOf(CALLOUT_SIZES),
37 | };
38 |
39 | export default Callout;
40 |
--------------------------------------------------------------------------------
/src/close-button/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/close-button';
3 |
4 | @include foundation-close-button;
5 |
--------------------------------------------------------------------------------
/src/close-button/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 |
6 | import { HideForScreenReader } from '../visibility';
7 | import styles from './_styles.scss';
8 |
9 | const cxStyles = cxBinder.bind(styles);
10 |
11 | export const CloseButton = ({
12 | className,
13 | ...restProps,
14 | }) => {
15 | const classNames = cx(className, cxStyles('close-button'));
16 |
17 | return (
18 |
19 | ×
20 |
21 | );
22 | };
23 |
24 | CloseButton.propTypes = {
25 | className: PropTypes.string,
26 | };
27 |
28 | export default CloseButton;
29 |
--------------------------------------------------------------------------------
/src/collapse/_custom.scss:
--------------------------------------------------------------------------------
1 | .collapsible {
2 | display: none;
3 |
4 | &.in {
5 | display: block;
6 |
7 | tr {
8 | display: table-row;
9 | }
10 |
11 | tbody {
12 | display: table-row-group;
13 | }
14 | }
15 | }
16 |
17 | .collapsing {
18 | position: relative;
19 | overflow: hidden;
20 | transition-duration: .35s;
21 | transition-timing-function: ease;
22 |
23 | &.height {
24 | height: 0;
25 | transition-property: height, visibility;
26 | }
27 |
28 | &.width {
29 | width: 0;
30 | transition-property: width, visibility;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/collapse/_styles.scss:
--------------------------------------------------------------------------------
1 | @import './custom';
2 |
--------------------------------------------------------------------------------
/src/collapse/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import Transition from 'react-overlays/lib/Transition';
6 | import css from 'dom-helpers/style';
7 | import capitalize from 'underscore.string/capitalize';
8 |
9 | import styles from './_styles.scss';
10 |
11 | const cxStyles = cxBinder.bind(styles);
12 | const MARGINS = {
13 | height: ['marginTop', 'marginBottom'],
14 | width: ['marginLeft', 'marginRight'],
15 | };
16 |
17 | export const Collapse = ({
18 | className,
19 | dimension,
20 | onEnter,
21 | onEntered,
22 | onEntering,
23 | onExit,
24 | onExiting,
25 | ...restProps,
26 | }) => {
27 | const classNames = cx(className, cxStyles(dimension));
28 | const onTransitionEnter = (...args) => {
29 | const [elem] = args;
30 |
31 | elem.style[dimension] = '0';
32 |
33 | if (onEnter) {
34 | onEnter(...args);
35 | }
36 | };
37 | const onTransitionEntered = (...args) => {
38 | const [elem] = args;
39 |
40 | elem.style[dimension] = null;
41 |
42 | if (onEntered) {
43 | onEntered(...args);
44 | }
45 | };
46 | const onTransitionEntering = (...args) => {
47 | const [elem] = args;
48 | const size = elem[`scroll${capitalize(dimension)}`];
49 |
50 | elem.style[dimension] = `${size}px`;
51 |
52 | if (onEntering) {
53 | onEntering(...args);
54 | }
55 | };
56 | const onTransitionExit = (...args) => {
57 | const [elem] = args;
58 | const baseValue = elem[`offset${capitalize(dimension)}`];
59 | const margins = MARGINS[dimension];
60 | const value =
61 | baseValue
62 | + Number.parseInt(css(elem, margins[0]), 10)
63 | + Number.parseInt(css(elem, margins[1]), 10);
64 |
65 | elem.style[dimension] = `${value}px`;
66 |
67 | if (onExit) {
68 | onExit(...args);
69 | }
70 | };
71 | const onTransitionExiting = (...args) => {
72 | function triggerBrowserReflow(node) {
73 | return node.offsetHeight;
74 | }
75 |
76 | const [elem] = args;
77 |
78 | triggerBrowserReflow(elem);
79 | elem.style[dimension] = '0';
80 |
81 | if (onExiting) {
82 | onExiting(...args);
83 | }
84 | };
85 |
86 | return (
87 |
100 | );
101 | };
102 |
103 | Collapse.propTypes = {
104 | className: PropTypes.string,
105 | dimension: PropTypes.oneOf(['height', 'width']),
106 | onEnter: PropTypes.func,
107 | onEntered: PropTypes.func,
108 | onEntering: PropTypes.func,
109 | onExit: PropTypes.func,
110 | onExiting: PropTypes.func,
111 | };
112 | Collapse.defaultProps = {
113 | dimension: 'height',
114 | timeout: 350,
115 | };
116 |
117 | export default Collapse;
118 |
--------------------------------------------------------------------------------
/src/dropdown/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/dropdown';
3 |
4 | @include foundation-dropdown;
5 |
--------------------------------------------------------------------------------
/src/dropdown/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { isValidElement, cloneElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import {
8 | COMPONENT_SIZES,
9 | OVERLAY_POSITIONS,
10 | OVERLAY_POSITIONS_INTERNAL,
11 | OVERLAY_ALIGNMENTS,
12 | } from '../util/constants';
13 | import OverlayTrigger from '../util/overlay-trigger';
14 | import styles from './_styles.scss';
15 |
16 | const cxStyles = cxBinder.bind(styles);
17 | const DROPDOWN_ALIGNMENTS_FROM_POSITION = {
18 | top: 'left',
19 | bottom: 'left',
20 | left: 'top',
21 | right: 'top',
22 | };
23 |
24 | export const Dropdown = ({
25 | className,
26 | position,
27 | size,
28 | ...restProps,
29 | }) => {
30 | const classNames =
31 | cx(
32 | className,
33 | cxStyles(
34 | 'dropdown-pane',
35 | 'is-open',
36 | {
37 | [position]: includes(OVERLAY_POSITIONS_INTERNAL, position),
38 | [size]: includes(COMPONENT_SIZES, size),
39 | }
40 | )
41 | );
42 |
43 | return
;
44 | };
45 |
46 | Dropdown.propTypes = {
47 | className: PropTypes.string,
48 | position: PropTypes.oneOf(OVERLAY_POSITIONS),
49 | size: PropTypes.oneOf(COMPONENT_SIZES),
50 | };
51 | Dropdown.defaultProps = {
52 | position: 'bottom',
53 | };
54 |
55 | const DropdownOverlay = ({
56 | placement, // eslint-disable-line no-unused-vars, react/prop-types
57 | arrowOffsetLeft, // eslint-disable-line no-unused-vars, react/prop-types
58 | arrowOffsetTop, // eslint-disable-line no-unused-vars, react/prop-types
59 | positionLeft, // eslint-disable-line no-unused-vars, react/prop-types
60 | positionTop, // eslint-disable-line no-unused-vars, react/prop-types
61 | ...restProps,
62 | }) => ;
63 |
64 | export const LinkWithDropdown = ({
65 | children,
66 | dropdownClassName,
67 | dropdownContent,
68 | dropdownId,
69 | dropdownPosition,
70 | dropdownAlignment = DROPDOWN_ALIGNMENTS_FROM_POSITION[dropdownPosition],
71 | dropdownSize,
72 | dropdownStyle,
73 | ...restProps,
74 | }) => {
75 | const childProps = {
76 | 'aria-haspopup': true,
77 | 'aria-controls': dropdownId,
78 | };
79 | let labelledBy = null;
80 | let clonedChild = null;
81 |
82 | if (isValidElement(children)) {
83 | labelledBy = children.props.id;
84 | clonedChild = cloneElement(children, childProps);
85 | } else {
86 | clonedChild = {children} ;
87 | }
88 |
89 | const dropdown = (
90 |
98 | {dropdownContent}
99 |
100 | );
101 |
102 | return (
103 |
109 | {clonedChild}
110 |
111 | );
112 | };
113 |
114 | LinkWithDropdown.propTypes = {
115 | children: PropTypes.node,
116 | dropdownAlignment: PropTypes.oneOf(OVERLAY_ALIGNMENTS),
117 | dropdownClassName: PropTypes.string,
118 | dropdownContent: PropTypes.node,
119 | dropdownId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
120 | dropdownPosition: PropTypes.oneOf(OVERLAY_POSITIONS),
121 | dropdownSize: PropTypes.oneOf(COMPONENT_SIZES),
122 | dropdownStyle: PropTypes.shape({}),
123 | };
124 | LinkWithDropdown.defaultProps = {
125 | dropdownPosition: 'bottom',
126 | triggerClick: true,
127 | triggerOverlayHover: true,
128 | };
129 |
130 | Dropdown.LinkWith = LinkWithDropdown;
131 |
132 | export default Dropdown;
133 |
--------------------------------------------------------------------------------
/src/fade/_custom.scss:
--------------------------------------------------------------------------------
1 | .fade {
2 | transition: opacity .15s linear;
3 | opacity: 0;
4 |
5 | &.in {
6 | opacity: 1;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/fade/_styles.scss:
--------------------------------------------------------------------------------
1 | @import './custom';
2 |
--------------------------------------------------------------------------------
/src/fade/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import Transition from 'react-overlays/lib/Transition';
6 |
7 | import styles from './_styles.scss';
8 |
9 | const cxStyles = cxBinder.bind(styles);
10 |
11 | export const Fade = ({
12 | className,
13 | ...restProps,
14 | }) => {
15 | const classNames = cx(className, cxStyles('fade'));
16 |
17 | return (
18 |
24 | );
25 | };
26 | Fade.propTypes = {
27 | className: PropTypes.string,
28 | };
29 | Fade.defaultProps = {
30 | timeout: 150,
31 | };
32 |
33 | export default Fade;
34 |
--------------------------------------------------------------------------------
/src/flex-mock/_styles.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aruberto/react-foundation-components/3db1a7a9bae46734089635bffb53d107b43d973b/src/flex-mock/_styles.scss
--------------------------------------------------------------------------------
/src/flex-mock/index.js:
--------------------------------------------------------------------------------
1 | import DefaultComponent from '../util/default-component';
2 |
3 | export const FlexParent = DefaultComponent;
4 |
5 | export const FlexChild = DefaultComponent;
6 |
7 | export const Flex = { Parent: FlexParent, Child: FlexChild };
8 |
9 | export default Flex;
10 |
--------------------------------------------------------------------------------
/src/flex-video/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/flex-video';
3 |
4 | @include foundation-flex-video;
5 |
--------------------------------------------------------------------------------
/src/flex-video/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 |
6 | import styles from './_styles.scss';
7 |
8 | const cxStyles = cxBinder.bind(styles);
9 |
10 | export const FlexVideo = ({
11 | containerClassName,
12 | containerStyle,
13 | vimeo,
14 | widescreen,
15 | ...restProps,
16 | }) => {
17 | const classNames = cx(containerClassName, cxStyles('flex-video', { vimeo, widescreen }));
18 |
19 | return (
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | FlexVideo.propTypes = {
27 | containerClassName: PropTypes.string,
28 | containerStyle: PropTypes.shape({}),
29 | vimeo: PropTypes.bool,
30 | widescreen: PropTypes.bool,
31 | };
32 |
33 | export default FlexVideo;
34 |
--------------------------------------------------------------------------------
/src/flex/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/flex';
3 |
4 | @include foundation-flex-classes;
5 |
--------------------------------------------------------------------------------
/src/flex/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import includes from 'lodash/includes';
3 |
4 | import {
5 | FLEX_HORIZONTAL_ALIGNMENTS,
6 | FLEX_VERTICAL_ALIGNMENTS,
7 | FLEX_PARENT_CLASS_NAMES,
8 | FLEX_CHILD_CLASS_NAMES,
9 | } from '../util/constants';
10 | import createWrapperComponent from '../util/create-wrapper-component';
11 | import {
12 | createScreenSizeProps,
13 | createScreenSizePropTypes,
14 | createScreenSizeClassNames,
15 | } from '../util/screen-size';
16 | import styles from './_styles.scss';
17 |
18 | const flexParentProps = createScreenSizeProps(FLEX_PARENT_CLASS_NAMES);
19 | const flexChildProps = createScreenSizeProps(FLEX_CHILD_CLASS_NAMES);
20 |
21 | export const FlexParent = createWrapperComponent({
22 | displayName: 'FlexParent',
23 | styles,
24 | propTypes: ({
25 | ...createScreenSizePropTypes(flexParentProps),
26 | horizontalAlignment: PropTypes.oneOf(FLEX_HORIZONTAL_ALIGNMENTS),
27 | verticalAlignment: PropTypes.oneOf(FLEX_VERTICAL_ALIGNMENTS),
28 | }),
29 | mapProps: ({ horizontalAlignment, verticalAlignment, ...restProps }) => {
30 | const { classNames, props } = createScreenSizeClassNames(flexParentProps, restProps);
31 |
32 | return {
33 | props,
34 | classNames: {
35 | ...classNames,
36 | [`align-${horizontalAlignment}`]: includes(FLEX_HORIZONTAL_ALIGNMENTS, horizontalAlignment),
37 | [`align-${verticalAlignment}`]: includes(FLEX_VERTICAL_ALIGNMENTS, verticalAlignment),
38 | },
39 | style: { display: 'flex' },
40 | };
41 | },
42 | defaultComponentClass: 'div',
43 | });
44 |
45 | export const FlexChild = createWrapperComponent({
46 | displayName: 'FlexChild',
47 | styles,
48 | propTypes: ({
49 | ...createScreenSizePropTypes(flexChildProps),
50 | verticalAlignment: PropTypes.oneOf(FLEX_VERTICAL_ALIGNMENTS),
51 | }),
52 | mapProps: ({ verticalAlignment, ...restProps }) => {
53 | const { classNames, props } = createScreenSizeClassNames(flexChildProps, restProps);
54 |
55 | return {
56 | props,
57 | classNames: {
58 | ...classNames,
59 | [`align-self-${verticalAlignment}`]: includes(FLEX_VERTICAL_ALIGNMENTS, verticalAlignment),
60 | },
61 | };
62 | },
63 | defaultComponentClass: 'div',
64 | });
65 |
66 | export const Flex = { Parent: FlexParent, Child: FlexChild };
67 |
68 | export default Flex;
69 |
--------------------------------------------------------------------------------
/src/flexbox.js:
--------------------------------------------------------------------------------
1 | export { Grid, Row, Column } from './grid-flex';
2 | export {
3 | FormField,
4 | FormFieldInput,
5 | FormFieldLabel,
6 | FormFieldError,
7 | FormFieldHelp,
8 | FormFieldInline,
9 | FormFieldButton,
10 | } from './forms-flex'; // eslint-disable-line import/no-unresolved
11 | export {
12 | Visibility,
13 | ShowForScreenSize,
14 | ShowOnlyForScreenSize,
15 | HideForScreenSize,
16 | HideOnlyForScreenSize,
17 | Hide,
18 | Invisible,
19 | ShowForScreenOrientation,
20 | HideForScreenOrientation,
21 | ShowForScreenReader,
22 | HideForScreenReader,
23 | ShowOnFocus,
24 | } from './visibility';
25 | export { Float, ClearFix } from './float';
26 | export { Flex, FlexParent, FlexChild } from './flex';
27 |
28 | export { Print, ShowForPrint, HideForPrint } from './print';
29 | export {
30 | TypographyHelpers,
31 | Subheader,
32 | Lead,
33 | UnbulletedList,
34 | Statistic,
35 | } from './typography-helpers';
36 | export { TextAlignment } from './text-alignment';
37 |
38 | export { Button } from './button';
39 | export { ButtonGroup } from './button-group-flex'; // eslint-disable-line import/no-unresolved
40 | export { CloseButton } from './close-button';
41 | export {
42 | Switch,
43 | RadioSwitch,
44 | SwitchCheckedLabel,
45 | SwitchUncheckedLabel,
46 | SwitchPadelLabel,
47 | } from './switch';
48 |
49 | export { Menu, MenuItem } from './menu-flex'; // eslint-disable-line import/no-unresolved
50 | export {
51 | TopBar,
52 | TopBarItem,
53 | TopBarTitle,
54 | } from './top-bar-flex'; // eslint-disable-line import/no-unresolved
55 |
56 | export { Accordion, AccordionItem } from './accordion';
57 | export { Callout } from './callout';
58 | export { Dropdown, LinkWithDropdown } from './dropdown';
59 | export {
60 | MediaObject,
61 | MediaObjectSection,
62 | } from './media-object-flex'; // eslint-disable-line import/no-unresolved
63 | export { MenuIcon } from './menu-icon';
64 | export { OffCanvas, OffCanvasContent, OffCanvasContainer } from './off-canvas';
65 | export { Reveal } from './reveal';
66 | export { Table } from './table';
67 | export { Tabs, Tab } from './tabs';
68 | export {
69 | TitleBar,
70 | TitleBarItem,
71 | TitleBarTitle,
72 | TitleBarMenuIcon,
73 | } from './title-bar-flex'; // eslint-disable-line import/no-unresolved
74 |
75 | export { Badge } from './badge';
76 | export { FlexVideo } from './flex-video';
77 | export { Label } from './label';
78 | export { ProgressBar } from './progress-bar';
79 | export { Thumbnail } from './thumbnail';
80 | export { Tooltip, LinkWithTooltip } from './tooltip';
81 |
82 | export { ToggleSwitch, ToggleSwitchItem } from './toggle-switch';
83 |
84 | export { Fade } from './fade';
85 | export { Collapse } from './collapse';
86 |
--------------------------------------------------------------------------------
/src/float/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/float';
3 |
4 | @include foundation-float-classes;
5 |
--------------------------------------------------------------------------------
/src/float/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import includes from 'lodash/includes';
3 |
4 | import { FLOAT_POSITIONS } from '../util/constants';
5 | import createWrapperComponent from '../util/create-wrapper-component';
6 | import styles from './_styles.scss';
7 |
8 | export const Float = createWrapperComponent({
9 | displayName: 'Float',
10 | styles,
11 | propTypes: {
12 | position: PropTypes.oneOf(FLOAT_POSITIONS).isRequired,
13 | },
14 | mapProps: ({ position, ...props }) => ({
15 | props,
16 | classNames: {
17 | [`float-${position}`]: includes(FLOAT_POSITIONS, position),
18 | },
19 | }),
20 | defaultComponentClass: 'div',
21 | });
22 |
23 | export const ClearFix = createWrapperComponent({
24 | displayName: 'ClearFix',
25 | styles,
26 | mapProps: props => ({ props, classNames: 'clearfix' }),
27 | defaultComponentClass: 'div',
28 | });
29 |
30 | Float.ClearFix = ClearFix;
31 |
32 | export default Float;
33 |
--------------------------------------------------------------------------------
/src/forms/_custom.scss:
--------------------------------------------------------------------------------
1 | @include -zf-each-breakpoint {
2 | @if $-zf-size != 'small' {
3 | label {
4 | &.#{$-zf-size}-middle {
5 | @include form-label-middle;
6 | }
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/forms/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/button';
3 | @import '~foundation-sites/scss/forms/forms';
4 |
5 | .form-field {
6 | @include foundation-form-text;
7 | @include foundation-form-checkbox;
8 | @include foundation-form-label;
9 | @include foundation-form-helptext;
10 | @include foundation-form-prepostfix;
11 | @include foundation-form-select;
12 | @include foundation-form-error;
13 |
14 | @import './custom';
15 | }
16 |
--------------------------------------------------------------------------------
/src/grid-flex/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/grid/grid';
3 |
4 | @include foundation-flex-grid;
5 |
--------------------------------------------------------------------------------
/src/grid-flex/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import {
8 | SCREEN_SIZES,
9 | LARGER_SCREEN_SIZES,
10 | FLEX_GRID_ROW_CLASS_NAMES,
11 | FLEX_GRID_COLUMN_CLASS_NAMES,
12 | } from '../util/constants';
13 | import {
14 | createScreenSizeProps,
15 | createScreenSizePropTypes,
16 | createScreenSizeClassNames,
17 | } from '../util/screen-size';
18 | import { FlexParent, FlexChild } from '../flex';
19 | import styles from './_styles.scss';
20 |
21 | const cxStyles = cxBinder.bind(styles);
22 |
23 | const rowProps = createScreenSizeProps(FLEX_GRID_ROW_CLASS_NAMES);
24 | const columnProps = createScreenSizeProps(FLEX_GRID_COLUMN_CLASS_NAMES);
25 |
26 | export const Row = ({
27 | className,
28 | collapse,
29 | expanded,
30 | unstack,
31 | ...restProps,
32 | }) => {
33 | const {
34 | classNames: screenSizeClassNames,
35 | props,
36 | } = createScreenSizeClassNames(rowProps, restProps);
37 | const classNames =
38 | cx(
39 | className,
40 | cxStyles(
41 | 'row',
42 | {
43 | ...screenSizeClassNames,
44 | collapse,
45 | expanded,
46 | [`${unstack}-unstack`]: includes(LARGER_SCREEN_SIZES, unstack),
47 | }
48 | )
49 | );
50 |
51 | return ;
52 | };
53 |
54 | Row.propTypes = {
55 | ...createScreenSizePropTypes(rowProps),
56 | className: PropTypes.string,
57 | collapse: PropTypes.bool,
58 | expanded: PropTypes.bool,
59 | unstack: PropTypes.oneOf(SCREEN_SIZES),
60 | };
61 |
62 | export const Column = ({
63 | className,
64 | expand,
65 | shrink,
66 | ...restProps,
67 | }) => {
68 | const {
69 | classNames: screenSizeClassNames,
70 | props,
71 | } = createScreenSizeClassNames(columnProps, restProps);
72 | const classNames =
73 | cx(
74 | className,
75 | cxStyles(
76 | 'column',
77 | {
78 | ...screenSizeClassNames,
79 | [`${expand}-expand`]: includes(LARGER_SCREEN_SIZES, expand),
80 | shrink,
81 | }
82 | )
83 | );
84 |
85 | return ;
86 | };
87 |
88 | Column.propTypes = {
89 | ...createScreenSizePropTypes(columnProps),
90 | className: PropTypes.string,
91 | shrink: PropTypes.bool,
92 | expand: PropTypes.oneOf(SCREEN_SIZES),
93 | };
94 |
95 | export const Grid = { Row, Column };
96 |
97 | export default Grid;
98 |
--------------------------------------------------------------------------------
/src/grid/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/grid/grid';
3 |
4 | @include foundation-grid;
5 |
--------------------------------------------------------------------------------
/src/grid/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | import { GRID_ROW_CLASS_NAMES, GRID_COLUMN_CLASS_NAMES } from '../util/constants';
4 | import createWrapperComponent from '../util/create-wrapper-component';
5 | import {
6 | createScreenSizeProps,
7 | createScreenSizePropTypes,
8 | createScreenSizeClassNames,
9 | } from '../util/screen-size';
10 | import styles from './_styles.scss';
11 |
12 | const rowProps = createScreenSizeProps(GRID_ROW_CLASS_NAMES);
13 | const columnProps = createScreenSizeProps(GRID_COLUMN_CLASS_NAMES);
14 |
15 | export const Row = createWrapperComponent({
16 | displayName: 'Row',
17 | styles,
18 | propTypes: {
19 | ...createScreenSizePropTypes(rowProps),
20 | collapse: PropTypes.bool,
21 | expanded: PropTypes.bool,
22 | },
23 | mapProps: ({ collapse, expanded, ...restProps }) => {
24 | const { classNames, props } = createScreenSizeClassNames(rowProps, restProps);
25 |
26 | return {
27 | props,
28 | classNames: ['row', { ...classNames, collapse, expanded }],
29 | };
30 | },
31 | defaultComponentClass: 'div',
32 | });
33 |
34 | export const Column = createWrapperComponent({
35 | displayName: 'Column',
36 | styles,
37 | propTypes: {
38 | ...createScreenSizePropTypes(columnProps),
39 | end: PropTypes.bool,
40 | },
41 | mapProps: ({ end, ...restProps }) => {
42 | const { classNames, props } = createScreenSizeClassNames(columnProps, restProps);
43 |
44 | return {
45 | props,
46 | classNames: ['column', { ...classNames, end }],
47 | };
48 | },
49 | defaultComponentClass: 'div',
50 | });
51 |
52 | export const Grid = { Row, Column };
53 |
54 | export default Grid;
55 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { Grid, Row, Column } from './grid';
2 | export {
3 | FormField,
4 | FormFieldInput,
5 | FormFieldLabel,
6 | FormFieldError,
7 | FormFieldHelp,
8 | FormFieldInline,
9 | FormFieldButton,
10 | } from './forms';
11 | export {
12 | Visibility,
13 | ShowForScreenSize,
14 | ShowOnlyForScreenSize,
15 | HideForScreenSize,
16 | HideOnlyForScreenSize,
17 | Hide,
18 | Invisible,
19 | ShowForScreenOrientation,
20 | HideForScreenOrientation,
21 | ShowForScreenReader,
22 | HideForScreenReader,
23 | ShowOnFocus,
24 | } from './visibility';
25 | export { Float, ClearFix } from './float';
26 |
27 | export { Print, ShowForPrint, HideForPrint } from './print';
28 | export {
29 | TypographyHelpers,
30 | Subheader,
31 | Lead,
32 | UnbulletedList,
33 | Statistic,
34 | } from './typography-helpers';
35 | export { TextAlignment } from './text-alignment';
36 |
37 | export { Button } from './button';
38 | export { ButtonGroup } from './button-group';
39 | export { CloseButton } from './close-button';
40 | export {
41 | Switch,
42 | RadioSwitch,
43 | SwitchCheckedLabel,
44 | SwitchUncheckedLabel,
45 | SwitchPadelLabel,
46 | } from './switch';
47 |
48 | export { Menu, MenuItem } from './menu';
49 | export { TopBar, TopBarItem, TopBarTitle } from './top-bar';
50 |
51 | export { Accordion, AccordionItem } from './accordion';
52 | export { Callout } from './callout';
53 | export { Dropdown, LinkWithDropdown } from './dropdown';
54 | export { MediaObject, MediaObjectSection } from './media-object';
55 | export { MenuIcon } from './menu-icon';
56 | export { OffCanvas, OffCanvasContent, OffCanvasContainer } from './off-canvas';
57 | export { Reveal } from './reveal';
58 | export { Table } from './table';
59 | export { Tabs, Tab } from './tabs';
60 | export { TitleBar, TitleBarItem, TitleBarTitle, TitleBarMenuIcon } from './title-bar';
61 |
62 | export { Badge } from './badge';
63 | export { FlexVideo } from './flex-video';
64 | export { Label } from './label';
65 | export { ProgressBar } from './progress-bar';
66 | export { Thumbnail } from './thumbnail';
67 | export { Tooltip, LinkWithTooltip } from './tooltip';
68 |
69 | export { ToggleSwitch, ToggleSwitchItem } from './toggle-switch';
70 |
71 | export { Fade } from './fade';
72 | export { Collapse } from './collapse';
73 |
--------------------------------------------------------------------------------
/src/label/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/label';
3 |
4 | @include foundation-label;
5 |
--------------------------------------------------------------------------------
/src/label/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import includes from 'lodash/includes';
3 |
4 | import { COMPONENT_COLORS, COMPONENT_ALTERNATIVE_COLORS } from '../util/constants';
5 | import createWrapperComponent from '../util/create-wrapper-component';
6 | import styles from './_styles.scss';
7 |
8 | export const Label = createWrapperComponent({
9 | displayName: 'Label',
10 | styles,
11 | propTypes: {
12 | color: PropTypes.oneOf(COMPONENT_COLORS),
13 | },
14 | mapProps: ({ color, ...props }) => ({
15 | props,
16 | classNames: ['label', { [color]: includes(COMPONENT_ALTERNATIVE_COLORS, color) }],
17 | }),
18 | });
19 |
20 | export default Label;
21 |
--------------------------------------------------------------------------------
/src/media-object/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/media-object';
3 |
4 | @include foundation-media-object;
5 |
--------------------------------------------------------------------------------
/src/media-object/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import { MEDIA_OBJECT_SECTION_ALIGNMENTS, FLEX_VERTICAL_ALIGNMENTS } from '../util/constants';
8 | import { FlexParent, FlexChild } from '../flex-mock';
9 | import styles from './_styles.scss';
10 |
11 | const cxStyles = cxBinder.bind(styles);
12 | const IS_FLEX_MODE = false;
13 |
14 | export const MediaObjectSection = ({
15 | className,
16 | main,
17 | verticalAlignment,
18 | ...restProps,
19 | }) => {
20 | const classNames =
21 | cx(
22 | className,
23 | cxStyles(
24 | 'media-object-section',
25 | {
26 | [verticalAlignment]:
27 | !IS_FLEX_MODE && includes(MEDIA_OBJECT_SECTION_ALIGNMENTS, verticalAlignment),
28 | 'main-section': IS_FLEX_MODE && main,
29 | }
30 | )
31 | );
32 | const flexProps = {};
33 |
34 | if (IS_FLEX_MODE) {
35 | flexProps.verticalAlignment = verticalAlignment;
36 | }
37 |
38 | return (
39 |
44 | );
45 | };
46 |
47 | MediaObjectSection.propTypes = {
48 | className: PropTypes.string,
49 | main: PropTypes.bool,
50 | verticalAlignment:
51 | PropTypes.oneOf(IS_FLEX_MODE ? FLEX_VERTICAL_ALIGNMENTS : MEDIA_OBJECT_SECTION_ALIGNMENTS),
52 | };
53 |
54 | export const MediaObject = ({
55 | className,
56 | stackForSmall,
57 | ...restProps,
58 | }) => {
59 | const classNames = cx(className, cxStyles('media-object', { 'stack-for-small': stackForSmall }));
60 |
61 | return ;
62 | };
63 |
64 | MediaObject.propTypes = {
65 | className: PropTypes.string,
66 | stackForSmall: PropTypes.bool,
67 | };
68 |
69 | MediaObject.Section = MediaObjectSection;
70 |
71 | export default MediaObject;
72 |
--------------------------------------------------------------------------------
/src/menu-icon/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/title-bar';
3 | @import '~foundation-sites/scss/components/menu-icon';
4 |
5 | @include foundation-menu-icon;
6 |
--------------------------------------------------------------------------------
/src/menu-icon/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 |
6 | import styles from './_styles.scss';
7 |
8 | const cxStyles = cxBinder.bind(styles);
9 |
10 | export const MenuIcon = ({
11 | className,
12 | dark,
13 | controls,
14 | open,
15 | ...restProps,
16 | }) => {
17 | const classNames = cx(className, cxStyles('menu-icon', { dark }));
18 |
19 | return (
20 |
27 | );
28 | };
29 |
30 | MenuIcon.propTypes = {
31 | className: PropTypes.string,
32 | dark: PropTypes.bool,
33 | controls: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
34 | open: PropTypes.bool,
35 | };
36 |
37 | export default MenuIcon;
38 |
--------------------------------------------------------------------------------
/src/menu/_custom.scss:
--------------------------------------------------------------------------------
1 | // width 100% conflicting with off canvas
2 | @if $global-flexbox {
3 | .menu {
4 | width: initial;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/menu/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/menu';
3 |
4 | @include foundation-menu;
5 |
6 | @import './custom';
7 |
--------------------------------------------------------------------------------
/src/menu/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import {
8 | SCREEN_SIZES,
9 | LARGER_SCREEN_SIZES,
10 | CENTER_POSITION,
11 | MENU_ALIGNMENTS,
12 | FLEX_HORIZONTAL_ALIGNMENTS,
13 | } from '../util/constants';
14 | import { FlexParent, FlexChild } from '../flex-mock';
15 | import styles from './_styles.scss';
16 |
17 | const cxStyles = cxBinder.bind(styles);
18 | const IS_FLEX_MODE = false;
19 |
20 | export const Menu = ({
21 | centerContainerClassName,
22 | centerContainerStyle,
23 | className,
24 | expanded,
25 | horizontal,
26 | horizontalAlignment,
27 | iconTop,
28 | nested,
29 | simple,
30 | vertical,
31 | ...restProps,
32 | }) => {
33 | const classNames =
34 | cx(
35 | className,
36 | cxStyles(
37 | 'menu',
38 | {
39 | [`align-${horizontalAlignment}`]:
40 | !IS_FLEX_MODE
41 | && horizontalAlignment !== CENTER_POSITION
42 | && includes(MENU_ALIGNMENTS, horizontalAlignment),
43 | expanded,
44 | [`${horizontal}-horizontal`]: includes(LARGER_SCREEN_SIZES, horizontal),
45 | 'icon-top': iconTop,
46 | nested,
47 | simple,
48 | vertical: vertical && !includes(LARGER_SCREEN_SIZES, vertical),
49 | [`${vertical}-vertical`]: includes(LARGER_SCREEN_SIZES, vertical),
50 | }
51 | )
52 | );
53 | const flexProps = {};
54 |
55 | if (IS_FLEX_MODE) {
56 | flexProps.horizontalAlignment = horizontalAlignment;
57 | }
58 |
59 | const content = (
60 |
66 | );
67 |
68 | if (!IS_FLEX_MODE && horizontalAlignment === CENTER_POSITION) {
69 | const centerContainerClassNames = cx(centerContainerClassName, cxStyles('menu-centered'));
70 |
71 | return {content}
;
72 | }
73 |
74 | return content;
75 | };
76 |
77 | Menu.propTypes = {
78 | centerContainerClassName: PropTypes.string,
79 | centerContainerStyle: PropTypes.shape({}),
80 | className: PropTypes.string,
81 | expanded: PropTypes.bool,
82 | horizontal: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(SCREEN_SIZES)]),
83 | horizontalAlignment: PropTypes.oneOf(IS_FLEX_MODE ? FLEX_HORIZONTAL_ALIGNMENTS : MENU_ALIGNMENTS),
84 | iconTop: PropTypes.bool,
85 | nested: PropTypes.bool,
86 | simple: PropTypes.bool,
87 | vertical: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(SCREEN_SIZES)]),
88 | };
89 |
90 | export const MenuItem = ({
91 | active,
92 | className,
93 | text,
94 | ...restProps,
95 | }) => {
96 | const classNames = cx(className, cxStyles({ active, 'menu-text': text }));
97 |
98 | return ;
99 | };
100 |
101 | MenuItem.propTypes = {
102 | active: PropTypes.bool,
103 | className: PropTypes.string,
104 | text: PropTypes.bool,
105 | };
106 |
107 | Menu.Item = MenuItem;
108 |
109 | export default Menu;
110 |
--------------------------------------------------------------------------------
/src/off-canvas/_custom.scss:
--------------------------------------------------------------------------------
1 | // Remove when https://github.com/zurb/foundation-sites/issues/7776 is fixed
2 | .off-canvas,
3 | .off-canvas-content {
4 | min-height: 100vh;
5 | }
6 |
--------------------------------------------------------------------------------
/src/off-canvas/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/off-canvas';
3 |
4 | @include foundation-off-canvas;
5 |
6 | @import './custom';
7 |
--------------------------------------------------------------------------------
/src/off-canvas/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Children, cloneElement, isValidElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import { LARGER_SCREEN_SIZES, OFF_CANVAS_POSITIONS } from '../util/constants';
8 | import styles from './_styles.scss';
9 |
10 | const cxStyles = cxBinder.bind(styles);
11 |
12 | export const OffCanvas = ({
13 | className,
14 | position,
15 | revealFor,
16 | ...restProps,
17 | }) => {
18 | const classNames =
19 | cx(
20 | className,
21 | cxStyles(
22 | 'off-canvas',
23 | {
24 | [`position-${position}`]: includes(OFF_CANVAS_POSITIONS, position),
25 | [`reveal-for-${revealFor}`]: includes(LARGER_SCREEN_SIZES, revealFor),
26 | }
27 | )
28 | );
29 |
30 | return
;
31 | };
32 |
33 | OffCanvas.propTypes = {
34 | className: PropTypes.string,
35 | position: PropTypes.oneOf(OFF_CANVAS_POSITIONS),
36 | revealFor: PropTypes.oneOf(LARGER_SCREEN_SIZES),
37 | };
38 |
39 | export const OffCanvasContent = ({
40 | blocked,
41 | children,
42 | className,
43 | contentBlockerClassName,
44 | contentBlockerStyle,
45 | onContentBlockerClick,
46 | ...restProps,
47 | }) => {
48 | const classNames = cx(className, cxStyles('off-canvas-content'));
49 | const contentBlockerClassNames = cx(contentBlockerClassName, cxStyles('js-off-canvas-exit'));
50 |
51 | return (
52 |
53 | {children}
54 |
59 |
60 | );
61 | };
62 |
63 | OffCanvasContent.propTypes = {
64 | blocked: PropTypes.bool,
65 | children: PropTypes.node,
66 | className: PropTypes.string,
67 | contentBlockerClassName: PropTypes.string,
68 | contentBlockerStyle: PropTypes.shape({}),
69 | onContentBlockerClick: PropTypes.func,
70 | };
71 |
72 | export const OffCanvasContainer = ({
73 | children,
74 | className,
75 | innerClassName,
76 | innerStyle,
77 | open,
78 | ...restProps,
79 | }) => {
80 | const blocked = includes(OFF_CANVAS_POSITIONS, open);
81 | const classNames = cx(className, cxStyles('off-canvas-wrapper'));
82 | const innerClassNames =
83 | cx(innerClassName, cxStyles('off-canvas-wrapper-inner', { [`is-open-${open}`]: blocked }));
84 | const clonedChildren = Children.map(children, (child) => {
85 | if (isValidElement(child) && child.type === OffCanvasContent) {
86 | return cloneElement(child, { blocked });
87 | }
88 |
89 | return child;
90 | });
91 |
92 | return (
93 |
94 |
95 | {clonedChildren}
96 |
97 |
98 | );
99 | };
100 |
101 | OffCanvasContainer.propTypes = {
102 | children: PropTypes.node,
103 | className: PropTypes.string,
104 | innerClassName: PropTypes.string,
105 | innerStyle: PropTypes.shape({}),
106 | open: PropTypes.oneOf(OFF_CANVAS_POSITIONS),
107 | };
108 |
109 | OffCanvas.Content = OffCanvasContent;
110 | OffCanvas.Container = OffCanvasContainer;
111 |
112 | export default OffCanvas;
113 |
--------------------------------------------------------------------------------
/src/pagination/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/pagination';
3 |
4 | @include foundation-pagination;
5 |
--------------------------------------------------------------------------------
/src/print/_styles.scss:
--------------------------------------------------------------------------------
1 | // Do not include in _foundation.scss since _typography.scss already includes it.
2 |
3 | .show-for-print {
4 | display: none !important;
5 |
6 | @media print {
7 | display: block !important;
8 | }
9 | }
10 |
11 | .hide-for-print {
12 | @media print {
13 | display: none !important;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/print/index.js:
--------------------------------------------------------------------------------
1 | import createWrapperComponent from '../util/create-wrapper-component';
2 | import styles from './_styles.scss';
3 |
4 | export const ShowForPrint = createWrapperComponent({
5 | displayName: 'ShowOnlyForPrint',
6 | styles,
7 | mapProps: props => ({ props, classNames: 'show-for-print' }),
8 | });
9 |
10 | export const HideForPrint = createWrapperComponent({
11 | displayName: 'HideOnlyForPrint',
12 | styles,
13 | mapProps: props => ({ props, classNames: 'hide-for-print' }),
14 | });
15 |
16 | export const Print = { ShowFor: ShowForPrint, HideFor: HideForPrint };
17 |
18 | export default Print;
19 |
--------------------------------------------------------------------------------
/src/progress-bar/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/forms/progress';
3 | @import '~foundation-sites/scss/components/progress-bar';
4 |
5 | @include foundation-progress-bar;
6 |
--------------------------------------------------------------------------------
/src/progress-bar/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import { COMPONENT_COLORS } from '../util/constants';
8 | import styles from './_styles.scss';
9 |
10 | const cxStyles = cxBinder.bind(styles);
11 |
12 | export const ProgressBar = ({
13 | className,
14 | color,
15 | labelFormatter,
16 | max,
17 | meterClassName,
18 | meterStyle,
19 | meterTextClassName,
20 | meterTextStyle,
21 | min,
22 | value,
23 | ...restProps,
24 | }) => {
25 | const classNames =
26 | cx(className, cxStyles('progress', { [color]: includes(COMPONENT_COLORS, color) }));
27 | const meterClassNames = cx(meterClassName, cxStyles('progress-meter'));
28 | const boundedValue = Math.min(Math.max(min, value), max);
29 | const percent = (boundedValue - min) / (max - min);
30 | const width = Math.round((percent * 100) * 1000) / 1000;
31 | let label = null;
32 |
33 | if (labelFormatter) {
34 | const meterTextClassNames = cx(meterTextClassName, cxStyles('progress-meter-text'));
35 |
36 | label = (
37 |
38 | {labelFormatter(percent, boundedValue, min, max)}
39 |
40 | );
41 | }
42 |
43 | return (
44 |
53 |
54 | {label}
55 |
56 |
57 | );
58 | };
59 |
60 | ProgressBar.propTypes = {
61 | className: PropTypes.string,
62 | color: PropTypes.oneOf(COMPONENT_COLORS),
63 | labelFormatter: PropTypes.func,
64 | max: PropTypes.number,
65 | meterClassName: PropTypes.string,
66 | meterStyle: PropTypes.object,
67 | meterTextClassName: PropTypes.string,
68 | meterTextStyle: PropTypes.object,
69 | min: PropTypes.number,
70 | value: PropTypes.number,
71 | };
72 | ProgressBar.defaultProps = {
73 | max: 100,
74 | min: 0,
75 | value: 0,
76 | };
77 |
78 | export default ProgressBar;
79 |
--------------------------------------------------------------------------------
/src/reveal/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/reveal';
3 |
4 | @include foundation-reveal;
5 |
--------------------------------------------------------------------------------
/src/reveal/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 | import elementType from 'prop-types-extra/lib/elementType';
7 | import Modal from 'react-overlays/lib/Modal';
8 |
9 | import { MODAL_SIZES } from '../util/constants';
10 | import { Fade } from '../fade';
11 | import styles from './_styles.scss';
12 |
13 | const cxStyles = cxBinder.bind(styles);
14 |
15 | export const Reveal = ({
16 | children,
17 | containerClassName,
18 | containerStyle,
19 | revealClassName,
20 | revealStyle,
21 | overlay,
22 | overlayClassName,
23 | overlayStyle,
24 | size,
25 | ...restProps,
26 | }) => {
27 | const revealClassNames =
28 | cx(revealClassName, cxStyles('reveal', { [size]: includes(MODAL_SIZES, size) }));
29 | const overlayClassNames = cx(overlayClassName, cxStyles('reveal-overlay'));
30 | const containerStyleMerged = {
31 | ...containerStyle,
32 | bottom: 0,
33 | left: 0,
34 | overflowY: 'scroll',
35 | position: 'fixed',
36 | right: 0,
37 | top: 0,
38 | };
39 |
40 | return (
41 |
49 |
50 | {children}
51 |
52 |
53 | );
54 | };
55 |
56 | Reveal.propTypes = {
57 | children: PropTypes.node,
58 | containerClassName: PropTypes.string,
59 | containerStyle: PropTypes.object,
60 | overlay: PropTypes.oneOfType([
61 | PropTypes.bool,
62 | PropTypes.oneOf(['static']),
63 | ]),
64 | overlayClassName: PropTypes.string,
65 | overlayStyle: PropTypes.object,
66 | revealClassName: PropTypes.string,
67 | revealStyle: PropTypes.object,
68 | size: PropTypes.oneOf(MODAL_SIZES),
69 | transition: elementType,
70 | };
71 | Reveal.defaultProps = {
72 | overlay: true,
73 | transition: Fade,
74 | };
75 |
76 | export default Reveal;
77 |
--------------------------------------------------------------------------------
/src/switch/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/switch';
3 |
4 | @include foundation-switch;
5 |
--------------------------------------------------------------------------------
/src/switch/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Children, cloneElement, isValidElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import uncontrollable from 'uncontrollable';
6 | import includes from 'lodash/includes';
7 | import noop from 'lodash/noop';
8 |
9 | import { COMPONENT_SIZES } from '../util/constants';
10 | import { ShowForScreenReader, HideForScreenReader } from '../visibility';
11 | import styles from './_styles.scss';
12 |
13 | const cxStyles = cxBinder.bind(styles);
14 |
15 | function createCheckedLabel(baseClassName, displayName = 'SwitchCheckedLabelBase') {
16 | const SwitchCheckedLabelBase = ({
17 | className,
18 | ...restProps,
19 | }) => {
20 | const classNames = cx(className, cxStyles(baseClassName));
21 |
22 | return ;
23 | };
24 |
25 | SwitchCheckedLabelBase.displayName = displayName;
26 | SwitchCheckedLabelBase.propTypes = {
27 | className: PropTypes.string,
28 | };
29 |
30 | return SwitchCheckedLabelBase;
31 | }
32 |
33 | export const SwitchCheckedLabel = createCheckedLabel('switch-active', 'SwitchCheckedLabel');
34 |
35 | export const SwitchUncheckedLabel = createCheckedLabel('switch-inactive', 'SwitchUncheckedLabel');
36 |
37 | export const SwitchPadelLabel = (props) => ;
38 |
39 | const SwitchControlled = ({
40 | checked,
41 | children,
42 | className,
43 | containerClassName,
44 | containerStyle,
45 | eventKey,
46 | id,
47 | onChange,
48 | onSelect,
49 | onToggle,
50 | paddleClassName,
51 | paddleStyle,
52 | size,
53 | ...restProps,
54 | }) => {
55 | const containerClassNames =
56 | cx(containerClassName, cxStyles('switch', { [size]: includes(COMPONENT_SIZES, size) }));
57 | const classNames = cx(className, cxStyles('switch-input'));
58 | const paddleClassNames = cx(paddleClassName, cxStyles('switch-paddle'));
59 | const onInputChange = (...args) => {
60 | if (onChange) {
61 | onChange(...args);
62 | }
63 |
64 | if (onToggle) {
65 | onToggle(!checked, ...args);
66 | }
67 |
68 | if (onSelect) {
69 | onSelect(eventKey, ...args);
70 | }
71 | };
72 | const onLabelClick = (...args) => {
73 | const [event] = args;
74 |
75 | event.preventDefault();
76 |
77 | onInputChange(...args);
78 | };
79 |
80 | return (
81 |
82 |
90 |
96 | {children}
97 |
98 |
99 | );
100 | };
101 |
102 | SwitchControlled.propTypes = {
103 | checked: PropTypes.bool,
104 | children: PropTypes.node,
105 | className: PropTypes.string,
106 | containerClassName: PropTypes.string,
107 | containerStyle: PropTypes.object,
108 | eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
109 | id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
110 | onChange: PropTypes.func,
111 | onSelect: PropTypes.func,
112 | onToggle: PropTypes.func,
113 | paddleClassName: PropTypes.string,
114 | paddleStyle: PropTypes.object,
115 | size: PropTypes.oneOf(COMPONENT_SIZES),
116 | };
117 |
118 | export const Switch = uncontrollable(SwitchControlled, { checked: 'onToggle' });
119 | Switch.displayName = 'Switch';
120 |
121 | const RadioSwitchControlled = ({
122 | activeKey,
123 | children,
124 | onSelect,
125 | size,
126 | ...restProps,
127 | }) => {
128 | const clonedChildren = Children.map(children, (child) => {
129 | if (isValidElement(child)) {
130 | return cloneElement(child, {
131 | checked: child.props.eventKey === activeKey,
132 | onSelect,
133 | onToggle: noop,
134 | size,
135 | });
136 | }
137 |
138 | return child;
139 | });
140 |
141 | return {clonedChildren}
;
142 | };
143 |
144 | RadioSwitchControlled.propTypes = {
145 | activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
146 | children: PropTypes.node,
147 | onSelect: PropTypes.func,
148 | size: PropTypes.oneOf(COMPONENT_SIZES),
149 | };
150 |
151 | export const RadioSwitch = uncontrollable(RadioSwitchControlled, { activeKey: 'onSelect' });
152 | RadioSwitch.displayName = 'RadioSwitch';
153 |
154 | Switch.Radio = RadioSwitch;
155 | Switch.CheckedLabel = SwitchCheckedLabel;
156 | Switch.UncheckedLabel = SwitchUncheckedLabel;
157 | Switch.PadelLabel = SwitchPadelLabel;
158 |
159 | export default Switch;
160 |
--------------------------------------------------------------------------------
/src/table/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/table';
3 |
4 | .table {
5 | @include foundation-table;
6 | }
7 |
--------------------------------------------------------------------------------
/src/table/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 |
6 | import styles from './_styles.scss';
7 |
8 | const cxStyles = cxBinder.bind(styles);
9 |
10 | export const Table = ({
11 | className,
12 | containerClassName,
13 | containerStyle,
14 | hover,
15 | scroll,
16 | scrollContainerClassName,
17 | scrollContainerStyle,
18 | stack,
19 | ...restProps,
20 | }) => {
21 | const containerClassNames = cx(containerClassName, cxStyles('table'));
22 | const classNames = cx(className, cxStyles({ hover, stack }));
23 | let table = ;
24 |
25 | if (scroll) {
26 | const scrollContainerClassNames = cx(scrollContainerClassName, cxStyles('table-scroll'));
27 |
28 | table = {table}
;
29 | }
30 |
31 | return {table}
;
32 | };
33 |
34 | Table.propTypes = {
35 | className: PropTypes.string,
36 | containerClassName: PropTypes.string,
37 | containerStyle: PropTypes.object,
38 | hover: PropTypes.bool,
39 | scroll: PropTypes.bool,
40 | scrollContainerClassName: PropTypes.string,
41 | scrollContainerStyle: PropTypes.object,
42 | stack: PropTypes.bool,
43 | };
44 |
45 | export default Table;
46 |
--------------------------------------------------------------------------------
/src/tabs/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/tabs';
3 |
4 | @include foundation-tabs;
5 |
--------------------------------------------------------------------------------
/src/tabs/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Children, cloneElement, isValidElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import uncontrollable from 'uncontrollable';
6 | import isBlank from 'underscore.string/isBlank';
7 |
8 | import styles from './_styles.scss';
9 |
10 | const cxStyles = cxBinder.bind(styles);
11 |
12 | export const Tab = ({
13 | active,
14 | className,
15 | id,
16 | eventKey, // eslint-disable-line no-unused-vars
17 | ...restProps,
18 | }) => {
19 | const classNames = cx(className, cxStyles('tabs-panel', { 'is-active': active }));
20 |
21 | return (
22 |
30 | );
31 | };
32 |
33 | Tab.propTypes = {
34 | active: PropTypes.bool,
35 | className: PropTypes.string,
36 | id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
37 | eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
38 | };
39 |
40 | const TabTitle = ({
41 | active,
42 | containerClassName,
43 | containerStyle,
44 | eventKey,
45 | onSelect,
46 | tabId,
47 | ...restProps,
48 | }) => {
49 | const classNames = cx(containerClassName, cxStyles('tabs-title', { 'is-active': active }));
50 | const onClick = (...args) => {
51 | const [event] = args;
52 |
53 | event.preventDefault();
54 |
55 | if (onSelect && !isBlank(eventKey)) {
56 | onSelect(eventKey, ...args);
57 | }
58 | };
59 |
60 | return (
61 |
66 |
74 |
75 | );
76 | };
77 |
78 | TabTitle.propTypes = {
79 | active: PropTypes.bool,
80 | containerClassName: PropTypes.string,
81 | containerStyle: PropTypes.object,
82 | eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
83 | onSelect: PropTypes.func,
84 | tabId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
85 | };
86 |
87 | const TabsHeader = ({
88 | className,
89 | vertical,
90 | ...restProps,
91 | }) => {
92 | const classNames = cx(className, cxStyles('tabs', { vertical }));
93 |
94 | return
;
95 | };
96 |
97 | TabsHeader.propTypes = {
98 | className: PropTypes.string,
99 | vertical: PropTypes.bool,
100 | };
101 |
102 | const TabsContent = ({
103 | className,
104 | ...restProps,
105 | }) => {
106 | const classNames = cx(className, cxStyles('tabs-content'));
107 |
108 | return
;
109 | };
110 |
111 | TabsContent.propTypes = {
112 | className: PropTypes.string,
113 | };
114 |
115 | const TabsControlled = ({
116 | activeKey,
117 | children,
118 | contentClassName,
119 | contentStyle,
120 | headerClassName,
121 | headerStyle,
122 | onSelect,
123 | vertical,
124 | ...restProps,
125 | }) => {
126 | const headerChildren = Children.map(children, (child) => {
127 | if (isValidElement(child)) {
128 | return (
129 |
136 | {child.props.title}
137 |
138 | );
139 | }
140 |
141 | return null;
142 | });
143 | const contentChildren = Children.map(children, (child) => {
144 | if (isValidElement(child)) {
145 | return cloneElement(child, { active: activeKey === child.props.eventKey });
146 | }
147 |
148 | return child;
149 | });
150 |
151 | return (
152 |
153 |
154 | {headerChildren}
155 |
156 |
157 | {contentChildren}
158 |
159 |
160 | );
161 | };
162 |
163 | TabsControlled.propTypes = {
164 | activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
165 | children: PropTypes.node,
166 | contentClassName: PropTypes.string,
167 | contentStyle: PropTypes.object,
168 | headerClassName: PropTypes.string,
169 | headerStyle: PropTypes.object,
170 | onSelect: PropTypes.func,
171 | vertical: PropTypes.bool,
172 | };
173 |
174 | export const Tabs = uncontrollable(TabsControlled, { activeKey: 'onSelect' });
175 | Tabs.displayName = 'Tabs';
176 |
177 | Tabs.Tab = Tab;
178 |
179 | export default Tabs;
180 |
--------------------------------------------------------------------------------
/src/text-alignment/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/typography/alignment';
3 |
4 | @include foundation-text-alignment;
5 |
--------------------------------------------------------------------------------
/src/text-alignment/index.js:
--------------------------------------------------------------------------------
1 | import { TEXT_ALIGNMENT_CLASS_NAMES } from '../util/constants';
2 | import createWrapperComponent from '../util/create-wrapper-component';
3 | import {
4 | createScreenSizeProps,
5 | createScreenSizePropTypes,
6 | createScreenSizeClassNames,
7 | } from '../util/screen-size';
8 | import styles from './_styles.scss';
9 |
10 | const textAlignmentProps = createScreenSizeProps(TEXT_ALIGNMENT_CLASS_NAMES);
11 |
12 | export const TextAlignment = createWrapperComponent({
13 | displayName: 'TextAlignment',
14 | styles,
15 | propTypes: createScreenSizePropTypes(textAlignmentProps),
16 | mapProps: props => createScreenSizeClassNames(textAlignmentProps, props),
17 | defaultComponentClass: 'div',
18 | });
19 |
20 | export default TextAlignment;
21 |
--------------------------------------------------------------------------------
/src/thumbnail/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/thumbnail';
3 |
4 | @include foundation-thumbnail;
5 |
--------------------------------------------------------------------------------
/src/thumbnail/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 |
6 | import styles from './_styles.scss';
7 |
8 | const cxStyles = cxBinder.bind(styles);
9 |
10 | export const Thumbnail = ({
11 | alt,
12 | className,
13 | role = alt ? null : 'presentation',
14 | ...restProps,
15 | }) => {
16 | const classNames = cx(className, cxStyles('thumbnail'));
17 |
18 | return ;
19 | };
20 |
21 | Thumbnail.propTypes = {
22 | alt: PropTypes.string,
23 | className: PropTypes.string,
24 | role: PropTypes.string,
25 | };
26 |
27 | export default Thumbnail;
28 |
--------------------------------------------------------------------------------
/src/title-bar/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/title-bar';
3 |
4 | @include foundation-title-bar;
5 |
--------------------------------------------------------------------------------
/src/title-bar/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import { TITLE_BAR_POSITIONS } from '../util/constants';
8 | import { MenuIcon } from '../menu-icon';
9 | import styles from './_styles.scss';
10 |
11 | const cxStyles = cxBinder.bind(styles);
12 |
13 | export const TitleBarItem = ({
14 | className,
15 | position,
16 | ...restProps,
17 | }) => {
18 | const classNames =
19 | cx(
20 | className,
21 | cxStyles(
22 | {
23 | [`title-bar-${position}`]: includes(TITLE_BAR_POSITIONS, position),
24 | }
25 | )
26 | );
27 |
28 | return
;
29 | };
30 |
31 | TitleBarItem.propTypes = {
32 | className: PropTypes.string,
33 | position: PropTypes.oneOf(TITLE_BAR_POSITIONS).isRequired,
34 | };
35 |
36 | export const TitleBarTitle = ({
37 | className,
38 | ...restProps,
39 | }) => {
40 | const classNames = cx(className, cxStyles('title-bar-title'));
41 |
42 | return ;
43 | };
44 |
45 | TitleBarTitle.propTypes = {
46 | className: PropTypes.string,
47 | };
48 |
49 | export const TitleBarMenuIcon = ({
50 | className,
51 | dark,
52 | ...restProps,
53 | }) => {
54 | const classNames = cx(className, cxStyles('menu-icon', { dark }));
55 |
56 | return ;
57 | };
58 |
59 | TitleBarMenuIcon.propTypes = {
60 | className: PropTypes.string,
61 | dark: PropTypes.bool,
62 | };
63 |
64 | export const TitleBar = ({
65 | className,
66 | ...restProps,
67 | }) => {
68 | const classNames = cx(className, cxStyles('title-bar'));
69 |
70 | return
;
71 | };
72 |
73 | TitleBar.propTypes = {
74 | className: PropTypes.string,
75 | };
76 |
77 | TitleBar.Item = TitleBarItem;
78 | TitleBar.Title = TitleBarTitle;
79 | TitleBar.MenuIcon = TitleBarMenuIcon;
80 |
81 | export default TitleBar;
82 |
--------------------------------------------------------------------------------
/src/toggle-switch/_custom.scss:
--------------------------------------------------------------------------------
1 | .switch-toggle {
2 | position: relative;
3 | padding: 0;
4 |
5 | .switch-toggle-item {
6 | display: block;
7 | position: relative;
8 | float: left;
9 | z-index: 2;
10 | margin: 0;
11 | padding: 0 .5rem;
12 | text-align: center;
13 | }
14 |
15 | .switch-toggle-paddle {
16 | position: absolute;
17 | top: 0;
18 | bottom: 0;
19 | left: 0;
20 | transition: left .2s ease-out;
21 | z-index: 1;
22 | margin: .05rem 0;
23 | padding: 0;
24 | width: 0;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/toggle-switch/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 |
3 | @import './custom';
4 |
--------------------------------------------------------------------------------
/src/toggle-switch/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Children, cloneElement, isValidElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import uncontrollable from 'uncontrollable';
6 |
7 | import { COMPONENT_COLORS } from '../util/constants';
8 | import { ClearFix } from '../float';
9 | import { Callout } from '../callout';
10 | import { Button } from '../button';
11 | import styles from './_styles.scss';
12 |
13 | const cxStyles = cxBinder.bind(styles);
14 |
15 | export const ToggleSwitchItem = ({
16 | className,
17 | eventKey,
18 | onClick,
19 | onSelect,
20 | ...restProps,
21 | }) => {
22 | const classNames = cx(className, cxStyles('switch-toggle-item'));
23 | const onTitleClick = (...args) => {
24 | if (onClick) {
25 | onClick(...args);
26 | }
27 |
28 | if (onSelect) {
29 | onSelect(eventKey);
30 | }
31 | };
32 |
33 | return
;
34 | };
35 |
36 | ToggleSwitchItem.propTypes = {
37 | className: PropTypes.string,
38 | eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
39 | onClick: PropTypes.func,
40 | onSelect: PropTypes.func,
41 | };
42 |
43 | const ToggleSwitchControlled = ({
44 | activeKey,
45 | children,
46 | className,
47 | onSelect,
48 | paddleClassName,
49 | paddleColor,
50 | paddleStyle,
51 | ...restProps,
52 | }) => {
53 | const classNames = cx(className, cxStyles('switch-toggle'));
54 | const paddleClassNames = cx(paddleClassName, cxStyles('switch-toggle-paddle'));
55 | const childrenCount = Children.count(children);
56 | const width = childrenCount > 0 ? 100 / childrenCount : 0;
57 | let paddle = null;
58 | let selectedIndex = null;
59 | const clonedChildren = Children.map(children, (child, index) => {
60 | if (isValidElement(child)) {
61 | if (child.props.eventKey === activeKey) {
62 | selectedIndex = index;
63 | }
64 |
65 | return cloneElement(child, { onSelect, style: { ...child.props.style, width: `${width}%` } });
66 | }
67 |
68 | return child;
69 | });
70 |
71 | if (Number.isInteger(selectedIndex)) {
72 | paddle = (
73 |
78 | );
79 | }
80 |
81 | return (
82 |
83 |
84 | {clonedChildren}
85 | {paddle}
86 |
87 |
88 | );
89 | };
90 |
91 | ToggleSwitchControlled.propTypes = {
92 | activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
93 | children: PropTypes.node,
94 | className: PropTypes.string,
95 | onSelect: PropTypes.func,
96 | paddleClassName: PropTypes.string,
97 | paddleColor: PropTypes.oneOf(COMPONENT_COLORS),
98 | paddleStyle: PropTypes.object,
99 | };
100 |
101 | export const ToggleSwitch = uncontrollable(ToggleSwitchControlled, { activeKey: 'onSelect' });
102 | ToggleSwitch.displayName = 'ToggleSwitch';
103 |
104 | ToggleSwitch.Item = ToggleSwitchItem;
105 |
106 | export default ToggleSwitch;
107 |
--------------------------------------------------------------------------------
/src/tooltip/_custom.scss:
--------------------------------------------------------------------------------
1 | .tooltip {
2 | margin-top: $tooltip-pip-width;
3 |
4 | &.top {
5 | margin-top: -$tooltip-pip-width;
6 | }
7 |
8 | &.left {
9 | margin-top: 0;
10 | margin-left: -$tooltip-pip-width;
11 | }
12 |
13 | &.right {
14 | margin-top: 0;
15 | margin-left: $tooltip-pip-width;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/tooltip/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/tooltip';
3 |
4 | @include foundation-tooltip;
5 |
6 | @import './custom';
7 |
--------------------------------------------------------------------------------
/src/tooltip/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { isValidElement, cloneElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import {
8 | OVERLAY_POSITIONS,
9 | OVERLAY_POSITIONS_INTERNAL,
10 | OVERLAY_ALIGNMENTS,
11 | } from '../util/constants';
12 | import OverlayTrigger from '../util/overlay-trigger';
13 | import { Fade } from '../fade';
14 | import styles from './_styles.scss';
15 |
16 | const cxStyles = cxBinder.bind(styles);
17 |
18 | export const Tooltip = ({
19 | className,
20 | position,
21 | ...restProps,
22 | }) => {
23 | const classNames =
24 | cx(
25 | className,
26 | cxStyles(
27 | 'tooltip',
28 | {
29 | [position]: includes(OVERLAY_POSITIONS_INTERNAL, position),
30 | }
31 | )
32 | );
33 |
34 | return
;
35 | };
36 |
37 | Tooltip.propTypes = {
38 | className: PropTypes.string,
39 | position: PropTypes.oneOf(OVERLAY_POSITIONS),
40 | };
41 | Tooltip.defaultProps = {
42 | position: 'bottom',
43 | };
44 |
45 | const TooltipOverlay = ({
46 | placement, // eslint-disable-line no-unused-vars, react/prop-types
47 | arrowOffsetLeft, // eslint-disable-line no-unused-vars, react/prop-types
48 | arrowOffsetTop, // eslint-disable-line no-unused-vars, react/prop-types
49 | positionLeft, // eslint-disable-line no-unused-vars, react/prop-types
50 | positionTop, // eslint-disable-line no-unused-vars, react/prop-types
51 | ...restProps,
52 | }) => ;
53 |
54 | export const LinkWithTooltip = ({
55 | children,
56 | tooltipAlignment,
57 | tooltipClassName,
58 | tooltipContent,
59 | tooltipIndicator,
60 | tooltipId,
61 | tooltipPosition,
62 | tooltipStyle,
63 | ...restProps,
64 | }) => {
65 | const childProps = {
66 | 'aria-haspopup': true,
67 | 'aria-describedby': tooltipId,
68 | };
69 | const childClassNames = cxStyles({ 'has-tip': tooltipIndicator });
70 | let clonedChild = null;
71 |
72 | if (isValidElement(children)) {
73 | clonedChild = cloneElement(children, {
74 | ...childProps,
75 | className: cx(children.props.className, childClassNames),
76 | });
77 | } else {
78 | clonedChild = {children} ;
79 | }
80 |
81 | const tooltip = (
82 |
88 | {tooltipContent}
89 |
90 | );
91 |
92 | return (
93 |
99 | {clonedChild}
100 |
101 | );
102 | };
103 |
104 | LinkWithTooltip.propTypes = {
105 | children: PropTypes.node,
106 | tooltipAlignment: PropTypes.oneOf(OVERLAY_ALIGNMENTS),
107 | tooltipClassName: PropTypes.string,
108 | tooltipContent: PropTypes.node,
109 | tooltipIndicator: PropTypes.bool,
110 | tooltipId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
111 | tooltipPosition: PropTypes.oneOf(OVERLAY_POSITIONS),
112 | tooltipStyle: PropTypes.object,
113 | };
114 | LinkWithTooltip.defaultProps = {
115 | closeOnClickOutside: true,
116 | tooltipIndicator: true,
117 | tooltipPosition: 'bottom',
118 | transition: Fade,
119 | triggerClick: true,
120 | triggerFocus: true,
121 | triggerHover: true,
122 | };
123 |
124 | Tooltip.LinkWith = LinkWithTooltip;
125 |
126 | export default Tooltip;
127 |
--------------------------------------------------------------------------------
/src/top-bar/_custom.scss:
--------------------------------------------------------------------------------
1 | // Remove when https://github.com/zurb/foundation-sites/pull/9177/files is fixed
2 | @if $global-flexbox {
3 | .top-bar-left {
4 | flex: 1 1 auto !important;
5 | }
6 |
7 | .top-bar-right {
8 | flex: 0 1 auto !important;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/top-bar/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/top-bar';
3 |
4 | @include foundation-top-bar;
5 |
6 | @import './custom';
7 |
--------------------------------------------------------------------------------
/src/top-bar/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import includes from 'lodash/includes';
6 |
7 | import { SCREEN_SIZES, SCREEN_SIZE_SMALL, TOP_BAR_POSITIONS } from '../util/constants';
8 | import styles from './_styles.scss';
9 |
10 | const cxStyles = cxBinder.bind(styles);
11 | const IS_FLEX_MODE = false;
12 |
13 | export const TopBarItem = ({
14 | className,
15 | position,
16 | ...restProps,
17 | }) => {
18 | const classNames =
19 | cx(className, cxStyles({ [`top-bar-${position}`]: includes(TOP_BAR_POSITIONS, position) }));
20 |
21 | return
;
22 | };
23 |
24 | TopBarItem.propTypes = {
25 | className: PropTypes.string,
26 | position: PropTypes.oneOf(TOP_BAR_POSITIONS).isRequired,
27 | };
28 |
29 | export const TopBarTitle = ({
30 | className,
31 | ...restProps,
32 | }) => {
33 | const classNames = cx(className, cxStyles('top-bar-title'));
34 |
35 | return
;
36 | };
37 |
38 | TopBarTitle.propTypes = {
39 | className: PropTypes.string,
40 | };
41 |
42 | export const TopBar = ({
43 | className,
44 | stack,
45 | ...restProps,
46 | }) => {
47 | const classNames =
48 | cx(
49 | className,
50 | cxStyles(
51 | 'top-bar',
52 | {
53 | [`stacked-for-${stack}`]: includes(SCREEN_SIZES, stack),
54 | }
55 | )
56 | );
57 |
58 | return
;
59 | };
60 |
61 | TopBar.propTypes = {
62 | className: PropTypes.string,
63 | stack: PropTypes.oneOf(SCREEN_SIZES),
64 | };
65 | TopBar.defaultProps = {
66 | stack: SCREEN_SIZE_SMALL,
67 | };
68 |
69 | TopBar.Item = TopBarItem;
70 | TopBar.Title = TopBarTitle;
71 |
72 | export default TopBar;
73 |
--------------------------------------------------------------------------------
/src/typography-helpers/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/typography/helpers';
3 |
4 | @include foundation-typography-helpers;
5 |
--------------------------------------------------------------------------------
/src/typography-helpers/index.js:
--------------------------------------------------------------------------------
1 | import createWrapperComponent from '../util/create-wrapper-component';
2 | import styles from './_styles.scss';
3 |
4 | export const Subheader = createWrapperComponent({
5 | displayName: 'Subheader',
6 | styles,
7 | mapProps: props => ({ props, classNames: 'subheader' }),
8 | });
9 |
10 | export const Lead = createWrapperComponent({
11 | displayName: 'Lead',
12 | styles,
13 | mapProps: props => ({ props, classNames: 'lead' }),
14 | });
15 |
16 | export const UnbulletedList = createWrapperComponent({
17 | displayName: 'UnbulletedList',
18 | styles,
19 | mapProps: props => ({ props, classNames: 'no-bullet' }),
20 | defaultComponentClass: 'ul',
21 | });
22 |
23 | export const Statistic = createWrapperComponent({
24 | displayName: 'Statistic',
25 | styles,
26 | mapProps: props => ({ props, classNames: 'stat' }),
27 | });
28 |
29 | export const TypographyHelpers = { Subheader, Lead, UnbulletedList, Statistic };
30 |
31 | export default TypographyHelpers;
32 |
--------------------------------------------------------------------------------
/src/util/constants/index.js:
--------------------------------------------------------------------------------
1 | import keyMirrorArray from '../key-mirror-array';
2 |
3 | export const SCREEN_SIZE_SMALL = 'small';
4 | export const BUTTON_GROUP_STACK_SCREEN_SIZES = [SCREEN_SIZE_SMALL, 'medium'];
5 | export const SCREEN_SIZES = [...BUTTON_GROUP_STACK_SCREEN_SIZES, 'large', 'xlarge', 'xxlarge'];
6 | export const [, ...LARGER_SCREEN_SIZES] = SCREEN_SIZES;
7 |
8 | export const SCREEN_ORIENTATIONS = ['landscape', 'portrait'];
9 |
10 | export const COMPONENT_ALTERNATIVE_COLORS = ['secondary', 'success', 'alert', 'warning'];
11 | export const COMPONENT_COLORS = ['primary', ...COMPONENT_ALTERNATIVE_COLORS];
12 |
13 | export const CALLOUT_SIZES = ['small', 'large'];
14 | export const COMPONENT_SIZES = ['tiny', ...CALLOUT_SIZES];
15 | export const MODAL_SIZES = [...COMPONENT_SIZES, 'full'];
16 |
17 | export const CENTER_POSITION = 'center';
18 | export const OFF_CANVAS_POSITIONS = ['left', 'right'];
19 | export const TITLE_BAR_POSITIONS = [...OFF_CANVAS_POSITIONS];
20 | export const TOP_BAR_POSITIONS = [...OFF_CANVAS_POSITIONS];
21 | export const OVERLAY_POSITIONS_INTERNAL = [...OFF_CANVAS_POSITIONS, 'top'];
22 | export const OVERLAY_POSITIONS = [...OVERLAY_POSITIONS_INTERNAL, 'bottom'];
23 | export const OVERLAY_ALIGNMENTS = [...OVERLAY_POSITIONS, CENTER_POSITION];
24 | export const FLOAT_POSITIONS = [...OFF_CANVAS_POSITIONS, CENTER_POSITION];
25 | export const TEXT_ALIGNMENTS = [...FLOAT_POSITIONS, 'justify'];
26 | export const MENU_ALIGNMENTS = [...FLOAT_POSITIONS];
27 | export const MEDIA_OBJECT_SECTION_ALIGNMENTS = ['middle', 'bottom'];
28 | export const FLEX_HORIZONTAL_ALIGNMENTS = [...TEXT_ALIGNMENTS, 'spaced'];
29 | export const FLEX_VERTICAL_ALIGNMENTS = ['top', ...MEDIA_OBJECT_SECTION_ALIGNMENTS, 'stretch'];
30 |
31 | export const CLASS_NAME_TYPES = keyMirrorArray(['BOOL', 'RANGE', 'ENUM']);
32 | export const COLLAPSE_MODES = ['collapse', 'uncollapse'];
33 | export const CENTERED_MODES = ['centered', 'uncentered'];
34 | export const TEXT_ALIGNMENT_CLASS_NAMES = [
35 | {
36 | baseClassName: 'text',
37 | basePropName: 'Alignment',
38 | type: CLASS_NAME_TYPES.ENUM,
39 | values: TEXT_ALIGNMENTS,
40 | flattenSmall: true,
41 | },
42 | ];
43 | export const GRID_ROW_CLASS_NAMES = [
44 | {
45 | baseClassName: '',
46 | basePropName: 'Collapse',
47 | type: CLASS_NAME_TYPES.ENUM,
48 | values: COLLAPSE_MODES,
49 | },
50 | {
51 | baseClassName: 'up',
52 | basePropName: 'Up',
53 | type: CLASS_NAME_TYPES.RANGE,
54 | min: 1,
55 | max: 8,
56 | },
57 | ];
58 | export const GRID_COLUMN_CLASS_NAMES = [
59 | {
60 | baseClassName: '',
61 | basePropName: '',
62 | type: CLASS_NAME_TYPES.RANGE,
63 | min: 1,
64 | max: 12,
65 | },
66 | {
67 | baseClassName: 'offset',
68 | basePropName: 'Offset',
69 | type: CLASS_NAME_TYPES.RANGE,
70 | min: 0,
71 | max: 11,
72 | },
73 | {
74 | baseClassName: '',
75 | basePropName: 'Centered',
76 | type: CLASS_NAME_TYPES.ENUM,
77 | values: CENTERED_MODES,
78 | },
79 | {
80 | baseClassName: 'push',
81 | basePropName: 'Push',
82 | type: CLASS_NAME_TYPES.RANGE,
83 | min: 1,
84 | max: 11,
85 | },
86 | {
87 | baseClassName: 'pull',
88 | basePropName: 'Pull',
89 | type: CLASS_NAME_TYPES.RANGE,
90 | min: 1,
91 | max: 11,
92 | },
93 | ];
94 |
95 | export const FLEX_PARENT_CLASS_NAMES = [];
96 | export const FLEX_CHILD_CLASS_NAMES = [
97 | {
98 | baseClassName: 'order',
99 | basePropName: 'Order',
100 | type: CLASS_NAME_TYPES.RANGE,
101 | min: 1,
102 | max: 6,
103 | },
104 | ];
105 | export const FLEX_GRID_ROW_CLASS_NAMES = [
106 | {
107 | baseClassName: '',
108 | basePropName: 'Collapse',
109 | type: CLASS_NAME_TYPES.ENUM,
110 | values: COLLAPSE_MODES,
111 | },
112 | {
113 | baseClassName: 'up',
114 | basePropName: 'Up',
115 | type: CLASS_NAME_TYPES.RANGE,
116 | min: 1,
117 | max: 8,
118 | },
119 | ];
120 | export const FLEX_GRID_COLUMN_CLASS_NAMES = [
121 | {
122 | baseClassName: '',
123 | basePropName: '',
124 | type: CLASS_NAME_TYPES.RANGE,
125 | min: 1,
126 | max: 12,
127 | },
128 | {
129 | baseClassName: 'offset',
130 | basePropName: 'Offset',
131 | type: CLASS_NAME_TYPES.RANGE,
132 | min: 0,
133 | max: 11,
134 | },
135 | ];
136 |
--------------------------------------------------------------------------------
/src/util/create-wrapper-component/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Children, cloneElement } from 'react';
3 | import cx from 'classnames';
4 | import cxBinder from 'classnames/bind';
5 | import elementType from 'prop-types-extra/lib/elementType';
6 |
7 | export default function createWrapperComponent({
8 | displayName = 'Wrapper',
9 | styles = {},
10 | propTypes = {},
11 | mapProps = props => ({ props, classNames: {}, style: {} }),
12 | defaultComponentClass = 'span',
13 | } = {}) {
14 | const cxStyles = cxBinder.bind(styles);
15 | const Wrapper = ({
16 | children,
17 | className,
18 | componentClass,
19 | style,
20 | noWrap,
21 | ...restProps,
22 | }) => {
23 | const ComponentClass = componentClass || defaultComponentClass;
24 | const {
25 | props: mappedProps,
26 | classNames: mappedClassNames,
27 | style: mappedStyle,
28 | } = mapProps(restProps);
29 |
30 | if (noWrap) {
31 | const child = Children.only(children);
32 | const childProps = child.props ? child.props : {};
33 |
34 | return cloneElement(child, {
35 | ...childProps,
36 | ...mappedProps,
37 | className: cx(className, child.props.className, cxStyles(mappedClassNames)),
38 | style: { ...style, ...child.props.style, ...mappedStyle },
39 | });
40 | }
41 |
42 | return (
43 |
48 | {children}
49 |
50 | );
51 | };
52 |
53 | Wrapper.displayName = displayName;
54 | Wrapper.propTypes = {
55 | children: PropTypes.node,
56 | className: PropTypes.string,
57 | componentClass: elementType,
58 | style: PropTypes.shape({}),
59 | noWrap: PropTypes.bool,
60 | ...propTypes,
61 | };
62 |
63 | return Wrapper;
64 | }
65 |
--------------------------------------------------------------------------------
/src/util/default-component/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import elementType from 'prop-types-extra/lib/elementType';
3 |
4 | export const DefaultComponent = ({
5 | componentClass: ComponentClass,
6 | ...restProps,
7 | }) => ;
8 |
9 | DefaultComponent.propTypes = {
10 | componentClass: elementType,
11 | };
12 | DefaultComponent.defaultProps = {
13 | componentClass: 'div',
14 | };
15 |
16 | export default DefaultComponent;
17 |
--------------------------------------------------------------------------------
/src/util/key-mirror-array/index.js:
--------------------------------------------------------------------------------
1 | export default function keyMirrorArray(array) {
2 | const result = {};
3 |
4 | array.forEach((item) => {
5 | result[item] = item;
6 | });
7 |
8 | return result;
9 | }
10 |
--------------------------------------------------------------------------------
/src/util/screen-size/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import includes from 'lodash/includes';
3 | import mapValues from 'lodash/mapValues';
4 | import decapitalize from 'underscore.string/decapitalize';
5 |
6 | import { CLASS_NAME_TYPES, SCREEN_SIZES, SCREEN_SIZE_SMALL } from '../constants';
7 |
8 | export function createScreenSizeProps(classNameMapping = []) {
9 | return classNameMapping.reduce(
10 | (columnAgg, { baseClassName, basePropName, flattenSmall, ...column }) => {
11 | const props =
12 | SCREEN_SIZES.reduce(
13 | (sizeAgg, size) => {
14 | let className = '';
15 | let propName = '';
16 |
17 | if (flattenSmall && size === SCREEN_SIZE_SMALL) {
18 | className = baseClassName;
19 | propName = decapitalize(basePropName);
20 | } else {
21 | className = size + (baseClassName ? `-${baseClassName}` : '');
22 | propName = `${size}${basePropName}`;
23 | }
24 |
25 | return {
26 | ...sizeAgg,
27 | [propName]: {
28 | ...column,
29 | className,
30 | },
31 | };
32 | },
33 | {}
34 | );
35 |
36 | return { ...columnAgg, ...props };
37 | },
38 | {}
39 | );
40 | }
41 |
42 | export function createScreenSizePropTypes(screenSizeProps = {}) {
43 | return mapValues(screenSizeProps, ({ type, values }) => {
44 | if (type === CLASS_NAME_TYPES.RANGE) {
45 | return PropTypes.number;
46 | } else if (type === CLASS_NAME_TYPES.ENUM) {
47 | return PropTypes.oneOf(values);
48 | }
49 |
50 | return PropTypes.bool;
51 | });
52 | }
53 |
54 | export function createScreenSizeClassNames(screenSizeProps = {}, props = {}) {
55 | const classNames = {};
56 | const remainingProps = {};
57 |
58 | Object.keys(props).forEach((prop) => {
59 | const propValue = props[prop];
60 |
61 | if (screenSizeProps[prop]) {
62 | const { className, type, min, max, values } = screenSizeProps[prop];
63 |
64 | if (type === CLASS_NAME_TYPES.RANGE) {
65 | classNames[`${className}-${propValue}`] =
66 | Number.isInteger(propValue) && propValue >= min && propValue <= max;
67 | } else if (type === CLASS_NAME_TYPES.ENUM) {
68 | classNames[`${className}-${propValue}`] = includes(values, propValue);
69 | } else {
70 | classNames[className] = propValue;
71 | }
72 | } else {
73 | remainingProps[prop] = propValue;
74 | }
75 | });
76 |
77 | return { classNames, props: remainingProps };
78 | }
79 |
--------------------------------------------------------------------------------
/src/visibility/_styles.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 | @import '~foundation-sites/scss/components/visibility';
3 |
4 | @include foundation-visibility-classes;
5 |
--------------------------------------------------------------------------------
/src/visibility/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import includes from 'lodash/includes';
3 |
4 | import { SCREEN_SIZES, LARGER_SCREEN_SIZES, SCREEN_ORIENTATIONS } from '../util/constants';
5 | import createWrapperComponent from '../util/create-wrapper-component';
6 | import styles from './_styles.scss';
7 |
8 | export const ShowForScreenSize = createWrapperComponent({
9 | displayName: 'ShowForScreenSize',
10 | styles,
11 | propTypes: {
12 | screenSize: PropTypes.oneOf(SCREEN_SIZES).isRequired,
13 | },
14 | mapProps: ({ screenSize, ...props }) => ({
15 | props,
16 | classNames: {
17 | [`show-for-${screenSize}`]: includes(LARGER_SCREEN_SIZES, screenSize),
18 | },
19 | }),
20 | });
21 |
22 | export const ShowOnlyForScreenSize = createWrapperComponent({
23 | displayName: 'ShowOnlyForScreenSize',
24 | styles,
25 | propTypes: {
26 | screenSize: PropTypes.oneOf(SCREEN_SIZES).isRequired,
27 | },
28 | mapProps: ({ screenSize, ...props }) => ({
29 | props,
30 | classNames: {
31 | [`show-for-${screenSize}-only`]: includes(SCREEN_SIZES, screenSize),
32 | },
33 | }),
34 | });
35 |
36 | export const HideForScreenSize = createWrapperComponent({
37 | displayName: 'HideForScreenSize',
38 | styles,
39 | propTypes: {
40 | screenSize: PropTypes.oneOf(SCREEN_SIZES).isRequired,
41 | },
42 | mapProps: ({ screenSize, ...props }) => ({
43 | props,
44 | classNames: {
45 | hide: !includes(LARGER_SCREEN_SIZES, screenSize) && includes(SCREEN_SIZES, screenSize),
46 | [`hide-for-${screenSize}`]: includes(LARGER_SCREEN_SIZES, screenSize),
47 | },
48 | }),
49 | });
50 |
51 | export const HideOnlyForScreenSize = createWrapperComponent({
52 | displayName: 'HideOnlyForScreenSize',
53 | styles,
54 | propTypes: {
55 | screenSize: PropTypes.oneOf(SCREEN_SIZES).isRequired,
56 | },
57 | mapProps: ({ screenSize, ...props }) => ({
58 | props,
59 | classNames: {
60 | [`hide-for-${screenSize}-only`]: includes(SCREEN_SIZES, screenSize),
61 | },
62 | }),
63 | });
64 |
65 | export const Hide = createWrapperComponent({
66 | displayName: 'Hide',
67 | styles,
68 | mapProps: props => ({
69 | props,
70 | classNames: 'hide',
71 | }),
72 | });
73 |
74 | export const Invisible = createWrapperComponent({
75 | displayName: 'Invisible',
76 | styles,
77 | mapProps: props => ({
78 | props,
79 | classNames: 'invisible',
80 | }),
81 | });
82 |
83 | export const ShowForScreenOrientation = createWrapperComponent({
84 | displayName: 'ShowForScreenOrientation',
85 | styles,
86 | propTypes: {
87 | screenOrientation: PropTypes.oneOf(SCREEN_ORIENTATIONS).isRequired,
88 | },
89 | mapProps: ({ screenOrientation, ...props }) => ({
90 | props,
91 | classNames: {
92 | [`show-for-${screenOrientation}`]: includes(SCREEN_ORIENTATIONS, screenOrientation),
93 | },
94 | }),
95 | });
96 |
97 | export const HideForScreenOrientation = createWrapperComponent({
98 | displayName: 'HideForScreenOrientation',
99 | styles,
100 | propTypes: {
101 | screenOrientation: PropTypes.oneOf(SCREEN_ORIENTATIONS).isRequired,
102 | },
103 | mapProps: ({ screenOrientation, ...props }) => ({
104 | props,
105 | classNames: {
106 | [`hide-for-${screenOrientation}`]: includes(SCREEN_ORIENTATIONS, screenOrientation),
107 | },
108 | }),
109 | });
110 |
111 | export const ShowForScreenReader = createWrapperComponent({
112 | displayName: 'ShowForScreenReader',
113 | styles,
114 | mapProps: props => ({
115 | props,
116 | classNames: 'show-for-sr',
117 | }),
118 | });
119 |
120 | export const HideForScreenReader = createWrapperComponent({
121 | displayName: 'HideForScreenReader',
122 | styles,
123 | mapProps: props => ({
124 | props: { ...props, 'aria-hidden': true },
125 | }),
126 | });
127 |
128 | export const ShowOnFocus = createWrapperComponent({
129 | displayName: 'ShowOnFocus',
130 | styles,
131 | mapProps: props => ({
132 | props,
133 | classNames: 'show-on-focus',
134 | }),
135 | });
136 |
137 | export const Visibility = {
138 | ShowForScreenSize,
139 | ShowOnlyForScreenSize,
140 | HideForScreenSize,
141 | HideOnlyForScreenSize,
142 | Hide,
143 | Invisible,
144 | ShowForScreenOrientation,
145 | HideForScreenOrientation,
146 | ShowForScreenReader,
147 | HideForScreenReader,
148 | ShowOnFocus,
149 | };
150 |
151 | export default Visibility;
152 |
--------------------------------------------------------------------------------