-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
RFC: Tracking feature state for interactivity #6021
Comments
I like the idea of doing both, implementing Option 1 using the primitives defined by Option 2. If we implement only Option 2, I suspect we will disappoint the majority of users, who just want a good-enough out-of-the-box solution for feature interactivity. If we implement only Option 1, I suspect we will disappoint our power users, who may run into hard limitations with our feature interactivity system. I wonder if there isn't an Option 3 that's fully declarative and supports all the listed use cases... |
/cc @mapbox/studio |
Capturing our discussion from last week: It seems like we're leaning towards doing a combination of 1 and 2:
On 1) Would states would we track? (hover? active? selected?) We talked about how event though there are a lot of possible types of selectors (checkbox, radio, etc) a simple "zero or one features are selected" would cover a lot of map use cases and might be worth adding. @mollymerp is looking into this ^ On 2) I'm looking into these ^ |
Below is my proposal for the state tracking work. Very interested in folks' feedback/questions/comments: Goals:
What states would we track?
What are the details of when they are set and not?
Which layers can receive events? Is there a way to explicitly enable/disable this?options:
|
Would exposing this as an expression (
With CSS pseudo-classes it is possible for an element to be two things at once, for example both
Which of these do you think we should do? would a combination make sense or would that be weird? |
I was thinking that if we want gl-js to have full control over
hmm yeah I didn't consider this... do you think it makes sense to store state in multiple property keys then? (e.g.
well option 2 doesn't really make sense by itself – so maybe a combination would make sense for ease-of-use. instead of disabling a hover effect by completely overwriting each interactive property's style rules to remove references to tracked state properties, being able to have a one-liner to enable/disable all interactive properties seems useful |
If the hover effects are enabled by explicit requests, does this prevent us from having two features with the same ID on different layers be in a given state at the same time? i.e Can I have a polygon and its label (using a point feature on a separate layer) be hovered together using the style-spec, or does this require using event handlers? |
In the same way that we moved away from
Given @ansis 's point above about multiple states being possible simultaneously, what about having a separate boolean expression for each kind of state:
Yeah, I'm hesitant about this one, too: this feels like a more complicated and less well-defined type of state. Is a feature always visited once it's been clicked? "Visited" since when? Can the user programmatically "reset" the map's set of visited features? Etc. I think we can come back to this in the future if we start to see a clear set of use cases for which a precise design could emerge. |
don't understand this bit but,
yes – I was assuming so – the symbol layer would just have an expression for whatever paint property would be changed on hover as well. |
@anandthakker is the main difference here that |
Let's keep state and properties fully distinct and independent, both in how they are accessed in expressions, and how they are stored internally. (AKA follow the React model.) |
The difference I was referring to was between having a 'special' feature property |
I get that “Hover” is effectively an “active” state. This behavior allows whatever was associated with Our existing hover examples illustrate the problem well:
At a minimum, the style specification would need a way to distinguish between these intentions. Even if we rename “hover” to something less obviously desktop-centric, we’d still need some sort of media query syntax so that developers can choose whether to associate this state with an initial tap on mobile devices. When designing for a touch-enabled device, it’s important to make tappable features always appear tappable from the moment they appear, but that means the effect has to be more subtle than a typical highlight effect. For example, on a desktop, “Get features under the mouse pointer” might highlight a feature only on hover, whereas on a phone, it might subtly outline the feature at all times, since it isn’t possible to scrub a cursor over the map to uncover hidden hit targets. If we go down the route of media queries, why not rework this proposal into a more general framework for state tracking? The style specification would allow arbitrarily named states, and it would be up to the developer to associate these states with predefined events at runtime via an API. The set of events could vary by platform. This would keep the style JSON file format platform-agnostic while also making it possible for mobile platforms to associate states with mobile-specific gestures like force-touch. /ref #200 (comment) |
The link explains that in React "Props are set by the parent and they are fixed throughout the lifetime of a component. For data that is going to change, we have to use state." @jfirebaugh Do you think we should disallow updating feature properties completely? If a user wants to implement a custom
|
It would be nice to be able to update feature properties piecemeal, but I see that as a distinct feature, unrelated to feature interactivity (mapbox/geojson-vt#26). I think there should be an independent API for updating state, under application control. I'm not sure what you mean by "internally tracked state". One major reason to keep properties and state fully separated in this way is to avoid introducing for source data the gnarly refresh/merge challenge we've hit with styles in #4225 (comment). |
Thanks, would there be functional differences between state and properties or would it be mostly just convention? Would state be supported by vectortile and geojson sources while property updates would be geojson-only?
Yep, that's what I meant. State that could potentially be set automatically without the user adding any code @1ec5 we talked about this a bit a week ago, but could you expand on your thoughts here? I think I remember you saying that these kinds of states might sometimes be platform specific, but that having them could still be useful? and that the iOS sdk has a bit of precedent for this with the built-in annotation selection tracking? |
I'm not sure I understand the distinction. If we track some state automatically, we'll need to document that behavior, including the platform-specific nuances. Is that not equivalent to "specifying" it? |
We'd necessarily want to track some state internally so that a style can be used across platforms without having to write code for each. |
I don't think it's exactly equivalent. One way or another a user writing their style sheet has to refer somewhere to know that they can use the word "hover" in The alternative of having the SDKs only provide an API for updating state is appealing at one level, but I think part of the problem we're trying to solve here is that it feels like it more lines of code than it should to "just" have features be styled differently on hover/tap/click. To what extent would the lower-level state update API deliver on that problem? |
It sounds like there's agreement on having an arbitrary list of named states, with some being predefined (and implemented) for each platform. Applications should be responsible for enforcing any rules related to these states. For instance, internal mouse event handlers would be responsible for the following
Whether or not we allow externally defined states, the need for different states per-platform makes it hard to support a separate expression for each kind of state. If features could be in multiple states at the same time, then the |
Yes, there is some precedent for an SDK tracking state: the iOS and macOS SDKs track which annotation is currently selected. Selection means that the annotation dons its selected appearance and any associated callout (popup) is shown. However, that’s the extent of it; the other built-in states proposed above get into behaviors that would differ from platform to platform, which is not a problem for selection.
Associating a particular state with a particular event handler could be a one-liner on each platform, no? At any rate, it would be less code and hopefully more performant than setting up a gesture recognizer/mouse event listener and querying visible features every time it fires. If the style specification allows a library to reserve certain states to be associated with platform-specific behaviors (as with |
Hm, yeah I suppose so (maybe a two-liner for hover -- |
Are there any downsides to
This would allow users to design feature interactivity in Studio (which I understand to be a major design goal) and each platform to behave idiomatically.
|
I'm splitting off my thoughts on per-feature event listeners into a separate issue #6215 to avoid breaking up the "what is feature state" conversation here. |
Bringing over from #6020(comment) Setting stateState should be tracked independently of feature data and assigned per source. This would look like: //Set a feature_id to a named state
map.setState("source", "state", feature_id);
//Clear state
map.setState("source", "state");
//Set one or more feature Ids to a named state
map.setState("source", "state", [feature_id]); States are tracked per source in the SourceCache where they can be applied to tiles when preparing them for upload before every frame. Question: Is there a need for an API to un-set a named state on a single feature or set of features , while preserving other features in that state? Using stateState can be referenced in expressions as part of layer paint properties // Increase opacity for features in the 'highlight' state
"fill-opacity": [ "case", ["state", "highlight"], ["number", 0.9], ["number", 0.5]] or queried through an API: map.getState("state");
// Where the returned object looks like:
{
"source_A" : { "highlight": [1234, ...], "dragging" : [...], ... } ,
"source_B": { "visited" : ["foo", "bar"] }
} |
This would be reminiscent of how CSS works today: there are certain desktop-centric pseudoclasses built into the language, Web design tools expose them all, and each platform figures out how to map them in appropriate ways. Unfortunately, this approach disadvantages mobile platforms, often making it inconvenient or impossible to access content on a multitouch device. #6021 (comment) demonstrates that interactive map features would tend to suffer the same incompatibilities between desktop and mobile devices. One mitigating factor is that CSS has If we were to introduce media query expressions into the style specification, we’d have to coerce designers to take advantage of them. For example, Studio could disallow As I see it, the choice is essentially to make developers bind states to events:
The first option seems to me like it would ultimately involve less code (if you count expressions as code). I don’t know if there’s been any thought towards how Studio would simulate feature interactivity, but I think the answer would look very different depending on which option we go with. |
Motivation
#6020 provides a way to update the appearance of features. But how do you know if a feature is being hovered on? if is being activated? if it's currently selected? if it was selected at some point? We need to have an approach to tracking state.
we should make all of these possible somehow:
:active
)Design Alternatives
Option 1: track state internally
We could define several possible feature interactivity states including
hover
andactive
. We'd listen to mousemove events internally, query for the features under the mouse and update state. Users would access the state using a data-driven expression:Open questions:
"pointer-events": "none"
equivalent?hover
andactive
, what would we want to support?clicked
?focus
?target
?Advantages:
Studio
Disadvantages:
Option 2: provide the tools to let users track state themselves
We would have to:
We would maybe have to:
stopPropagation
)multi-selection
single-selection
Open questions:
Advantages:
Disadvantages:
Design
I think we need to implement Option 2 to cover all the cases we want to cover. But I think it makes sense to also implement Option 1 to provide a solid foundation for basic use cases. So... both?
Concepts
Option 1 introduces new concepts like hover and active which would need to be documented. Option 2 would introduce more complexity around events.
What existing precedents support the new concepts? Pseudo-classes from the web are a precedent for Option 1. Interactivity event listeners in Option 2 have precedents both on the web and mobile.
@kkaefer @asheemmamoowala @mollymerp @anandthakker @mourner @lucaswoj
The text was updated successfully, but these errors were encountered: