From 21bb9eb63e5dbae5e1fbd6318b3059d56000d3d2 Mon Sep 17 00:00:00 2001 From: Etienne Lemay Date: Tue, 31 May 2016 12:35:08 -0400 Subject: [PATCH] Sticky categories label --- css/emoji-picker.css | 18 ++++++++--- src/components/category.js | 65 ++++++++++++++++++++++++++++++-------- src/components/picker.js | 33 +++++++++++++++++-- 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/css/emoji-picker.css b/css/emoji-picker.css index d491201..9495154 100644 --- a/css/emoji-picker.css +++ b/css/emoji-picker.css @@ -8,7 +8,7 @@ font-size: 16px; display: inline-block; color: #222427; - border: 1px solid rgba(0, 0, 0, .15); + border: 1px solid #d9d9d9; border-radius: 5px; } @@ -28,8 +28,8 @@ .emoji-picker-scroll { overflow: scroll; max-height: 270px; - padding: 6px; - border: solid rgba(0, 0, 0, .15); + padding: 0 6px 6px 6px; + border: solid #d9d9d9; border-width: 1px 0; } @@ -38,8 +38,9 @@ display: block; width: 100%; padding: .2em .6em; + margin-top: 6px; border-radius: 25px; - border: 1px solid rgba(0, 0, 0, .15); + border: 1px solid #d9d9d9; outline: 0; } @@ -48,8 +49,17 @@ } .emoji-picker-category-label { + position: relative; + position: -webkit-sticky; + top: 0; +} + +.emoji-picker-category-label span { + display: block; + width: 100%; font-weight: bold; padding: 5px 6px; + background-color: #fff; background-color: rgba(255, 255, 255, .95); } diff --git a/src/components/category.js b/src/components/category.js index b435eae..9345a94 100644 --- a/src/components/category.js +++ b/src/components/category.js @@ -2,32 +2,67 @@ import React from 'react' import Emoji from './emoji' export default class Category extends React.Component { - shouldComponentUpdate(props) { - if (props.perLine != this.props.perLine) { - return true - } + componentDidMount() { + this.container = this.refs.container + this.label = this.refs.label + this.parent = this.container.parentNode - for (let k in props.emojiProps) { - if (props.emojiProps[k] != this.props.emojiProps[k]) { - return true - } - } + this.margin = 0 + this.minMargin = 0 - return false + this.memoizeSize() + } + + componentDidUpdate() { + this.memoizeSize() + } + + memoizeSize() { + var { top, height } = this.container.getBoundingClientRect() + var { top: parentTop } = this.parent.getBoundingClientRect() + var { height: labelHeight } = this.label.getBoundingClientRect() + + this.top = top - parentTop + this.parent.scrollTop + this.maxMargin = height - labelHeight + } + + handleScroll(scrollTop) { + var margin = scrollTop - this.top + margin = margin < this.minMargin ? this.minMargin : margin + margin = margin > this.maxMargin ? this.maxMargin : margin + + if (margin == this.margin) return + this.label.style.top = `${margin}px` + this.margin = margin } render() { - var { name, emojis, perLine, emojiProps } = this.props, + var { name, emojis, perLine, hasStickyPosition, emojiProps } = this.props, emojis = emojis.slice(0), lines = [], - linesCount = Math.ceil(emojis.length / perLine) + linesCount = Math.ceil(emojis.length / perLine), + labelStyles = {}, + labelSpanStyles = {} Array(linesCount).fill().forEach((_, i) => lines.push(emojis.splice(0, perLine)) ) - return
-
{name}
+ if (!hasStickyPosition) { + labelStyles = { + height: 28, + } + + labelSpanStyles = { + position: 'absolute', + } + } + + return
+
+ {name} +
+ {lines.map((emojis, i) =>
{emojis.map((emoji) => @@ -45,6 +80,7 @@ export default class Category extends React.Component { Category.propTypes = { emojis: React.PropTypes.array, + hasStickyPosition: React.PropTypes.bool, name: React.PropTypes.string.isRequired, perLine: React.PropTypes.number.isRequired, emojiProps: React.PropTypes.object.isRequired, @@ -52,4 +88,5 @@ Category.propTypes = { Category.defaultProps = { emojis: [], + hasStickyPosition: true, } diff --git a/src/components/picker.js b/src/components/picker.js index 2472375..7c15870 100644 --- a/src/components/picker.js +++ b/src/components/picker.js @@ -5,11 +5,38 @@ import Preview from './preview' import Category from './category' export default class Picker extends React.Component { + componentWillMount() { + var stickyTestElement = document.createElement('div') + for (let prefix of ['', '-webkit-', '-ms-', '-moz-', '-o-']) { + stickyTestElement.style.position = `${prefix}sticky` + } + + this.hasStickyPosition = !!stickyTestElement.style.position.length + } + + componentDidUpdate() { + this.handleScroll() + } + handleEmojiOver(emoji) { var { preview } = this.refs preview.setState({ emoji: emoji }) } + handleScroll() { + var target = this.refs.scroll, + scrollTop = target.scrollTop + + if (!this.hasStickyPosition) { + Array(data.categories.length).fill().forEach((_, i) => { + var category = this.refs[`category-${i}`] + if (category) { + category.handleScroll(scrollTop) + } + }) + } + } + render() { var { perLine, emojiSize, sheetURL } = this.props @@ -18,20 +45,22 @@ export default class Picker extends React.Component { Categories
-
+
- {data.categories.map((category) => { + {data.categories.map((category, i) => { if (category.name == 'Skins') return null return