-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/enyo 4811 roysutton #1301
Changes from 15 commits
a8b70cb
97db4d6
a6e9b0a
43ba215
6770d65
2c4f9dc
eccc37c
828998b
237bf44
b7264cd
ca7eef6
67baa87
7c3be74
4a0b920
7e966cf
963e3ba
858a079
6211278
48c88f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import {forward} from '@enact/core/handle'; | ||
import hoc from '@enact/core/hoc'; | ||
import {Job} from '@enact/core/util'; | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
|
@@ -33,6 +34,22 @@ const contextTypes = { | |
*/ | ||
complete: PropTypes.func, | ||
|
||
/** | ||
* Called by Marquee instances when hovered | ||
* | ||
* @type {Function} | ||
* @memberof moonstone/Marquee.Marquee.contextTypes | ||
*/ | ||
enter: PropTypes.func, | ||
|
||
/** | ||
* Called by Marquee instances when unhovered | ||
* | ||
* @type {Function} | ||
* @memberof moonstone/Marquee.Marquee.contextTypes | ||
*/ | ||
leave: PropTypes.func, | ||
|
||
/** | ||
* Called to register a Marquee instance to be synchronized | ||
* | ||
|
@@ -111,12 +128,20 @@ const MarqueeController = hoc(defaultConfig, (config, Wrapped) => { | |
return { | ||
cancel: this.handleCancel, | ||
complete: this.handleComplete, | ||
enter: this.handleEnter, | ||
leave: this.handleLeave, | ||
register: this.handleRegister, | ||
start: this.handleStart, | ||
unregister: this.handleUnregister | ||
}; | ||
} | ||
|
||
componentWillUnmount () { | ||
this.cancelJob.stop(); | ||
} | ||
|
||
cancelJob = new Job(() => this.doCancel(), 30) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a bit magical. It was supposed to be long enough to make it async but not so long that the user would notice a lag between unhovering/unspotting and the marquee stopping. It should be long enough that as the move they pointer between the different parts of a controlled set that it wouldn't reset. I was thinking 100-300ms was probably a reasonable number but 30 seems to work well in testing. It may be possible to go without it given the ordering of the events but I didn't want to rely on possibly unpredictable event ordering. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎩 🐰 Admittedly feels a bit dirty... |
||
|
||
/* | ||
* Registers `component` with a set of handlers for `start` and `stop`. | ||
* | ||
|
@@ -169,6 +194,7 @@ const MarqueeController = hoc(defaultConfig, (config, Wrapped) => { | |
* @returns {undefined} | ||
*/ | ||
handleStart = (component) => { | ||
this.cancelJob.stop(); | ||
if (!this.anyRunning()) { | ||
this.markAll(STATE.ready); | ||
this.dispatch('start', component); | ||
|
@@ -182,9 +208,16 @@ const MarqueeController = hoc(defaultConfig, (config, Wrapped) => { | |
* | ||
* @returns {undefined} | ||
*/ | ||
handleCancel = (component) => { | ||
handleCancel = () => { | ||
this.cancelJob.start(); | ||
} | ||
|
||
doCancel = () => { | ||
if (this.isHovered || this.isFocused) { | ||
return; | ||
} | ||
this.markAll(STATE.inactive); | ||
this.dispatch('stop', component); | ||
this.dispatch('stop'); | ||
} | ||
|
||
/* | ||
|
@@ -197,17 +230,34 @@ const MarqueeController = hoc(defaultConfig, (config, Wrapped) => { | |
handleComplete = (component) => { | ||
const complete = this.markReady(component); | ||
if (complete) { | ||
this.cancelJob.stop(); | ||
this.markAll(STATE.ready); | ||
this.dispatch('start'); | ||
} | ||
} | ||
|
||
handleEnter = () => { | ||
this.isHovered = true; | ||
if (!this.anyRunning()) { | ||
this.dispatch('start'); | ||
} | ||
this.cancelJob.stop(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to stop the job if we're hovered now? Will the job's fn exit early? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 We -could- rely on the job to run out and be trapped by the hover event. It seemed wiser to always clean up the job than to rely on logic elsewhere. I'm game for removing these extra ones. |
||
} | ||
|
||
handleLeave = () => { | ||
this.isHovered = false; | ||
this.cancelJob.start(); | ||
} | ||
|
||
/* | ||
* Handler for the focus event | ||
*/ | ||
handleFocus = (ev) => { | ||
this.isFocused = true; | ||
this.dispatch('start'); | ||
if (!this.anyRunning()) { | ||
this.dispatch('start'); | ||
} | ||
this.cancelJob.stop(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar question here as for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar answer here as for |
||
forwardFocus(ev, this.props); | ||
} | ||
|
||
|
@@ -216,8 +266,7 @@ const MarqueeController = hoc(defaultConfig, (config, Wrapped) => { | |
*/ | ||
handleBlur = (ev) => { | ||
this.isFocused = false; | ||
this.dispatch('stop'); | ||
this.markAll(STATE.inactive); | ||
this.cancelJob.start(); | ||
forwardBlur(ev, this.props); | ||
} | ||
|
||
|
@@ -312,7 +361,10 @@ const MarqueeController = hoc(defaultConfig, (config, Wrapped) => { | |
props = { | ||
...this.props, | ||
onBlur: this.handleBlur, | ||
onFocus: this.handleFocus | ||
onFocus: this.handleFocus, | ||
// When picker button becomes disabled, it doesn't fire blur, but does fire | ||
// `onSpotlightDisappear`. We should investigate why `onBlur` does not fire | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any clues from facebook/react#9142 (comment)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting find! Could be related. Talked some with @germboy about it but we didn't reach any conclusions. |
||
onSpotlightDisappear: this.handleBlur | ||
}; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -299,6 +299,8 @@ const MarqueeDecorator = hoc(defaultConfig, (config, Wrapped) => { | |
this.textDirectionValidated = false; | ||
} else if (next.marqueeOn !== marqueeOn || next.marqueeDisabled !== marqueeDisabled || next.marqueeSpeed !== marqueeSpeed) { | ||
this.cancelAnimation(); | ||
} else if (next.disabled && this.isHovered && marqueeOn === 'focus' && this.sync) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain how the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this scenario, we're checking for a transition to disabled when we were in 'focus' start. In this case, we could get a 'blur' event, which would cancel the marquee. Since we're hovered, we want to make sure the marquee continues. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not to mention that we don't fire the 'enter' event when focused and enabled. |
||
this.context.enter(this); | ||
} | ||
} | ||
|
||
|
@@ -372,7 +374,8 @@ const MarqueeDecorator = hoc(defaultConfig, (config, Wrapped) => { | |
this.forceRestartMarquee || | ||
!this.sync && ( | ||
(this.isFocused && this.props.marqueeOn === 'focus') || | ||
(this.isHovered && this.props.marqueeOn === 'hover') | ||
(this.isHovered && this.props.marqueeOn === 'hover') || | ||
(this.isHovered && this.props.marqueeOn === 'focus' && this.props.disabled) | ||
) | ||
); | ||
} | ||
|
@@ -568,6 +571,7 @@ const MarqueeDecorator = hoc(defaultConfig, (config, Wrapped) => { | |
cancelAnimation = () => { | ||
if (this.sync) { | ||
this.context.cancel(this); | ||
return; | ||
} | ||
|
||
this.stop(); | ||
|
@@ -592,35 +596,51 @@ const MarqueeDecorator = hoc(defaultConfig, (config, Wrapped) => { | |
|
||
handleFocus = (ev) => { | ||
this.isFocused = true; | ||
this.setState((prevState) => { | ||
if (!prevState.animating) { | ||
this.startAnimation(); | ||
} | ||
return null; | ||
}); | ||
if (!this.sync) { | ||
this.setState((prevState) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth throwing this into a fn? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :indeed: |
||
if (!prevState.animating) { | ||
this.startAnimation(); | ||
} | ||
return null; | ||
}); | ||
} | ||
forwardFocus(ev, this.props); | ||
} | ||
|
||
handleBlur = (ev) => { | ||
this.isFocused = false; | ||
this.cancelAnimation(); | ||
if (!this.sync) { | ||
this.cancelAnimation(); | ||
} | ||
forwardBlur(ev, this.props); | ||
} | ||
|
||
handleEnter = (ev) => { | ||
this.isHovered = true; | ||
this.setState((prevState) => { | ||
if (!prevState.animating) { | ||
this.startAnimation(); | ||
if (this.props.disabled || this.props.marqueeOn === 'hover') { | ||
if (this.sync) { | ||
this.context.enter(this); | ||
} else { | ||
this.setState((prevState) => { | ||
if (!prevState.animating) { | ||
this.startAnimation(); | ||
} | ||
return null; | ||
}); | ||
} | ||
return null; | ||
}); | ||
} | ||
forwardEnter(ev, this.props); | ||
} | ||
|
||
handleLeave = (ev) => { | ||
this.isHovered = false; | ||
this.cancelAnimation(); | ||
if (this.props.disabled || this.props.marqueeOn === 'hover') { | ||
if (this.sync) { | ||
this.context.leave(this); | ||
} else { | ||
this.cancelAnimation(); | ||
} | ||
} | ||
forwardLeave(ev, this.props); | ||
} | ||
|
||
|
@@ -669,7 +689,7 @@ const MarqueeDecorator = hoc(defaultConfig, (config, Wrapped) => { | |
} | ||
|
||
// TODO: cancel others on hover | ||
if (marqueeOnHover || (disabled && marqueeOnFocus)) { | ||
if (marqueeOnHover || marqueeOnFocus) { | ||
rest[enter] = this.handleEnter; | ||
rest[leave] = this.handleLeave; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,6 +93,7 @@ | |
border-width: 0; | ||
border-style: solid; | ||
border-radius: inherit; | ||
pointer-events: none; | ||
} | ||
|
||
.icon { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
import Holdable from '../Holdable'; | ||
import {forward, handle} from '@enact/core/handle'; | ||
import kind from '@enact/core/kind'; | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import Pure from '@enact/ui/internal/Pure'; | ||
|
||
import {contextTypes} from '../../Marquee/MarqueeController'; | ||
import Holdable from '../Holdable'; | ||
import Icon from '../../Icon'; | ||
import IconButton from '../../IconButton'; | ||
import {withSkinnableProps} from '../../Skinnable'; | ||
|
@@ -26,10 +28,35 @@ const PickerButtonBase = kind({ | |
spotlightDisabled: PropTypes.bool | ||
}, | ||
|
||
defaultProps: { | ||
joined: false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, not any longer since I removed |
||
}, | ||
|
||
contextTypes: contextTypes, | ||
|
||
styles: { | ||
css | ||
}, | ||
|
||
handlers: { | ||
onMouseEnter: handle( | ||
forward('onMouseEnter'), | ||
(ev, props, context) => { | ||
if (context.enter) { | ||
context.enter(null); | ||
} | ||
} | ||
), | ||
onMouseLeave: handle( | ||
forward('onMouseLeave'), | ||
(ev, props, context) => { | ||
if (context.leave) { | ||
context.leave(null); | ||
} | ||
} | ||
) | ||
}, | ||
|
||
computed: { | ||
className: ({hidden, styler}) => styler.append({ | ||
hidden | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming we couldn't do a shallow test here, there should be a cleaner way to write this test so that we aren't checking the specific number of elements (1) with a
disabled
prop set totrue
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @viodragon2