vben
2020-11-13 1d45617e4a311e339eb008a629cd342cd673ecf1
提交 | 用户 | age
2f6253 1 import type { VNodeChild } from 'vue';
2
3 export function convertToUnit(
4   str: string | number | null | undefined,
5   unit = 'px'
6 ): string | undefined {
7   if (str == null || str === '') {
8     return undefined;
9   } else if (isNaN(+str!)) {
10     return String(str);
11   } else {
12     return `${Number(str)}${unit}`;
13   }
14 }
15
16 /**
17  * Camelize a hyphen-delimited string.
18  */
19 const camelizeRE = /-(\w)/g;
20 export const camelize = (str: string): string => {
21   return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
22 };
23
24 export function wrapInArray<T>(v: T | T[] | null | undefined): T[] {
25   return v != null ? (Array.isArray(v) ? v : [v]) : [];
26 }
27
28 const pattern = {
29   styleList: /;(?![^(]*\))/g,
30   styleProp: /:(.*)/,
31 } as const;
32
33 function parseStyle(style: string) {
34   const styleMap: Dictionary<any> = {};
35
36   for (const s of style.split(pattern.styleList)) {
37     let [key, val] = s.split(pattern.styleProp);
38     key = key.trim();
39     if (!key) {
40       continue;
41     }
42     // May be undefined if the `key: value` pair is incomplete.
43     if (typeof val === 'string') {
44       val = val.trim();
45     }
46     styleMap[camelize(key)] = val;
47   }
48
49   return styleMap;
50 }
51
52 /**
53  * Intelligently merges data for createElement.
54  * Merges arguments left to right, preferring the right argument.
55  * Returns new VNodeData object.
56  */
57 export function mergeData(...vNodeData: VNodeChild[]): VNodeChild;
58 export function mergeData(...args: any[]): VNodeChild {
59   const mergeTarget: any = {};
60   let i: number = args.length;
61   let prop: string;
62
63   // Allow for variadic argument length.
64   while (i--) {
65     // Iterate through the data properties and execute merge strategies
66     // Object.keys eliminates need for hasOwnProperty call
67     for (prop of Object.keys(args[i])) {
68       switch (prop) {
69         // Array merge strategy (array concatenation)
70         case 'class':
71         case 'directives':
72           if (args[i][prop]) {
73             mergeTarget[prop] = mergeClasses(mergeTarget[prop], args[i][prop]);
74           }
75           break;
76         case 'style':
77           if (args[i][prop]) {
78             mergeTarget[prop] = mergeStyles(mergeTarget[prop], args[i][prop]);
79           }
80           break;
81         // Space delimited string concatenation strategy
82         case 'staticClass':
83           if (!args[i][prop]) {
84             break;
85           }
86           if (mergeTarget[prop] === undefined) {
87             mergeTarget[prop] = '';
88           }
89           if (mergeTarget[prop]) {
90             // Not an empty string, so concatenate
91             mergeTarget[prop] += ' ';
92           }
93           mergeTarget[prop] += args[i][prop].trim();
94           break;
95         // Object, the properties of which to merge via array merge strategy (array concatenation).
96         // Callback merge strategy merges callbacks to the beginning of the array,
97         // so that the last defined callback will be invoked first.
98         // This is done since to mimic how Object.assign merging
99         // uses the last given value to assign.
100         case 'on':
101         case 'nativeOn':
102           if (args[i][prop]) {
103             mergeTarget[prop] = mergeListeners(mergeTarget[prop], args[i][prop]);
104           }
105           break;
106         // Object merge strategy
107         case 'attrs':
108         case 'props':
109         case 'domProps':
110         case 'scopedSlots':
111         case 'staticStyle':
112         case 'hook':
113         case 'transition':
114           if (!args[i][prop]) {
115             break;
116           }
117           if (!mergeTarget[prop]) {
118             mergeTarget[prop] = {};
119           }
120           mergeTarget[prop] = { ...args[i][prop], ...mergeTarget[prop] };
121           break;
122         // Reassignment strategy (no merge)
123         default:
124           // slot, key, ref, tag, show, keepAlive
125           if (!mergeTarget[prop]) {
126             mergeTarget[prop] = args[i][prop];
127           }
128       }
129     }
130   }
131
132   return mergeTarget;
133 }
134
135 export function mergeStyles(
136   target: undefined | string | object[] | object,
137   source: undefined | string | object[] | object
138 ) {
139   if (!target) return source;
140   if (!source) return target;
141
142   target = wrapInArray(typeof target === 'string' ? parseStyle(target) : target);
143
144   return (target as object[]).concat(typeof source === 'string' ? parseStyle(source) : source);
145 }
146
147 export function mergeClasses(target: any, source: any) {
148   if (!source) return target;
149   if (!target) return source;
150
151   return target ? wrapInArray(target).concat(source) : source;
152 }
153
154 export function mergeListeners(
155   target: { [key: string]: Function | Function[] } | undefined,
156   source: { [key: string]: Function | Function[] } | undefined
157 ) {
158   if (!target) return source;
159   if (!source) return target;
160
161   let event: string;
162
163   for (event of Object.keys(source)) {
164     // Concat function to array of functions if callback present.
165     if (target[event]) {
166       // Insert current iteration data in beginning of merged array.
167       target[event] = wrapInArray(target[event]);
168       (target[event] as Function[]).push(...wrapInArray(source[event]));
169     } else {
170       // Straight assign.
171       target[event] = source[event];
172     }
173   }
174
175   return target;
176 }