[improvement] rename packages dir to src (#3659)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
export const RED = '#f44';
|
||||
export const BLUE = '#1989fa';
|
||||
export const GREEN = '#07c160';
|
||||
export const WHITE = '#fff';
|
||||
export const GRAY = '#c9c9c9';
|
||||
export const GRAY_DARK = '#969799';
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* bem helper
|
||||
* b() // 'button'
|
||||
* b('text') // 'button__text'
|
||||
* b({ disabled }) // 'button button--disabled'
|
||||
* b('text', { disabled }) // 'button__text button__text--disabled'
|
||||
* b(['disabled', 'primary']) // 'button button--disabled button--primary'
|
||||
*/
|
||||
|
||||
export type Mod = string | { [key: string]: any };
|
||||
export type Mods = Mod | Mod[];
|
||||
|
||||
const ELEMENT = '__';
|
||||
const MODS = '--';
|
||||
|
||||
function join(name: string, el?: string, symbol?: string): string {
|
||||
return el ? name + symbol + el : name;
|
||||
}
|
||||
|
||||
function prefix(name: string, mods: Mods): Mods {
|
||||
if (typeof mods === 'string') {
|
||||
return join(name, mods, MODS);
|
||||
}
|
||||
|
||||
if (Array.isArray(mods)) {
|
||||
return mods.map(item => <Mod>prefix(name, item));
|
||||
}
|
||||
|
||||
const ret: Mods = {};
|
||||
if (mods) {
|
||||
Object.keys(mods).forEach(key => {
|
||||
ret[name + MODS + key] = mods[key];
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function createBEM(name: string) {
|
||||
return function (el?: Mods, mods?: Mods): Mods {
|
||||
if (el && typeof el !== 'string') {
|
||||
mods = el;
|
||||
el = '';
|
||||
}
|
||||
el = join(name, el, ELEMENT);
|
||||
|
||||
return mods ? [el, prefix(el, mods)] : el;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Create a basic component with common options
|
||||
*/
|
||||
import '../../locale';
|
||||
import { camelize } from '../format/string';
|
||||
import { SlotsMixin } from '../../mixins/slots';
|
||||
import Vue, { VNode, VueConstructor, ComponentOptions, RenderContext } from 'vue';
|
||||
import { DefaultProps, FunctionComponent } from '../types';
|
||||
|
||||
export interface VantComponentOptions extends ComponentOptions<Vue> {
|
||||
functional?: boolean;
|
||||
install?: (Vue: VueConstructor) => void;
|
||||
}
|
||||
|
||||
export type TsxBaseProps<Slots> = {
|
||||
key: string | number;
|
||||
// hack for jsx prop spread
|
||||
props: any;
|
||||
class: any;
|
||||
style: string | object[] | object;
|
||||
scopedSlots: Slots;
|
||||
};
|
||||
|
||||
export type TsxComponent<Props, Events, Slots> = (
|
||||
props: Partial<Props & Events & TsxBaseProps<Slots>>
|
||||
) => VNode;
|
||||
|
||||
const arrayProp = {
|
||||
type: Array,
|
||||
default: () => []
|
||||
};
|
||||
|
||||
const numberProp = {
|
||||
type: Number,
|
||||
default: 0
|
||||
};
|
||||
|
||||
function defaultProps(props: any) {
|
||||
Object.keys(props).forEach(key => {
|
||||
if (props[key] === Array) {
|
||||
props[key] = arrayProp;
|
||||
} else if (props[key] === Number) {
|
||||
props[key] = numberProp;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function install(this: ComponentOptions<Vue>, Vue: VueConstructor) {
|
||||
const { name } = this;
|
||||
Vue.component(name as string, this);
|
||||
Vue.component(camelize(`-${name}`), this);
|
||||
}
|
||||
|
||||
// unify slots & scopedSlots
|
||||
export function unifySlots(context: RenderContext) {
|
||||
// use data.scopedSlots in lower Vue version
|
||||
const scopedSlots = context.scopedSlots || context.data.scopedSlots || {};
|
||||
const slots = context.slots();
|
||||
|
||||
Object.keys(slots).forEach(key => {
|
||||
if (!scopedSlots[key]) {
|
||||
scopedSlots[key] = () => slots[key];
|
||||
}
|
||||
});
|
||||
|
||||
return scopedSlots;
|
||||
}
|
||||
|
||||
// should be removed after Vue 3
|
||||
function transformFunctionComponent(pure: FunctionComponent): VantComponentOptions {
|
||||
return {
|
||||
functional: true,
|
||||
props: pure.props,
|
||||
model: pure.model,
|
||||
render: (h, context): any => pure(h, context.props, unifySlots(context), context)
|
||||
};
|
||||
}
|
||||
|
||||
export function createComponent(name: string) {
|
||||
return function<Props = DefaultProps, Events = {}, Slots = {}> (
|
||||
sfc: VantComponentOptions | FunctionComponent
|
||||
): TsxComponent<Props, Events, Slots> {
|
||||
if (typeof sfc === 'function') {
|
||||
sfc = transformFunctionComponent(sfc);
|
||||
}
|
||||
|
||||
if (!sfc.functional) {
|
||||
sfc.mixins = sfc.mixins || [];
|
||||
sfc.mixins.push(SlotsMixin);
|
||||
}
|
||||
|
||||
if (sfc.props) {
|
||||
defaultProps(sfc.props);
|
||||
}
|
||||
|
||||
sfc.name = name;
|
||||
sfc.install = install;
|
||||
|
||||
return sfc as TsxComponent<Props, Events, Slots>;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { get } from '..';
|
||||
import { camelize } from '../format/string';
|
||||
import locale from '../../locale';
|
||||
|
||||
export function createI18N(name: string) {
|
||||
const prefix = camelize(name) + '.';
|
||||
|
||||
return function (path: string, ...args: any[]): string {
|
||||
const message = get(locale.messages(), prefix + path) || get(locale.messages(), path);
|
||||
return typeof message === 'function' ? message(...args) : message;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { createBEM } from './bem';
|
||||
import { createComponent } from './component';
|
||||
import { createI18N } from './i18n';
|
||||
|
||||
type CreateNamespaceReturn = [
|
||||
ReturnType<typeof createComponent>,
|
||||
ReturnType<typeof createBEM>,
|
||||
ReturnType<typeof createI18N>
|
||||
];
|
||||
|
||||
export function createNamespace(name: string): CreateNamespaceReturn {
|
||||
name = 'van-' + name;
|
||||
return [createComponent(name), createBEM(name), createI18N(name)];
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
import { isDef, isObj } from '.';
|
||||
import { ObjectIndex } from './types';
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
function assignKey(to: ObjectIndex, from: ObjectIndex, key: string) {
|
||||
const val = from[key];
|
||||
|
||||
if (!isDef(val)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasOwnProperty.call(to, key) || !isObj(val) || typeof val === 'function') {
|
||||
to[key] = val;
|
||||
} else {
|
||||
to[key] = deepAssign(Object(to[key]), from[key]);
|
||||
}
|
||||
}
|
||||
|
||||
export function deepAssign(to: ObjectIndex, from: ObjectIndex): ObjectIndex {
|
||||
Object.keys(from).forEach(key => {
|
||||
assignKey(to, from, key);
|
||||
});
|
||||
|
||||
return to;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { deepAssign } from './deep-assign';
|
||||
|
||||
export function deepClone(obj: object): object {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(item => deepClone(item));
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
return deepAssign({}, obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* eslint-disable no-empty */
|
||||
/* eslint-disable getter-return */
|
||||
/* eslint-disable import/no-mutable-exports */
|
||||
import { isServer } from '..';
|
||||
import { EventHanlder } from '../types';
|
||||
|
||||
export let supportsPassive = false;
|
||||
|
||||
if (!isServer) {
|
||||
try {
|
||||
const opts = {};
|
||||
Object.defineProperty(opts, 'passive', {
|
||||
get() {
|
||||
/* istanbul ignore next */
|
||||
supportsPassive = true;
|
||||
}
|
||||
});
|
||||
window.addEventListener('test-passive', null as any, opts);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
export function on(
|
||||
target: HTMLElement,
|
||||
event: string,
|
||||
handler: EventHanlder,
|
||||
passive = false
|
||||
) {
|
||||
if (!isServer) {
|
||||
target.addEventListener(
|
||||
event,
|
||||
handler,
|
||||
supportsPassive ? { capture: false, passive } : false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function off(target: HTMLElement, event: string, handler: EventHanlder) {
|
||||
if (!isServer) {
|
||||
target.removeEventListener(event, handler);
|
||||
}
|
||||
}
|
||||
|
||||
export function stopPropagation(event: Event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
export function preventDefault(event: Event, isStopPropagation?: boolean) {
|
||||
/* istanbul ignore else */
|
||||
if (typeof event.cancelable !== 'boolean' || event.cancelable) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if (isStopPropagation) {
|
||||
stopPropagation(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* requestAnimationFrame polyfill
|
||||
*/
|
||||
|
||||
import { isServer } from '..';
|
||||
|
||||
let prev = Date.now();
|
||||
|
||||
/* istanbul ignore next */
|
||||
function fallback(fn: FrameRequestCallback): number {
|
||||
const curr = Date.now();
|
||||
const ms = Math.max(0, 16 - (curr - prev));
|
||||
const id = setTimeout(fn, ms);
|
||||
prev = curr + ms;
|
||||
return id;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
const root = <Window>(isServer ? global : window);
|
||||
|
||||
/* istanbul ignore next */
|
||||
const iRaf = root.requestAnimationFrame || fallback;
|
||||
|
||||
/* istanbul ignore next */
|
||||
const iCancel = root.cancelAnimationFrame || root.clearTimeout;
|
||||
|
||||
export function raf(fn: FrameRequestCallback): number {
|
||||
return iRaf.call(root, fn);
|
||||
}
|
||||
|
||||
export function cancelRaf(id: number) {
|
||||
iCancel.call(root, id);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
type ScrollElement = HTMLElement | Window;
|
||||
|
||||
// get nearest scroll element
|
||||
// http://w3help.org/zh-cn/causes/SD9013
|
||||
// http://stackoverflow.com/questions/17016740/onscroll-function-is-not-working-for-chrome
|
||||
export function getScrollEventTarget(element: HTMLElement, rootParent: ScrollElement = window) {
|
||||
let node = element;
|
||||
while (
|
||||
node &&
|
||||
node.tagName !== 'HTML' &&
|
||||
node.tagName !== 'BODY' &&
|
||||
node.nodeType === 1 &&
|
||||
node !== rootParent
|
||||
) {
|
||||
const { overflowY } = window.getComputedStyle(node);
|
||||
if (overflowY === 'scroll' || overflowY === 'auto') {
|
||||
return node;
|
||||
}
|
||||
node = <HTMLElement>node.parentNode;
|
||||
}
|
||||
return rootParent;
|
||||
}
|
||||
|
||||
export function getScrollTop(element: ScrollElement): number {
|
||||
return 'scrollTop' in element ? element.scrollTop : element.pageYOffset;
|
||||
}
|
||||
|
||||
export function setScrollTop(element: ScrollElement, value: number) {
|
||||
'scrollTop' in element ? (element.scrollTop = value) : element.scrollTo(element.scrollX, value);
|
||||
}
|
||||
|
||||
export function getRootScrollTop(): number {
|
||||
return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||
}
|
||||
|
||||
// get distance from element top to page top
|
||||
export function getElementTop(element: ScrollElement) {
|
||||
return (
|
||||
(element === window ? 0 : (<HTMLElement>element).getBoundingClientRect().top) +
|
||||
getScrollTop(window)
|
||||
);
|
||||
}
|
||||
|
||||
export function getVisibleHeight(element: ScrollElement) {
|
||||
return element === window
|
||||
? element.innerHeight
|
||||
: (<HTMLElement>element).getBoundingClientRect().height;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function range(num: number, min: number, max: number): number {
|
||||
return Math.min(Math.max(num, min), max);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
const camelizeRE = /-(\w)/g;
|
||||
|
||||
export function camelize(str: string): string {
|
||||
return str.replace(camelizeRE, (_, c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
export function padZero(num: number | string): string {
|
||||
return (num < 10 ? '0' : '') + num;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { isDef } from '..';
|
||||
import { isNumber } from '../validate/number';
|
||||
|
||||
export function suffixPx(value?: string | number): string | undefined {
|
||||
if (!isDef(value)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
value = String(value);
|
||||
return isNumber(value) ? `${value}px` : value;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import Vue, { RenderContext, VNodeData } from 'vue';
|
||||
import { ObjectIndex } from './types';
|
||||
|
||||
type Context = RenderContext & { data: VNodeData & ObjectIndex };
|
||||
|
||||
type InheritContext = Partial<VNodeData> & ObjectIndex;
|
||||
|
||||
const inheritKey = [
|
||||
'ref',
|
||||
'style',
|
||||
'class',
|
||||
'attrs',
|
||||
'nativeOn',
|
||||
'directives',
|
||||
'staticClass',
|
||||
'staticStyle'
|
||||
];
|
||||
|
||||
const mapInheritKey: ObjectIndex = { nativeOn: 'on' };
|
||||
|
||||
// inherit partial context, map nativeOn to on
|
||||
export function inherit(context: Context, inheritListeners?: boolean): InheritContext {
|
||||
const result = inheritKey.reduce(
|
||||
(obj, key) => {
|
||||
if (context.data[key]) {
|
||||
obj[mapInheritKey[key] || key] = context.data[key];
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
{} as InheritContext
|
||||
);
|
||||
|
||||
if (inheritListeners) {
|
||||
result.on = result.on || {};
|
||||
Object.assign(result.on, context.data.on);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// emit event
|
||||
export function emit(context: Context, eventName: string, ...args: any[]) {
|
||||
const listeners = context.listeners[eventName];
|
||||
if (listeners) {
|
||||
if (Array.isArray(listeners)) {
|
||||
listeners.forEach(listener => {
|
||||
listener(...args);
|
||||
});
|
||||
} else {
|
||||
listeners(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mount functional component
|
||||
export function mount(Component: any, data?: VNodeData) {
|
||||
const instance = new Vue({
|
||||
el: document.createElement('div'),
|
||||
props: Component.props,
|
||||
render(h) {
|
||||
return h(Component, {
|
||||
props: this.$props,
|
||||
...data
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
document.body.appendChild(instance.$el);
|
||||
|
||||
return instance;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
export { createNamespace } from './create';
|
||||
export { suffixPx } from './format/unit';
|
||||
|
||||
export const isServer: boolean = Vue.prototype.$isServer;
|
||||
|
||||
export function noop() {}
|
||||
|
||||
export function isDef(value: any): boolean {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
|
||||
export function isObj(x: any): boolean {
|
||||
const type = typeof x;
|
||||
return x !== null && (type === 'object' || type === 'function');
|
||||
}
|
||||
|
||||
export function get(object: any, path: string): any {
|
||||
const keys = path.split('.');
|
||||
let result = object;
|
||||
|
||||
keys.forEach(key => {
|
||||
result = isDef(result[key]) ? result[key] : '';
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Vue Router support
|
||||
*/
|
||||
|
||||
import { RenderContext } from 'vue/types';
|
||||
import VueRouter, { RawLocation } from 'vue-router/types';
|
||||
|
||||
export type RouteConfig = {
|
||||
url?: string;
|
||||
to?: RawLocation;
|
||||
replace?: boolean;
|
||||
};
|
||||
|
||||
export function route(router: VueRouter, config: RouteConfig) {
|
||||
const { to, url, replace } = config;
|
||||
if (to && router) {
|
||||
router[replace ? 'replace' : 'push'](to);
|
||||
} else if (url) {
|
||||
replace ? location.replace(url) : (location.href = url);
|
||||
}
|
||||
}
|
||||
|
||||
export function functionalRoute(context: RenderContext) {
|
||||
route(context.parent && context.parent.$router, context.props);
|
||||
}
|
||||
|
||||
export type RouteProps = {
|
||||
url?: string,
|
||||
replace?: boolean;
|
||||
to?: RawLocation;
|
||||
}
|
||||
|
||||
export const routeProps = {
|
||||
url: String,
|
||||
replace: Boolean,
|
||||
to: [String, Object]
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
import { deepClone } from '../deep-clone';
|
||||
import { deepAssign } from '../deep-assign';
|
||||
import { isDef, get } from '..';
|
||||
import { raf, cancelRaf } from '../dom/raf';
|
||||
import { later } from '../../../test/utils';
|
||||
import { isEmail } from '../validate/email';
|
||||
import { isMobile } from '../validate/mobile';
|
||||
import { isNumber } from '../validate/number';
|
||||
import { isAndroid } from '../validate/system';
|
||||
import { camelize } from '../format/string';
|
||||
|
||||
test('deepClone', () => {
|
||||
const a = { foo: 0 };
|
||||
const b = { foo: 0, bar: 1 };
|
||||
const fn = () => {};
|
||||
const arr = [a, b];
|
||||
expect(deepClone(a)).toEqual(a);
|
||||
expect(deepClone(b)).toEqual(b);
|
||||
expect(deepClone(fn)).toEqual(fn);
|
||||
expect(deepClone(arr)).toEqual(arr);
|
||||
expect(deepClone(undefined)).toEqual(undefined);
|
||||
expect(deepClone(1)).toEqual(1);
|
||||
});
|
||||
|
||||
test('deepAssign', () => {
|
||||
const fn = () => {};
|
||||
|
||||
expect(deepAssign({}, { foo: null })).toEqual({});
|
||||
expect(deepAssign({}, { foo: undefined })).toEqual({});
|
||||
expect(deepAssign({ fn: null }, { fn })).toEqual({ fn });
|
||||
expect(deepAssign({ foo: 0 }, { bar: 1 })).toEqual({ foo: 0, bar: 1 });
|
||||
expect(deepAssign({ foo: { bar: false } }, { foo: { bar: true, foo: false } })).toEqual(
|
||||
{
|
||||
foo: {
|
||||
bar: true,
|
||||
foo: false
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('isDef', () => {
|
||||
expect(isDef(null)).toBeFalsy();
|
||||
expect(isDef(undefined)).toBeFalsy();
|
||||
expect(isDef(1)).toBeTruthy();
|
||||
expect(isDef('1')).toBeTruthy();
|
||||
expect(isDef({})).toBeTruthy();
|
||||
expect(isDef(() => {})).toBeTruthy();
|
||||
});
|
||||
|
||||
test('camelize', () => {
|
||||
expect(camelize('ab')).toEqual('ab');
|
||||
expect(camelize('a-b')).toEqual('aB');
|
||||
expect(camelize('a-b-c-d')).toEqual('aBCD');
|
||||
expect(camelize('a-b-')).toEqual('aB-');
|
||||
expect(camelize('-a-b')).toEqual('AB');
|
||||
expect(camelize('-')).toEqual('-');
|
||||
});
|
||||
|
||||
test('get', () => {
|
||||
expect(get({ a: 1 }, 'a')).toEqual(1);
|
||||
expect(get({ a: { b: 2 } }, 'a.b')).toEqual(2);
|
||||
expect(get({ a: { b: 2 } }, 'a.b.c')).toEqual('');
|
||||
});
|
||||
|
||||
test('isAndroid', () => {
|
||||
expect(isAndroid()).toBeFalsy();
|
||||
});
|
||||
|
||||
test('raf', async () => {
|
||||
const spy = jest.fn();
|
||||
raf(spy);
|
||||
|
||||
await later(50);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
cancelRaf(1);
|
||||
});
|
||||
|
||||
test('is-email', () => {
|
||||
expect(isEmail('abc@gmail.com')).toBeTruthy();
|
||||
expect(isEmail('abc@@gmail.com')).toBeFalsy();
|
||||
expect(isEmail('@gmail.com')).toBeFalsy();
|
||||
expect(isEmail('abc@')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('is-mobile', () => {
|
||||
expect(isMobile('13000000000')).toBeTruthy();
|
||||
expect(isMobile('+8613000000000')).toBeTruthy();
|
||||
expect(isMobile('8613000000000')).toBeTruthy();
|
||||
expect(isMobile('1300000000')).toBeFalsy();
|
||||
expect(isMobile('abc')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('is-number', () => {
|
||||
expect(isNumber('1')).toBeTruthy();
|
||||
expect(isNumber('1.2')).toBeTruthy();
|
||||
expect(isNumber('1..2')).toBeFalsy();
|
||||
expect(isNumber('abc')).toBeFalsy();
|
||||
expect(isNumber('1b2')).toBeFalsy();
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { VNode, CreateElement, RenderContext } from 'vue';
|
||||
import { InjectOptions, PropsDefinition } from 'vue/types/options';
|
||||
|
||||
export type EventHanlder = (eventName?: Event) => void;
|
||||
|
||||
export type ObjectIndex = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export type ScopedSlot<Props = any> = (props?: Props) => VNode[] | VNode | undefined;
|
||||
|
||||
export type DefaultSlots = {
|
||||
default?: ScopedSlot;
|
||||
};
|
||||
|
||||
export type ScopedSlots = DefaultSlots & {
|
||||
[key: string]: ScopedSlot | undefined;
|
||||
};
|
||||
|
||||
export type ModelOptions = {
|
||||
prop?: string;
|
||||
event?: string;
|
||||
};
|
||||
|
||||
export type DefaultProps = Record<string, any>;
|
||||
|
||||
export type FunctionComponent<Props = DefaultProps, PropDefs = PropsDefinition<Props>> = {
|
||||
(h: CreateElement, props: Props, slots: ScopedSlots, context: RenderContext<Props>):
|
||||
| VNode
|
||||
| undefined;
|
||||
props?: PropDefs;
|
||||
model?: ModelOptions;
|
||||
inject?: InjectOptions;
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
/* eslint-disable */
|
||||
export function isEmail(value: string): boolean {
|
||||
const reg = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
|
||||
return reg.test(value);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export function isMobile(value: string): boolean {
|
||||
value = value.replace(/[^-|\d]/g, '');
|
||||
return /^((\+86)|(86))?(1)\d{10}$/.test(value) || /^0[0-9-]{10,13}$/.test(value);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function isNumber(value: string): boolean {
|
||||
return /^\d+(\.\d+)?$/.test(value);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { isServer } from '..';
|
||||
|
||||
export function isAndroid(): boolean {
|
||||
/* istanbul ignore next */
|
||||
return isServer ? false : /android/.test(navigator.userAgent.toLowerCase());
|
||||
}
|
||||
|
||||
export function isIOS(): boolean {
|
||||
/* istanbul ignore next */
|
||||
return isServer ? false : /ios|iphone|ipad|ipod/.test(navigator.userAgent.toLowerCase());
|
||||
}
|
||||
Reference in New Issue
Block a user