├── .gitignore
├── README.md
├── examples
├── collection.js
├── drag-and-drop-key-points.js
├── dynamic-cell-measurer.js
├── dynamic-row-height-getter.js
├── element-child.js
├── force-update.js
├── forgotten-styles.js
├── function-child.js
├── function-children-example-1.js
├── function-children-example-2.js
├── function-children-example-3.js
├── is-scrolling-cell-renderer.js
├── multi-grid-renderers.js
├── multi-grid.js
├── pass-through-props.js
├── perf-comparison-non-virtualized.js
├── perf-comparison-plain.js
├── perf-comparison-virtualized.js
├── pure-component.js
├── resizable-cells-key-points.js
├── row-renderer.js
└── shallow-compare.js
├── lib
└── font-awesome
│ ├── css
│ ├── font-awesome.css
│ └── font-awesome.min.css
│ └── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── package.json
├── public
├── books.jpg
├── browser-limits-cutoff.png
├── cache-all-the-things.png
├── computer-guy.png
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── how-is-it-used-calendar.gif
├── how-is-it-used-dropdown.gif
├── how-is-it-used-table.gif
├── how-is-it-used-tree.gif
├── index.html
├── kindle.jpg
├── non-uniform-heights-chat.png
├── non-uniform-heights-drop-down.png
├── occlusion-culling.jpg
├── overscan-example.mp4
├── profile-picture.jpg
└── v9-dev-mode-style-warning.png
├── src
├── App.js
├── Components
│ ├── AnimatedList.css
│ ├── AnimatedList.js
│ ├── AnimatedListRow.css
│ ├── AnimatedListRow.js
│ ├── BuildingBlocks.css
│ ├── BuildingBlocks.js
│ ├── BuildingBlocksSvgWrapper.css
│ ├── BuildingBlocksSvgWrapper.js
│ ├── CrossHatchRect.css
│ ├── CrossHatchRect.js
│ ├── DeferMeasurements.js
│ ├── DemoCollection.css
│ ├── DemoCollection.js
│ ├── DragAndDropList.css
│ ├── DragAndDropList.js
│ ├── ExampleList.js
│ ├── HowDoesWindowingWork.css
│ ├── HowDoesWindowingWork.js
│ ├── LabeledCircle.js
│ ├── LabeledRect.js
│ ├── LabeledSvg.css
│ ├── MarioSvg.js
│ ├── Note.js
│ ├── ResizableRowsList.css
│ ├── ResizableRowsList.js
│ ├── ScaledList.css
│ ├── ScaledList.js
│ ├── Scaler.js
│ ├── ScuChart.css
│ ├── ScuChart.js
│ ├── Spreadsheet.css
│ ├── Spreadsheet.js
│ ├── StyledListElements.js
│ ├── SvgWrapper.js
│ └── TreasureDataIcon.js
├── ForwardTheme.js
├── Presentation
│ ├── ContentSlide.js
│ ├── TitleSlide.js
│ └── presenterSlideStyle.js
├── Slides
│ ├── 000.js
│ ├── 001.js
│ ├── 002.js
│ ├── 003.js
│ ├── 004.js
│ ├── 100.js
│ ├── 101.js
│ ├── 102.js
│ ├── 103.js
│ ├── 104.js
│ ├── 200.js
│ ├── 201.js
│ ├── 202.js
│ ├── 203.js
│ ├── 204.js
│ ├── 205.js
│ ├── 206.js
│ ├── 300.js
│ ├── 301.js
│ ├── 400.js
│ ├── 401.js
│ ├── 402.js
│ ├── 403.js
│ ├── 404.js
│ ├── 405.js
│ ├── 406.js
│ ├── 500.js
│ ├── 501.js
│ ├── 502.js
│ ├── 503.js
│ ├── 504.js
│ ├── 600.js
│ ├── 601.js
│ ├── 602.js
│ └── 700.js
├── Utils
│ └── generateRandomList.js
├── index.js
└── shared-list-styles.css
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # testing
7 | coverage
8 |
9 | # production
10 | build
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | npm-debug.log
16 |
--------------------------------------------------------------------------------
/examples/collection.js:
--------------------------------------------------------------------------------
1 | function cellSizeAndPositionGetter ({ index }) {
2 | const item = itemsArray[index]
3 |
4 | return {
5 | height: item.size,
6 | width: item.size,
7 | x: item.left,
8 | y: item.top
9 | }
10 | }
11 |
12 | function cellRenderer ({ index, key, style }) {
13 | return (
14 |
18 | {itemsArray[index].name}
19 |
20 | )
21 | }
22 |
23 | function renderCollection (props) {
24 | return (
25 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/examples/drag-and-drop-key-points.js:
--------------------------------------------------------------------------------
1 | import { arrayMove, SortableContainer, SortableElement } from 'react-sortable-hoc'
2 | import { List } from 'react-virtualized'
3 |
4 | // Connect react-virtualized and react-sortable-hoc
5 | const SortableList = SortableContainer(List, { withRef: true })
6 | const SortableRow = SortableElement(({ children }) => children)
7 |
8 | function rowRenderer ({ index, key, style }) {
9 | return (
10 |
11 |
12 | {itemsArray[index].name}
13 |
14 |
15 | )
16 | }
17 |
18 | function renderList (props) {
19 | return (
20 | arrayMove(list, oldIndex, newIndex)
24 | }
25 | rowRenderer={rowRenderer}
26 | />
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/examples/dynamic-cell-measurer.js:
--------------------------------------------------------------------------------
1 | const cache = new CellMeasurerCache({ fixedWidth: true });
2 |
3 | function rowRenderer ({ index, key, parent, style }) {
4 | return (
5 |
12 | {/* Your content here */}
13 |
14 | );
15 | }
16 |
17 | function renderList (props) {
18 | return (
19 |
25 | );
26 | }
--------------------------------------------------------------------------------
/examples/dynamic-row-height-getter.js:
--------------------------------------------------------------------------------
1 | function getRowHeight ({ index }) {
2 | return itemArray[index].type === 'header' ? 20 : 30
3 | }
4 |
5 | function renderList (listProps) {
6 | return (
7 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/examples/element-child.js:
--------------------------------------------------------------------------------
1 |
2 | Brian
3 |
--------------------------------------------------------------------------------
/examples/force-update.js:
--------------------------------------------------------------------------------
1 | class Example extends Component {
2 | componentDidUpdate (prevProps, prevState) {
3 | if (this.props.sortOrder !== prevProps.sortOrder) {
4 | this._grid.forceUpdate()
5 | }
6 | }
7 |
8 | render () {
9 | return (
10 | this._grid = ref}
13 | />
14 | )
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/forgotten-styles.js:
--------------------------------------------------------------------------------
1 | // Wrong:
2 | function rowRenderer ({ key, index, style }) {
3 | return (
4 |
5 | {items[index]}
6 |
7 | )
8 | }
9 |
10 | // Right:
11 | function rowRenderer ({ key, index, style }) {
12 | return (
13 |
17 | {items[index]}
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/examples/function-child.js:
--------------------------------------------------------------------------------
1 |
2 | {(name) => {name}}
3 |
--------------------------------------------------------------------------------
/examples/function-children-example-1.js:
--------------------------------------------------------------------------------
1 | // Pseudo-code
2 | class WithUser extends Component {
3 | componentDidMount () {
4 | // Load data...
5 | }
6 |
7 | render () {
8 | const { user } = this.state
9 | const { children } = this.props
10 |
11 | return user
12 | ? children(user)
13 | : null
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/function-children-example-2.js:
--------------------------------------------------------------------------------
1 | // Pseudo-code
2 | class WithLocalization extends Component {
3 | componentDidMount () {
4 | // Load data...
5 | }
6 |
7 | render () {
8 | const { localization } = this.state
9 | const { children } = this.props
10 |
11 | return localization
12 | ? children(localization)
13 | : null
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/function-children-example-3.js:
--------------------------------------------------------------------------------
1 | function LocalizedUserBadge () {
2 | return (
3 |
4 | {(localization) => (
5 |
6 | {(user) => (
7 |
8 | {localization.get('user_greeting', { name: user.name })}
9 |
10 | )}
11 |
12 | )}
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/examples/is-scrolling-cell-renderer.js:
--------------------------------------------------------------------------------
1 | function rowRenderer ({ isScrolling, ...rest }) {
2 | return isScrolling
3 | ?
4 | :
5 | }
6 |
--------------------------------------------------------------------------------
/examples/multi-grid-renderers.js:
--------------------------------------------------------------------------------
1 | function cellRendererBody ({ columnIndex, key, rowIndex, style }) {
2 | return (
3 | updateCellValue(key, event.target.value)
7 | }
8 | style={style}
9 | value={getCellValue(key)}
10 | />
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/examples/multi-grid.js:
--------------------------------------------------------------------------------
1 | function renderSpreadsheet () {
2 | return (
3 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/examples/pass-through-props.js:
--------------------------------------------------------------------------------
1 | function renderList (props) {
2 | const { sortOrder, ...rest } = props
3 |
4 | return (
5 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/examples/perf-comparison-non-virtualized.js:
--------------------------------------------------------------------------------
1 | const itemsArray = [...] // This is our data
2 |
3 | function rowRenderer ({ index }) {
4 | return (
5 |
6 | {itemsArray[index].name}
7 |
8 | )
9 | }
10 |
11 | function renderList () {
12 | return (
13 |
19 | {itemsArray.map((item, index) => rowRenderer({ index }))}
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/examples/perf-comparison-plain.js:
--------------------------------------------------------------------------------
1 | const itemsArray = [...] // This is our data
2 |
3 | function renderList () {
4 | return (
5 |
11 | {itemsArray.map((item, index) => {
12 |
13 | {item.name}
14 |
15 | })}
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/examples/perf-comparison-virtualized.js:
--------------------------------------------------------------------------------
1 | const itemsArray = [...] // This is our data
2 |
3 | function rowRenderer ({ index, key, style }) {
4 | return (
5 |
9 | {itemsArray[index].name}
10 |
11 | )
12 | }
13 |
14 | function renderList () {
15 | return (
16 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/examples/pure-component.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 |
3 | class MyComponent extends PureComponent {
4 | render () {
5 | // Your render logic here
6 | }
7 | }
--------------------------------------------------------------------------------
/examples/resizable-cells-key-points.js:
--------------------------------------------------------------------------------
1 | import Draggable from 'react-draggable';
2 | import { List } from 'react-virtualized';
3 |
4 | function rowRenderer ({ index, isScrolling, key, style }) {
5 | return (
6 |
10 | {...}
11 |
12 | updateRowSize({ delta: data.y, index })
16 | }
17 | />
18 |
19 | );
20 | }
21 |
22 | function renderList (listProps) {
23 | return (
24 | this._list = ref}
27 | rowRenderer={rowRenderer}
28 | />
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/examples/row-renderer.js:
--------------------------------------------------------------------------------
1 | function rowRenderer ({ key, index, style }) {
2 | return (
3 |
7 | {itemsArray[index].name}
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/examples/shallow-compare.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import shallowCompare from 'react-addons-shallow-compare'
3 |
4 | class MyComponent extends Component {
5 | shouldComponentUpdate(nextProps, nextState) {
6 | return shallowCompare(this, nextProps, nextState)
7 | }
8 |
9 | render () {
10 | // Your render logic here
11 | }
12 | }
--------------------------------------------------------------------------------
/lib/font-awesome/css/font-awesome.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
5 |
--------------------------------------------------------------------------------
/lib/font-awesome/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/lib/font-awesome/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/lib/font-awesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/lib/font-awesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/lib/font-awesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/lib/font-awesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/lib/font-awesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/lib/font-awesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/lib/font-awesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/lib/font-awesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "forward-js-2017",
3 | "version": "0.0.1",
4 | "private": true,
5 | "devDependencies": {
6 | "react-addons-shallow-compare": "^15.3.2",
7 | "react-scripts": "0.6.1"
8 | },
9 | "homepage": "https://bvaughn.github.io/forward-js-2017/",
10 | "dependencies": {
11 | "classnames": "^2.2.5",
12 | "codemirror": "^5.19.0",
13 | "immutable": "^3.8.1",
14 | "lodash.times": "^4.3.2",
15 | "numeral": "^1.5.3",
16 | "performance-now": "^0.2.0",
17 | "raw-loader": "^0.5.1",
18 | "react": "^15.3.2",
19 | "react-addons-shallow-compare": "^15.3.2",
20 | "react-codemirror": "^0.2.6",
21 | "react-dom": "^15.3.2",
22 | "react-draggable": "^2.2.2",
23 | "react-presents": "0.7.6",
24 | "react-radio-group": "^3.0.1",
25 | "react-router-dom": "4.0.0-beta.6",
26 | "react-sortable-hoc": "0.0.9",
27 | "react-virtualized": "^9.0.0",
28 | "react-virtualized-select": "^2.1.0",
29 | "spectacle": "^1.2.3"
30 | },
31 | "scripts": {
32 | "start": "react-scripts start",
33 | "build": "react-scripts build",
34 | "postbuild": "git checkout -B gh-pages && git add -f build && git commit -am 'Rebuild website' && git filter-branch -f --prune-empty --subdirectory-filter build && git push -f origin gh-pages && git checkout -",
35 | "test": "react-scripts test --env=jsdom",
36 | "eject": "react-scripts eject"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/public/books.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/books.jpg
--------------------------------------------------------------------------------
/public/browser-limits-cutoff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/browser-limits-cutoff.png
--------------------------------------------------------------------------------
/public/cache-all-the-things.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/cache-all-the-things.png
--------------------------------------------------------------------------------
/public/computer-guy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/computer-guy.png
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/favicon.ico
--------------------------------------------------------------------------------
/public/how-is-it-used-calendar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/how-is-it-used-calendar.gif
--------------------------------------------------------------------------------
/public/how-is-it-used-dropdown.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/how-is-it-used-dropdown.gif
--------------------------------------------------------------------------------
/public/how-is-it-used-table.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/how-is-it-used-table.gif
--------------------------------------------------------------------------------
/public/how-is-it-used-tree.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/how-is-it-used-tree.gif
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 | Forward JS
21 |
22 |
23 |
24 |
25 |
26 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/public/kindle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/kindle.jpg
--------------------------------------------------------------------------------
/public/non-uniform-heights-chat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/non-uniform-heights-chat.png
--------------------------------------------------------------------------------
/public/non-uniform-heights-drop-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/non-uniform-heights-drop-down.png
--------------------------------------------------------------------------------
/public/occlusion-culling.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/occlusion-culling.jpg
--------------------------------------------------------------------------------
/public/overscan-example.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/overscan-example.mp4
--------------------------------------------------------------------------------
/public/profile-picture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/profile-picture.jpg
--------------------------------------------------------------------------------
/public/v9-dev-mode-style-warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/public/v9-dev-mode-style-warning.png
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { DropDownNav, Presentation, Slide } from 'react-presents';
3 | import generateRandomList from './Utils/generateRandomList';
4 | import ForwardTheme from './ForwardTheme';
5 |
6 | // Load all slides in the Slides folder
7 | const slides = require.context('./Slides/', false, /\.js$/)
8 | .keys()
9 | .map((filename) => filename.replace('./', ''))
10 | .map((filename) => require(`./Slides/${filename}`).default);
11 |
12 | // Support navigating to any slides also tagged with a :title
13 | const options = slides
14 | .map((slide, index) => ({
15 | label: slide.title,
16 | value: index
17 | }))
18 | .filter((option) => option.label);
19 |
20 | // Test data for use in performance examples
21 | const list = generateRandomList();
22 |
23 | export default class App extends Component {
24 | static childContextTypes = {
25 | list: PropTypes.array.isRequired
26 | };
27 |
28 | getChildContext () {
29 | return {
30 | list
31 | };
32 | }
33 |
34 | render() {
35 | return (
36 |
37 | {slides.map((Component, index) => (
38 |
42 | )).concat(
43 |
47 | )}
48 |
49 |
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Components/AnimatedList.css:
--------------------------------------------------------------------------------
1 | .AnimatedList {
2 | position: relative;
3 | width: 150px;
4 | height: 300px;
5 | overflow: visible;
6 | margin: 8px;
7 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
8 | font-size: 12px;
9 | }
10 |
11 | .Positioned {
12 | overflow: visible;
13 | }
14 |
15 | .InnerFrame {
16 | position: absolute;
17 | top: 100px;
18 | height: 100px;
19 | left: -8px;
20 | right: -8px;
21 | border: 4px solid #222;
22 | border-radius: 8px;
23 | }
24 |
25 | .AnimatedListDimmerBottom,
26 | .AnimatedListDimmerTop {
27 | position: absolute;
28 | left: 0;
29 | right: 0;
30 | background-color: #fff;
31 | opacity: 0.35;
32 | }
33 |
34 | .AnimatedListDimmerBottom {
35 | top: 200px;
36 | height: 130px;
37 | }
38 |
39 |
40 | .AnimatedListDimmerTop {
41 | top: 0;
42 | height: 100px;
43 | }
44 |
--------------------------------------------------------------------------------
/src/Components/AnimatedList.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import times from 'lodash.times';
3 | import React from 'react';
4 | import Row from './AnimatedListRow';
5 | import './AnimatedList.css';
6 |
7 | // Assumes 10 total rows; renders 11 to allow for partial visibility of first and last row
8 | // Assumes 30px row height, 1 overscan, 2 hidden rows, 4 visible rows (10 total rows)
9 | export default function AnimatedListList ({
10 | className = '',
11 | direction = 0,
12 | iteration = 0,
13 | offset = 0,
14 | overscanCount = 2
15 | }) {
16 | const visibleRows = [];
17 | for (let index = 0; index < 11; index++) {
18 | const rowTop = index * 30 + offset;
19 | const rowBottom = rowTop + 30;
20 |
21 | // 90..210 allows enough room for 4 fully visible rows (5 with partials)
22 | if (!(rowBottom <= 100 || rowTop >= 200)) {
23 | visibleRows.push(index);
24 | }
25 | }
26 |
27 | const overscanRows = [];
28 | const firstVisibleRow = visibleRows[0];
29 | const lastVisibleRow = visibleRows[visibleRows.length - 1];
30 | switch (direction) {
31 | case -1: // Scrolling up
32 | for (let i = 1; i <= overscanCount; i++) {
33 | overscanRows.push(firstVisibleRow - i);
34 | }
35 | break;
36 | case 1: // Scrolling down
37 | for (let i = 1; i <= overscanCount; i++) {
38 | overscanRows.push(lastVisibleRow + i);
39 | }
40 | break;
41 | default: // Resting
42 | for (let i = 1; i <= overscanCount / 2; i++) {
43 | overscanRows.push(firstVisibleRow - i);
44 | overscanRows.push(lastVisibleRow + i);
45 | }
46 | break;
47 | }
48 |
49 | return (
50 |
51 |
57 | {times(11).map((index) => {
58 | const isVisibleRow = visibleRows.includes(index);
59 | const isOverscanRow = overscanRows.includes(index);
60 | const isInvisibleRow = !isVisibleRow && !isOverscanRow;
61 |
62 | let label;
63 | if (isOverscanRow) {
64 | label = 'rendered (overscan)';
65 | } else if (isInvisibleRow) {
66 | label = 'not rendered';
67 | } else {
68 | label = 'rendered';
69 | }
70 |
71 | return (
72 |
80 | )
81 | })}
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | );
90 | }
91 |
--------------------------------------------------------------------------------
/src/Components/AnimatedListRow.css:
--------------------------------------------------------------------------------
1 | .AnimatedListRow {
2 | display: inline-flex;
3 | align-items: center;
4 | padding: 0 0.5rem;
5 | background-color: #a2d4da;
6 | width: 100%;
7 | height: 26px;
8 | margin: 2px 0;
9 | transition: 0 all;
10 | }
11 |
12 | .AnimatedList--Overscan {
13 | background-color: #C0E0D0;
14 | }
15 |
16 | .AnimatedList--Invisible {
17 | background-color: #aaa;
18 | }
19 |
20 | .AnimatedList--Overscan,
21 | .AnimatedList--Invisible {
22 | transition: 500ms all;
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/src/Components/AnimatedListRow.js:
--------------------------------------------------------------------------------
1 | import cn from 'classnames';
2 | import React, { Component, PropTypes } from 'react';
3 | import './AnimatedListRow.css';
4 |
5 | export default class AnimatedListRow extends Component {
6 | static propTypes = {
7 | direction: PropTypes.number.isRequired,
8 | invisible: PropTypes.bool.isRequired,
9 | iteration: PropTypes.number.isRequired,
10 | label: PropTypes.string.isRequired,
11 | overscan: PropTypes.bool.isRequired
12 | };
13 |
14 | componentWillUpdate (nextProps, nextState) {
15 | const { direction, invisible, iteration, overscan } = this.props;
16 |
17 | if (iteration !== nextProps.iteration) {
18 | this._transitionClass = null
19 | } else if (direction !== 0) {
20 | if (invisible && !nextProps.invisible) {
21 | this._transitionClass = 'TransitionToVisible'
22 | } else if (!overscan && nextProps.overscan) {
23 | this._transitionClass = 'TransitionToOverscan'
24 | }
25 | }
26 | }
27 |
28 | render () {
29 | const { invisible, label, overscan } = this.props;
30 |
31 | const className = cn('AnimatedListRow', this._transitionClass, {
32 | 'AnimatedList--Invisible': invisible,
33 | 'AnimatedList--Overscan': overscan
34 | });
35 |
36 | return (
37 |
38 | {label}
39 |
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Components/BuildingBlocks.css:
--------------------------------------------------------------------------------
1 | .svgOuterBox {
2 | fill: transparent;
3 | stroke: #222;
4 | stroke-width: 4px;
5 | rx: 8px;
6 | ry: 8px;
7 | }
8 |
9 | .svgCollectionBoxNotRendered,
10 | .svgGridBoxNotRendered,
11 | .svgTableColumnNotRendered,
12 | .svgListRowNotRendered {
13 | fill: #aaa;
14 | stroke: transparent;
15 | }
16 |
17 | .svgCollectionBox,
18 | .svgGridBox,
19 | .svgListRow,
20 | .svgTableColumn {
21 | fill: #C0E0D0;
22 | stroke: transparent;
23 | }
24 |
25 | .svgTableHeader {
26 | fill: transparent;
27 | stroke: #222;
28 | }
29 |
30 | .svgNotRenderedDimmer {
31 | fill: #fff;
32 | opacity: 0.65;
33 | }
34 |
--------------------------------------------------------------------------------
/src/Components/BuildingBlocks.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import React from 'react';
3 | import BuildingBlocksSvgWrapper from './BuildingBlocksSvgWrapper';
4 | import LabeledCircle from './LabeledCircle';
5 | import LabeledRect from './LabeledRect';
6 | import './BuildingBlocks.css';
7 |
8 | const LIST_ROW_OFFSETS = [10, 60, 110, 160, 210, 260, 310, 360]
9 |
10 | export function ListSvg (props) {
11 | return (
12 |
19 | {LIST_ROW_OFFSETS.map((yOffset, index) => (
20 | = 6
29 | })}
30 | hidden={index >= 6}
31 | >
32 | {index < 6
33 | ? 'Row'
34 | : 'Not Rendered'
35 | }
36 |
37 | ))}
38 |
39 |
40 | );
41 | }
42 |
43 | const TABLE_COLUMN_TUPLES = [[10, 128], [143, 127]]
44 |
45 | export function TableSvg (props) {
46 | return (
47 |
54 | {LIST_ROW_OFFSETS.map((yOffset, index) => (
55 | TABLE_COLUMN_TUPLES.map(([xOffset, width]) => (
56 | = 6
66 | })}
67 | hidden={index >= 6}
68 | >
69 | {index === 0 && 'Header'}
70 | {index > 0 && index < 6 && 'Column'}
71 | {index >= 6 && 'Not Rendered'}
72 |
73 | ))
74 | ))}
75 |
76 |
77 | );
78 | }
79 |
80 |
81 | const GRID_BOX_OFFSETS = [10, 105, 200]
82 | const HIDDEN_GRID_BOX_OFFSETS = [[295, 10], [295, 105], [295, 200], [10, 295], [105, 295], [200, 295], [295, 295]]
83 |
84 | export function GridSvg (props) {
85 | return (
86 |
93 | {GRID_BOX_OFFSETS.map((xOffset, xIndex) => (
94 | GRID_BOX_OFFSETS.map((yOffset, yIndex) => (
95 |
103 | Cell
104 |
105 | ))
106 | ))}
107 | {HIDDEN_GRID_BOX_OFFSETS.map(([xOffset, yOffset], index) => (
108 |
117 | Not Rendered
118 |
119 | ))}
120 |
121 |
122 |
123 | );
124 | }
125 |
126 | export function CollectionSvg (props) {
127 | return (
128 |
135 |
141 | Rendered
142 |
143 |
150 | Rendered
151 |
152 |
159 | Rendered
160 |
161 |
168 | Rendered
169 |
170 |
177 | Not Rendered
178 |
179 |
187 | Not Rendered
188 |
189 |
190 |
191 |
192 | );
193 | }
194 |
--------------------------------------------------------------------------------
/src/Components/BuildingBlocksSvgWrapper.css:
--------------------------------------------------------------------------------
1 | .BuildingBlocksSvgWrapper {
2 | max-width: 100%;
3 | }
--------------------------------------------------------------------------------
/src/Components/BuildingBlocksSvgWrapper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SvgWrapper from './SvgWrapper';
3 | import './BuildingBlocksSvgWrapper.css';
4 |
5 | export default function BuildingBlocksSvgWrapper ({ children, ...rest }) {
6 | return (
7 |
15 |
16 | {children}
17 |
18 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/Components/CrossHatchRect.css:
--------------------------------------------------------------------------------
1 | .CrossHatchRectPattern {
2 | stroke: #fff;
3 | stroke-width:10;
4 | }
5 |
6 | .CrossHatchRectHidden,
7 | .CrossHatchRectVisible {
8 | transition: all 2.5s ease;
9 | }
10 | .CrossHatchRectHidden {
11 | opacity: 0;
12 | }
13 | .CrossHatchRectVisible {
14 | opacity: 0.25;
15 | }
--------------------------------------------------------------------------------
/src/Components/CrossHatchRect.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import React from 'react';
3 | import './CrossHatchRect.css';
4 |
5 | export default function CrossHatchRect ({ className, x, y, width, height, visible }) {
6 | className = classnames('CrossHatchRectHidden', className, {
7 | CrossHatchRectVisible: visible
8 | })
9 |
10 | return (
11 |
12 |
13 |
20 |
27 |
28 |
29 |
30 |
38 |
39 | );
40 | }
--------------------------------------------------------------------------------
/src/Components/DeferMeasurements.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import React from 'react';
3 | import CrossHatchRect from '../Components/CrossHatchRect';
4 | import LabeledRect from '../Components/LabeledRect';
5 | import SvgWrapper from '../Components/SvgWrapper';
6 |
7 | const BOXES = [
8 | [2, 40],
9 | [42, 20],
10 | [62, 35],
11 | [97, 30],
12 | [127, 20],
13 | [147, 40],
14 | [187, 40],
15 | [227, 40]
16 | ]
17 |
18 | export default function DeferMeasurements () {
19 | return (
20 |
26 |
27 |
28 | {BOXES.map(([y, height], index) => (
29 | 5,
38 | HowWorksRowRendered: index >= 2 && index <= 5
39 | })}
40 | >
41 | {index < 2 && 'Not rendered'}
42 | {index >= 2 && index <= 5 && 'Rendered'}
43 | {index > 5 && 'Estimated'}
44 |
45 | ))}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | <ul>
66 |
67 |
68 |
69 |
70 | );
71 | }
--------------------------------------------------------------------------------
/src/Components/DemoCollection.css:
--------------------------------------------------------------------------------
1 | .Collection {
2 | border: 1px solid #ddd;
3 | }
4 |
5 | .CollectionCell {
6 | display: flex;
7 | flex-direction: row;
8 | align-items: center;
9 | justify-content: center;
10 | color: #fff;
11 | }
12 |
13 | .CollectionCellCircle {
14 | border-radius: 100%;
15 | }
16 |
--------------------------------------------------------------------------------
/src/Components/DemoCollection.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import React, { Component, PropTypes } from 'react';
3 | import { AutoSizer, Collection } from 'react-virtualized';
4 | import './DemoCollection.css';
5 |
6 | export default class MySlide extends Component {
7 | static propTypes = {
8 | list: PropTypes.array
9 | };
10 |
11 | constructor (props, context) {
12 | super(props, context);
13 |
14 | this._cellRenderer = this._cellRenderer.bind(this);
15 | this._cellSizeAndPositionGetter = this._cellSizeAndPositionGetter.bind(this);
16 | }
17 |
18 | componentWillUpdate (nextProps, nextState) {
19 | // HACK Weird React bug where this.state === null
20 | // Seems to happen when component is about to unmount.
21 | // Going to just hack around it for now...
22 | if (!this.state) {
23 | return;
24 | }
25 |
26 | const { cellValues, focusedColumnIndex, focusedRowIndex } = this.state;
27 |
28 | if (
29 | focusedColumnIndex !== nextState.focusedColumnIndex ||
30 | focusedRowIndex !== nextState.focusedRowIndex
31 | ) {
32 | this._leftGrid.forceUpdate();
33 | this._mainGrid.forceUpdate();
34 | this._topGrid.forceUpdate();
35 | } else if (cellValues !== nextState.cellValues) {
36 | this._mainGrid.forceUpdate();
37 | }
38 | }
39 |
40 | render () {
41 | const { list } = this.props;
42 |
43 | return (
44 |
45 | {({ width }) => (
46 |
54 | )}
55 |
56 | )
57 | }
58 |
59 | _cellRenderer ({ index, key, style }) {
60 | const { list } = this.props;
61 |
62 | const datum = list[index];
63 |
64 | // Customize style
65 | style.backgroundColor = datum.color;
66 |
67 | return (
68 |
75 | {index}
76 |
77 | )
78 | }
79 |
80 | _cellSizeAndPositionGetter ({ index }) {
81 | const { list } = this.props;
82 |
83 | const datum = list[index];
84 |
85 | return {
86 | height: datum.size,
87 | width: datum.size,
88 | x: datum.left,
89 | y: datum.top
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/Components/DragAndDropList.css:
--------------------------------------------------------------------------------
1 | .SortableListRow {
2 | cursor: pointer;
3 | font-size: 12px;
4 | user-select: none;
5 | border-bottom: 1px solid #e9e9e9;
6 | }
7 |
8 | .SortableListRow:hover {
9 | background-color: #e9e9e9;
10 | }
11 |
12 | .SortableListRowActive {
13 | font-size: 13px;
14 | background-color: #C0E0D0;
15 | overflow: hidden;
16 | }
17 |
--------------------------------------------------------------------------------
/src/Components/DragAndDropList.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { arrayMove, SortableContainer, SortableElement } from 'react-sortable-hoc';
3 | import { List } from 'react-virtualized';
4 | import './DragAndDropList.css';
5 |
6 | // Connect react-virtualized and react-sortable-hoc
7 | const SortableList = SortableContainer(List, {
8 | withRef: true
9 | });
10 | const SortableListRow = SortableElement(({ children }) => children);
11 |
12 | export default class DragAndDropList extends Component {
13 | static propTypes = {
14 | list: PropTypes.array
15 | };
16 |
17 | constructor (props, context) {
18 | super(props, context);
19 |
20 | this.state = {
21 | listIndices: props.list.map((item, index) => index)
22 | };
23 |
24 | this._onSortEnd = this._onSortEnd.bind(this)
25 | this._rowRenderer = this._rowRenderer.bind(this)
26 | }
27 |
28 | render () {
29 | const { list } = this.props;
30 |
31 | return (
32 |
43 | );
44 | }
45 |
46 | _onSortEnd ({ newIndex, oldIndex }) {
47 | const { list } = this.props;
48 |
49 | if (newIndex === oldIndex) {
50 | return;
51 | }
52 |
53 | arrayMove(list, oldIndex, newIndex);
54 |
55 | this.forceUpdate(); // Re-render
56 | }
57 |
58 | _rowRenderer ({ index, key, style }) {
59 | const { list } = this.props;
60 |
61 | return (
62 |
66 |
67 |
68 | {list[index].name}
69 |
70 |
71 |
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Components/ExampleList.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { ListRow, ListRowActive, RowName, RowNumber, RowStack, RowSummary, VirtualList } from './StyledListElements';
3 | import '../shared-list-styles.css';
4 |
5 | export default class ExampleList extends Component {
6 | static contextTypes = {
7 | list: PropTypes.array
8 | };
9 |
10 | static propTypes = {
11 | scrollToAlignment: PropTypes.string,
12 | scrollToIndex: PropTypes.number
13 | };
14 |
15 | render () {
16 | const { list } = this.context;
17 | const { scrollToAlignment, scrollToIndex } = this.props;
18 |
19 | return (
20 | {
26 | const RowComponent = index === scrollToIndex
27 | ? ListRowActive
28 | : ListRow
29 |
30 | return (
31 |
35 |
40 | {list[index].name.substr(0, 1)}
41 |
42 |
43 | {list[index].name}
44 | This is row {index}
45 |
46 |
47 | )
48 | }}
49 | scrollToAlignment={scrollToAlignment}
50 | scrollToIndex={scrollToIndex}
51 | width={240}
52 | />
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Components/HowDoesWindowingWork.css:
--------------------------------------------------------------------------------
1 | .HowWorksGroup {
2 | transform: skewX(0deg);
3 | }
4 |
5 | .HowWorksGroupSkewed {
6 | transition: all 1s ease;
7 | transform: skewX(15deg);
8 | }
9 |
10 | .HowWorksOuterGroup {
11 | transition: transform 1s ease;
12 | transform: translate(-75px, -68px);
13 | }
14 |
15 | .HowWorksOuterRect {
16 | rx: 8px;
17 | ry: 8px;
18 | stroke-width: 4px;
19 | fill: transparent;
20 | stroke: #222;
21 | transition: transform 1s ease;
22 | }
23 |
24 | .HowWorksOuterRectShifted {
25 | transform: translate(0, -30px);
26 | }
27 |
28 | .HowWorksHidden {
29 | opacity: 0;
30 | transition: all 1s ease;
31 | }
32 |
33 | .HowWorksVisible {
34 | opacity: 1;
35 | transition: all 1s ease;
36 | }
37 |
38 | .HowWorksInnerLineAnimated {
39 | animation: scrolling 1s linear;
40 | animation-iteration-count: infinite;
41 | }
42 |
43 | @keyframes scrolling {
44 | from {
45 | transform: translateY(0);
46 | }
47 | to {
48 | transform: translateY(30px);
49 | }
50 | }
51 |
52 | .HowWorksInnerRect,
53 | .HowWorksInnerLine {
54 | fill: transparent;
55 | stroke-width: 1px;
56 | stroke-dasharray: 2, 4;
57 | stroke: #222;
58 | }
59 |
60 | .HowWorksRowGroup {
61 | font-size: 10px;
62 | }
63 |
64 | .HowWorksRowRendered,
65 | .HowWorksRowNotRendered,
66 | .HowWorksRowEstimated {
67 | stroke-width: 2px;
68 | stroke: #fff;
69 | }
70 |
71 | .HowWorksRowEstimated {
72 | fill: #ddd;
73 | }
74 |
75 | .HowWorksRowRendered {
76 | fill: #a2d4da;
77 | }
78 |
79 | .HowWorksRowNotRendered {
80 | fill: #aaa;
81 | }
82 |
83 | .HowWorksViewportLine {
84 | stroke-width: 1px;
85 | stroke-dasharray: 2, 4;
86 | stroke: #222;
87 | transition-delay: 1s;
88 | }
89 |
90 | .HowWorksScrollTrack {
91 | fill: rgba(0,0,0,.1);
92 | }
93 |
94 | .HowWorksScrollThumb {
95 | fill: rgba(0,0,0,.5);
96 | }
97 |
98 | .HowWorksScrollingLoop {
99 | animation: scrollingLoop 2.5s linear;
100 | animation-iteration-count: infinite;
101 | }
102 |
103 | @keyframes scrollingLoop {
104 | 0% { transform: translateY(0px); }
105 | 50% { transform: translateY(-40px); }
106 | 100% { transform: translateY(0px); }
107 | }
108 |
109 | .HowWorksScrollingThumbLoop {
110 | animation: scrollingThumbLoop 2.5s linear;
111 | animation-iteration-count: infinite;
112 | }
113 |
114 | @keyframes scrollingThumbLoop {
115 | 0% { transform: translateY(0); }
116 | 50% { transform: translateY(15px); }
117 | 100% { transform: translateY(0); }
118 | }
119 |
120 | .HowWorksTopConditionalScrollingFill {
121 | animation: topConditionalScrollingFill 2.5s linear;
122 | animation-iteration-count: infinite;
123 | }
124 |
125 | @keyframes topConditionalScrollingFill {
126 | 0% { fill: #a2d4da; }
127 | 14% { fill: #a2d4da; }
128 | 15% { fill: #aaa; }
129 | 84% { fill: #aaa; }
130 | 85% { fill: #a2d4da; }
131 | }
132 |
133 | .HowWorksBottomConditionalScrollingFill {
134 | animation: bottomConditionalScrollingFill 2.5s linear;
135 | animation-iteration-count: infinite;
136 | }
137 |
138 | @keyframes bottomConditionalScrollingFill {
139 | 0% { fill: #aaa; }
140 | 14% { fill: #aaa; }
141 | 15% { fill: #a2d4da; }
142 | 84% { fill: #a2d4da; }
143 | 85% { fill: #aaa; }
144 | }
145 |
146 |
--------------------------------------------------------------------------------
/src/Components/HowDoesWindowingWork.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import times from 'lodash.times';
3 | import React from 'react';
4 | import CrossHatchRect from './CrossHatchRect';
5 | import LabeledRect from './LabeledRect';
6 | import SvgWrapper from './SvgWrapper';
7 | import './HowDoesWindowingWork.css';
8 |
9 | export default function HowDoesWindowingWork ({ index }) {
10 | const groupClassName = classnames('HowWorksGroup', {
11 | HowWorksGroupSkewed: index > 2
12 | })
13 | const innerGroupClassName = classnames('HowWorksHidden', {
14 | HowWorksVisible: index > 1
15 | })
16 | const innerLinesClassName = classnames('HowWorksInnerLine', {
17 | HowWorksHidden: index > 3,
18 | HowWorksInnerLineAnimated: index > 2
19 | })
20 | const outerGroupClassName = classnames('HowWorksOuterGroup', 'HowWorksHidden', {
21 | HowWorksVisible: index > 0,
22 | HowWorksOuterRectShifted: index > 2
23 | })
24 | const rowGroupClassName = classnames('HowWorksRowGroup', 'HowWorksHidden', {
25 | HowWorksVisible: index > 3,
26 | HowWorksScrollingLoop: index >= 5
27 | })
28 | const viewportLineClassName = classnames('HowWorksViewportLine', 'HowWorksHidden', {
29 | HowWorksVisible: index > 2
30 | })
31 | const scrollbarClassName = classnames('HowWorksHidden', {
32 | HowWorksVisible: index > 3
33 | })
34 | const thumbClassName = classnames('HowWorksScrollThumb', {
35 | HowWorksScrollingThumbLoop: index >= 5
36 | })
37 | const dimmerVisible = index > 3 && index < 5
38 |
39 | return (
40 |
46 |
47 |
48 | {times(10).map((i) => (
49 |
50 | ))}
51 | = 4
54 | })} />
55 |
56 |
57 | {times(10).map((i) => (
58 | = 5,
66 | HowWorksBottomConditionalScrollingFill: i === 6 && index >= 5,
67 | HowWorksRowNotRendered: i < 2 || i > 5,
68 | HowWorksRowRendered: i >= 2 && i <= 5
69 | })}
70 | textClassName={index >= 5 ? 'HowWorksHidden' : 'HowWorksVisible'}
71 | >
72 | {i >= 2 && i <= 5
73 | ? 'Rendered'
74 | : 'Not Rendered'
75 | }
76 |
77 | ))}
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | <ul>
100 |
101 |
102 |
103 |
104 | );
105 | }
106 |
--------------------------------------------------------------------------------
/src/Components/LabeledCircle.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import React from 'react';
3 | import './LabeledSvg.css';
4 |
5 | export default function LabeledCircle ({
6 | className,
7 | children,
8 | cx,
9 | cy,
10 | hidden,
11 | r,
12 | textClassName
13 | }) {
14 | return (
15 |
16 |
22 |
31 | {children}
32 |
33 |
34 | );
35 | }
--------------------------------------------------------------------------------
/src/Components/LabeledRect.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import React from 'react';
3 | import './LabeledSvg.css';
4 |
5 | export default function LabeledRect ({
6 | children,
7 | className,
8 | height,
9 | hidden,
10 | mono,
11 | textClassName,
12 | x,
13 | width,
14 | y
15 | }) {
16 | textClassName = classnames('LabeledSvgText', textClassName, {
17 | LabeledSvgTextMono: mono,
18 | NotRenderedLabeledSvgText: hidden
19 | })
20 | return (
21 |
22 |
29 |
36 | {children}
37 |
38 |
39 | );
40 | }
--------------------------------------------------------------------------------
/src/Components/LabeledSvg.css:
--------------------------------------------------------------------------------
1 | .LabeledSvgText {
2 | font-size: 14px;
3 | }
4 |
5 | .NotRenderedLabeledSvgText {
6 | font-size: 12px;
7 | }
8 |
9 | .LabeledSvgTextMono {
10 | font-family: monospace;
11 | }
--------------------------------------------------------------------------------
/src/Components/MarioSvg.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SvgWrapper from './SvgWrapper';
3 |
4 | export default function MarioSvg () {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | );
155 | }
156 |
--------------------------------------------------------------------------------
/src/Components/Note.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const DefaultNote = styled.div`
5 | margin: 0.25rem 0 0.75rem;
6 | background: #4A90E2;
7 | color: #fff;
8 | display: inline-block;
9 | padding: 0.5rem;
10 | border-radius: 0.5rem;
11 |
12 | .fa {
13 | margin-right: 0.25rem;
14 | color: #fff;
15 | }
16 |
17 | a {
18 | color: inherit;
19 | }
20 | `
21 |
22 | const WarningNote = styled(DefaultNote)`
23 | background: #f2905c;
24 | `
25 |
26 | export default function Note ({
27 | children,
28 | type = 'default'
29 | }) {
30 | const Component = type === 'warning'
31 | ? WarningNote
32 | : DefaultNote
33 |
34 | return (
35 |
36 |
37 | {type === 'default' && (
38 |
39 | )}
40 | {type === 'warning' && (
41 |
42 | )}
43 |
44 | {children}
45 |
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/src/Components/ResizableRowsList.css:
--------------------------------------------------------------------------------
1 | .ResizableListRow {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | padding: 0 0.5rem;
6 | border-bottom: 1px solid #e9e9e9;
7 | }
8 |
9 | .DragHandle {
10 | position: absolute;
11 | bottom: 0;
12 | left: 0;
13 | right: 0;
14 | height: 16px;
15 | z-index: 2;
16 | cursor: row-resize;
17 | color: rgba(0, 0, 0, 0.2);
18 | }
19 | .DragHandle:hover {
20 | background-color: rgba(0, 0, 0, 0.1);
21 | }
22 |
23 | .DragHandleActive,
24 | .DragHandleActive:hover {
25 | color: rgba(0, 0, 0, 0.6);
26 | z-index: 3;
27 | }
28 |
29 | .DragHandleIcon {
30 | flex: 0 0 12px;
31 | display: flex;
32 | flex-direction: column;
33 | justify-content: center;
34 | align-items: center;
35 | }
36 |
--------------------------------------------------------------------------------
/src/Components/ResizableRowsList.js:
--------------------------------------------------------------------------------
1 | import Immutable from 'immutable';
2 | import React, { Component, PropTypes } from 'react';
3 | import Draggable from 'react-draggable';
4 | import { List } from 'react-virtualized';
5 | import SvgWrapper from './SvgWrapper';
6 | import './ResizableRowsList.css';
7 |
8 | export default class ResizableRowsList extends Component {
9 | static propTypes = {
10 | list: PropTypes.array
11 | };
12 |
13 | constructor (props, context) {
14 | super(props, context);
15 |
16 | this.state = {
17 | rowHeights: Immutable.Map()
18 | };
19 |
20 | this._rowRenderer = this._rowRenderer.bind(this)
21 | this._rowHeight = this._rowHeight.bind(this)
22 | }
23 |
24 | componentWillUpdate (nextProps, nextState) {
25 | const { rowHeights } = this.state;
26 |
27 | if (rowHeights !== nextState.rowHeights) {
28 | this._list.recomputeRowHeights(0);
29 | }
30 | }
31 |
32 | render () {
33 | const { list } = this.props;
34 |
35 | return (
36 | this._list = ref}
41 | rowCount={list.length}
42 | rowHeight={this._rowHeight}
43 | rowRenderer={this._rowRenderer}
44 | width={240}
45 | />
46 | );
47 | }
48 |
49 | _resizeRow ({ deltaY, index }) {
50 | const { rowHeights } = this.state;
51 |
52 | let rowHeight = this._rowHeight({ index });
53 | rowHeight = Math.max(40, Math.min(200, rowHeight + deltaY));
54 |
55 | this.setState({
56 | rowHeights: rowHeights.set(index, rowHeight)
57 | });
58 | }
59 |
60 | _rowHeight ({ index }) {
61 | const { rowHeights } = this.state;
62 |
63 | return rowHeights.get(index, 50);
64 | }
65 |
66 | _rowRenderer ({ index, isScrolling, key, style }) {
67 | const { list } = this.props;
68 |
69 | return (
70 |
75 | {list[index].name}
76 |
77 |
this._resizeRow({
82 | deltaY: data.y,
83 | index: index
84 | })}
85 | position={{
86 | x: 0,
87 | y: 0
88 | }}
89 | zIndex={999}
90 | >
91 |
92 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | );
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/Components/ScaledList.css:
--------------------------------------------------------------------------------
1 | .BottomGroup,
2 | .MiddleGroup,
3 | .TopGroup {
4 | transition: all 250ms linear;
5 | }
6 |
7 | .MiddleGroup {
8 | position: relative;
9 | z-index: 2;
10 | }
11 |
12 | .BottomGroupScaled {
13 | transform: scaleY(0.5) translateY(-137px);
14 | }
15 |
16 | .MiddleGroupScaled {
17 | transform: translateY(-35px);
18 | }
19 |
20 | .TopGroupScaled {
21 | transform: scaleY(0.5) translateY(-30px);
22 | }
23 |
24 | .OuterScrollContainer {
25 | position: absolute;
26 | top: 5px;
27 | width: 100px;
28 | height: 100px;
29 | border: 4px solid #222;
30 | border-radius: 8px;
31 | }
32 |
33 | .UnscaledListItem,
34 | .UnscaledListItemActive {
35 | display: block;
36 | width: 88px;
37 | height: 26px;
38 | margin: 2px 6px;
39 | }
40 |
41 | .UnscaledListItem {
42 | background-color: #aaa;
43 | }
44 |
45 | .UnscaledListItemActive {
46 | background-color: #C0E0D0;
47 | }
48 |
--------------------------------------------------------------------------------
/src/Components/ScaledList.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import times from 'lodash.times';
3 | import React from 'react';
4 | import './ScaledList.css';
5 |
6 | export default ({ scaled = false }) => (
7 |
8 |
11 | {times(3).map((index) => (
12 |
13 | ))}
14 |
15 |
16 |
19 | {times(4).map((index) => (
20 |
21 | ))}
22 |
23 |
24 |
25 |
26 |
29 | {times(5).map((index) => (
30 |
31 | ))}
32 |
33 |
34 | );
35 |
--------------------------------------------------------------------------------
/src/Components/Scaler.js:
--------------------------------------------------------------------------------
1 | import { Component, PropTypes } from 'react';
2 | import { findDOMNode } from 'react-dom';
3 | import './Slide.css';
4 |
5 | const WIDTH = 1024;
6 | const HEIGHT = 800;
7 |
8 | export default class Scaler extends Component {
9 | static propTypes = {
10 | children: PropTypes.func.isRequired
11 | };
12 |
13 | constructor (props, context) {
14 | super(props, context);
15 |
16 | this._onResize = this._onResize.bind(this);
17 | }
18 |
19 | componentDidMount () {
20 | window.addEventListener('resize', this._onResize);
21 |
22 | this._scaleToFit();
23 | }
24 |
25 | componentWillUnmount () {
26 | window.removeEventListener('resize', this._onResize);
27 | }
28 |
29 | render () {
30 | const { children } = this.props;
31 |
32 | return children ({
33 | style: this.style
34 | });
35 | }
36 |
37 | // Inspired by https://github.com/gnab/remark/blob/develop/src/remark/scaler.js
38 | _scaleToFit () {
39 | const node = findDOMNode(document.body);
40 | const { clientHeight, clientWidth } = node;
41 |
42 | let scale;
43 | // let scaledWidth;
44 | let scaledHeight;
45 | let left;
46 | let top;
47 |
48 | if (WIDTH / clientWidth > HEIGHT / clientHeight) {
49 | scale = clientWidth / WIDTH;
50 | } else {
51 | scale = clientHeight / HEIGHT;
52 | }
53 |
54 | // scaledWidth = Math.max(0, clientWidth * scale);
55 | scaledHeight = Math.max(0, clientHeight * scale);
56 |
57 | // left = (clientWidth - scaledWidth);
58 | // top = (clientHeight - scaledHeight);
59 | scale = `scale(${scale})`
60 |
61 | this.style = {
62 | MozTransform: scale,
63 | WebkitTransform: scale,
64 | transform: scale
65 | // margin: `-${top}px -${left}px`
66 | };
67 |
68 | this.forceUpdate();
69 | }
70 |
71 | _onResize (event) {
72 | this._scaleToFit();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Components/ScuChart.css:
--------------------------------------------------------------------------------
1 | .scuFalse {
2 | fill: #aaa;
3 | }
4 |
5 | .scuTrue {
6 | fill: #C0E0D0;
7 | }
8 |
9 | .scuAnimatedLine,
10 | .scuFrozenLine {
11 | stroke-width: 1px;
12 | stroke-dasharray: 2, 4;
13 | stroke: #222;
14 | }
15 | .scuAnimatedLine {
16 | animation: dash 500ms linear;
17 | animation-iteration-count: infinite;
18 | }
19 |
20 | @keyframes dash {
21 | from {
22 | stroke-dashoffset: 6;
23 | }
24 | to {
25 | stroke-dashoffset: 0;
26 | }
27 | }
28 |
29 | .scuArrow {
30 | fill: #222;
31 | }
32 |
33 | .scuText {
34 | font-family: monospace;
35 | font-size: 12px;
36 | }
--------------------------------------------------------------------------------
/src/Components/ScuChart.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LabeledCircle from './LabeledCircle';
3 | import SvgWrapper from './SvgWrapper';
4 | import './ScuChart.css';
5 |
6 | function Circle ({ label = 'div', scu = false, ...rest }) {
7 | const className = scu
8 | ? 'scuTrue'
9 | : 'scuFalse';
10 |
11 | return (
12 |
18 | {label}
19 |
20 | );
21 | }
22 |
23 | // SVG coordinates
24 | const y0 = 25;
25 | const y1 = 75;
26 | const y3 = 150;
27 | const xRoot = 150;
28 | const xL1a = 75;
29 | const xL1b = 225;
30 | const xL2a = 25;
31 | const xL2b = 125;
32 | const xL2c = 175;
33 | const xL2d = 275;
34 |
35 | export default function Chart ({ step }) {
36 | const allLineClassName = step === 2
37 | ? 'scuAnimatedLine'
38 | : 'scuFrozenLine'
39 | const scuLineClassName = step >= 2
40 | ? 'scuAnimatedLine'
41 | : 'scuFrozenLine'
42 |
43 | const allMarkerClassName = step === 2
44 | ? 'url(#arrow)'
45 | : undefined
46 | const scuMarkerClassName = step >= 2
47 | ? 'url(#arrow)'
48 | : undefined
49 |
50 | return (
51 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | 0} />
71 | 1 && step < 3} />
72 | 1 && step < 3} />
73 | 1 && step < 3} />
74 | 1} />
75 | 1} />
76 | 1 && step < 3} />
77 |
78 | );
79 | }
--------------------------------------------------------------------------------
/src/Components/Spreadsheet.css:
--------------------------------------------------------------------------------
1 | .GridContainer {
2 | height: 300px;
3 | position: relative;
4 | border: 1px solid #dadada;
5 | overflow: hidden;
6 | }
7 |
8 | .TopLeftCell {
9 | height: 40px;
10 | width: 50px;
11 | background-color: #f3f3f3;
12 | border-bottom: 4px solid #bcbcbc;
13 | border-right: 4px solid #bcbcbc;
14 | }
15 |
16 | .MainGrid {
17 | position: absolute !important;
18 | left: 50px;
19 | top: 40px;
20 | }
21 |
22 | .MainGridCell {
23 | display: flex;
24 | flex-direction: row;
25 | align-items: center;
26 | justify-content: flex-start;
27 | padding: 0.25rem;
28 | outline: 0;
29 | border: none;
30 | border-right: 1px solid #dadada;
31 | border-bottom: 1px solid #dadada;
32 | background-color: #fff;
33 | font-size: 1rem;
34 | }
35 |
36 | .MainGridCellFocused {
37 | box-shadow: 0 0 0 2px #4285FA inset;
38 | }
39 |
40 | .LeftGrid {
41 | position: absolute !important;
42 | left: 0;
43 | top: 40px;
44 | overflow: hidden !important;
45 | }
46 |
47 | .TopGrid {
48 | position: absolute !important;
49 | left: 50px;
50 | top: 0;
51 | height: 40px;
52 | overflow: hidden !important;
53 | }
54 |
55 | .FixedGridCell {
56 | display: flex;
57 | flex-direction: row;
58 | align-items: center;
59 | justify-content: center;
60 | background-color: #f3f3f3;
61 | border-right: 1px solid #ccc;
62 | border-bottom: 1px solid #ccc;
63 | }
64 |
65 | .FixedGridCellFocused {
66 | background-color: #dddddd;
67 | }
68 |
--------------------------------------------------------------------------------
/src/Components/Spreadsheet.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import React, { Component } from 'react';
3 | import { AutoSizer, MultiGrid } from 'react-virtualized';
4 | import './Spreadsheet.css';
5 |
6 | const LETTERS = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ';
7 |
8 | export default class MySlide extends Component {
9 | constructor (props, context) {
10 | super(props, context);
11 |
12 | this.state = {
13 | cellValues: {},
14 | focusedColumnIndex: null,
15 | focusedRowIndex: null
16 | };
17 |
18 | this._cellRenderer = this._cellRenderer.bind(this);
19 | this._columnWidth = this._columnWidth.bind(this);
20 | this._setRef = this._setRef.bind(this);
21 | }
22 |
23 | componentWillUpdate (nextProps, nextState) {
24 | const { cellValues, focusedColumnIndex, focusedRowIndex } = this.state;
25 |
26 | if (
27 | focusedColumnIndex !== nextState.focusedColumnIndex ||
28 | focusedRowIndex !== nextState.focusedRowIndex
29 | ) {
30 | this._multiGrid.forceUpdate();
31 | } else if (cellValues !== nextState.cellValues) {
32 | this._multiGrid.forceUpdate();
33 | }
34 | }
35 |
36 | render () {
37 | return (
38 |
39 | {({ width }) => (
40 |
66 | )}
67 |
68 | );
69 | }
70 |
71 | _cellRenderer ({ columnIndex, key, rowIndex, style }) {
72 | if (columnIndex === 0 && rowIndex === 0) {
73 | return
74 | } else if (columnIndex === 0) {
75 | return this._cellRendererLeft({ columnIndex, key, rowIndex, style })
76 | } else if (rowIndex === 0) {
77 | return this._cellRendererTop({ columnIndex, key, rowIndex, style })
78 | } else {
79 | return this._cellRendererMain({ columnIndex, key, rowIndex, style })
80 | }
81 | }
82 |
83 | _cellRendererLeft ({ columnIndex, key, rowIndex, style }) {
84 | const { focusedRowIndex } = this.state;
85 |
86 | return (
87 |
94 | {rowIndex}
95 |
96 | );
97 | }
98 |
99 | _cellRendererMain ({ columnIndex, key, rowIndex, style }) {
100 | const { cellValues, focusedColumnIndex, focusedRowIndex } = this.state;
101 |
102 | const value = cellValues[key] || '';
103 |
104 | const isFocused = (
105 | columnIndex === focusedColumnIndex &&
106 | rowIndex === focusedRowIndex
107 | );
108 |
109 | return (
110 | {
116 | this.setState({
117 | cellValues: {
118 | ...cellValues,
119 | [key]: event.target.value
120 | }
121 | })
122 | }}
123 | onFocus={() => this.setState({
124 | focusedColumnIndex: columnIndex,
125 | focusedRowIndex: rowIndex
126 | })}
127 | placeholder={`cell ${rowIndex}, ${columnIndex}`}
128 | style={style}
129 | value={value}
130 | />
131 | );
132 | }
133 |
134 | _cellRendererTop ({ columnIndex, key, rowIndex, style }) {
135 | const { focusedColumnIndex } = this.state;
136 |
137 | return (
138 |
145 | {LETTERS[columnIndex]}
146 |
147 | );
148 | }
149 |
150 | _columnWidth ({ index }) {
151 | return index === 0 ? 40 : 100;
152 | }
153 |
154 | _setRef (ref) {
155 | this._multiGrid = ref;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/Components/StyledListElements.js:
--------------------------------------------------------------------------------
1 | import { List as ReactVirtualizedList } from 'react-virtualized';
2 | import styled from 'styled-components';
3 |
4 | export const VirtualList = styled(ReactVirtualizedList)`
5 | width: 15rem;
6 | height: 15rem;
7 | overflow: auto;
8 | font-size: 12px;
9 | border: 1px solid #CFD8DC;
10 | margin: 1rem 0;
11 | `;
12 |
13 | export const List = styled.div`
14 | width: 15rem;
15 | height: 15rem;
16 | overflow: auto;
17 | font-size: 12px;
18 | border: 1px solid #CFD8DC;
19 | margin: 1rem 0;
20 | `;
21 |
22 | export const ListWithBorderRadius = styled(List)`
23 | border-radius: 1rem;
24 | border-width: 4px;
25 | overflow: auto;
26 | `;
27 |
28 | export const ListRow = styled.div`
29 | padding: 0 0.5rem;
30 | width: 100%;
31 | height: 40px;
32 | line-height: 40px;
33 | transition: 0 all;
34 | white-space: pre;
35 | overflow: hidden;
36 | text-overflow: ellipsis;
37 | display: flex;
38 | flex-direction: row;
39 | align-items: center;
40 | border-bottom: 1px solid #e9e9e9;
41 | `;
42 |
43 | export const ListRowActive = styled(ListRow)`
44 | background-color: #e9e9e9;
45 | `;
46 |
47 | export const RowNumber = styled.div`
48 | flex: 0 0 2rem;
49 | display: inline-block;
50 | width: 2rem;
51 | height: 2rem;
52 | line-height: 2rem;
53 | border-radius: 2rem;
54 | text-align: center;
55 | color: #fff;
56 | `;
57 |
58 | export const RowStack = styled.div`
59 | flex: 1 0 5.25rem;
60 | margin: 0 0.5rem;
61 | font-size: 20px;
62 | `;
63 |
64 | export const RowName = styled.div`
65 | font-size: .8rem;
66 | line-height: .8rem;
67 | font-weight: bold;
68 | `;
69 |
70 | export const RowSummary = styled.div`
71 | font-size: 0.6rem;
72 | line-height: 0.6rem;
73 | margin-top: 0.25rem;
74 | `;
75 |
76 | export const RowStar = styled.div`
77 | flex: 0 0 auto;
78 | font-size: 1rem !important;
79 | color: #FF502F !important;
80 | `;
81 |
82 | export const ListScrolling = styled.div`
83 | font-style: italic;
84 | opacity: 0.5;
85 | `;
86 |
--------------------------------------------------------------------------------
/src/Components/SvgWrapper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function SvgWrapper ({ viewBoxHeight, viewBoxWidth, ...rest }) {
4 | return (
5 |
10 | );
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/src/Components/TreasureDataIcon.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bvaughn/forward-js-2017/d871899f8a4805ab325cf8c51cdcf5676f5360fc/src/Components/TreasureDataIcon.js
--------------------------------------------------------------------------------
/src/ForwardTheme.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { injectGlobal } from 'styled-components'
3 |
4 | const globalStyles = `
5 | html, body {
6 | height: 100%;
7 | box-sizing: border-box;
8 | margin: 0;
9 | padding: 0;
10 | }
11 | * {
12 | box-sizing: inherit;
13 | }
14 |
15 | body {
16 | font-family: 'Roboto', sans-serif;
17 | font-size: 20px;
18 | }
19 | h1, h2, h3, h4 {
20 | font-weight: 400;
21 | margin: 0 0 1rem;
22 | }
23 | h1 {
24 | font-size: 2.5rem;
25 | }
26 | h2 {
27 | font-size: 1.75rem;
28 | }
29 | h3 {
30 | font-size: 1.5rem;
31 | }
32 | h4 {
33 | font-size: 1rem;
34 | }
35 |
36 | li {
37 | margin: 0 0 0.5rem;
38 | }
39 |
40 | html, body, #root {
41 | height: 100%;
42 | }
43 |
44 | a {
45 | color: #FF502F;
46 | text-decoration: none;
47 | }
48 |
49 | p {
50 | margin: 0 0 1rem;
51 | }
52 |
53 | code {
54 | background: rgba(0,0,0,.05);
55 | border-radius: 5px;
56 | font-family: monospace;
57 | }
58 |
59 | strike {
60 | color: #666;
61 | }
62 |
63 | button {
64 | padding: 0.5rem 1rem;
65 | background-color: #FF502F;
66 | border: 0.1em solid #FFFFFF;
67 | border-radius: 0;
68 | color: #fff;
69 | font-weight: 400;
70 | font-size: 20px;
71 | cursor: pointer;
72 | }
73 |
74 | button:disabled {
75 | opacity: 0.25;
76 | cursor: default;
77 | }
78 |
79 | .CodeMirror-scroll {
80 | overflow: hidden !important;
81 | }
82 |
83 | .VirtualizedSelect {
84 | font-size: 14px;
85 | }
86 | `
87 |
88 | export default class ForwardTheme extends Component {
89 | componentWillMount () {
90 | injectGlobal([`${globalStyles}`])
91 | }
92 |
93 | render () {
94 | return (
95 |
100 | )
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/Presentation/ContentSlide.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import presenterSlideStyle from './presenterSlideStyle'
3 |
4 | export default styled.div`
5 | height: 100%;
6 | padding: 1rem;
7 | ${presenterSlideStyle}
8 | @media (max-width: 600px) {
9 | padding: 0.5rem;
10 | }
11 | h1 {
12 | paddingRight: 9rem;
13 | display: flex;
14 | flex-direction: row;
15 | align-items: center;
16 | color: #444;
17 | }
18 | `
19 |
--------------------------------------------------------------------------------
/src/Presentation/TitleSlide.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import presenterSlideStyle from './presenterSlideStyle'
3 |
4 | export default styled.div`
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: center;
8 | align-items: center;
9 | text-align: center;
10 | height: 100%;
11 | background-color: #000;
12 | color: #fff;
13 | padding: 1rem;
14 | ${presenterSlideStyle}
15 | @media (max-width: 600px) {
16 | padding: 0.5rem;
17 | }
18 | `
19 |
--------------------------------------------------------------------------------
/src/Presentation/presenterSlideStyle.js:
--------------------------------------------------------------------------------
1 | export default (props) => props.theme.isPresenterMode && `
2 | outline: 0.25rem solid #37F;
3 | outline-offset: -0.25rem;
4 | &::before {
5 | content: 'presenter';
6 | position: fixed;
7 | top: 0;
8 | left: 0;
9 | background-color: #36F;
10 | padding: 0.25rem;
11 | color: #fff;
12 | font-size: 0.65rem;
13 | }
14 | `
15 |
--------------------------------------------------------------------------------
/src/Slides/000.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TitleSlide from '../Presentation/TitleSlide';
3 | import styled from 'styled-components';
4 |
5 | const Container = styled.p`
6 | display: flex;
7 | flex-direction: row;
8 | align-items: center;
9 | justify-content: center;
10 | `;
11 |
12 | const Spacer = styled.span`
13 | width: 3rem;
14 | `;
15 |
16 | const Icon = styled.i`
17 | font-size: 2rem !important;
18 | line-height: 2rem !important;
19 | margin: 0 0.5rem;
20 | color: #ccc;
21 | `;
22 |
23 | const slide = () => (
24 |
25 | {slide.title}
26 |
29 |
30 | @bvaughn
31 |
32 | @brian_d_vaughn
33 |
34 |
35 | );
36 |
37 | slide.title = 'Creating more efficient React views with windowing'
38 |
39 | export default slide;
40 |
--------------------------------------------------------------------------------
/src/Slides/001.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import styled from 'styled-components';
5 |
6 | const HeartIcon = styled.i`
7 | color: #f92672 !important;
8 | `;
9 | const FacebookIcon = styled.i`
10 | color: #3b5998 !important;
11 | `;
12 | const GithubIcon = styled.i`
13 | color: #222 !important;
14 | `;
15 |
16 | const slide = () => (
17 |
18 | {slide.title}
19 |
20 |
21 | -
22 | Facebook + React
23 |
24 |
25 |
26 | -
27 | github.com/bvaughn
28 |
29 |
30 |
31 | -
32 | performance, search, and app architecture
33 |
34 |
35 |
36 |
37 | );
38 |
39 | slide.title = 'Who am I?';
40 |
41 | export default slide;
42 |
--------------------------------------------------------------------------------
/src/Slides/002.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 |
5 | const slide = () => (
6 |
7 | {slide.title}
8 | To talk about performance.
9 |
10 | - What slows React applications down?
11 | - How can we fix?
12 | - Share some lessons-learned
13 |
14 |
15 | );
16 |
17 | slide.title = 'Why are we here?';
18 |
19 | export default slide;
20 |
--------------------------------------------------------------------------------
/src/Slides/003.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import image from '../../public/computer-guy.png';
5 |
6 | const slide = () => (
7 |
8 | {slide.title}
9 |
10 |
16 |
17 |
18 | - Older hardware
19 | - Load times
20 | - Scrolling UX for mobile
21 | - Battery life
22 |
23 |
24 | );
25 |
26 | slide.title = 'Why does performance matter?';
27 |
28 | export default slide;
29 |
--------------------------------------------------------------------------------
/src/Slides/004.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import styled from 'styled-components';
5 |
6 | const SectionCheck = styled.i`
7 | margin-left: 0.5rem;
8 | color: #C0E0D0 !important;
9 | `
10 |
11 | const slide = () => (
12 |
13 | {slide.title}
14 |
15 |
16 |
17 | Browser
18 |
19 |
20 |
21 |
22 |
23 | - DOM elements and mutations
24 | - Repaints and reflows
25 | - Garbage collection
26 |
27 |
28 |
29 |
30 |
31 | React
32 |
33 |
34 |
35 |
36 | - Unnecessary renders
37 | - Development build of React 🤡
38 |
39 |
40 |
41 | );
42 |
43 | slide.title = 'What can slow a React app down?';
44 |
45 | export default slide;
46 |
--------------------------------------------------------------------------------
/src/Slides/100.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TitleSlide from '../Presentation/TitleSlide';
3 |
4 | const slide = () => (
5 |
6 | {slide.title}
7 |
8 | );
9 |
10 | slide.title = 'General React tips'
11 |
12 | export default slide;
13 |
--------------------------------------------------------------------------------
/src/Slides/101.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 |
5 | const slide = () => (
6 |
7 | {slide.title}
8 |
9 |
13 |
14 | );
15 |
16 | slide.title = 'Use the production build of React';
17 |
18 | export default slide;
19 |
--------------------------------------------------------------------------------
/src/Slides/102.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import styled from 'styled-components';
4 | import ContentSlide from '../Presentation/ContentSlide';
5 | import ScuChart from '../Components/ScuChart';
6 |
7 | const Spacer = styled.div`
8 | margin-top: 0.5rem;
9 | `
10 |
11 | const sourceA = require('raw!../../examples/pure-component.js');
12 | const sourceB = require('raw!../../examples/shallow-compare.js');
13 |
14 | const slide = ({ stepIndex }) => (
15 |
16 | {slide.title}
17 |
18 |
19 |
20 |
21 |
22 |
23 | Use shouldComponentUpdate
24 |
25 |
26 |
27 |
28 | React 15.3+ use PureComponent
29 |
30 |
31 | - Compares current
props
and state
to next
32 | - Only render when changes
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
49 |
50 |
51 |
52 | );
53 |
54 | slide.title = 'Avoid unnecessary renders';
55 |
56 | export default slide;
57 |
--------------------------------------------------------------------------------
/src/Slides/103.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import styled from 'styled-components';
4 | import ContentSlide from '../Presentation/ContentSlide';
5 |
6 | const Badge = styled.span`
7 | padding: 6px 8px;
8 | background-color: #4285f4;
9 | color: #fff;
10 | box-shadow: 0 1px 4px 0 rgba(0,0,0,0.14);
11 | border-radius: 4px;
12 | `
13 |
14 | const slide = ({ stepIndex }) => (
15 |
16 | {slide.title}
17 |
18 |
19 |
20 |
21 |
22 | User badge:
23 | Brian Vaughn (brian.david.vaughn@gmail.com)
24 |
25 |
26 |
27 | - 🙁
users
(array), index
(number)
28 | - 🙂
user
(object)
29 | - 😁
name
(string), email
(string)
30 | - Simplifies testing!
31 |
32 |
33 |
34 |
35 | -
36 | Immutable data
37 |
38 |
39 | - Mutation creates new instance
40 | - Faster change detection
41 |
42 |
43 |
44 |
45 |
46 | );
47 |
48 | slide.title = 'Choose props carefully';
49 |
50 | export default slide;
51 |
--------------------------------------------------------------------------------
/src/Slides/104.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import { Code, Step } from 'react-presents';
4 | import ContentSlide from '../Presentation/ContentSlide';
5 |
6 | const sourceA = require('raw!../../examples/function-children-example-2.js');
7 | const sourceB = require('raw!../../examples/function-children-example-1.js');
8 | const sourceC = require('raw!../../examples/function-children-example-3.js');
9 | const sourceD = require('raw!../../examples/element-child.js');
10 | const sourceE = require('raw!../../examples/function-child.js');
11 |
12 | const DIM_LINES = [
13 | undefined,
14 | [[0,1], [4,10], [13,14]],
15 | [[0,3], [6,8], [11,14]],
16 | [[0,5], [9,14]]
17 | ]
18 |
19 | const Spacer = styled.div`
20 | margin-top: 0.5rem;
21 | `
22 |
23 | const slide = ({ stepIndex }) => (
24 |
25 | {slide.title}
26 |
27 |
28 |
29 |
Element child
30 |
31 |
32 |
33 |
34 |
Function child
35 |
36 |
37 |
38 |
39 |
40 |
41 | ...but why?
42 |
43 |
44 |
45 |
46 |
47 |
48 | Let's say our app is localized...
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | Let's say we have a session...
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Now we can compose them...
65 |
66 |
67 |
68 |
72 |
73 |
74 | );
75 |
76 | slide.title = 'Render callbacks (aka function children)';
77 |
78 | export default slide;
79 |
--------------------------------------------------------------------------------
/src/Slides/200.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TitleSlide from '../Presentation/TitleSlide';
3 |
4 | const slide = () => (
5 |
6 | {slide.title}
7 |
8 | );
9 |
10 | slide.title = 'What about too many DOM elements, GC, etc?'
11 |
12 | export default slide;
13 |
--------------------------------------------------------------------------------
/src/Slides/201.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import styled from 'styled-components';
5 | import MarioSvg from '../Components/MarioSvg';
6 | import kindleImage from '../../public/kindle.jpg';
7 | import occlusionImage from '../../public/occlusion-culling.jpg';
8 |
9 | const OcclusionImage = styled.img`
10 | width: 300px;
11 | max-width: 100%;
12 | height: auto;
13 | `
14 |
15 | const KindleImage = styled.img`
16 | width: 250px;
17 | max-width: 100%;
18 | height: auto;
19 | margin-left: 2.5rem;
20 | `
21 |
22 | const Spacer = styled.div`
23 | margin-top: 0.5rem;
24 | `
25 |
26 | const slide = () => (
27 |
28 | {slide.title}
29 |
30 |
31 |
32 |
33 | - Known as "windowing".
34 | - Analog example:
books Kindle
35 |
36 |
37 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
"Occlusion culling" for video games
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
61 |
62 |
63 |
64 | - Given a perspective of a user
65 | - Which objects are visible?
66 |
67 |
68 |
69 |
70 |
71 |
72 |
"Windowing" for web and mobile
73 |
74 |
75 | - Given a small list (eg
<ul>
, UITableView
)
76 | - And large set of items (eg
<li>
, UITableViewCell
)
77 | - Which items are visible?
78 |
79 |
80 |
81 |
82 | );
83 |
84 | slide.title = 'Only create elements the user can see'
85 |
86 | export default slide;
87 |
--------------------------------------------------------------------------------
/src/Slides/202.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import styled from 'styled-components';
5 | import HowDoesWindowingWork from '../Components/HowDoesWindowingWork';
6 |
7 | const HiddenLi = styled.li`
8 | opacity: 0;
9 | `;
10 |
11 | function MaybeLI ({ children, index, stepIndex }) {
12 | return stepIndex >= index
13 | ? (
14 | {children}
15 | ) : (
16 | {children}
17 | );
18 | }
19 |
20 | const title = 'How does windowing work?';
21 |
22 | const slide = ({ stepIndex }, { slide }) => {
23 | slide.setNumSteps(6);
24 |
25 | return (
26 |
27 | {title}
28 |
29 |
30 |
31 |
32 | Small DOM element (eg <ul>
)
33 |
34 |
35 |
36 |
37 | Items
38 |
39 |
40 |
41 |
42 | Big DOM element for scrolling
43 |
44 |
45 |
46 |
47 | Absolutely positioned visible rows
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | slide.contextTypes = {
58 | slide: PropTypes.any
59 | };
60 | slide.title = title;
61 |
62 | export default slide;
63 |
--------------------------------------------------------------------------------
/src/Slides/203.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import styled from 'styled-components';
5 | import calendarGif from '../../public/how-is-it-used-calendar.gif';
6 | import dropDownGif from '../../public/how-is-it-used-dropdown.gif';
7 | import tableGif from '../../public/how-is-it-used-table.gif';
8 | import treeGif from '../../public/how-is-it-used-tree.gif';
9 |
10 | const ImageContainer = styled.i`
11 | display: flex;
12 | justify-content: center;
13 | align-items: center;
14 | `
15 |
16 | const Image = styled.img`
17 | margin-top: 1rem;
18 | border: 1px solid #ddd;
19 | `
20 |
21 | const slide = () => (
22 |
23 | {slide.title}
24 |
25 |
26 |
45 |
46 |
47 |
48 | Key question: Will the data grow?
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | );
67 |
68 | slide.title = 'What can windowing be used for?';
69 |
70 | export default slide;
71 |
--------------------------------------------------------------------------------
/src/Slides/204.js:
--------------------------------------------------------------------------------
1 | import now from 'performance-now';
2 | import React, { Component, PropTypes } from 'react';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import Note from '../Components/Note';
5 | import { List, ListRow, RowName, RowNumber, RowStack, RowSummary } from '../Components/StyledListElements';
6 |
7 | export default class Slide extends Component {
8 | static contextTypes = {
9 | list: PropTypes.array
10 | };
11 |
12 | static title = 'How much can windowing improve performance?';
13 |
14 | constructor (props, context) {
15 | super(props, context);
16 |
17 | this.state = {
18 | initializationTime: 0,
19 | initialized: false
20 | };
21 | }
22 |
23 | componentDidUpdate (prevProps, prevState) {
24 | const { initialized } = this.state;
25 |
26 | if (initialized && !prevState.initialized) {
27 | window.requestIdleCallback(() => {
28 | this.setState({
29 | initializationTime: now() - this._initializationStartedAt
30 | });
31 | });
32 | }
33 | }
34 |
35 | componentWillUpdate (nextProps, nextState) {
36 | const { initialized } = this.state;
37 |
38 | if (nextState.initialized && !initialized) {
39 | this._initializationStartedAt = now();
40 | }
41 | }
42 |
43 | render () {
44 | const { list } = this.context;
45 | const { initializationTime, initialized } = this.state;
46 |
47 | const ListComponent = List;
48 |
49 | return (
50 |
51 | {Slide.title}
52 | Rendering a big list with React
53 | {initialized || (
54 |
57 | )}
58 | {initialized && (
59 |
60 | {initializationTime === 0 && (
61 |
62 | Measuring ...
63 |
64 | )}
65 | {initializationTime > 0 && (
66 |
67 | Created list in {Math.round(initializationTime) / 1e3} seconds
68 |
69 | )}
70 |
71 |
72 | {list.map((item, index) => (
73 |
74 |
79 | {item.name.substr(0, 1)}
80 |
81 |
82 | {item.name}
83 | This is row {index}
84 |
85 |
86 | ))}
87 |
88 |
89 |
90 | Scrolling performance also poor if repaints are triggered (eg border-radius
)
91 |
92 |
93 | )}
94 |
95 | );
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/Slides/205.js:
--------------------------------------------------------------------------------
1 | import now from 'performance-now';
2 | import numeral from 'numeral';
3 | import React, { Component, PropTypes } from 'react';
4 | import ContentSlide from '../Presentation/ContentSlide';
5 | import ExampleList from '../Components/ExampleList';
6 | import Note from '../Components/Note';
7 |
8 | export default class Slide extends Component {
9 | static contextTypes = {
10 | list: PropTypes.array
11 | };
12 |
13 | static title = 'How much can windowing improve performance?';
14 |
15 | constructor (props, context) {
16 | super(props, context);
17 |
18 | this.state = {
19 | formattedListSize: numeral(context.list.length).format(),
20 | initializationTime: 0,
21 | initialized: false
22 | };
23 | }
24 |
25 | componentDidUpdate (prevProps, prevState) {
26 | const { initialized } = this.state;
27 |
28 | if (initialized && !prevState.initialized) {
29 | window.requestIdleCallback(() => {
30 | this.setState({
31 | initializationTime: now() - this._initializationStartedAt
32 | });
33 | });
34 | }
35 | }
36 |
37 | componentWillUpdate (nextProps, nextState) {
38 | const { initialized } = this.state;
39 |
40 | if (nextState.initialized && !initialized) {
41 | this._initializationStartedAt = now();
42 | }
43 | }
44 |
45 | render () {
46 | const { formattedListSize, initializationTime, initialized } = this.state;
47 |
48 | return (
49 |
50 | {Slide.title}
51 | Rendering the same list with windowing
52 | {initialized || (
53 |
56 | )}
57 | {initialized && (
58 |
59 | {initializationTime === 0 && (
60 |
61 | Creating List ...
62 |
63 | )}
64 | {initializationTime > 0 && (
65 |
66 | Created list in {Math.round(initializationTime) / 1e3} seconds ({formattedListSize} items)
67 |
68 | )}
69 |
70 |
71 |
72 |
73 | Open the browser Timeline view and scroll
74 |
75 |
76 | )}
77 |
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Slides/206.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 |
5 | const sourceNonVirtualized = require('raw!../../examples/perf-comparison-non-virtualized.js');
6 | const sourceVirtualized = require('raw!../../examples/perf-comparison-virtualized.js');
7 |
8 | const slide = () => (
9 |
10 | {slide.title}
11 |
12 |
13 |
Rendering a list with React
14 |
15 |
16 |
17 |
18 |
19 |
Rendering a list with React
20 |
24 |
25 |
26 |
27 |
28 |
Rendering a list with React
29 |
33 |
34 |
35 |
36 |
37 |
Rendering a list with react-virtualized
38 |
39 |
40 |
41 |
42 |
43 |
Rendering a list with react-virtualized
44 |
48 |
49 |
50 |
51 |
52 |
Rendering a list with react-virtualized
53 |
57 |
58 |
59 |
60 | );
61 |
62 | slide.title = 'How complicated is it to use windowing?';
63 |
64 | export default slide;
65 |
--------------------------------------------------------------------------------
/src/Slides/300.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TitleSlide from '../Presentation/TitleSlide';
3 |
4 | const slide = () => (
5 |
6 | {slide.title}
7 |
8 |
9 | );
10 |
11 | slide.title = 'react-virtualized'
12 |
13 | export default slide;
14 |
--------------------------------------------------------------------------------
/src/Slides/301.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import { CollectionSvg, GridSvg, ListSvg, TableSvg } from '../Components/BuildingBlocks';
5 |
6 | const slide = () => (
7 |
8 | {slide.title}
9 |
10 |
11 |
12 |
List
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
Grid
25 |
26 |
27 |
28 |
29 |
30 |
Collection
31 |
32 |
33 |
34 |
35 | );
36 |
37 | slide.title = 'The building blocks';
38 |
39 | export default slide;
40 |
--------------------------------------------------------------------------------
/src/Slides/400.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TitleSlide from '../Presentation/TitleSlide';
3 |
4 | const slide = () => (
5 |
6 | {slide.title}
7 |
8 | );
9 |
10 | slide.title = 'Deeper dive'
11 |
12 | export default slide;
13 |
--------------------------------------------------------------------------------
/src/Slides/401.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import chatImage from '../../public/non-uniform-heights-chat.png';
5 | import dropDownImage from '../../public/non-uniform-heights-drop-down.png';
6 |
7 | const sourceRowHeightGetter = require('raw!../../examples/dynamic-row-height-getter.js');
8 | const sourceCellMeasurer = require('raw!../../examples/dynamic-cell-measurer.js');
9 |
10 | const CELL_MEASURER_LINES = [
11 | [],
12 | [[0,1], [3,10], [12,27]],
13 | [[0,3], [11,11], [13,21], [23,27]],
14 | [[1,4], [6,19], [22,27]]
15 | ]
16 |
17 | const slide = ({ stepIndex }) => (
18 |
19 | {slide.title}
20 |
21 |
22 |
23 |
This introduces a couple of challenges:
24 |
25 | - Total size calculation
26 | - Initial performance
27 |
28 |
29 |
30 |
31 |
32 |
33 |
eg Size can be inferred from data
34 |

35 |
36 |
37 |
38 |
39 |
43 |
44 |
45 |
46 |
47 |
eg Must be measured by browser
48 |

49 |
50 |
51 |
52 |
53 |
57 |
58 |
59 | );
60 |
61 | slide.title = 'What about variable sized content?';
62 |
63 | export default slide;
64 |
--------------------------------------------------------------------------------
/src/Slides/402.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import styled from 'styled-components';
5 | import DeferMeasurements from '../Components/DeferMeasurements';
6 |
7 | const Spacer = styled.div`
8 | margin-top: 0.5rem;
9 | `
10 |
11 | const slide = () => (
12 |
13 | {slide.title}
14 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 |
24 | slide.title = 'Is measuring expensive?';
25 |
26 | export default slide;
27 |
--------------------------------------------------------------------------------
/src/Slides/403.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import styled from 'styled-components';
5 | import image from '../../public/cache-all-the-things.png';
6 |
7 | const Image = styled.img`
8 | margin-left: 0.5rem;
9 | `;
10 |
11 | const slide = () => (
12 |
13 | {slide.title}
14 |
15 |
16 |
17 |
18 | - Measurements?
19 | - Cells?
20 |
21 | -
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Stateful views are tricky.
38 |
39 |
40 |
41 |
42 | - Cache while scrolling
43 |
44 |
45 | - Clear cache after
46 |
47 |
48 |
49 |
50 |
51 | );
52 |
53 | slide.title = 'What can be cached?';
54 |
55 | export default slide;
56 |
--------------------------------------------------------------------------------
/src/Slides/404.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import styled from 'styled-components';
5 | import AnimatedList from '../Components/AnimatedList';
6 | import ExampleList from '../Components/ExampleList';
7 | import Note from '../Components/Note';
8 | import movie from '../../public/overscan-example.mp4';
9 |
10 | const Video = styled.video`
11 | border: 1px solid #ddd;
12 | border-radius: 0.25rem;
13 | `
14 |
15 | const slide = () => (
16 |
17 | {slide.title}
18 |
19 |
20 |
21 |
22 |
33 |
34 | Unique to windowing.
35 |
36 |
37 |
38 |
39 |
40 |
Why does windowing cause this?
41 |
42 |
43 | - Separate thread
44 |
45 |
46 | - JavaScript updated periodically
47 |
48 |
49 | - Sometimes JS isn't fast enough (16ms frame budget)
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | View source to see overscanned rows
69 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 |
77 | slide.title = 'Will scrolling still feel natural?';
78 |
79 | export default slide;
80 |
--------------------------------------------------------------------------------
/src/Slides/405.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import ScaledList from '../Components/ScaledList';
5 | import image from '../../public/browser-limits-cutoff.png';
6 |
7 | const slide = ({ stepIndex }) => {
8 | return (
9 |
10 | {slide.title}
11 |
12 |
13 |
14 |
15 |
16 | DOM element size limits (eg Chrome 33.5M px, IE 1.5M px)
17 |
18 |
19 |
20 | - Browser won't render past
21 | - Can't scroll past
22 | - Layout gets wonky
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
Compression things
38 |
39 | 8} />
40 |
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | slide.title = 'Will we run into browser limitations?';
48 |
49 | export default slide;
50 |
--------------------------------------------------------------------------------
/src/Slides/406.js:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 | import React, { Component, PropTypes } from 'react';
3 | import { Code, Step } from 'react-presents';
4 | import ContentSlide from '../Presentation/ContentSlide';
5 | import { List } from 'react-virtualized';
6 | import styled from 'styled-components';
7 | import Note from '../Components/Note';
8 | import image from '../../public/profile-picture.jpg';
9 |
10 | const source = require('raw!../../examples/is-scrolling-cell-renderer.js').trim();
11 |
12 | const Image = styled.img`
13 | width: 60px;
14 | height: 60px;
15 | `;
16 |
17 | const ImageContainer = styled.div`
18 | position: relative;
19 | width: 60px;
20 | height: 60px;
21 | margin-right: 0.5rem;
22 | `
23 |
24 | const ImageTinter = styled.div`
25 | content: "";
26 | display: block;
27 | position: absolute;
28 | top: 0;
29 | bottom: 0;
30 | left: 0;
31 | right: 0;
32 | `
33 |
34 | const ImagePlaceholder = styled.div`
35 | width: 60px;
36 | height: 60px;
37 | background-color: #aaa;
38 | border-radius: 0.5rem;
39 | margin-right: 0.5rem;
40 | `
41 |
42 | const ImageListRowText = styled.span`
43 | font-size: 1rem;
44 | `
45 |
46 | export default class Slide extends Component {
47 | static contextTypes = {
48 | list: PropTypes.array
49 | };
50 |
51 | static title = 'What about elements that are really complex?';
52 |
53 | render () {
54 | const { list } = this.context;
55 |
56 | return (
57 |
58 | {Slide.title}
59 |
60 |
61 |
62 |
63 | <canvas>
or <svg>
64 | - Images (trigger network requests)
65 |
66 |
67 |
68 |
69 |
70 |
71 |
Render less while scrolling.
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
(
88 |
96 | {isScrolling && (
97 |
98 | )}
99 | {!isScrolling && (
100 |
101 |
105 |
110 |
111 | )}
112 |
113 | {list[index].name}
114 |
115 |
116 | )}
117 | width={240}
118 | />
119 |
120 |
121 | Scroll to see row renderer changes.
122 |
123 |
124 |
125 |
126 | );
127 | }
128 | }
129 |
130 | function hexToRgba(hex, alpha) {
131 | const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
132 | const r = parseInt(result[1], 16);
133 | const g = parseInt(result[2], 16);
134 | const b = parseInt(result[3], 16);
135 | return `rgba(${r}, ${g}, ${b}, ${alpha})`
136 | }
137 |
--------------------------------------------------------------------------------
/src/Slides/500.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TitleSlide from '../Presentation/TitleSlide';
3 |
4 | const slide = () => (
5 |
6 | {slide.title}
7 |
8 | );
9 |
10 | slide.title = 'Examples & recipes'
11 |
12 | export default slide;
13 |
--------------------------------------------------------------------------------
/src/Slides/501.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import Note from '../Components/Note';
5 | import DragAndDropList from '../Components/DragAndDropList';
6 |
7 | const source = require('raw!../../examples/drag-and-drop-key-points.js');
8 |
9 | const DIM_LINES = [
10 | [[1,3], [6,27]],
11 | [[0,9], [13,18], [26,27]],
12 | [[0,20], [24,27]]
13 | ]
14 |
15 | export default class Slide extends Component {
16 | static contextTypes = {
17 | list: PropTypes.array
18 | };
19 |
20 | static propTypes = {
21 | stepIndex: PropTypes.number.isRequired
22 | };
23 |
24 | static title = 'Drag-and-drop rows';
25 |
26 | render () {
27 | const { list } = this.context;
28 | const { stepIndex } = this.props;
29 |
30 | return (
31 |
32 | {Slide.title}
33 |
34 |
35 |
36 |
37 | Use react-sortable-hoc with react-virtualized for drag-and-drop.
38 |
39 |
40 |
41 |
42 |
43 | Click and drag rows above
44 |
45 |
46 |
47 |
48 |
49 |
50 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Slides/502.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import ResizableRowsList from '../Components/ResizableRowsList';
5 |
6 | const source = require('raw!../../examples/resizable-cells-key-points.js');
7 |
8 | const DIM_LINES = [
9 | [[1,10], [17,29]]
10 | ]
11 |
12 | const slide = ({ stepIndex }, { list }) => (
13 |
14 | {slide.title}
15 |
16 |
17 |
18 |
19 | Use react-draggable with react-virtualized for resizable rows.
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
33 |
34 |
35 | );
36 |
37 | slide.contextTypes = {
38 | list: PropTypes.array.isRequired
39 | };
40 |
41 | slide.title = 'Resizable rows';
42 |
43 | export default slide;
44 |
--------------------------------------------------------------------------------
/src/Slides/503.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import Note from '../Components/Note';
5 | import Spreadsheet from '../Components/Spreadsheet';
6 |
7 | const sourceGrids = require('raw!../../examples/multi-grid.js');
8 | const sourceRenderer = require('raw!../../examples/multi-grid-renderers.js');
9 |
10 | const GRID_DIM_LINES = [
11 | undefined,
12 | [[0,5], [8,14]]
13 | ];
14 |
15 | const RENDERER_DIM_LINES = [
16 | undefined,
17 | [[0,1], [4,6], [8,8], [10,11]],
18 | [[0,3], [7,7], [9,11]]
19 | ];
20 |
21 | const slide = ({ stepIndex }, { list }) => {
22 | return (
23 |
24 | {slide.title}
25 |
26 |
27 |
28 |
29 | Create sticky rows or columns (eg spreadsheet) with MultiGrid
.
30 |
31 |
32 |
33 |
34 |
35 |
36 | Scroll around; click to edit a cell above.
37 |
38 |
39 |
40 |
41 |
42 |
43 |
47 |
48 |
49 |
50 |
54 |
55 |
56 | );
57 | }
58 |
59 | slide.contextTypes = {
60 | list: PropTypes.array.isRequired
61 | };
62 |
63 | slide.title = 'Sticky rows & columns';
64 |
65 | export default slide;
66 |
--------------------------------------------------------------------------------
/src/Slides/504.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import DemoCollection from '../Components/DemoCollection';
5 |
6 | const source = require('raw!../../examples/collection.js');
7 |
8 | const DIM_LINES = [
9 | [[0,21]],
10 | [[10,30]],
11 | [[0,10], [21,30]]
12 | ]
13 |
14 | const slide = ({ stepIndex }, { list }) => {
15 | return (
16 |
17 | {slide.title}
18 |
19 |
20 |
21 |
22 | For more complex layouts (eg Pinterest layout, Gantt chart) use Collection
.
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 |
38 | );
39 | }
40 |
41 | slide.contextTypes = {
42 | list: PropTypes.array.isRequired
43 | };
44 |
45 | slide.title = 'Collection demo';
46 |
47 | export default slide;
48 |
--------------------------------------------------------------------------------
/src/Slides/600.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TitleSlide from '../Presentation/TitleSlide';
3 |
4 | const slide = () => (
5 |
6 | {slide.title}
7 |
8 | );
9 |
10 | slide.title = 'Bonus slide: Common integration mistakes';
11 |
12 | export default slide;
13 |
--------------------------------------------------------------------------------
/src/Slides/601.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 | import warningImage from '../../public/v9-dev-mode-style-warning.png';
5 |
6 | const source = require('raw!../../examples/forgotten-styles.js')
7 |
8 | const slide = () => (
9 |
10 | {slide.title}
11 |
12 | Most common mistake: forgot to set the style
.
13 |
14 |
15 |
19 |
20 |
21 |
22 |
Version 9 now logs dev warning for this!
23 |
24 |
28 |
29 |
30 |
31 |
32 | );
33 |
34 | slide.title = 'Styles';
35 |
36 | export default slide;
37 |
--------------------------------------------------------------------------------
/src/Slides/602.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Code, Step } from 'react-presents';
3 | import ContentSlide from '../Presentation/ContentSlide';
4 |
5 | const sourceA = require('raw!../../examples/pass-through-props.js');
6 | const sourceB = require('raw!../../examples/force-update.js');
7 |
8 | const slide = ({ stepIndex }) => (
9 |
10 | {slide.title}
11 |
12 |
13 |
14 |
15 | - All components extend
PureComponent
16 | - No access to array/collection
17 |
18 |
19 |
20 |
What about...
21 |
22 | - Sorting?
23 | - State?
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Let react-virtualized know that something external has changed!
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
The simplest way is with pass-through properties:
41 |
45 |
46 |
47 |
48 |
49 |
50 |
But you can also use Api methods (eg forceUpdate
)
51 |
55 |
56 |
57 |
58 |
59 |
60 | );
61 |
62 | slide.title = 'Pure components';
63 |
64 | export default slide;
65 |
--------------------------------------------------------------------------------
/src/Slides/700.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TitleSlide from '../Presentation/TitleSlide';
3 | import styled from 'styled-components';
4 |
5 | const Container = styled.div`
6 | display: flex;
7 | flex-direction: row;
8 | align-items: center;
9 | justify-content: center;
10 | `;
11 |
12 | const Icon = styled.i`
13 | font-size: 2rem !important;
14 | line-height: 2rem !important;
15 | margin: 0 0.5rem;
16 | color: #ccc;
17 | `;
18 |
19 | const Spacer = styled.div`
20 | width: 3rem;
21 | `;
22 |
23 | const slide = () => (
24 |
25 | {slide.title}
26 |
27 | slides @ github.com/bvaughn/forward-js-2017
28 |
29 |
30 | @bvaughn
31 |
32 | @brian_d_vaughn
33 |
34 |
35 | );
36 |
37 | slide.title = 'Questions?'
38 |
39 | export default slide;
40 |
--------------------------------------------------------------------------------
/src/Utils/generateRandomList.js:
--------------------------------------------------------------------------------
1 | const BADGE_COLORS = ['#f44336', '#3f51b5', '#4caf50', '#ff9800', '#2196f3', '#374046', '#cddc39', '#2196f3', '#9c27b0', '#ffc107', '#009688', '#673ab7', '#ffeb3b', '#cddc39', '#795548']
2 | const NAMES = ['Peter Brimer', 'Tera Gaona', 'Kandy Liston', 'Lonna Wrede', 'Kristie Yard', 'Raul Host', 'Yukiko Binger', 'Velvet Natera', 'Donette Ponton', 'Loraine Grim', 'Shyla Mable', 'Marhta Sing', 'Alene Munden', 'Holley Pagel', 'Randell Tolman', 'Wilfred Juneau', 'Naida Madson', 'Marine Amison', 'Glinda Palazzo', 'Lupe Island', 'Cordelia Trotta', 'Samara Berrier', 'Era Stepp', 'Malka Spradlin', 'Edward Haner', 'Clemencia Feather', 'Loretta Rasnake', 'Dana Hasbrouck', 'Sanda Nery', 'Soo Reiling', 'Apolonia Volk', 'Liliana Cacho', 'Angel Couchman', 'Yvonne Adam', 'Jonas Curci', 'Tran Cesar', 'Buddy Panos', 'Rosita Ells', 'Rosalind Tavares', 'Renae Keehn', 'Deandrea Bester', 'Kelvin Lemmon', 'Guadalupe Mccullar', 'Zelma Mayers', 'Laurel Stcyr', 'Edyth Everette', 'Marylin Shevlin', 'Hsiu Blackwelder', 'Mark Ferguson', 'Winford Noggle', 'Shizuko Gilchrist', 'Roslyn Cress', 'Nilsa Lesniak', 'Agustin Grant', 'Earlie Jester', 'Libby Daigle', 'Shanna Maloy', 'Brendan Wilken', 'Windy Knittel', 'Alice Curren', 'Eden Lumsden', 'Klara Morfin', 'Sherryl Noack', 'Gala Munsey', 'Stephani Frew', 'Twana Anthony', 'Mauro Matlock', 'Claudie Meisner', 'Adrienne Petrarca', 'Pearlene Shurtleff', 'Rachelle Piro', 'Louis Cocco', 'Susann Mcsweeney', 'Mandi Kempker', 'Ola Moller', 'Leif Mcgahan', 'Tisha Wurster', 'Hector Pinkett', 'Benita Jemison', 'Kaley Findley', 'Jim Torkelson', 'Freda Okafor', 'Rafaela Markert', 'Stasia Carwile', 'Evia Kahler', 'Rocky Almon', 'Sonja Beals', 'Dee Fomby', 'Damon Eatman', 'Alma Grieve', 'Linsey Bollig', 'Stefan Cloninger', 'Giovanna Blind', 'Myrtis Remy', 'Marguerita Dostal', 'Junior Baranowski', 'Allene Seto', 'Margery Caves', 'Nelly Moudy', 'Felix Sailer']
3 | const ROW_HEIGHTS = [50, 75, 100]
4 |
5 | const loremIpsum = [
6 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
7 | 'Phasellus vulputate odio commodo tortor sodales, et vehicula ipsum viverra.',
8 | 'Cras tincidunt nisi in urna molestie varius.',
9 | 'Curabitur ac enim dictum arcu varius fermentum vel sodales dui.',
10 | 'Ut tristique augue at congue molestie.',
11 | 'Cras eget enim nec odio feugiat tristique eu quis ante.',
12 | 'Phasellus eget enim vitae nunc luctus sodales a eu erat.',
13 | 'Nulla bibendum quam id velit blandit dictum.',
14 | 'Donec dignissim mi ac libero feugiat, vitae lacinia odio viverra.',
15 | 'Praesent vel lectus venenatis, elementum mauris vitae, ullamcorper nulla.',
16 | 'Quisque sollicitudin nulla nec tellus feugiat hendrerit.',
17 | 'Vestibulum a eros accumsan, lacinia eros non, pretium diam.',
18 | 'Donec ornare felis et dui hendrerit, eget bibendum nibh interdum.',
19 | 'Donec nec diam vel tellus egestas lobortis.',
20 | 'Sed ornare nisl sit amet dolor pellentesque, eu fermentum leo interdum.',
21 | 'Sed eget mauris condimentum, molestie justo eu, feugiat felis.',
22 | 'Sed luctus justo vitae nibh bibendum blandit.',
23 | 'Nulla ac eros vestibulum, mollis ante eu, rutrum nulla.',
24 | 'Sed cursus magna ut vehicula rutrum.'
25 | ]
26 |
27 | export default function generateRandomList (count = 15e3) {
28 | const list = []
29 |
30 | for (var i = 0; i < count; i++) {
31 | list.push({
32 | age: 20 + Math.round(Math.random() * 50),
33 | color: BADGE_COLORS[i % BADGE_COLORS.length],
34 | index: i,
35 | left: Math.round(Math.random() * 20e3),
36 | name: NAMES[i % NAMES.length],
37 | random: loremIpsum[i % loremIpsum.length],
38 | size: ROW_HEIGHTS[Math.floor(Math.random() * ROW_HEIGHTS.length)],
39 | top: Math.round(Math.random() * 20e3)
40 | })
41 | }
42 |
43 | return list
44 | }
45 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | // Import global stylesheets here
6 | import '../lib/font-awesome/css/font-awesome.min.css';
7 |
8 | ReactDOM.render(
9 | ,
10 | document.getElementById('root')
11 | );
12 |
--------------------------------------------------------------------------------
/src/shared-list-styles.css:
--------------------------------------------------------------------------------
1 | .List {
2 | width: 15rem;
3 | height: 15rem;
4 | overflow: auto;
5 | font-size: 12px;
6 | border: 1px solid #CFD8DC;
7 | margin: 1rem 0;
8 | }
9 |
10 | .ListWithBorderRadius {
11 | border-radius: 1rem;
12 | border-width: 4px;
13 | overflow: auto;
14 | }
15 |
16 | .ListRow {
17 | padding: 0 0.5rem;
18 | width: 100%;
19 | height: 40px;
20 | line-height: 40px;
21 | transition: 0 all;
22 | white-space: pre;
23 | overflow: hidden;
24 | text-overflow: ellipsis;
25 | display: flex;
26 | flex-direction: row;
27 | align-items: center;
28 | border-bottom: 1px solid #e9e9e9;
29 | }
30 |
31 | .ListRowActived {
32 | background-color: #e9e9e9;
33 | }
34 |
35 | .RowNumber {
36 | flex: 0 0 2rem;
37 | display: inline-block;
38 | width: 2rem;
39 | height: 2rem;
40 | line-height: 2rem;
41 | border-radius: 2rem;
42 | text-align: center;
43 | color: #fff;
44 | }
45 |
46 | .RowStack {
47 | flex: 1 0 5.25rem;
48 | margin: 0 0.5rem;
49 | font-size: 20px;
50 | }
51 |
52 | .RowName {
53 | font-size: .8rem;
54 | line-height: .8rem;
55 | font-weight: bold;
56 | }
57 |
58 | .RowRowNumber {
59 | font-size: 0.6rem;
60 | line-height: 0.6rem;
61 | margin-top: 0.25rem;
62 | }
63 |
64 | .RowStar {
65 | flex: 0 0 auto;
66 | font-size: 1rem !important;
67 | color: #FF502F !important;
68 | }
69 |
70 | .ListScrolling {
71 | font-style: italic;
72 | opacity: 0.5;
73 | }
74 |
--------------------------------------------------------------------------------