-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
feat(material-experimental/mdc-tabs): add option to fit ink bar to content #17507
feat(material-experimental/mdc-tabs): add option to fit ink bar to content #17507
Conversation
private _createIndicator(document: Document) { | ||
this._indicator = document.createElement('span'); |
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.
The name of this class seems a little strange, it's easy to confuse with the concept of a Foundation
in MDC. (I know it wasn't created in this PR, but just something I noticed now).
Why does this class do so much direct DOM manipulation as opposed to being its own component with its own template?
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.
I called this class a foundation because it's a wrapper around MDC's MDCSlidingTabIndicatorFoundation
.
The reason why it needs to do so much manual DOM manipulation is because we use it both for the tab group and the tab nav bar. The nav bar uses a directive for the links so we don't have control over the template.
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.
Ah, I see. Obviously outside the scope of this PR, but maybe we could encapsulate the behavior in a component and then just use ViewContainerRef
to create it and attach it to the element with the directive? Not sure if that's actually feasible, just brainstorming...
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.
I think the boilerplate-wise it would end up being about the same amount of code. Ideally we'd turn the tab link into a component, but that would be a breaking change.
* to match the width of the content rather than the tab host element. | ||
*/ | ||
get fitToContent(): boolean { return this._fitToContent; } | ||
set fitToContent(fitToContent: boolean) { |
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.
I don't think we need the getter here and the setter could be turned into a function. Since this is a private class we don't need to deal with getters and setters.
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.
I just tried that approach but I'm not sure I feel as comfortable with it. There are two conditions I need to meet:
- Let clients see what the current value is
- Let clients set a new value, and the foundation reacts to it
To satisfy the first condition, there needs to be a public value fitToContent
. However, it would not be clear to clients that in order to change this value, they should not write directly to it, but instead use a setFitToContent
function.
If clients didn't need to read the value, I'd agree that a setter would work, but in this case I think the getter/setter pattern is ideal
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.
Kristiyan is saying that this component (ink bar) is not part of the public API, and as such doesn't need a getter/setter (which we only need for Angular inputs) compared to using functions
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.
EDIT: Talked with Jeremy, I did miss that y'all are saying to do "getFitToContent" and "setFitToContent" rather than the ES2015 getter/setter. I'll make the change
OLD REPLY:
Sorry, I see that my explanation may be vague regarding this.
When fitInkBarToContent
is provided as an input to the MatTabGroup
and MatTabNav
, they store the value into the MatInkBarFoundation
. This will be the source of truth for it's value (not cached in the tab group or nav). When the value is set, the foundation needs to render again, hence the need for a setter (this can be either a native setter or setFitToContent
).
When our users want to access the property fitInkBarToContent
, we give them the value stored in MatInkBarFoundation
. There are two ways we can do this, expose the property fitToContent
, or provide a getFitToContent
function. If we expose the property, it's not clear from the MatTabGroup
/MatTabNav
that it should only use that property for getting, not for setting. In the future, we may make a change and not realize the setter must be used, not the property.
If this doesn't make sense, I may be missing what you are proposing. Let's chat in the next team meeting
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.
Andrew and I chatted, he just didn't follow that we meant do e.g. getFitToContent
and setFitToContent
instead
private _createIndicator(document: Document) { | ||
this._indicator = document.createElement('span'); |
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.
I think the boilerplate-wise it would end up being about the same amount of code. Ideally we'd turn the tab link into a component, but that would be a breaking change.
* to match the width of the content rather than the tab host element. | ||
*/ | ||
get fitToContent(): boolean { return this._fitToContent; } | ||
set fitToContent(fitToContent: boolean) { |
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.
Kristiyan is saying that this component (ink bar) is not part of the public API, and as such doesn't need a getter/setter (which we only need for Angular inputs) compared to using functions
@@ -128,16 +141,36 @@ export class MatInkBarFoundation { | |||
this._destroyed = true; | |||
} | |||
|
|||
/** Creates and appends the indicator element. */ |
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.
This should also explain what an "indicator" is.
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.
Since indicator and ink bar are synonymous, I renamed our use of the indicator to be consistently called the "ink bar". Alternatively we can rename everything to "indicator" (and also rename classes like MatInkBar
to MatTabIndicator
).
Where we do mention indicator (in comments referring to MDC's indicator), I made it clearer that we named it ink bar.
get fitInkBarToContent(): boolean { return this._foundation.fitToContent; } | ||
set fitInkBarToContent(v: boolean) { this._foundation.fitToContent = coerceBooleanProperty(v); } | ||
|
||
constructor(public elementRef: ElementRef, @Inject(DOCUMENT) private _document: any) { |
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.
This should define this._document
at the class level as type Document
, and then set the injected document: any
in the constructor
(we use this pattern elsewhere in the library)
cb3cbde
to
011831f
Compare
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.
LGTM
* to match the width of the content rather than the tab host element. | ||
*/ | ||
get fitToContent(): boolean { return this._fitToContent; } | ||
set fitToContent(fitToContent: boolean) { |
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.
Andrew and I chatted, he just didn't follow that we meant do e.g. getFitToContent
and setFitToContent
instead
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
See material components documentation for an example of the ink bar fitting to the tab label content
https://material-components.github.io/material-components-web-catalog/#/component/tabs