/* eslint-disable no-param-reassign */ import BaseFoundation, { DefaultAdapter } from '../base/foundation'; import warning from '../utils/warning'; export interface RatingAdapter
, S = Record {
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 , S = Record ) {
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');
}
}
handleStarBlur = (event: React.FocusEvent) => {
const { emptyStarFocusVisible } = this.getStates();
if (emptyStarFocusVisible) {
this._adapter.setEmptyStarFocusVisible(false);
}
}
}
export interface RatingItemAdapter , S = Record {
setFirstStarFocus: (value: boolean) => void;
setSecondStarFocus: (value: boolean) => void;
}
export class RatingItemFoundation , S = Record ) {
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');
}
}
handleBlur = (event: React.FocusEvent, star: string) => {
const { firstStarFocus, secondStarFocus } = this.getStates();
if (star === 'first') {
firstStarFocus && this._adapter.setFirstStarFocus(false);
} else {
secondStarFocus && this._adapter.setSecondStarFocus(false);
}
}
}