All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.lib.labs.VTimePicker.VTimePickerClock.mjs Maven / Gradle / Ivy

import { createVNode as _createVNode } from "vue";
// StylesthisValue
// Styles
import "./VTimePickerClock.css";

// Composables
import { useBackgroundColor, useTextColor } from "../../composables/color.mjs"; // Utilities
import { computed, ref, toRef, watch } from 'vue';
import { genericComponent, propsFactory, useRender } from "../../util/index.mjs"; // Types
export const makeVTimePickerClockProps = propsFactory({
  allowedValues: Function,
  ampm: Boolean,
  color: String,
  disabled: Boolean,
  displayedValue: null,
  double: Boolean,
  format: {
    type: Function,
    default: val => val
  },
  max: {
    type: Number,
    required: true
  },
  min: {
    type: Number,
    required: true
  },
  scrollable: Boolean,
  readonly: Boolean,
  rotate: {
    type: Number,
    default: 0
  },
  step: {
    type: Number,
    default: 1
  },
  modelValue: {
    type: Number
  }
}, 'VTimePickerClock');
export const VTimePickerClock = genericComponent()({
  name: 'VTimePickerClock',
  props: makeVTimePickerClockProps(),
  emits: {
    change: val => true,
    input: val => true
  },
  setup(props, _ref) {
    let {
      emit
    } = _ref;
    const clockRef = ref(null);
    const innerClockRef = ref(null);
    const inputValue = ref(undefined);
    const isDragging = ref(false);
    const valueOnMouseDown = ref(null);
    const valueOnMouseUp = ref(null);
    const {
      textColorClasses,
      textColorStyles
    } = useTextColor(toRef(props, 'color'));
    const {
      backgroundColorClasses,
      backgroundColorStyles
    } = useBackgroundColor(toRef(props, 'color'));
    const count = computed(() => props.max - props.min + 1);
    const roundCount = computed(() => props.double ? count.value / 2 : count.value);
    const degreesPerUnit = computed(() => 360 / roundCount.value);
    const degrees = computed(() => degreesPerUnit.value * Math.PI / 180);
    const displayedValue = computed(() => props.modelValue == null ? props.min : props.modelValue);
    const innerRadiusScale = computed(() => 0.62);
    const genChildren = computed(() => {
      const children = [];
      for (let value = props.min; value <= props.max; value = value + props.step) {
        children.push(value);
      }
      return children;
    });
    watch(() => props.modelValue, val => {
      inputValue.value = val;
    });
    function update(value) {
      if (inputValue.value !== value) {
        inputValue.value = value;
      }
      emit('input', value);
    }
    function isAllowed(value) {
      return !props.allowedValues || props.allowedValues(value);
    }
    function wheel(e) {
      if (!props.scrollable || props.disabled) return;
      e.preventDefault();
      const delta = Math.sign(-e.deltaY || 1);
      let value = displayedValue.value;
      do {
        value = value + delta;
        value = (value - props.min + count.value) % count.value + props.min;
      } while (!isAllowed(value) && value !== displayedValue.value);
      if (value !== props.displayedValue) {
        update(value);
      }
    }
    function isInner(value) {
      return props.double && value - props.min >= roundCount.value;
    }
    function handScale(value) {
      return isInner(value) ? innerRadiusScale.value : 1;
    }
    function getPosition(value) {
      const rotateRadians = props.rotate * Math.PI / 180;
      return {
        x: Math.sin((value - props.min) * degrees.value + rotateRadians) * handScale(value),
        y: -Math.cos((value - props.min) * degrees.value + rotateRadians) * handScale(value)
      };
    }
    function angleToValue(angle, insideClick) {
      const value = (Math.round(angle / degreesPerUnit.value) + (insideClick ? roundCount.value : 0)) % count.value + props.min;

      // Necessary to fix edge case when selecting left part of the value(s) at 12 o'clock
      if (angle < 360 - degreesPerUnit.value / 2) return value;
      return insideClick ? props.max - roundCount.value + 1 : props.min;
    }
    function getTransform(i) {
      const {
        x,
        y
      } = getPosition(i);
      return {
        left: `${50 + x * 50}%`,
        top: `${50 + y * 50}%`
      };
    }
    function euclidean(p0, p1) {
      const dx = p1.x - p0.x;
      const dy = p1.y - p0.y;
      return Math.sqrt(dx * dx + dy * dy);
    }
    function angle(center, p1) {
      const value = 2 * Math.atan2(p1.y - center.y - euclidean(center, p1), p1.x - center.x);
      return Math.abs(value * 180 / Math.PI);
    }
    function setMouseDownValue(value) {
      if (valueOnMouseDown.value === null) {
        valueOnMouseDown.value = value;
      }
      valueOnMouseUp.value = value;
      update(value);
    }
    function onDragMove(e) {
      e.preventDefault();
      if (!isDragging.value && e.type !== 'click' || !clockRef.value) return;
      const {
        width,
        top,
        left
      } = clockRef.value?.getBoundingClientRect();
      const {
        width: innerWidth
      } = innerClockRef.value?.getBoundingClientRect() ?? {
        width: 0
      };
      const {
        clientX,
        clientY
      } = 'touches' in e ? e.touches[0] : e;
      const center = {
        x: width / 2,
        y: -width / 2
      };
      const coords = {
        x: clientX - left,
        y: top - clientY
      };
      const handAngle = Math.round(angle(center, coords) - props.rotate + 360) % 360;
      const insideClick = props.double && euclidean(center, coords) < (innerWidth + innerWidth * innerRadiusScale.value) / 4;
      const checksCount = Math.ceil(15 / degreesPerUnit.value);
      let value;
      for (let i = 0; i < checksCount; i++) {
        value = angleToValue(handAngle + i * degreesPerUnit.value, insideClick);
        if (isAllowed(value)) return setMouseDownValue(value);
        value = angleToValue(handAngle - i * degreesPerUnit.value, insideClick);
        if (isAllowed(value)) return setMouseDownValue(value);
      }
    }
    function onMouseDown(e) {
      if (props.disabled) return;
      e.preventDefault();
      window.addEventListener('mousemove', onDragMove);
      window.addEventListener('touchmove', onDragMove);
      window.addEventListener('mouseup', onMouseUp);
      window.addEventListener('touchend', onMouseUp);
      valueOnMouseDown.value = null;
      valueOnMouseUp.value = null;
      isDragging.value = true;
      onDragMove(e);
    }
    function onMouseUp(e) {
      e.stopPropagation();
      window.removeEventListener('mousemove', onDragMove);
      window.removeEventListener('touchmove', onDragMove);
      window.removeEventListener('mouseup', onMouseUp);
      window.removeEventListener('touchend', onMouseUp);
      isDragging.value = false;
      if (valueOnMouseUp.value !== null && isAllowed(valueOnMouseUp.value)) {
        emit('change', valueOnMouseUp.value);
      }
    }
    useRender(() => {
      return _createVNode("div", {
        "class": [{
          'v-time-picker-clock': true,
          'v-time-picker-clock--indeterminate': props.modelValue == null,
          'v-time-picker-clock--readonly': props.readonly
        }],
        "onMousedown": onMouseDown,
        "onTouchstart": onMouseDown,
        "onWheel": wheel,
        "ref": clockRef
      }, [_createVNode("div", {
        "class": "v-time-picker-clock__inner",
        "ref": innerClockRef
      }, [_createVNode("div", {
        "class": [{
          'v-time-picker-clock__hand': true,
          'v-time-picker-clock__hand--inner': isInner(props.modelValue)
        }, textColorClasses.value],
        "style": [{
          transform: `rotate(${props.rotate + degreesPerUnit.value * (displayedValue.value - props.min)}deg) scaleY(${handScale(displayedValue.value)})`
        }, textColorStyles.value]
      }, null), genChildren.value.map(value => {
        const isActive = value === displayedValue.value;
        return _createVNode("div", {
          "class": [{
            'v-time-picker-clock__item': true,
            'v-time-picker-clock__item--active': isActive,
            'v-time-picker-clock__item--disabled': props.disabled || !isAllowed(value)
          }, isActive && backgroundColorClasses.value],
          "style": [getTransform(value), isActive && backgroundColorStyles.value]
        }, [_createVNode("span", null, [props.format(value)])]);
      })])]);
    });
  }
});
//# sourceMappingURL=VTimePickerClock.mjs.map




© 2015 - 2024 Weber Informatics LLC | Privacy Policy