index.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /* eslint-disable prefer-spread */
  2. import { use } from '../utils';
  3. import { emit, inherit } from '../utils/functional';
  4. import { preventDefault } from '../utils/event';
  5. import Icon from '../icon';
  6. // Types
  7. import { CreateElement, RenderContext } from 'vue/types';
  8. import { DefaultSlots } from '../utils/use/sfc';
  9. const [sfc, bem] = use('rate');
  10. export type RateProps = {
  11. size: number;
  12. icon: string;
  13. count: number;
  14. color: string;
  15. value: number;
  16. voidIcon: string;
  17. voidColor: string;
  18. readonly?: boolean;
  19. disabled?: boolean;
  20. allowHalf?: boolean;
  21. disabledColor: string;
  22. };
  23. export type RateEvents = {
  24. input(index: number): void;
  25. change(index: number): void;
  26. };
  27. type RateStatus = 'full' | 'half' | 'void';
  28. function getRateStatus(value: number, index: number, allowHalf?: boolean): RateStatus {
  29. if (value >= index) {
  30. return 'full';
  31. }
  32. if (value + 0.5 >= index && allowHalf) {
  33. return 'half';
  34. }
  35. return 'void';
  36. }
  37. function Rate(
  38. h: CreateElement,
  39. props: RateProps,
  40. slots: DefaultSlots,
  41. ctx: RenderContext<RateProps>
  42. ) {
  43. const {
  44. icon,
  45. size,
  46. color,
  47. voidIcon,
  48. readonly,
  49. disabled,
  50. voidColor,
  51. disabledColor
  52. } = props;
  53. const list: RateStatus[] = [];
  54. for (let i = 1; i <= props.count; i++) {
  55. list.push(getRateStatus(props.value, i, props.allowHalf));
  56. }
  57. function onSelect(index: number) {
  58. if (!disabled && !readonly) {
  59. emit(ctx, 'input', index);
  60. emit(ctx, 'change', index);
  61. }
  62. }
  63. function onTouchMove(event: TouchEvent) {
  64. if (readonly || disabled || !document.elementFromPoint) {
  65. return;
  66. }
  67. preventDefault(event);
  68. const { clientX, clientY } = event.touches[0];
  69. const target = document.elementFromPoint(clientX, clientY) as HTMLElement;
  70. if (target && target.dataset) {
  71. const { score } = target.dataset;
  72. /* istanbul ignore else */
  73. if (score) {
  74. onSelect(+score);
  75. }
  76. }
  77. }
  78. function renderStar(status: RateStatus, index: number) {
  79. const isFull = status === 'full';
  80. const isVoid = status === 'void';
  81. return (
  82. <div key={index} class={bem('item')}>
  83. <Icon
  84. name={isFull ? icon : voidIcon}
  85. size={`${size}px`}
  86. class={bem('icon')}
  87. data-score={index + 1}
  88. color={disabled ? disabledColor : isFull ? color : voidColor}
  89. onClick={() => {
  90. onSelect(index + 1);
  91. }}
  92. />
  93. {props.allowHalf && (
  94. <Icon
  95. name={isVoid ? voidIcon : icon}
  96. size={`${size}px`}
  97. class={bem('icon', 'half')}
  98. data-score={index + 0.5}
  99. color={disabled ? disabledColor : isVoid ? voidColor : color}
  100. onClick={() => {
  101. onSelect(index + 0.5);
  102. }}
  103. />
  104. )}
  105. </div>
  106. );
  107. }
  108. return (
  109. <div class={bem()} {...inherit(ctx)} onTouchmove={onTouchMove}>
  110. {list.map((status, index) => renderStar(status, index))}
  111. </div>
  112. );
  113. }
  114. Rate.props = {
  115. value: Number,
  116. readonly: Boolean,
  117. disabled: Boolean,
  118. allowHalf: Boolean,
  119. size: {
  120. type: Number,
  121. default: 20
  122. },
  123. icon: {
  124. type: String,
  125. default: 'star'
  126. },
  127. voidIcon: {
  128. type: String,
  129. default: 'star-o'
  130. },
  131. color: {
  132. type: String,
  133. default: '#ffd21e'
  134. },
  135. voidColor: {
  136. type: String,
  137. default: '#c7c7c7'
  138. },
  139. disabledColor: {
  140. type: String,
  141. default: '#bdbdbd'
  142. },
  143. count: {
  144. type: Number,
  145. default: 5
  146. }
  147. };
  148. export default sfc<RateProps, RateEvents>(Rate);