foundation.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /* eslint-disable no-param-reassign */
  2. import BaseFoundation, { DefaultAdapter } from '../base/foundation';
  3. const KeyCode = {
  4. LEFT: 37,
  5. RIGHT: 39
  6. };
  7. export interface RatingAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
  8. focus: () => void;
  9. getStarDOM: (index: number) => Element;
  10. notifyHoverChange: (hoverValue: number, clearedValue: number) => void;
  11. updateValue: (value: number) => void;
  12. clearValue: (clearedValue: number) => void;
  13. notifyFocus: (e: any) => void;
  14. notifyBlur: (e: any) => void;
  15. notifyKeyDown: (e: any) => void;
  16. }
  17. export default class RatingFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<RatingAdapter<P, S>, P, S> {
  18. constructor(adapter: RatingAdapter<P, S>) {
  19. super({ ...RatingFoundation.defaultAdapter, ...adapter });
  20. }
  21. init() {
  22. const { autoFocus, disabled } = this.getProps();
  23. if (autoFocus && !disabled) {
  24. this._adapter.focus();
  25. }
  26. }
  27. _getScroll(w: Window, top?: boolean) {
  28. let ret = top ? w.pageYOffset : w.pageXOffset;
  29. const method = top ? 'scrollTop' : 'scrollLeft';
  30. if (typeof ret !== 'number') {
  31. const d = w.document;
  32. // ie6,7,8 standard mode
  33. ret = d.documentElement[method];
  34. if (typeof ret !== 'number') {
  35. // quirks mode
  36. ret = d.body[method];
  37. }
  38. }
  39. return ret;
  40. }
  41. _getClientPosition(elem: Element) {
  42. let x, y;
  43. const doc = elem.ownerDocument;
  44. const { body } = doc;
  45. const docElem = doc && doc.documentElement;
  46. const box = elem.getBoundingClientRect();
  47. x = box.left;
  48. y = box.top;
  49. x -= docElem.clientLeft || body.clientLeft || 0;
  50. y -= docElem.clientTop || body.clientTop || 0;
  51. return {
  52. left: x,
  53. top: y,
  54. };
  55. }
  56. _getOffsetLeft(el: Element) {
  57. const pos = this._getClientPosition(el);
  58. const doc = el.ownerDocument;
  59. const w: Window = doc.defaultView || (doc as any).parentWindow;
  60. pos.left += this._getScroll(w);
  61. return pos.left;
  62. }
  63. getStarValue(index: number, pos: number) {
  64. const { allowHalf } = this.getProps();
  65. const direction = this._adapter.getContext('direction');
  66. const reverse = direction === 'rtl';
  67. let value = index + 1;
  68. if (allowHalf) {
  69. const starEle = this._adapter.getStarDOM(index);
  70. const leftDis = this._getOffsetLeft(starEle);
  71. const width = starEle.clientWidth;
  72. if (reverse && pos - leftDis > width / 2) {
  73. value -= 0.5;
  74. } else if (!reverse && pos - leftDis < width / 2) {
  75. value -= 0.5;
  76. }
  77. }
  78. return value;
  79. }
  80. handleHover(event: any, index: number) {
  81. const currValue = this.getStarValue(index, event.pageX);
  82. const { clearedValue, hoverValue } = this.getStates();
  83. if ((currValue !== hoverValue) && (currValue !== clearedValue)) {
  84. this._adapter.notifyHoverChange(currValue, null);
  85. }
  86. }
  87. handleMouseLeave() {
  88. this._adapter.notifyHoverChange(undefined, null);
  89. }
  90. handleClick(event: any, index: number) {
  91. const { allowClear } = this.getProps();
  92. const { value } = this.getStates();
  93. const newValue = this.getStarValue(index, event.pageX);
  94. const isReset = allowClear ? newValue === value : false;
  95. this._adapter.updateValue(isReset ? 0 : newValue);
  96. if (isReset) {
  97. this._adapter.notifyHoverChange(0, newValue);
  98. } else {
  99. this._adapter.notifyHoverChange(newValue, null);
  100. }
  101. }
  102. handleFocus(e: any) {
  103. this._adapter.notifyFocus(e);
  104. }
  105. handleBlur(e: any) {
  106. this._adapter.notifyBlur(e);
  107. }
  108. handleKeyDown(event: any, value: number) {
  109. const { keyCode } = event;
  110. const { count, allowHalf } = this.getProps();
  111. const direction = this._adapter.getContext('direction');
  112. const reverse = direction === 'rtl';
  113. if (keyCode === KeyCode.RIGHT && value < count && !reverse) {
  114. if (allowHalf) {
  115. value += 0.5;
  116. } else {
  117. value += 1;
  118. }
  119. } else if (keyCode === KeyCode.LEFT && value > 0 && !reverse) {
  120. if (allowHalf) {
  121. value -= 0.5;
  122. } else {
  123. value -= 1;
  124. }
  125. } else if (keyCode === KeyCode.RIGHT && value > 0 && reverse) {
  126. if (allowHalf) {
  127. value -= 0.5;
  128. } else {
  129. value -= 1;
  130. }
  131. } else if (keyCode === KeyCode.LEFT && value < count && reverse) {
  132. if (allowHalf) {
  133. value += 0.5;
  134. } else {
  135. value += 1;
  136. }
  137. }
  138. this._adapter.updateValue(value);
  139. event.preventDefault();
  140. this._adapter.notifyKeyDown(event);
  141. }
  142. }