buefy
Version:
Lightweight UI components for Vue.js (v3) based on Bulma
669 lines (660 loc) • 23.6 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var vue = require('vue');
var TimepickerMixin = require('./TimepickerMixin-C9WVvcUL.js');
var config = require('./config-DR826Ki2.js');
var Dropdown = require('./Dropdown-DtpKU9qf.js');
var Input = require('./Input-BcloGeZ3.js');
var _pluginVue_exportHelper = require('./_plugin-vue_export-helper-Die8u8yB.js');
var plugins = require('./plugins-DbyYGVpp.js');
require('./CompatFallthroughMixin-hhK0Gkhr.js');
require('./FormElementMixin-DavX4iOv.js');
require('./helpers.js');
require('./trapFocus-BlX6xykt.js');
require('./Icon-lsDKE2wQ.js');
const indicatorSize = 40;
const paddingInner = 5;
var _sfc_main$1 = vue.defineComponent({
name: "BClockpickerFace",
props: {
pickerSize: Number,
min: Number,
max: Number,
double: Boolean,
value: Number,
faceNumbers: Array,
disabledValues: Function
},
emits: {
/* eslint-disable @typescript-eslint/no-unused-vars */
change: (_value) => true,
input: (_value) => true
/* eslint-enable @typescript-eslint/no-unused-vars */
},
data() {
return {
isDragging: false,
inputValue: this.value,
prevAngle: 720
};
},
computed: {
/*
* How many number indicators are shown on the face
*/
count() {
return this.max - this.min + 1;
},
/*
* How many number indicators are shown per ring on the face
*/
countPerRing() {
return this.double ? this.count / 2 : this.count;
},
/*
* Radius of the clock face
*/
radius() {
return this.pickerSize / 2;
},
/*
* Radius of the outer ring of number indicators
*/
outerRadius() {
return this.radius - paddingInner - indicatorSize / 2;
},
/*
* Radius of the inner ring of number indicators
*/
innerRadius() {
return Math.max(
this.outerRadius * 0.6,
this.outerRadius - paddingInner - indicatorSize
);
},
/*
* The angle for each selectable value
* For hours this ends up being 30 degrees, for minutes 6 degrees
*/
degreesPerUnit() {
return 360 / this.countPerRing;
},
/*
* Used for calculating x/y grid location based on degrees
*/
degrees() {
return this.degreesPerUnit * Math.PI / 180;
},
/*
* Calculates the angle the clock hand should be rotated for the
* selected value
*/
handRotateAngle() {
let currentAngle = this.prevAngle;
while (currentAngle < 0) currentAngle += 360;
const targetAngle = this.calcHandAngle(this.displayedValue);
const degreesDiff = this.shortestDistanceDegrees(currentAngle, targetAngle);
const angle = this.prevAngle + degreesDiff;
return angle;
},
/*
* Determines how long the selector hand is based on if the
* selected value is located along the outer or inner ring
*/
handScale() {
return this.calcHandScale(this.displayedValue);
},
handStyle() {
return {
transform: `rotate(${this.handRotateAngle}deg) scaleY(${this.handScale})`,
transition: ".3s cubic-bezier(.25,.8,.50,1)"
};
},
/*
* The value the hand should be pointing at
*/
displayedValue() {
return this.inputValue == null ? this.min : this.inputValue;
}
},
watch: {
value(value) {
if (value !== this.inputValue) {
this.prevAngle = this.handRotateAngle;
}
this.inputValue = value;
}
},
methods: {
isDisabled(value) {
return this.disabledValues && this.disabledValues(value);
},
/*
* Calculates the distance between two points
*/
euclidean(p0, p1) {
const dx = p1.x - p0.x;
const dy = p1.y - p0.y;
return Math.sqrt(dx * dx + dy * dy);
},
shortestDistanceDegrees(start, stop) {
const modDiff = (stop - start) % 360;
const shortestDistance = 180 - Math.abs(Math.abs(modDiff) - 180);
return (modDiff + 360) % 360 < 180 ? shortestDistance * 1 : shortestDistance * -1;
},
/*
* Calculates the angle of the line from the center point
* to the given point.
*/
coordToAngle(center, p1) {
const value = 2 * Math.atan2(p1.y - center.y - this.euclidean(center, p1), p1.x - center.x);
return Math.abs(value * 180 / Math.PI);
},
/*
* Generates the inline style translate() property for a
* number indicator, which determines it's location on the
* clock face
*/
getNumberTranslate(value) {
const { x, y } = this.getNumberCoords(value);
return `translate(${x}px, ${y}px)`;
},
/*
* Calculates the coordinates on the clock face for a number
* indicator value
*/
getNumberCoords(value) {
const radius = this.isInnerRing(value) ? this.innerRadius : this.outerRadius;
return {
x: Math.round(radius * Math.sin((value - this.min) * this.degrees)),
y: Math.round(-radius * Math.cos((value - this.min) * this.degrees))
};
},
getFaceNumberClasses(num) {
return {
active: num.value === this.displayedValue,
disabled: this.isDisabled(num.value)
};
},
/*
* Determines if a value resides on the inner ring
*/
isInnerRing(value) {
return this.double && value - this.min >= this.countPerRing;
},
calcHandAngle(value) {
let angle = this.degreesPerUnit * (value - this.min);
if (this.isInnerRing(value)) angle -= 360;
return angle;
},
calcHandScale(value) {
return this.isInnerRing(value) ? this.innerRadius / this.outerRadius : 1;
},
onMouseDown(e) {
e.preventDefault();
this.isDragging = true;
this.onDragMove(e);
},
onMouseUp() {
this.isDragging = false;
if (!this.isDisabled(this.inputValue)) {
this.$emit("change", this.inputValue);
}
},
onDragMove(e) {
e.preventDefault();
if (!this.isDragging && e.type !== "click") return;
const { width, top, left } = this.$refs.clock.getBoundingClientRect();
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(this.coordToAngle(center, coords) + 360) % 360;
const insideClick = this.double && this.euclidean(center, coords) < (this.outerRadius + this.innerRadius) / 2 - 16;
let value = Math.round(handAngle / this.degreesPerUnit) + this.min + (insideClick ? this.countPerRing : 0);
if (handAngle >= 360 - this.degreesPerUnit / 2) {
value = insideClick ? this.max : this.min;
}
this.update(value);
},
update(value) {
if (this.inputValue !== value && !this.isDisabled(value)) {
this.prevAngle = this.handRotateAngle;
this.inputValue = value;
this.$emit("input", value);
}
}
}
});
const _hoisted_1$1 = {
class: "b-clockpicker-face-outer-ring",
ref: "clock"
};
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock(
"div",
{
class: "b-clockpicker-face",
onMousedown: _cache[0] || (_cache[0] = (...args) => _ctx.onMouseDown && _ctx.onMouseDown(...args)),
onMouseup: _cache[1] || (_cache[1] = (...args) => _ctx.onMouseUp && _ctx.onMouseUp(...args)),
onMousemove: _cache[2] || (_cache[2] = (...args) => _ctx.onDragMove && _ctx.onDragMove(...args)),
onTouchstart: _cache[3] || (_cache[3] = (...args) => _ctx.onMouseDown && _ctx.onMouseDown(...args)),
onTouchend: _cache[4] || (_cache[4] = (...args) => _ctx.onMouseUp && _ctx.onMouseUp(...args)),
onTouchmove: _cache[5] || (_cache[5] = (...args) => _ctx.onDragMove && _ctx.onDragMove(...args))
},
[
vue.createElementVNode(
"div",
_hoisted_1$1,
[
vue.createElementVNode(
"div",
{
class: "b-clockpicker-face-hand",
style: vue.normalizeStyle(_ctx.handStyle)
},
null,
4
/* STYLE */
),
(vue.openBlock(true), vue.createElementBlock(
vue.Fragment,
null,
vue.renderList(_ctx.faceNumbers, (num, index) => {
return vue.openBlock(), vue.createElementBlock(
"span",
{
key: index,
class: vue.normalizeClass(["b-clockpicker-face-number", _ctx.getFaceNumberClasses(num)]),
style: vue.normalizeStyle({ transform: _ctx.getNumberTranslate(num.value) })
},
[
vue.createElementVNode(
"span",
null,
vue.toDisplayString(num.label),
1
/* TEXT */
)
],
6
/* CLASS, STYLE */
);
}),
128
/* KEYED_FRAGMENT */
))
],
512
/* NEED_PATCH */
)
],
32
/* NEED_HYDRATION */
);
}
var BClockpickerFace = /* @__PURE__ */ _pluginVue_exportHelper._export_sfc(_sfc_main$1, [["render", _sfc_render$1]]);
const outerPadding = 12;
var _sfc_main = vue.defineComponent({
name: "BClockpicker",
components: {
BClockpickerFace,
BInput: Input.BInput,
BDropdown: Dropdown.BDropdown
},
mixins: [TimepickerMixin.TimepickerMixin],
props: {
pickerSize: {
type: Number,
default: 290
},
incrementMinutes: {
type: Number,
default: 5
},
type: {
type: String,
default: "is-primary"
},
hoursLabel: {
type: String,
default: () => config.config.defaultClockpickerHoursLabel || "Hours"
},
minutesLabel: {
type: String,
default: () => config.config.defaultClockpickerMinutesLabel || "Min"
}
},
data() {
return {
isSelectingHour: true,
isDragging: false,
_isClockpicker: true
};
},
computed: {
hoursDisplay() {
if (this.hoursSelected == null) return "--";
if (this.isHourFormat24) return this.pad(this.hoursSelected);
let display = this.hoursSelected;
if (this.meridienSelected === this.pmString) {
display -= 12;
}
if (display === 0) display = 12;
return display;
},
minutesDisplay() {
return this.minutesSelected == null ? "--" : this.pad(this.minutesSelected);
},
minFaceValue() {
return this.isSelectingHour && !this.isHourFormat24 && this.meridienSelected === this.pmString ? 12 : 0;
},
maxFaceValue() {
return this.isSelectingHour ? !this.isHourFormat24 && this.meridienSelected === this.amString ? 11 : 23 : 59;
},
faceSize() {
return this.pickerSize - outerPadding * 2;
},
faceDisabledValues() {
return this.isSelectingHour ? this.isHourDisabled : this.isMinuteDisabled;
}
},
methods: {
onClockInput(value) {
if (this.isSelectingHour) {
this.hoursSelected = value;
this.onHoursChange(value);
} else {
this.minutesSelected = value;
this.onMinutesChange(value);
}
},
onClockChange() {
if (this.isSelectingHour) {
this.isSelectingHour = !this.isSelectingHour;
} else {
this.toggle(false);
}
},
/*
* Toggle clockpicker
*/
toggle(active) {
if (this.$refs.dropdown) {
const dropdown = this.$refs.dropdown;
dropdown.isActive = active != null ? active : !dropdown.isActive;
if (dropdown.isActive) {
this.isSelectingHour = true;
}
}
},
onMeridienClick(value) {
if (this.meridienSelected !== value) {
this.meridienSelected = value;
this.onMeridienChange(value);
}
},
/*
* Avoid dropdown toggle when is already visible
*/
onInputClick(event) {
if (this.$refs.dropdown.isActive) {
event.stopPropagation();
}
}
}
});
const _hoisted_1 = ["disabled"];
const _hoisted_2 = {
key: 0,
class: "card-header"
};
const _hoisted_3 = { class: "b-clockpicker-header card-header-title" };
const _hoisted_4 = { class: "b-clockpicker-time" };
const _hoisted_5 = {
key: 0,
class: "b-clockpicker-period"
};
const _hoisted_6 = { class: "card-content" };
const _hoisted_7 = {
key: 0,
class: "b-clockpicker-time"
};
const _hoisted_8 = {
key: 1,
class: "b-clockpicker-period"
};
const _hoisted_9 = {
key: 1,
class: "b-clockpicker-footer card-footer"
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_b_input = vue.resolveComponent("b-input");
const _component_b_clockpicker_face = vue.resolveComponent("b-clockpicker-face");
const _component_b_dropdown = vue.resolveComponent("b-dropdown");
return vue.openBlock(), vue.createElementBlock(
"div",
vue.mergeProps({
class: ["b-clockpicker control", [_ctx.size, _ctx.type, { "is-expanded": _ctx.expanded }]]
}, _ctx.rootAttrs),
[
!_ctx.isMobile || _ctx.inline ? (vue.openBlock(), vue.createBlock(_component_b_dropdown, {
key: 0,
ref: "dropdown",
position: _ctx.position,
disabled: _ctx.disabledOrUndefined,
inline: _ctx.inline,
"mobile-modal": _ctx.mobileModal,
"append-to-body": _ctx.appendToBody,
"append-to-body-copy-parent": "",
onActiveChange: _ctx.onActiveChange
}, vue.createSlots({
default: vue.withCtx(() => [
vue.createElementVNode("div", {
class: "card",
disabled: _ctx.disabledOrUndefined,
custom: ""
}, [
_ctx.inline ? (vue.openBlock(), vue.createElementBlock("header", _hoisted_2, [
vue.createElementVNode("div", _hoisted_3, [
vue.createElementVNode("div", _hoisted_4, [
vue.createElementVNode(
"span",
{
class: vue.normalizeClass(["b-clockpicker-btn", { active: _ctx.isSelectingHour }]),
onClick: _cache[3] || (_cache[3] = ($event) => _ctx.isSelectingHour = true)
},
vue.toDisplayString(_ctx.hoursDisplay),
3
/* TEXT, CLASS */
),
vue.createElementVNode(
"span",
null,
vue.toDisplayString(_ctx.hourLiteral),
1
/* TEXT */
),
vue.createElementVNode(
"span",
{
class: vue.normalizeClass(["b-clockpicker-btn", { active: !_ctx.isSelectingHour }]),
onClick: _cache[4] || (_cache[4] = ($event) => _ctx.isSelectingHour = false)
},
vue.toDisplayString(_ctx.minutesDisplay),
3
/* TEXT, CLASS */
)
]),
!_ctx.isHourFormat24 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5, [
vue.createElementVNode(
"div",
{
class: vue.normalizeClass(["b-clockpicker-btn", {
active: _ctx.meridienSelected === _ctx.amString || _ctx.meridienSelected === _ctx.AM
}]),
onClick: _cache[5] || (_cache[5] = ($event) => _ctx.onMeridienClick(_ctx.amString))
},
vue.toDisplayString(_ctx.amString),
3
/* TEXT, CLASS */
),
vue.createElementVNode(
"div",
{
class: vue.normalizeClass(["b-clockpicker-btn", {
active: _ctx.meridienSelected === _ctx.pmString || _ctx.meridienSelected === _ctx.PM
}]),
onClick: _cache[6] || (_cache[6] = ($event) => _ctx.onMeridienClick(_ctx.pmString))
},
vue.toDisplayString(_ctx.pmString),
3
/* TEXT, CLASS */
)
])) : vue.createCommentVNode("v-if", true)
])
])) : vue.createCommentVNode("v-if", true),
vue.createElementVNode("div", _hoisted_6, [
vue.createElementVNode(
"div",
{
class: "b-clockpicker-body",
style: vue.normalizeStyle({ width: _ctx.faceSize + "px", height: _ctx.faceSize + "px" })
},
[
!_ctx.inline ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_7, [
vue.createElementVNode(
"div",
{
class: vue.normalizeClass(["b-clockpicker-btn", { active: _ctx.isSelectingHour }]),
onClick: _cache[7] || (_cache[7] = ($event) => _ctx.isSelectingHour = true)
},
vue.toDisplayString(_ctx.hoursLabel),
3
/* TEXT, CLASS */
),
vue.createElementVNode(
"span",
{
class: vue.normalizeClass(["b-clockpicker-btn", { active: !_ctx.isSelectingHour }]),
onClick: _cache[8] || (_cache[8] = ($event) => _ctx.isSelectingHour = false)
},
vue.toDisplayString(_ctx.minutesLabel),
3
/* TEXT, CLASS */
)
])) : vue.createCommentVNode("v-if", true),
!_ctx.isHourFormat24 && !_ctx.inline ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_8, [
vue.createElementVNode(
"div",
{
class: vue.normalizeClass(["b-clockpicker-btn", {
active: _ctx.meridienSelected === _ctx.amString || _ctx.meridienSelected === _ctx.AM
}]),
onClick: _cache[9] || (_cache[9] = ($event) => _ctx.onMeridienClick(_ctx.amString))
},
vue.toDisplayString(_ctx.amString),
3
/* TEXT, CLASS */
),
vue.createElementVNode(
"div",
{
class: vue.normalizeClass(["b-clockpicker-btn", {
active: _ctx.meridienSelected === _ctx.pmString || _ctx.meridienSelected === _ctx.PM
}]),
onClick: _cache[10] || (_cache[10] = ($event) => _ctx.onMeridienClick(_ctx.pmString))
},
vue.toDisplayString(_ctx.pmString),
3
/* TEXT, CLASS */
)
])) : vue.createCommentVNode("v-if", true),
vue.createVNode(_component_b_clockpicker_face, {
ref: "clockpickerFace",
"picker-size": _ctx.faceSize,
min: _ctx.minFaceValue,
max: _ctx.maxFaceValue,
"face-numbers": _ctx.isSelectingHour ? _ctx.hours : _ctx.minutes,
"disabled-values": _ctx.faceDisabledValues,
double: _ctx.isSelectingHour && _ctx.isHourFormat24,
value: _ctx.isSelectingHour ? _ctx.hoursSelected ?? void 0 : _ctx.minutesSelected ?? void 0,
onInput: _ctx.onClockInput,
onChange: _ctx.onClockChange
}, null, 8, ["picker-size", "min", "max", "face-numbers", "disabled-values", "double", "value", "onInput", "onChange"])
],
4
/* STYLE */
)
]),
_ctx.$slots.default !== void 0 && _ctx.$slots.default([]).length ? (vue.openBlock(), vue.createElementBlock("footer", _hoisted_9, [
vue.renderSlot(_ctx.$slots, "default")
])) : vue.createCommentVNode("v-if", true)
], 8, _hoisted_1)
]),
_: 2
/* DYNAMIC */
}, [
!_ctx.inline ? {
name: "trigger",
fn: vue.withCtx(() => [
vue.renderSlot(_ctx.$slots, "trigger", {}, () => [
vue.createVNode(_component_b_input, vue.mergeProps({
ref: "input",
autocomplete: "off",
"model-value": _ctx.formatValue(_ctx.computedValue),
placeholder: _ctx.placeholder,
size: _ctx.size,
icon: _ctx.icon,
"icon-pack": _ctx.iconPack,
loading: _ctx.loading,
disabled: _ctx.disabledOrUndefined,
readonly: !_ctx.editable,
rounded: _ctx.rounded
}, _ctx.fallthroughAttrs, {
"use-html5-validation": _ctx.useHtml5Validation,
onClick: _ctx.onInputClick,
onKeyup: _cache[0] || (_cache[0] = vue.withKeys(($event) => _ctx.toggle(true), ["enter"])),
onChange: _cache[1] || (_cache[1] = ($event) => _ctx.onChange($event.target.value)),
onFocus: _ctx.handleOnFocus,
onBlur: _cache[2] || (_cache[2] = ($event) => _ctx.checkHtml5Validity())
}), null, 16, ["model-value", "placeholder", "size", "icon", "icon-pack", "loading", "disabled", "readonly", "rounded", "use-html5-validation", "onClick", "onFocus"])
])
]),
key: "0"
} : void 0
]), 1032, ["position", "disabled", "inline", "mobile-modal", "append-to-body", "onActiveChange"])) : (vue.openBlock(), vue.createBlock(_component_b_input, vue.mergeProps({
key: 1,
ref: "input",
type: "time",
autocomplete: "off",
"model-value": _ctx.formatHHMMSS(_ctx.computedValue),
placeholder: _ctx.placeholder,
size: _ctx.size,
icon: _ctx.icon,
"icon-pack": _ctx.iconPack,
loading: _ctx.loading,
max: _ctx.formatHHMMSS(_ctx.maxTime),
min: _ctx.formatHHMMSS(_ctx.minTime),
disabled: _ctx.disabledOrUndefined,
readonly: false
}, _ctx.fallthroughAttrs, {
"use-html5-validation": _ctx.useHtml5Validation,
onClick: _cache[11] || (_cache[11] = vue.withModifiers(($event) => _ctx.toggle(true), ["stop"])),
onKeyup: _cache[12] || (_cache[12] = vue.withKeys(($event) => _ctx.toggle(true), ["enter"])),
onChange: _ctx.onChangeNativePicker,
onFocus: _ctx.handleOnFocus,
onBlur: _cache[13] || (_cache[13] = ($event) => _ctx.onBlur() && _ctx.checkHtml5Validity())
}), null, 16, ["model-value", "placeholder", "size", "icon", "icon-pack", "loading", "max", "min", "disabled", "use-html5-validation", "onChange", "onFocus"]))
],
16
/* FULL_PROPS */
);
}
var Clockpicker = /* @__PURE__ */ _pluginVue_exportHelper._export_sfc(_sfc_main, [["render", _sfc_render]]);
const Plugin = {
install(Vue) {
plugins.registerComponent(Vue, Clockpicker);
}
};
exports.BClockpicker = Clockpicker;
exports.default = Plugin;