Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
coverage
dist
package-lock.json

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package-lock should be commited

3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,4 @@ language: node_js
node_js:
- stable

before_script:
- npm run build-faux-dom

script: npm test
88 changes: 68 additions & 20 deletions dist/hyperlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
var defaultConfig = {
width: '100%',
height: '100%'
};

// Check for valid number.
var isNumber = function isNumber(input) {
// Check for valid number.
};var isNumber = function isNumber(input) {
return Number(input) === Number(input);
};

Expand All @@ -35,20 +34,51 @@ var HyperList = function () {
return new HyperList(element, userProvidedConfig);
}

/**
* Update given attributes on an element
* @param {DOMElement} element
* @param {Object} values
*/

}, {
key: 'transformElement',
value: function transformElement(element, values) {
for (var i in values) {
element.setAttribute(i, values[i]);
}

return element;
}

/**
* Merge given css style on an element
* @param {DOMElement} element
* @param {Object} style
* @param {Boolean} forceClone
*/

}, {
key: 'mergeStyle',
value: function mergeStyle(element, style) {
value: function mergeStyle(element, style, forceClone) {
for (var i in style) {
if (element.style[i] !== style[i]) {
element.style[i] = style[i];
}
}

return element;
}

/**
* Return given attribute key of an element
* @param {DOMElement} element
* @param {String} key
*/

}, {
key: 'inspectElement',
value: function inspectElement(element, key) {
return element.getAttribute(key);
}
}, {
key: 'getMaxBrowserHeight',
Expand Down Expand Up @@ -90,6 +120,18 @@ var HyperList = function () {
this._lastRepaint = null;
this._maxElementHeight = HyperList.getMaxBrowserHeight();

if (!userProvidedConfig.inspectElement || typeof userProvidedConfig.inspectElement !== 'function') {
userProvidedConfig.inspectElement = HyperList.inspectElement;
}

if (!userProvidedConfig.transformElement || typeof userProvidedConfig.transformElement !== 'function') {
userProvidedConfig.transformElement = HyperList.transformElement;
}

if (!userProvidedConfig.mergeStyle || typeof userProvidedConfig.mergeStyle !== 'function') {
userProvidedConfig.mergeStyle = HyperList.mergeStyle;
}

this.refresh(element, userProvidedConfig);

var config = this._config;
Expand Down Expand Up @@ -131,7 +173,7 @@ var HyperList = function () {

Object.assign(this._config, defaultConfig, userProvidedConfig);

if (!element || element.nodeType !== 1) {
if (!element || typeof element.render !== 'function' && element.nodeType !== 1) {
throw new Error('HyperList requires a valid DOM Node container');
}

Expand Down Expand Up @@ -204,7 +246,7 @@ var HyperList = function () {
position: 'relative'
};

HyperList.mergeStyle(element, elementStyle);
config.mergeStyle(element, elementStyle);

var scrollerHeight = config.itemHeight * config.total;
var maxElementHeight = this._maxElementHeight;
Expand All @@ -218,10 +260,10 @@ var HyperList = function () {
position: 'absolute'
}, _defineProperty(_scrollerStyle, isHoriz ? 'height' : 'width', '1px'), _defineProperty(_scrollerStyle, isHoriz ? 'width' : 'height', scrollerHeight + 'px'), _scrollerStyle);

HyperList.mergeStyle(scroller, scrollerStyle);
config.mergeStyle(scroller, scrollerStyle);

// Only append the scroller element once.
if (!this._scroller) {
// Only append the scroller element once if DOM node.
if (!this._scroller && element.nodeType === 1) {
element.appendChild(scroller);
}

Expand Down Expand Up @@ -262,18 +304,22 @@ var HyperList = function () {
height = this._itemHeights[i];
}

if (!item || item.nodeType !== 1) {
if (!item || config.transformElement === HyperList.transformElement && item.nodeType !== 1) {
throw new Error('Generator did not return a DOM Node for index: ' + i);
}

var oldClass = item.getAttribute('class') || '';
item.setAttribute('class', oldClass + ' ' + (config.rowClassName || 'vrow'));
var oldClass = config.inspectElement(item, 'class') || '';
item = config.transformElement(item, {
key: i,
class: oldClass + ' ' + (config.rowClassName || 'vrow')
});

var top = this._itemPositions[i];

HyperList.mergeStyle(item, _defineProperty({
// Possibly need to clone element
item = config.mergeStyle(item, _defineProperty({
position: 'absolute'
}, config.horizontal ? 'left' : 'top', top + 'px'));
}, config.horizontal ? 'left' : 'top', top + 'px'), true);

return item;
}
Expand Down Expand Up @@ -337,13 +383,15 @@ var HyperList = function () {
return config.applyPatch(element, fragment);
}

element.innerHTML = '';
element.appendChild(fragment);
if (element.nodeType === 1) {
element.innerHTML = '';
element.appendChild(fragment);
}
}
}, {
key: '_computePositions',
value: function _computePositions() {
var from = arguments.length <= 0 || arguments[0] === undefined ? 1 : arguments[0];
var from = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;

var config = this._config;
var total = config.total;
Expand All @@ -368,7 +416,7 @@ var HyperList = function () {
}, {
key: '_computeScrollHeight',
value: function _computeScrollHeight() {
var _HyperList$mergeStyle2,
var _config$mergeStyle2,
_this2 = this;

var config = this._config;
Expand All @@ -378,10 +426,10 @@ var HyperList = function () {
return a + b;
}, 0);

HyperList.mergeStyle(this._scroller, (_HyperList$mergeStyle2 = {
config.mergeStyle(this._scroller, (_config$mergeStyle2 = {
opacity: 0,
position: 'absolute'
}, _defineProperty(_HyperList$mergeStyle2, isHoriz ? 'height' : 'width', '1px'), _defineProperty(_HyperList$mergeStyle2, isHoriz ? 'width' : 'height', scrollHeight + 'px'), _HyperList$mergeStyle2));
}, _defineProperty(_config$mergeStyle2, isHoriz ? 'height' : 'width', '1px'), _defineProperty(_config$mergeStyle2, isHoriz ? 'width' : 'height', scrollHeight + 'px'), _config$mergeStyle2));

// Calculate the height median
var sortedItemHeights = this._itemHeights.slice(0).sort(function (a, b) {
Expand Down
142 changes: 87 additions & 55 deletions examples/react-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,94 +54,126 @@
<main></main>

<script type="text/javascript" src="../dist/hyperlist.js"></script>
<script type="text/javascript" src="../dist/react-faux-dom.js"></script>
<script type="text/javascript" src="../node_modules/react/dist/react.js"></script>
<script type="text/javascript" src="../node_modules/react-dom/dist/react-dom.js"></script>
<script>
const main = document.querySelector('main');

class Container extends React.Component {
class HyperListReact extends React.Component {
render() {
return React.createElement('div', this.state.element,
this.state.fragment
);
}

constructor(props) {
super(props);

this.container = ReactFauxDOM.createElement('div');
this.handleRef = this.handleRef.bind(this);

this.state = {
// Actual state.
children: React.createElement('div'),

// Configuration for HyperList.
config: {
// User configurable via props.
height: props.height,
itemHeight: props.itemHeight,
total: props.total,
reverse: props.reverse,

useFragment: false,
scroller: ReactFauxDOM.createElement('div'),

// Required to override since React Faux DOM doesn't have scrollTop.
overrideScrollPosition() {
return main.firstChild.scrollTop;
},

applyPatch(element, fragment) {
element.childNodes.length = 0;
fragment.forEach(childNode => element.appendChild(childNode));
},

afterRender: () => {
this.state.children = this.container.toReact();
this.setState(this.state);
},
element: {
ref: this.handleRef
},
scroller: {
key: 'scroller',
style: {
position: 'relative'
}
},
fragment: [],
node: {}
};
}

generate(row) {
var el = ReactFauxDOM.createElement('div');
var p = ReactFauxDOM.createElement('p');
handleRef(node) {
this.setState({ node })
}

el.appendChild(p);
componentDidMount() {
const {
height = window.innerHeight,
itemHeight = 50,
total = 0,
reverse = false,
generate = () => {}
} = this.props;

const config = {
height,
itemHeight,
total,
generate,
scroller: React.createElement('div', this.state.scroller),
useFragment: false,
overrideScrollPosition: () => this.state.node.scrollTop || 0,
inspectElement: (element, key) => element.props[key === 'class' ? 'className' : key] || null,
transformElement: (element, values) => {
if (values.class) {
values.className = values.class
delete values.class
}

p.innerHTML = 'ITEM ' + row;
return React.cloneElement(element, values)
},
mergeStyle: (element, style, forceClone) => {
if (forceClone) {
return React.cloneElement(element, { style })
}

return el;
for (let i in style) {
if (element.props.style[i] !== style[i]) {
element.props.style[i] = style[i]
}
}
}
};
}

componentDidMount() {
const config = this.state.config;
return element
},
applyPatch: (element, fragment) => {
this.setState({
element: Object.assign({}, this.state.element, {
style: Object.assign({}, this.state.element.style, element.props.style)
}),
fragment
});
},
};

// Create the HyperList once and refresh it whenever the resize event
// triggers.
this.list = HyperList.create(this.container, config);
this.list = HyperList.create(this, config);

// Bind to the resize event, and since you should only ever have one
// handler bound to this, we pave over whatever you had set before.
window.onresize = e => {
config.height = window.innerHeight;
this.list.refresh(this.container, config);
this.list.refresh(this, config);
};

this.container.setAttribute('class', 'container');
}

componentWillUnmount() {
window.onresize = null;
this.list.destroy();
}
}

HyperListReact.defaultProps = {
style: {}
}

class MyComponent extends React.Component {
render() {
return this.state.children;
return React.createElement(HyperListReact, {
itemHeight: 30,
total: 100000,
generate: this.generate
})
}

generate(row) {
return React.createElement('div', {}, 'Item ' + row);
}
}

ReactDOM.render(React.createElement(Container, {
height: window.innerHeight,
itemHeight: 50,
total: 100000,
reverse: false,
}), main);
ReactDOM.render(React.createElement(MyComponent), main);
</script>
</body>
</html>
Loading