Code Monkey home page Code Monkey logo

component-mixins's Introduction

component-mixins's People

Contributors

dependabot[bot] avatar sissbruecker avatar web-padawan avatar yuriy-fix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

component-mixins's Issues

Implement KeyboardMixin for internal usage by other mixins

Currently, there are different mixins that add keydown and keyup listeners.

We should change that so we only have those listeners in one place.
The idea is that there will be KeyboardMixin handling those.

It has to be a deduping mixin (same as DirectionMixin) to only add listeners once.

Polymer dependency

I am not too sure how to phrase this without sounding like a too demanding jerk.
I am well aware it is ~epic. Also, it is mentioned in different issues that it is not going to happen anytime soon.

But anyway, removing Polymer dependency would be (really) nice.

Not that I don't like Polymer, on the contrary, but it is currently not possible to use recent lit-element based Vaadin components in application running older version of Polymer (mostly because dom-module is registered twice, once for every Polymer version).

Is there any plan to replace themable-mixin (I think the only reason for Polymer Dependency) with approaches tested by @jouni here: https://jelements.netlify.app/stylable-mixin ?

Thanks,
C.

[themable-element] Use theming hook from LitElement

The current hack with updating private _styles property is a temporary solution only.

There is an issue related to adding new API in lit/lit-element#870
Once the getStyles() API is implemented, we should update ThemableElement to use that.

This means there will be a minimal lit-element version required by Vaadin components (2.3.0 or newer). We should keep that in mind when we add components to platform.

Implement checked-state-mixin

In vaadin-checkbox and vaadin-radio-button we have checked property.

checked: {
  type: Boolean,
  value: false,
  notify: true,
  observer: '_checkedChanged',
  reflectToAttribute: true
}

Then there is also aria-checked attribute and _checkedChanged observer.

We should extract that to mixin. In future it could be also used by toggle switch component.

Research: prototype MediaQueryPropertyMixin

Problem

In certain components we apply CSS conditionally based on media queries.
We don't have a unified approach for it, so components handle it differently.

1. JS API

Example: vaadin-date-picker with _fullscreenMediaQuery set from JS which toggles the fullscreen state attribute on vaadin-date-picker-overlay.

When using this approach, we currently hardcode media query also in CSS (example) which makes overriding it more complex (as it needs to be done in multiple places).

2. CSS API

Example: vaadin-app-layout with following CSS custom properties:

Both of these are observed in window resize event handler (there is no platform way to observe changes to custom CSS properties so far, see also discussion about observedStyles).

Then those true / false are used to show / hide parts of the DOM and one of them is synced to overlay property (which reflects to attribute and is read-only).

While this approach lets the user to write their CSS and override the defaults, it still requires boilerplate work for every component and that doesn't sound like a good DX.

Solution proposal

Let's assume we have a mixin with the following API:

static get mediaQueries() {
  return {
    touchOptimized: '(max-width: 800px) and (min-height: 500px)'
  };
}

Then we declare property with noAccessor flag:

static get properties() {
  return {
    touchOptimized: {
      type: Boolean,
      reflect: true, // optional. set if we need to sync with an attribute
      attribute: 'touch-oprimized' // optional. set to have dash-case
    }
  };
}

For each of the properties declared like this, the mixin does the following:

  • create window.matchMedia and add listener to the mediaQueries[prop]
  • create custom accessors that make sure the property is read only
  • toggle property value between true and false depending on media query
  • when attribute is set manually, keep it and override media query based value
    • this is needed to allow forcing specific appearance e.g. touch-optimized
  • when attribute is removed manually, watch to media query changes again

This approach (only allow to force the value that is not the default one) is inspired by the way how we handle dir attribute in DirMixin when set by the user to a different value).

Align using extensions when importing mixins

The mixin imports (that end up in JS code) are currently imported with extension.
For dumb classes / interfaces we don't really need it so it's generally omitted.

It would be nice to align these cases in order to be consistent.

Agree on the structure: monorepo vs single npm package

This project is currently a monorepo managed by Lerna.
Whether we want to keep this is a question to decide on.

Monorepo

Pros

  • It's clear what should be imported from where (single big package could become messy)
  • Developers can choose what mixins to install in their components
  • Bonus: we can investigate how to run tests for affected packages only (maybe to Rush)

Cons

  • We can't cherry-pick to older feature branches.
  • Many versions to publish and update in components

Summary

We should be very careful if we want to go with a monorepo. It could work if we do it this way:

  • Every mixin only does exactly one thing it was designed to do.
  • No new features to existing mixins - only bug fixes that can land in patch releases.
  • New feature means there is a new mixin to be added for it.
  • Changing a mixin in a way that requires adding another mixin is a breaking change.

Single npm package

Pros

  • Everything in one package - easy to manage branches and releases

Cons

  • Whenever new feature is added, all the components must update.

Summary

Easier in terms of cherry-picking (as soon as "next generation" becomes LTS with minors).
Potentially challenging in terms of release frequency before that (if we have many mixins).

[element-base] Implement property change notifications

As se will be dispatching a lot of "-changed" events, we should have some kind of a helper (in element mixin for example) that would reduce the boilerplate from:

this.dispatchEvent(
  new CustomEvent('opened-changed', {
    detail: { value: this.opened }
  })
);

To something likethis._dispatchChangedEvent('opened');

Or if there is a cleaner way, we could make it like that:

static get notifyingProperties {
 return ['opened'];
}

Implement roving tabindex mixin

There is a part of duplicating logic in different components:

This logic should be extracted to a separate mixin so we can reuse it.

We might include _vertical as a protected property, and decouple it from orientation.
So technically it would allow us to have vertical accordion, alongside tabs / list box.

Implement single selection mixin

The mixin will be shared by vaadin-list-box and vaadin-tabs.

It should provide selected property, logic to set it on click and keydown.

The mixin should require to apply SlottedItemsMixin and KeyboardNavigationMixin

Implement disabled state mixin

  1. Extract the corresponding logic from vaadin-item-mixin to a separate mixin

  2. Use that mixin in ControlStateMixin instead of having that code duplicated

Implement button mixin

The mixin would be needed to create vaadin-drawer-toggle do not depend on vaadin-button.
The part which can be extracted is related to active state and corresponding event listeners.

The code related to mousedown / touchstart event handling can be adapted from the Polymer gestures in order to not depend on GestureEventListeners mixin anymore.

Implement active state mixin

The active state attribute set on mousedown / touchstart and removed on mouseup / touchend is currently handled differently in vaadin-button and vaadin-item-mixin.

We should have a unified implementation in ActiveStateMixin that would be used by:

  • ItemMixin (#5)
  • ButtonMixin (#6)

Initial code review

None of the code has been reviewed. This is an issue to track this.
There will be a PR only containing packages code linked to it.

Implement mixin-utils package for usage by other mixins

The following code is going to be needed in multiple places:

type Constructor<T = object> = new (...args: any[]) => T;

const appliedMixins = new Set();

function getPrototypeChain<T>(obj: T) {
  const chain = [];
  let proto = obj;
  while (proto) {
    chain.push(proto);
    proto = Object.getPrototypeOf(proto);
  }
  return chain;
}

function wasApplied(superClass: Constructor<HTMLElement>): boolean {
  const classes = getPrototypeChain(superClass);
  return classes.reduce((res: boolean, klass) => res || appliedMixins.has(klass), false);
}

getPrototypeChain and wasApplied are used in DirectionMixin and will be also needed to implement KeyboardMixin (#46) as well as others deduping mixins.

And the Constructor type is already copy-pasted pretty much everywhere.

Time to create a common utils package.

Create package for browser-specific helpers

We need a package where to place detectIosNavBar helper used by various components.
Currently it is duplicated at least in the following components:

  • vaadin-app-layout
  • vaadin-overlay

Other potential helpers that can be also added later.

  • touch device support: context-menu, grid (see also vaadin/vaadin-grid#1222)
  • IE, Safari and Firefox detection: grid, lot of tests

These should be moved to the separate package (could include a mixin, e.g. DeviceDetectorMixin or DeviceAwareMixin).

It is better to also do these checks once, in the form of constants, and then export these.

Implement orientation mixin

One more piece to extract from vaadin-list-mixin is code related to orientation property.

We only need it for vaadin-tabs but I still think it is worth extracting.
So it would be theoretically easier to implement one of the following:

  • horizontal orientation for vaadin-accordion
  • vertical orientation for vaadin-menu-bar

Someone could also extend vaadin-custom-field and apply that mixin.

The alternative would be to keep that code in vaadin-tabs only.
But IMO it's worth splitting to another mixin right away.

Implement resize-observer-mixin

We should replace IronResizableBehavior with the native Resize Observer.
This API is not supported by older Safari versions, so we need to load a polyfill.

The mixin should create an observer instance in the connectedCallback in order to observe element size change, and then call disconnect() inside of disconnectedCallback.

This mixin should be used in vaadin-tabs, vaadin-form-layout etc.

Implement slotted-label-mixin

In certain components, namely vaadin-checkbox and vaadin-radio-button we have this logic:

ready() {
  super.ready();

  this.shadowRoot.querySelector('[part~="label"]').querySelector('slot')
    .addEventListener('slotchange', this._updateLabelAttribute.bind(this));

  this._updateLabelAttribute();
}

_updateLabelAttribute() {
  const label = this.shadowRoot.querySelector('[part~="label"]');
  const assignedNodes = label.firstElementChild.assignedNodes();
  if (this._isAssignedNodesEmpty(assignedNodes)) {
    label.setAttribute('empty', '');
  } else {
    label.removeAttribute('empty');
  }
}

We should extract it to a mixin, e.g. @vaadin/slotted-label-mixin and use that.

Refactor slotted-items-mixin to not use underscored _items

Currently the _items property is protected but the components that implement mixin have to check for it in updated(props) where they test props.has('_items') which is an anti-pattern.

We should update the mixin to create custom accessors for public items property.

Agree on inheriting event listeners from the mixins

From #23 (comment)

Just started thinking that why do we have this kind of event handlers (with very generic name) as protected API?

I think this can cause problems if the user/dev provides a base class that already has an event handler with similar generic name and this can also soon become a problem/conflict in our own classes.

Currently these implementations don't check for possible super method, but even if they would, I think it's somehow weird that we have these generic event name based methods as part of an abstract class that should define the API for this mixin behaviour, but these method names don't seem to be any way related to the mixin behaviour in my opinion.

Should we handle these in some other way?

Current implementation with the dumb classes workarounds microsoft/TypeScript#17744.

While it works, having generic names like _onKeyDown and _onClick is questionable.
We need to agree on the API we use, to make sure it is convenient and extensible.

Use deduping for all the mixins to support using wasApplied

Originally mentioned at vaadin/vaadin-radio-button#141 (comment)

So we have wasApplied used for deduping mixins: DirectionMixin, KeyboardMixin.
And then we also have another approach in non-deduping (public) mixins:

/**
* Used for mixin detection because `instanceof` does not work with mixins.
*/
static hasSelectedStateMixin = true;

The idea is to always make mixins deduping and use wasApplied.

That might be slightly less performant in terms of prototype chain lookup.

Implement down & up mixin

We already have logic for event listeners (touchstart, touchend, mousedown, mouseup):

protected _onMouseDown(event: MouseEvent) {
// Only process events for the left button.
if (event.buttons !== 1 || this.disabled) {
return;
}
this._setActive(true);
const upListener = (e: MouseEvent) => {
this._onMouseUp(e);
document.removeEventListener('mouseup', upListener);
};
document.addEventListener('mouseup', upListener);
}

It would be nice to extract that to a mixin, and then use it:

We could consider providing methods like _onDown & _onUp as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.