UNPKG

vuetify

Version:

Vue.js 2 Semantic Component Framework

325 lines (263 loc) 8.22 kB
import { Vue } from 'vue/types/vue' import { VNode, VNodeDirective, FunctionalComponentOptions } from 'vue/types' export function createSimpleFunctional ( c: string, el = 'div', name?: string ): FunctionalComponentOptions { return { name: name || c.replace(/__/g, '-'), functional: true, render (h, { data, children }): VNode { data.staticClass = (`${c} ${data.staticClass || ''}`).trim() return h(el, data, children) } } } function mergeTransitions ( transitions: undefined | Function | Function[], array: Function[] ) { if (Array.isArray(transitions)) return transitions.concat(array) if (transitions) array.push(transitions) return array } export function createSimpleTransition ( name: string, origin = 'top center 0', mode?: string ): FunctionalComponentOptions { return { name, functional: true, props: { group: { type: Boolean, default: false }, hideOnLeave: { type: Boolean, default: false }, leaveAbsolute: { type: Boolean, default: false }, mode: { type: String, default: mode }, origin: { type: String, default: origin } }, render (h, context): VNode { const tag = `transition${context.props.group ? '-group' : ''}` context.data = context.data || {} context.data.props = { name, mode: context.props.mode } context.data.on = context.data.on || {} if (!Object.isExtensible(context.data.on)) { context.data.on = { ...context.data.on } } const ourBeforeEnter: Function[] = [] const ourLeave: Function[] = [] const absolute = (el: HTMLElement) => (el.style.position = 'absolute') ourBeforeEnter.push((el: HTMLElement) => { el.style.transformOrigin = context.props.origin el.style.webkitTransformOrigin = context.props.origin }) if (context.props.leaveAbsolute) ourLeave.push(absolute) if (context.props.hideOnLeave) { ourLeave.push((el: HTMLElement) => (el.style.display = 'none')) } const { beforeEnter, leave } = context.data.on // Type says Function | Function[] but // will only work if provided a function context.data.on.beforeEnter = () => mergeTransitions(beforeEnter, ourBeforeEnter) context.data.on.leave = mergeTransitions(leave, ourLeave) return h(tag, context.data, context.children) } } } export function createJavaScriptTransition ( name: string, functions: Record<string, () => any>, mode = 'in-out' ): FunctionalComponentOptions { return { name, functional: true, props: { mode: { type: String, default: mode } }, render (h, context): VNode { const data = { props: { ...context.props, name }, on: functions } return h('transition', data, context.children) } } } export type BindingConfig = Pick<VNodeDirective, 'arg' | 'modifiers' | 'value'> export function directiveConfig (binding: BindingConfig, defaults = {}): VNodeDirective { return { ...defaults, ...binding.modifiers, value: binding.arg, ...(binding.value || {}) } } export function addOnceEventListener (el: EventTarget, event: string, cb: () => void): void { var once = () => { cb() el.removeEventListener(event, once, false) } el.addEventListener(event, once, false) } export function getNestedValue (obj: any, path: (string | number)[], fallback?: any): any { const last = path.length - 1 if (last < 0) return obj === undefined ? fallback : obj for (let i = 0; i < last; i++) { if (obj == null) { return fallback } obj = obj[path[i]] } if (obj == null) return fallback return obj[path[last]] === undefined ? fallback : obj[path[last]] } export function deepEqual (a: any, b: any): boolean { if (a === b) return true if (a instanceof Date && b instanceof Date) { // If the values are Date, they were convert to timestamp with getTime and compare it if (a.getTime() !== b.getTime()) return false } if (a !== Object(a) || b !== Object(b)) { // If the values aren't objects, they were already checked for equality return false } const props = Object.keys(a) if (props.length !== Object.keys(b).length) { // Different number of props, don't bother to check return false } return props.every(p => deepEqual(a[p], b[p])) } export function getObjectValueByPath (obj: object, path: string, fallback?: any): any { // credit: http://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-with-string-key#comment55278413_6491621 if (!path || path.constructor !== String) return fallback path = path.replace(/\[(\w+)\]/g, '.$1') // convert indexes to properties path = path.replace(/^\./, '') // strip a leading dot return getNestedValue(obj, path.split('.'), fallback) } export function getPropertyFromItem ( item: object, property: string | (string | number)[] | ((item: object, fallback?: any) => any), fallback?: any ): any { if (property == null) return item === undefined ? fallback : item if (item !== Object(item)) return fallback === undefined ? item : fallback if (typeof property === 'string') return getObjectValueByPath(item, property, fallback) if (Array.isArray(property)) return getNestedValue(item, property, fallback) if (typeof property !== 'function') return fallback const value = property(item, fallback) return typeof value === 'undefined' ? fallback : value } export function createRange (length: number): number[] { return Array.from({ length }, (v, k) => k) } export function getZIndex (el?: Element | null): number { if (!el || el.nodeType !== Node.ELEMENT_NODE) return 0 const index = +window.getComputedStyle(el).getPropertyValue('z-index') if (isNaN(index)) return getZIndex(el.parentNode as Element) return index } const tagsToReplace = { '&': '&amp;', '<': '&lt;', '>': '&gt;' } as any export function escapeHTML (str: string): string { return str.replace(/[&<>]/g, tag => tagsToReplace[tag] || tag) } export function filterObjectOnKeys<T, K extends keyof T> (obj: T, keys: K[]): { [N in K]: T[N] } { const filtered = {} as { [N in K]: T[N] } for (let i = 0; i < keys.length; i++) { const key = keys[i] if (typeof obj[key] !== 'undefined') { filtered[key] = obj[key] } } return filtered } export function filterChildren (array: VNode[] = [], tag: string): VNode[] { return array.filter(child => { return child.componentOptions && child.componentOptions.Ctor.options.name === tag }) } export function convertToUnit (str: string | number | null | undefined, unit = 'px'): string | undefined { if (str == null || str === '') { return undefined } else if (isNaN(+str!)) { return String(str) } else { return `${Number(str)}${unit}` } } export function kebabCase (str: string): string { return (str || '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() } export function isObject (obj: any): obj is object { return obj !== null && typeof obj === 'object' } // KeyboardEvent.keyCode aliases export const keyCodes = Object.freeze({ enter: 13, tab: 9, delete: 46, esc: 27, space: 32, up: 38, down: 40, left: 37, right: 39, end: 35, home: 36, del: 46, backspace: 8, insert: 45, pageup: 33, pagedown: 34 }) const ICONS_PREFIX = '$vuetify.icons.' // This remaps internal names like '$vuetify.icons.cancel' to the current name // for that icon. export function remapInternalIcon (vm: Vue, iconName: string): string { if (!iconName.startsWith(ICONS_PREFIX)) { return iconName } // Now look up icon indirection name, e.g. '$vuetify.icons.cancel' return getObjectValueByPath(vm, iconName, iconName) } export function keys<O> (o: O) { return Object.keys(o) as (keyof O)[] } /** * Camelize a hyphen-delimited string. */ const camelizeRE = /-(\w)/g export const camelize = (str: string): string => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '') }