| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- /* eslint-disable no-param-reassign */
- import BaseFoundation, { DefaultAdapter } from '../base/foundation';
- import warning from '../utils/warning';
- export interface RatingAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
- focus: () => void;
- getStarDOM: (index: number) => Element;
- notifyHoverChange: (hoverValue: number, clearedValue: number) => void;
- updateValue: (value: number) => void;
- clearValue: (clearedValue: number) => void;
- notifyFocus: (e: any) => void;
- notifyBlur: (e: any) => void;
- notifyKeyDown: (e: any) => void;
- setEmptyStarFocusVisible: (focusVisible: boolean) => void
- }
- export default class RatingFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<RatingAdapter<P, S>, P, S> {
- constructor(adapter: RatingAdapter<P, S>) {
- super({ ...RatingFoundation.defaultAdapter, ...adapter });
- }
- init() {
- const { autoFocus, disabled } = this.getProps();
- if (autoFocus && !disabled) {
- this._adapter.focus();
- }
- }
- _getScroll(w: Window, top?: boolean) {
- let ret = top ? w.pageYOffset : w.pageXOffset;
- const method = top ? 'scrollTop' : 'scrollLeft';
- if (typeof ret !== 'number') {
- const d = w.document;
- // ie6,7,8 standard mode
- ret = d.documentElement[method];
- if (typeof ret !== 'number') {
- // quirks mode
- ret = d.body[method];
- }
- }
- return ret;
- }
- _getClientPosition(elem: Element) {
- let x, y;
- const doc = elem.ownerDocument;
- const { body } = doc;
- const docElem = doc && doc.documentElement;
- const box = elem.getBoundingClientRect();
- x = box.left;
- y = box.top;
- x -= docElem.clientLeft || body.clientLeft || 0;
- y -= docElem.clientTop || body.clientTop || 0;
- return {
- left: x,
- top: y,
- };
- }
- _getOffsetLeft(el: Element) {
- const pos = this._getClientPosition(el);
- const doc = el.ownerDocument;
- const w: Window = doc.defaultView || (doc as any).parentWindow;
- pos.left += this._getScroll(w);
- return pos.left;
- }
- getStarValue(index: number, pos: number) {
- const { allowHalf } = this.getProps();
- const direction = this._adapter.getContext('direction');
- const reverse = direction === 'rtl';
- let value = index + 1;
- if (allowHalf) {
- const starEle = this._adapter.getStarDOM(index);
- const leftDis = this._getOffsetLeft(starEle);
- const width = starEle.clientWidth;
- if (reverse && pos - leftDis > width / 2) {
- value -= 0.5;
- } else if (!reverse && pos - leftDis < width / 2) {
- value -= 0.5;
- }
- }
- return value;
- }
- handleHover(event: any, index: number) {
- const currValue = this.getStarValue(index, event.pageX);
- const { clearedValue, hoverValue } = this.getStates();
- if ((currValue !== hoverValue) && (currValue !== clearedValue)) {
- this._adapter.notifyHoverChange(currValue, null);
- }
- }
- handleMouseLeave() {
- this._adapter.notifyHoverChange(undefined, null);
- }
- handleClick(event: any, index: number) {
- const { allowClear } = this.getProps();
- const { value } = this.getStates();
- const newValue = this.getStarValue(index, event.pageX);
- const isReset = allowClear ? newValue === value : false;
- this._adapter.updateValue(isReset ? 0 : newValue);
- if (isReset) {
- this._adapter.notifyHoverChange(undefined, newValue);
- } else {
- this._adapter.clearValue(null);
- }
- }
- handleFocus(e: any) {
- this._adapter.notifyFocus(e);
- }
- handleBlur(e: any) {
- this._adapter.notifyBlur(e);
- }
- handleKeyDown(event: any, value: number) {
- const { key } = event;
- const { count, allowHalf } = this.getProps();
- const direction = this._adapter.getContext('direction');
- const reverse = direction === 'rtl';
- const step = allowHalf ? 0.5 : 1;
- let tempValue: number;
- let newValue: number;
- if (key === 'ArrowRight' || key === 'ArrowUp') {
- tempValue = value + (reverse ? - step : step);
- } else if (key === 'ArrowLeft' || key === 'ArrowDown') {
- tempValue = value + (reverse ? step : - step);
- }
- if (tempValue > count) {
- newValue = 0;
- } else if (tempValue < 0) {
- newValue = count;
- } else {
- newValue = tempValue;
- }
- if (['ArrowRight', 'ArrowUp', 'ArrowLeft', 'ArrowDown'].includes(key)) {
- this._adapter.notifyKeyDown(event);
- this._adapter.updateValue(newValue);
- this.changeFocusStar(newValue, event);
- event.preventDefault();
- this._adapter.notifyHoverChange(undefined, null);
- }
- }
- changeFocusStar(value: number, event: any) {
- const { count, allowHalf, preventScroll } = this.getProps();
- const index = Math.ceil(value) - 1;
- const starElement = [...event.currentTarget.childNodes].map(item => item.childNodes[0].childNodes);
- if (index < 0) {
- starElement[count][0].focus({ preventScroll });
- } else {
- starElement[index][allowHalf ? (value * 10 % 10 === 5 ? 0 : 1) : 0].focus({ preventScroll });
- }
- }
- handleStarFocusVisible = (event: any) => {
- const { target } = event;
- const { count } = this.getProps();
- // when rating 0 is focus visible
- try {
- if (target.matches(':focus-visible')) {
- this._adapter.setEmptyStarFocusVisible(true);
- }
- } catch (error) {
- warning(true, 'Warning: [Semi Rating] The current browser does not support the focus-visible');
- }
- }
- // e: FocusEvent
- handleStarBlur = (e: any) => {
- const { emptyStarFocusVisible } = this.getStates();
- if (emptyStarFocusVisible) {
- this._adapter.setEmptyStarFocusVisible(false);
- }
- }
- }
- export interface RatingItemAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
- setFirstStarFocus: (value: boolean) => void;
- setSecondStarFocus: (value: boolean) => void
- }
- export class RatingItemFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<RatingItemAdapter<P, S>, P, S> {
- constructor(adapter: RatingItemAdapter<P, S>) {
- super({ ...RatingItemFoundation.defaultAdapter, ...adapter });
- }
- handleFocusVisible = (event: any, star: string) => {
- const { target } = event;
- // when rating 0 is focus visible
- try {
- if (target.matches(':focus-visible')) {
- if (star === 'first') {
- this._adapter.setFirstStarFocus(true);
- } else {
- this._adapter.setSecondStarFocus(true);
- }
- }
- } catch (error) {
- warning(true, 'Warning: [Semi Rating] The current browser does not support the focus-visible');
- }
- }
- // e: FocusEvent
- handleBlur = (e: any, star: string) => {
- const { firstStarFocus, secondStarFocus } = this.getStates();
- if (star === 'first') {
- firstStarFocus && this._adapter.setFirstStarFocus(false);
- } else {
- secondStarFocus && this._adapter.setSecondStarFocus(false);
- }
- }
- }
|