bootstrap-vue
Version:
With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extens
401 lines (379 loc) • 14.7 kB
JavaScript
var _watch;
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
import { extend } from '../../vue';
import { NAME_FORM_RATING, NAME_FORM_RATING_STAR } from '../../constants/components';
import { EVENT_NAME_CHANGE, EVENT_NAME_SELECTED } from '../../constants/events';
import { PROP_TYPE_ARRAY_STRING, PROP_TYPE_BOOLEAN, PROP_TYPE_NUMBER, PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props';
import { CODE_LEFT, CODE_RIGHT, CODE_UP, CODE_DOWN } from '../../constants/key-codes';
import { SLOT_NAME_ICON_CLEAR, SLOT_NAME_ICON_EMPTY, SLOT_NAME_ICON_FULL, SLOT_NAME_ICON_HALF } from '../../constants/slots';
import { arrayIncludes, concat } from '../../utils/array';
import { attemptBlur, attemptFocus } from '../../utils/dom';
import { stopEvent } from '../../utils/events';
import { identity } from '../../utils/identity';
import { isNull } from '../../utils/inspect';
import { isLocaleRTL } from '../../utils/locale';
import { mathMax, mathMin } from '../../utils/math';
import { makeModelMixin } from '../../utils/model';
import { toInteger, toFloat } from '../../utils/number';
import { omit, sortKeys } from '../../utils/object';
import { makeProp, makePropsConfigurable } from '../../utils/props';
import { toString } from '../../utils/string';
import { formSizeMixin, props as formSizeProps } from '../../mixins/form-size';
import { idMixin, props as idProps } from '../../mixins/id';
import { normalizeSlotMixin } from '../../mixins/normalize-slot';
import { props as formControlProps } from '../../mixins/form-control';
import { BIcon } from '../../icons/icon';
import { BIconStar, BIconStarHalf, BIconStarFill, BIconX } from '../../icons/icons'; // --- Constants ---
var _makeModelMixin = makeModelMixin('value', {
type: PROP_TYPE_NUMBER_STRING,
event: EVENT_NAME_CHANGE
}),
modelMixin = _makeModelMixin.mixin,
modelProps = _makeModelMixin.props,
MODEL_PROP_NAME = _makeModelMixin.prop,
MODEL_EVENT_NAME = _makeModelMixin.event;
var MIN_STARS = 3;
var DEFAULT_STARS = 5; // --- Helper methods ---
var computeStars = function computeStars(stars) {
return mathMax(MIN_STARS, toInteger(stars, DEFAULT_STARS));
};
var clampValue = function clampValue(value, min, max) {
return mathMax(mathMin(value, max), min);
}; // --- Helper components ---
// @vue/component
var BVFormRatingStar = extend({
name: NAME_FORM_RATING_STAR,
mixins: [normalizeSlotMixin],
props: {
disabled: makeProp(PROP_TYPE_BOOLEAN, false),
// If parent is focused
focused: makeProp(PROP_TYPE_BOOLEAN, false),
hasClear: makeProp(PROP_TYPE_BOOLEAN, false),
rating: makeProp(PROP_TYPE_NUMBER, 0),
readonly: makeProp(PROP_TYPE_BOOLEAN, false),
star: makeProp(PROP_TYPE_NUMBER, 0),
variant: makeProp(PROP_TYPE_STRING)
},
methods: {
onClick: function onClick(event) {
if (!this.disabled && !this.readonly) {
stopEvent(event, {
propagation: false
});
this.$emit(EVENT_NAME_SELECTED, this.star);
}
}
},
render: function render(h) {
var rating = this.rating,
star = this.star,
focused = this.focused,
hasClear = this.hasClear,
variant = this.variant,
disabled = this.disabled,
readonly = this.readonly;
var minStar = hasClear ? 0 : 1;
var type = rating >= star ? 'full' : rating >= star - 0.5 ? 'half' : 'empty';
var slotScope = {
variant: variant,
disabled: disabled,
readonly: readonly
};
return h('span', {
staticClass: 'b-rating-star',
class: {
// When not hovered, we use this class to focus the current (or first) star
focused: focused && rating === star || !toInteger(rating) && star === minStar,
// We add type classes to we can handle RTL styling
'b-rating-star-empty': type === 'empty',
'b-rating-star-half': type === 'half',
'b-rating-star-full': type === 'full'
},
attrs: {
tabindex: !disabled && !readonly ? '-1' : null
},
on: {
click: this.onClick
}
}, [h('span', {
staticClass: 'b-rating-icon'
}, [this.normalizeSlot(type, slotScope)])]);
}
}); // --- Props ---
export var props = makePropsConfigurable(sortKeys(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, idProps), modelProps), omit(formControlProps, ['required', 'autofocus'])), formSizeProps), {}, {
// CSS color string (overrides variant)
color: makeProp(PROP_TYPE_STRING),
iconClear: makeProp(PROP_TYPE_STRING, 'x'),
iconEmpty: makeProp(PROP_TYPE_STRING, 'star'),
iconFull: makeProp(PROP_TYPE_STRING, 'star-fill'),
iconHalf: makeProp(PROP_TYPE_STRING, 'star-half'),
inline: makeProp(PROP_TYPE_BOOLEAN, false),
// Locale for the formatted value (if shown)
// Defaults to the browser locale. Falls back to `en`
locale: makeProp(PROP_TYPE_ARRAY_STRING),
noBorder: makeProp(PROP_TYPE_BOOLEAN, false),
precision: makeProp(PROP_TYPE_NUMBER_STRING),
readonly: makeProp(PROP_TYPE_BOOLEAN, false),
showClear: makeProp(PROP_TYPE_BOOLEAN, false),
showValue: makeProp(PROP_TYPE_BOOLEAN, false),
showValueMax: makeProp(PROP_TYPE_BOOLEAN, false),
stars: makeProp(PROP_TYPE_NUMBER_STRING, DEFAULT_STARS, function (value) {
return toInteger(value) >= MIN_STARS;
}),
variant: makeProp(PROP_TYPE_STRING)
})), NAME_FORM_RATING); // --- Main component ---
// @vue/component
export var BFormRating = /*#__PURE__*/extend({
name: NAME_FORM_RATING,
components: {
BIconStar: BIconStar,
BIconStarHalf: BIconStarHalf,
BIconStarFill: BIconStarFill,
BIconX: BIconX
},
mixins: [idMixin, modelMixin, formSizeMixin],
props: props,
data: function data() {
var value = toFloat(this[MODEL_PROP_NAME], null);
var stars = computeStars(this.stars);
return {
localValue: isNull(value) ? null : clampValue(value, 0, stars),
hasFocus: false
};
},
computed: {
computedStars: function computedStars() {
return computeStars(this.stars);
},
computedRating: function computedRating() {
var value = toFloat(this.localValue, 0);
var precision = toInteger(this.precision, 3); // We clamp the value between `0` and stars
return clampValue(toFloat(value.toFixed(precision)), 0, this.computedStars);
},
computedLocale: function computedLocale() {
var locales = concat(this.locale).filter(identity);
var nf = new Intl.NumberFormat(locales);
return nf.resolvedOptions().locale;
},
isInteractive: function isInteractive() {
return !this.disabled && !this.readonly;
},
isRTL: function isRTL() {
return isLocaleRTL(this.computedLocale);
},
formattedRating: function formattedRating() {
var precision = toInteger(this.precision);
var showValueMax = this.showValueMax;
var locale = this.computedLocale;
var formatOptions = {
notation: 'standard',
minimumFractionDigits: isNaN(precision) ? 0 : precision,
maximumFractionDigits: isNaN(precision) ? 3 : precision
};
var stars = this.computedStars.toLocaleString(locale);
var value = this.localValue;
value = isNull(value) ? showValueMax ? '-' : '' : value.toLocaleString(locale, formatOptions);
return showValueMax ? "".concat(value, "/").concat(stars) : value;
}
},
watch: (_watch = {}, _defineProperty(_watch, MODEL_PROP_NAME, function (newValue, oldValue) {
if (newValue !== oldValue) {
var value = toFloat(newValue, null);
this.localValue = isNull(value) ? null : clampValue(value, 0, this.computedStars);
}
}), _defineProperty(_watch, "localValue", function localValue(newValue, oldValue) {
if (newValue !== oldValue && newValue !== (this.value || 0)) {
this.$emit(MODEL_EVENT_NAME, newValue || null);
}
}), _defineProperty(_watch, "disabled", function disabled(newValue) {
if (newValue) {
this.hasFocus = false;
this.blur();
}
}), _watch),
methods: {
// --- Public methods ---
focus: function focus() {
if (!this.disabled) {
attemptFocus(this.$el);
}
},
blur: function blur() {
if (!this.disabled) {
attemptBlur(this.$el);
}
},
// --- Private methods ---
onKeydown: function onKeydown(event) {
var keyCode = event.keyCode;
if (this.isInteractive && arrayIncludes([CODE_LEFT, CODE_DOWN, CODE_RIGHT, CODE_UP], keyCode)) {
stopEvent(event, {
propagation: false
});
var value = toInteger(this.localValue, 0);
var min = this.showClear ? 0 : 1;
var stars = this.computedStars; // In RTL mode, LEFT/RIGHT are swapped
var amountRtl = this.isRTL ? -1 : 1;
if (keyCode === CODE_LEFT) {
this.localValue = clampValue(value - amountRtl, min, stars) || null;
} else if (keyCode === CODE_RIGHT) {
this.localValue = clampValue(value + amountRtl, min, stars);
} else if (keyCode === CODE_DOWN) {
this.localValue = clampValue(value - 1, min, stars) || null;
} else if (keyCode === CODE_UP) {
this.localValue = clampValue(value + 1, min, stars);
}
}
},
onSelected: function onSelected(value) {
if (this.isInteractive) {
this.localValue = value;
}
},
onFocus: function onFocus(event) {
this.hasFocus = !this.isInteractive ? false : event.type === 'focus';
},
// --- Render methods ---
renderIcon: function renderIcon(icon) {
return this.$createElement(BIcon, {
props: {
icon: icon,
variant: this.disabled || this.color ? null : this.variant || null
}
});
},
iconEmptyFn: function iconEmptyFn() {
return this.renderIcon(this.iconEmpty);
},
iconHalfFn: function iconHalfFn() {
return this.renderIcon(this.iconHalf);
},
iconFullFn: function iconFullFn() {
return this.renderIcon(this.iconFull);
},
iconClearFn: function iconClearFn() {
return this.$createElement(BIcon, {
props: {
icon: this.iconClear
}
});
}
},
render: function render(h) {
var _this = this;
var disabled = this.disabled,
readonly = this.readonly,
name = this.name,
form = this.form,
inline = this.inline,
variant = this.variant,
color = this.color,
noBorder = this.noBorder,
hasFocus = this.hasFocus,
computedRating = this.computedRating,
computedStars = this.computedStars,
formattedRating = this.formattedRating,
showClear = this.showClear,
isRTL = this.isRTL,
isInteractive = this.isInteractive,
$scopedSlots = this.$scopedSlots;
var $content = [];
if (showClear && !disabled && !readonly) {
var $icon = h('span', {
staticClass: 'b-rating-icon'
}, [($scopedSlots[SLOT_NAME_ICON_CLEAR] || this.iconClearFn)()]);
$content.push(h('span', {
staticClass: 'b-rating-star b-rating-star-clear flex-grow-1',
class: {
focused: hasFocus && computedRating === 0
},
attrs: {
tabindex: isInteractive ? '-1' : null
},
on: {
click: function click() {
return _this.onSelected(null);
}
},
key: 'clear'
}, [$icon]));
}
for (var index = 0; index < computedStars; index++) {
var value = index + 1;
$content.push(h(BVFormRatingStar, {
staticClass: 'flex-grow-1',
style: color && !disabled ? {
color: color
} : {},
props: {
rating: computedRating,
star: value,
variant: disabled ? null : variant || null,
disabled: disabled,
readonly: readonly,
focused: hasFocus,
hasClear: showClear
},
on: {
selected: this.onSelected
},
scopedSlots: {
empty: $scopedSlots[SLOT_NAME_ICON_EMPTY] || this.iconEmptyFn,
half: $scopedSlots[SLOT_NAME_ICON_HALF] || this.iconHalfFn,
full: $scopedSlots[SLOT_NAME_ICON_FULL] || this.iconFullFn
},
key: index
}));
}
if (name) {
$content.push(h('input', {
attrs: {
type: 'hidden',
value: isNull(this.localValue) ? '' : computedRating,
name: name,
form: form || null
},
key: 'hidden'
}));
}
if (this.showValue) {
$content.push(h('b', {
staticClass: 'b-rating-value flex-grow-1',
attrs: {
'aria-hidden': 'true'
},
key: 'value'
}, toString(formattedRating)));
}
return h('output', {
staticClass: 'b-rating form-control align-items-center',
class: [{
'd-inline-flex': inline,
'd-flex': !inline,
'border-0': noBorder,
disabled: disabled,
readonly: !disabled && readonly
}, this.sizeFormClass],
attrs: {
id: this.safeId(),
dir: isRTL ? 'rtl' : 'ltr',
tabindex: disabled ? null : '0',
disabled: disabled,
role: 'slider',
'aria-disabled': disabled ? 'true' : null,
'aria-readonly': !disabled && readonly ? 'true' : null,
'aria-live': 'off',
'aria-valuemin': showClear ? '0' : '1',
'aria-valuemax': toString(computedStars),
'aria-valuenow': computedRating ? toString(computedRating) : null
},
on: {
keydown: this.onKeydown,
focus: this.onFocus,
blur: this.onFocus
}
}, $content);
}
});