1 |
|
2 | import { unsetValue, CssProperty, CssAnimationProperty, ShorthandProperty, InheritedCssProperty } from '../core/properties';
|
3 | import { Style } from './style';
|
4 | import { Color } from '../../color';
|
5 | import { Font, parseFont, FontStyle, FontWeight, FontVariationSettings } from './font';
|
6 | import { Background } from './background';
|
7 | import { layout, hasDuplicates } from '../../utils';
|
8 | import { radiansToDegrees } from '../../utils/number-utils';
|
9 | import { decompose2DTransformMatrix, getTransformMatrix, matrixArrayToCssMatrix, multiplyAffine2d } from '../../matrix';
|
10 | import { Trace } from '../../trace';
|
11 | import { CoreTypes } from '../../core-types';
|
12 | import { parseBackground } from '../../css/parser';
|
13 | import { LinearGradient } from './linear-gradient';
|
14 | import { parseCSSShadow } from './css-shadow';
|
15 | function equalsCommon(a, b) {
|
16 | if (a == 'auto') {
|
17 |
|
18 | return b == 'auto';
|
19 | }
|
20 | if (typeof a === 'number') {
|
21 | if (b == 'auto') {
|
22 |
|
23 | return false;
|
24 | }
|
25 | if (typeof b === 'number') {
|
26 | return a == b;
|
27 | }
|
28 | if (!b) {
|
29 | return false;
|
30 | }
|
31 | return b.unit == 'dip' && a == b.value;
|
32 | }
|
33 | if (b == 'auto') {
|
34 |
|
35 | return false;
|
36 | }
|
37 | if (typeof b === 'number') {
|
38 | return a ? a.unit == 'dip' && a.value == b : false;
|
39 | }
|
40 | if (!a || !b) {
|
41 | return false;
|
42 | }
|
43 | return a.value == b.value && a.unit == b.unit;
|
44 | }
|
45 | function convertToStringCommon(length) {
|
46 | if (length == 'auto') {
|
47 |
|
48 | return 'auto';
|
49 | }
|
50 | if (typeof length === 'number') {
|
51 | return length.toString();
|
52 | }
|
53 | let val = length.value;
|
54 | if (length.unit === '%') {
|
55 | val *= 100;
|
56 | }
|
57 | return val + length.unit;
|
58 | }
|
59 | function toDevicePixelsCommon(length, auto = Number.NaN, parentAvailableWidth = Number.NaN) {
|
60 | if (length == 'auto') {
|
61 |
|
62 | return auto;
|
63 | }
|
64 | if (typeof length === 'number') {
|
65 | return layout.round(layout.toDevicePixels(length));
|
66 | }
|
67 | if (!length) {
|
68 | return auto;
|
69 | }
|
70 | switch (length.unit) {
|
71 | case 'px':
|
72 | return layout.round(length.value);
|
73 | case '%':
|
74 | return layout.round(parentAvailableWidth * length.value);
|
75 | case 'dip':
|
76 | default:
|
77 | return layout.round(layout.toDevicePixels(length.value));
|
78 | }
|
79 | }
|
80 | export var PercentLength;
|
81 | (function (PercentLength) {
|
82 | function parse(fromValue) {
|
83 | if (fromValue == 'auto') {
|
84 |
|
85 | return 'auto';
|
86 | }
|
87 | if (typeof fromValue === 'string') {
|
88 | let stringValue = fromValue.trim();
|
89 | const percentIndex = stringValue.indexOf('%');
|
90 | if (percentIndex !== -1) {
|
91 | let value;
|
92 |
|
93 | if (percentIndex !== stringValue.length - 1 || percentIndex === 0) {
|
94 | value = Number.NaN;
|
95 | }
|
96 | else {
|
97 |
|
98 | value = parseFloat(stringValue.substring(0, stringValue.length - 1).trim()) / 100;
|
99 | }
|
100 | if (isNaN(value) || !isFinite(value)) {
|
101 | throw new Error(`Invalid value: ${fromValue}`);
|
102 | }
|
103 | return { unit: '%', value };
|
104 | }
|
105 | else if (stringValue.indexOf('px') !== -1) {
|
106 | stringValue = stringValue.replace('px', '').trim();
|
107 | const value = parseFloat(stringValue);
|
108 | if (isNaN(value) || !isFinite(value)) {
|
109 | throw new Error(`Invalid value: ${fromValue}`);
|
110 | }
|
111 | return { unit: 'px', value };
|
112 | }
|
113 | else {
|
114 | const value = parseFloat(stringValue);
|
115 | if (isNaN(value) || !isFinite(value)) {
|
116 | throw new Error(`Invalid value: ${fromValue}`);
|
117 | }
|
118 | return value;
|
119 | }
|
120 | }
|
121 | else {
|
122 | return fromValue;
|
123 | }
|
124 | }
|
125 | PercentLength.parse = parse;
|
126 | PercentLength.equals = equalsCommon;
|
127 | PercentLength.toDevicePixels = toDevicePixelsCommon;
|
128 | PercentLength.convertToString = convertToStringCommon;
|
129 | })(PercentLength || (PercentLength = {}));
|
130 | export var Length;
|
131 | (function (Length) {
|
132 | function parse(fromValue) {
|
133 | if (fromValue == 'auto') {
|
134 |
|
135 | return 'auto';
|
136 | }
|
137 | if (typeof fromValue === 'string') {
|
138 | let stringValue = fromValue.trim();
|
139 | if (stringValue.indexOf('px') !== -1) {
|
140 | stringValue = stringValue.replace('px', '').trim();
|
141 | const value = parseFloat(stringValue);
|
142 | if (isNaN(value) || !isFinite(value)) {
|
143 | throw new Error(`Invalid value: ${stringValue}`);
|
144 | }
|
145 | return { unit: 'px', value };
|
146 | }
|
147 | else {
|
148 | const value = parseFloat(stringValue);
|
149 | if (isNaN(value) || !isFinite(value)) {
|
150 | throw new Error(`Invalid value: ${stringValue}`);
|
151 | }
|
152 | return value;
|
153 | }
|
154 | }
|
155 | else {
|
156 | return fromValue;
|
157 | }
|
158 | }
|
159 | Length.parse = parse;
|
160 | Length.equals = equalsCommon;
|
161 | Length.toDevicePixels = toDevicePixelsCommon;
|
162 | Length.convertToString = convertToStringCommon;
|
163 | })(Length || (Length = {}));
|
164 | export const minWidthProperty = new CssProperty({
|
165 | name: 'minWidth',
|
166 | cssName: 'min-width',
|
167 | defaultValue: CoreTypes.zeroLength,
|
168 | affectsLayout: global.isIOS,
|
169 | equalityComparer: Length.equals,
|
170 | valueChanged: (target, oldValue, newValue) => {
|
171 | const view = target.viewRef.get();
|
172 | if (view) {
|
173 | view.effectiveMinWidth = Length.toDevicePixels(newValue, 0);
|
174 | }
|
175 | else {
|
176 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
177 | }
|
178 | },
|
179 | valueConverter: Length.parse,
|
180 | });
|
181 | minWidthProperty.register(Style);
|
182 | export const minHeightProperty = new CssProperty({
|
183 | name: 'minHeight',
|
184 | cssName: 'min-height',
|
185 | defaultValue: CoreTypes.zeroLength,
|
186 | affectsLayout: global.isIOS,
|
187 | equalityComparer: Length.equals,
|
188 | valueChanged: (target, oldValue, newValue) => {
|
189 | const view = target.viewRef.get();
|
190 | if (view) {
|
191 | view.effectiveMinHeight = Length.toDevicePixels(newValue, 0);
|
192 | }
|
193 | else {
|
194 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
195 | }
|
196 | },
|
197 | valueConverter: Length.parse,
|
198 | });
|
199 | minHeightProperty.register(Style);
|
200 | export const widthProperty = new CssAnimationProperty({
|
201 | name: 'width',
|
202 | cssName: 'width',
|
203 | defaultValue: 'auto',
|
204 | equalityComparer: Length.equals,
|
205 |
|
206 |
|
207 | valueChanged: (target, oldValue, newValue) => {
|
208 | if (global.isIOS) {
|
209 | const view = target.viewRef.get();
|
210 | if (view) {
|
211 | view.requestLayout();
|
212 | }
|
213 | }
|
214 | },
|
215 | valueConverter: PercentLength.parse,
|
216 | });
|
217 | widthProperty.register(Style);
|
218 | export const heightProperty = new CssAnimationProperty({
|
219 | name: 'height',
|
220 | cssName: 'height',
|
221 | defaultValue: 'auto',
|
222 | equalityComparer: Length.equals,
|
223 |
|
224 |
|
225 | valueChanged: (target, oldValue, newValue) => {
|
226 | if (global.isIOS) {
|
227 | const view = target.viewRef.get();
|
228 | if (view) {
|
229 | view.requestLayout();
|
230 | }
|
231 | }
|
232 | },
|
233 | valueConverter: PercentLength.parse,
|
234 | });
|
235 | heightProperty.register(Style);
|
236 | const marginProperty = new ShorthandProperty({
|
237 | name: 'margin',
|
238 | cssName: 'margin',
|
239 | getter: function () {
|
240 | if (PercentLength.equals(this.marginTop, this.marginRight) && PercentLength.equals(this.marginTop, this.marginBottom) && PercentLength.equals(this.marginTop, this.marginLeft)) {
|
241 | return this.marginTop;
|
242 | }
|
243 | return `${PercentLength.convertToString(this.marginTop)} ${PercentLength.convertToString(this.marginRight)} ${PercentLength.convertToString(this.marginBottom)} ${PercentLength.convertToString(this.marginLeft)}`;
|
244 | },
|
245 | converter: convertToMargins,
|
246 | });
|
247 | marginProperty.register(Style);
|
248 | export const marginLeftProperty = new CssProperty({
|
249 | name: 'marginLeft',
|
250 | cssName: 'margin-left',
|
251 | defaultValue: CoreTypes.zeroLength,
|
252 | affectsLayout: global.isIOS,
|
253 | equalityComparer: Length.equals,
|
254 | valueConverter: PercentLength.parse,
|
255 | });
|
256 | marginLeftProperty.register(Style);
|
257 | export const marginRightProperty = new CssProperty({
|
258 | name: 'marginRight',
|
259 | cssName: 'margin-right',
|
260 | defaultValue: CoreTypes.zeroLength,
|
261 | affectsLayout: global.isIOS,
|
262 | equalityComparer: Length.equals,
|
263 | valueConverter: PercentLength.parse,
|
264 | });
|
265 | marginRightProperty.register(Style);
|
266 | export const marginTopProperty = new CssProperty({
|
267 | name: 'marginTop',
|
268 | cssName: 'margin-top',
|
269 | defaultValue: CoreTypes.zeroLength,
|
270 | affectsLayout: global.isIOS,
|
271 | equalityComparer: Length.equals,
|
272 | valueConverter: PercentLength.parse,
|
273 | });
|
274 | marginTopProperty.register(Style);
|
275 | export const marginBottomProperty = new CssProperty({
|
276 | name: 'marginBottom',
|
277 | cssName: 'margin-bottom',
|
278 | defaultValue: CoreTypes.zeroLength,
|
279 | affectsLayout: global.isIOS,
|
280 | equalityComparer: Length.equals,
|
281 | valueConverter: PercentLength.parse,
|
282 | });
|
283 | marginBottomProperty.register(Style);
|
284 | const paddingProperty = new ShorthandProperty({
|
285 | name: 'padding',
|
286 | cssName: 'padding',
|
287 | getter: function () {
|
288 | if (Length.equals(this.paddingTop, this.paddingRight) && Length.equals(this.paddingTop, this.paddingBottom) && Length.equals(this.paddingTop, this.paddingLeft)) {
|
289 | return this.paddingTop;
|
290 | }
|
291 | return `${Length.convertToString(this.paddingTop)} ${Length.convertToString(this.paddingRight)} ${Length.convertToString(this.paddingBottom)} ${Length.convertToString(this.paddingLeft)}`;
|
292 | },
|
293 | converter: convertToPaddings,
|
294 | });
|
295 | paddingProperty.register(Style);
|
296 | export const paddingLeftProperty = new CssProperty({
|
297 | name: 'paddingLeft',
|
298 | cssName: 'padding-left',
|
299 | defaultValue: CoreTypes.zeroLength,
|
300 | affectsLayout: global.isIOS,
|
301 | equalityComparer: Length.equals,
|
302 | valueChanged: (target, oldValue, newValue) => {
|
303 | const view = target.viewRef.get();
|
304 | if (view) {
|
305 | view.effectivePaddingLeft = Length.toDevicePixels(newValue, 0);
|
306 | }
|
307 | else {
|
308 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
309 | }
|
310 | },
|
311 | valueConverter: Length.parse,
|
312 | });
|
313 | paddingLeftProperty.register(Style);
|
314 | export const paddingRightProperty = new CssProperty({
|
315 | name: 'paddingRight',
|
316 | cssName: 'padding-right',
|
317 | defaultValue: CoreTypes.zeroLength,
|
318 | affectsLayout: global.isIOS,
|
319 | equalityComparer: Length.equals,
|
320 | valueChanged: (target, oldValue, newValue) => {
|
321 | const view = target.viewRef.get();
|
322 | if (view) {
|
323 | view.effectivePaddingRight = Length.toDevicePixels(newValue, 0);
|
324 | }
|
325 | else {
|
326 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
327 | }
|
328 | },
|
329 | valueConverter: Length.parse,
|
330 | });
|
331 | paddingRightProperty.register(Style);
|
332 | export const paddingTopProperty = new CssProperty({
|
333 | name: 'paddingTop',
|
334 | cssName: 'padding-top',
|
335 | defaultValue: CoreTypes.zeroLength,
|
336 | affectsLayout: global.isIOS,
|
337 | equalityComparer: Length.equals,
|
338 | valueChanged: (target, oldValue, newValue) => {
|
339 | const view = target.viewRef.get();
|
340 | if (view) {
|
341 | view.effectivePaddingTop = Length.toDevicePixels(newValue, 0);
|
342 | }
|
343 | else {
|
344 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
345 | }
|
346 | },
|
347 | valueConverter: Length.parse,
|
348 | });
|
349 | paddingTopProperty.register(Style);
|
350 | export const paddingBottomProperty = new CssProperty({
|
351 | name: 'paddingBottom',
|
352 | cssName: 'padding-bottom',
|
353 | defaultValue: CoreTypes.zeroLength,
|
354 | affectsLayout: global.isIOS,
|
355 | equalityComparer: Length.equals,
|
356 | valueChanged: (target, oldValue, newValue) => {
|
357 | const view = target.viewRef.get();
|
358 | if (view) {
|
359 | view.effectivePaddingBottom = Length.toDevicePixels(newValue, 0);
|
360 | }
|
361 | else {
|
362 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
363 | }
|
364 | },
|
365 | valueConverter: Length.parse,
|
366 | });
|
367 | paddingBottomProperty.register(Style);
|
368 | export const horizontalAlignmentProperty = new CssProperty({
|
369 | name: 'horizontalAlignment',
|
370 | cssName: 'horizontal-align',
|
371 | defaultValue: CoreTypes.HorizontalAlignment.stretch,
|
372 | affectsLayout: global.isIOS,
|
373 | valueConverter: CoreTypes.HorizontalAlignment.parse,
|
374 | });
|
375 | horizontalAlignmentProperty.register(Style);
|
376 | export const verticalAlignmentProperty = new CssProperty({
|
377 | name: 'verticalAlignment',
|
378 | cssName: 'vertical-align',
|
379 | defaultValue: CoreTypes.VerticalAlignmentText.stretch,
|
380 | affectsLayout: global.isIOS,
|
381 | valueConverter: CoreTypes.VerticalAlignmentText.parse,
|
382 | });
|
383 | verticalAlignmentProperty.register(Style);
|
384 | function parseThickness(value) {
|
385 | if (typeof value === 'string') {
|
386 | const arr = value.split(/[ ,]+/);
|
387 | let top;
|
388 | let right;
|
389 | let bottom;
|
390 | let left;
|
391 | if (arr.length === 1) {
|
392 | top = arr[0];
|
393 | right = arr[0];
|
394 | bottom = arr[0];
|
395 | left = arr[0];
|
396 | }
|
397 | else if (arr.length === 2) {
|
398 | top = arr[0];
|
399 | bottom = arr[0];
|
400 | right = arr[1];
|
401 | left = arr[1];
|
402 | }
|
403 | else if (arr.length === 3) {
|
404 | top = arr[0];
|
405 | right = arr[1];
|
406 | left = arr[1];
|
407 | bottom = arr[2];
|
408 | }
|
409 | else if (arr.length === 4) {
|
410 | top = arr[0];
|
411 | right = arr[1];
|
412 | bottom = arr[2];
|
413 | left = arr[3];
|
414 | }
|
415 | else {
|
416 | throw new Error('Expected 1, 2, 3 or 4 parameters. Actual: ' + value);
|
417 | }
|
418 | return {
|
419 | top: top,
|
420 | right: right,
|
421 | bottom: bottom,
|
422 | left: left,
|
423 | };
|
424 | }
|
425 | else {
|
426 | return value;
|
427 | }
|
428 | }
|
429 | function convertToMargins(value) {
|
430 | if (typeof value === 'string' && value !== 'auto') {
|
431 | const thickness = parseThickness(value);
|
432 | return [
|
433 | [marginTopProperty, PercentLength.parse(thickness.top)],
|
434 | [marginRightProperty, PercentLength.parse(thickness.right)],
|
435 | [marginBottomProperty, PercentLength.parse(thickness.bottom)],
|
436 | [marginLeftProperty, PercentLength.parse(thickness.left)],
|
437 | ];
|
438 | }
|
439 | else {
|
440 | return [
|
441 | [marginTopProperty, value],
|
442 | [marginRightProperty, value],
|
443 | [marginBottomProperty, value],
|
444 | [marginLeftProperty, value],
|
445 | ];
|
446 | }
|
447 | }
|
448 | function convertToPaddings(value) {
|
449 | if (typeof value === 'string' && value !== 'auto') {
|
450 | const thickness = parseThickness(value);
|
451 | return [
|
452 | [paddingTopProperty, Length.parse(thickness.top)],
|
453 | [paddingRightProperty, Length.parse(thickness.right)],
|
454 | [paddingBottomProperty, Length.parse(thickness.bottom)],
|
455 | [paddingLeftProperty, Length.parse(thickness.left)],
|
456 | ];
|
457 | }
|
458 | else {
|
459 | return [
|
460 | [paddingTopProperty, value],
|
461 | [paddingRightProperty, value],
|
462 | [paddingBottomProperty, value],
|
463 | [paddingLeftProperty, value],
|
464 | ];
|
465 | }
|
466 | }
|
467 | export const rotateProperty = new CssAnimationProperty({
|
468 | name: 'rotate',
|
469 | cssName: 'rotate',
|
470 | defaultValue: 0,
|
471 | valueConverter: parseFloat,
|
472 | });
|
473 | rotateProperty.register(Style);
|
474 | export const rotateXProperty = new CssAnimationProperty({
|
475 | name: 'rotateX',
|
476 | cssName: 'rotatex',
|
477 | defaultValue: 0,
|
478 | valueConverter: parseFloat,
|
479 | });
|
480 | rotateXProperty.register(Style);
|
481 | export const rotateYProperty = new CssAnimationProperty({
|
482 | name: 'rotateY',
|
483 | cssName: 'rotatey',
|
484 | defaultValue: 0,
|
485 | valueConverter: parseFloat,
|
486 | });
|
487 | rotateYProperty.register(Style);
|
488 | export const perspectiveProperty = new CssAnimationProperty({
|
489 | name: 'perspective',
|
490 | cssName: 'perspective',
|
491 | defaultValue: 1000,
|
492 | valueConverter: parseFloat,
|
493 | });
|
494 | perspectiveProperty.register(Style);
|
495 | export const scaleXProperty = new CssAnimationProperty({
|
496 | name: 'scaleX',
|
497 | cssName: 'scaleX',
|
498 | defaultValue: 1,
|
499 | valueConverter: parseFloat,
|
500 | });
|
501 | scaleXProperty.register(Style);
|
502 | export const scaleYProperty = new CssAnimationProperty({
|
503 | name: 'scaleY',
|
504 | cssName: 'scaleY',
|
505 | defaultValue: 1,
|
506 | valueConverter: parseFloat,
|
507 | });
|
508 | scaleYProperty.register(Style);
|
509 | function parseDIPs(value) {
|
510 | if (value.indexOf('px') !== -1) {
|
511 | return layout.toDeviceIndependentPixels(parseFloat(value.replace('px', '').trim()));
|
512 | }
|
513 | else {
|
514 | return parseFloat(value.replace('dip', '').trim());
|
515 | }
|
516 | }
|
517 | export const translateXProperty = new CssAnimationProperty({
|
518 | name: 'translateX',
|
519 | cssName: 'translateX',
|
520 | defaultValue: 0,
|
521 | valueConverter: parseDIPs,
|
522 | });
|
523 | translateXProperty.register(Style);
|
524 | export const translateYProperty = new CssAnimationProperty({
|
525 | name: 'translateY',
|
526 | cssName: 'translateY',
|
527 | defaultValue: 0,
|
528 | valueConverter: parseDIPs,
|
529 | });
|
530 | translateYProperty.register(Style);
|
531 | const transformProperty = new ShorthandProperty({
|
532 | name: 'transform',
|
533 | cssName: 'transform',
|
534 | getter: function () {
|
535 | const scaleX = this.scaleX;
|
536 | const scaleY = this.scaleY;
|
537 | const translateX = this.translateX;
|
538 | const translateY = this.translateY;
|
539 | const rotate = this.rotate;
|
540 | const rotateX = this.rotateX;
|
541 | const rotateY = this.rotateY;
|
542 | let result = '';
|
543 | if (translateX !== 0 || translateY !== 0) {
|
544 | result += `translate(${translateX}, ${translateY}) `;
|
545 | }
|
546 | if (scaleX !== 1 || scaleY !== 1) {
|
547 | result += `scale(${scaleX}, ${scaleY}) `;
|
548 | }
|
549 | if (rotateX !== 0 || rotateY !== 0 || rotate !== 0) {
|
550 | result += `rotate(${rotateX}, ${rotateY}, ${rotate}) `;
|
551 | result += `rotate (${rotate})`;
|
552 | }
|
553 | return result.trim();
|
554 | },
|
555 | converter: convertToTransform,
|
556 | });
|
557 | transformProperty.register(Style);
|
558 | const IDENTITY_TRANSFORMATION = {
|
559 | translate: { x: 0, y: 0 },
|
560 | rotate: { x: 0, y: 0, z: 0 },
|
561 | scale: { x: 1, y: 1 },
|
562 | };
|
563 | const TRANSFORM_SPLITTER = new RegExp(/\s*(.+?)\((.*?)\)/g);
|
564 | const TRANSFORMATIONS = Object.freeze(['rotate', 'rotateX', 'rotateY', 'rotate3d', 'translate', 'translate3d', 'translateX', 'translateY', 'scale', 'scale3d', 'scaleX', 'scaleY']);
|
565 | const STYLE_TRANSFORMATION_MAP = Object.freeze({
|
566 | scale: (value) => ({ property: 'scale', value }),
|
567 | scale3d: (value) => ({ property: 'scale', value }),
|
568 | scaleX: ({ x }) => ({
|
569 | property: 'scale',
|
570 | value: { x, y: IDENTITY_TRANSFORMATION.scale.y },
|
571 | }),
|
572 | scaleY: ({ y }) => ({
|
573 | property: 'scale',
|
574 | value: { y, x: IDENTITY_TRANSFORMATION.scale.x },
|
575 | }),
|
576 | translate: (value) => ({ property: 'translate', value }),
|
577 | translate3d: (value) => ({ property: 'translate', value }),
|
578 | translateX: ({ x }) => ({
|
579 | property: 'translate',
|
580 | value: { x, y: IDENTITY_TRANSFORMATION.translate.y },
|
581 | }),
|
582 | translateY: ({ y }) => ({
|
583 | property: 'translate',
|
584 | value: { y, x: IDENTITY_TRANSFORMATION.translate.x },
|
585 | }),
|
586 | rotate3d: (value) => ({ property: 'rotate', value }),
|
587 | rotateX: (x) => ({
|
588 | property: 'rotate',
|
589 | value: {
|
590 | x,
|
591 | y: IDENTITY_TRANSFORMATION.rotate.y,
|
592 | z: IDENTITY_TRANSFORMATION.rotate.z,
|
593 | },
|
594 | }),
|
595 | rotateY: (y) => ({
|
596 | property: 'rotate',
|
597 | value: {
|
598 | x: IDENTITY_TRANSFORMATION.rotate.x,
|
599 | y,
|
600 | z: IDENTITY_TRANSFORMATION.rotate.z,
|
601 | },
|
602 | }),
|
603 | rotate: (z) => ({
|
604 | property: 'rotate',
|
605 | value: {
|
606 | x: IDENTITY_TRANSFORMATION.rotate.x,
|
607 | y: IDENTITY_TRANSFORMATION.rotate.y,
|
608 | z,
|
609 | },
|
610 | }),
|
611 | });
|
612 | function convertToTransform(value) {
|
613 | if (value === unsetValue) {
|
614 | value = 'none';
|
615 | }
|
616 | const { translate, rotate, scale } = transformConverter(value);
|
617 | return [
|
618 | [translateXProperty, translate.x],
|
619 | [translateYProperty, translate.y],
|
620 | [scaleXProperty, scale.x],
|
621 | [scaleYProperty, scale.y],
|
622 | [rotateProperty, rotate.z],
|
623 | [rotateXProperty, rotate.x],
|
624 | [rotateYProperty, rotate.y],
|
625 | ];
|
626 | }
|
627 | export function transformConverter(text) {
|
628 | const transformations = parseTransformString(text);
|
629 | if (text === 'none' || text === '' || !transformations.length) {
|
630 | return IDENTITY_TRANSFORMATION;
|
631 | }
|
632 | const usedTransforms = transformations.map((t) => t.property);
|
633 | if (!hasDuplicates(usedTransforms)) {
|
634 | const fullTransformations = { ...IDENTITY_TRANSFORMATION };
|
635 | transformations.forEach((transform) => {
|
636 | fullTransformations[transform.property] = transform.value;
|
637 | });
|
638 | return fullTransformations;
|
639 | }
|
640 | const affineMatrix = transformations.map(getTransformMatrix).reduce(multiplyAffine2d);
|
641 | const cssMatrix = matrixArrayToCssMatrix(affineMatrix);
|
642 | return decompose2DTransformMatrix(cssMatrix);
|
643 | }
|
644 |
|
645 |
|
646 |
|
647 | function parseTransformString(text) {
|
648 | const matches = [];
|
649 | let match;
|
650 | while ((match = TRANSFORM_SPLITTER.exec(text)) !== null) {
|
651 | const property = match[1];
|
652 | const value = convertTransformValue(property, match[2]);
|
653 | if (TRANSFORMATIONS.indexOf(property) !== -1) {
|
654 | matches.push(normalizeTransformation({ property, value }));
|
655 | }
|
656 | }
|
657 | return matches;
|
658 | }
|
659 | function normalizeTransformation({ property, value }) {
|
660 | return STYLE_TRANSFORMATION_MAP[property](value);
|
661 | }
|
662 | function convertTransformValue(property, stringValue) {
|
663 |
|
664 | let [x, y, z] = stringValue.split(',').map(parseFloat);
|
665 | if (property === 'translate') {
|
666 | y ?? (y = IDENTITY_TRANSFORMATION.translate.y);
|
667 | }
|
668 | else {
|
669 | y ?? (y = x);
|
670 | z ?? (z = y);
|
671 | }
|
672 | if (property === 'rotate' || property === 'rotateX' || property === 'rotateY') {
|
673 | return stringValue.slice(-3) === 'rad' ? radiansToDegrees(x) : x;
|
674 | }
|
675 | return { x, y, z };
|
676 | }
|
677 |
|
678 | const backgroundProperty = new ShorthandProperty({
|
679 | name: 'background',
|
680 | cssName: 'background',
|
681 | getter: function () {
|
682 | return `${this.backgroundColor} ${this.backgroundImage} ${this.backgroundRepeat} ${this.backgroundPosition}`;
|
683 | },
|
684 | converter: convertToBackgrounds,
|
685 | });
|
686 | backgroundProperty.register(Style);
|
687 | export const backgroundInternalProperty = new CssProperty({
|
688 | name: 'backgroundInternal',
|
689 | cssName: '_backgroundInternal',
|
690 | defaultValue: Background.default,
|
691 | });
|
692 | backgroundInternalProperty.register(Style);
|
693 |
|
694 | export const backgroundImageProperty = new CssProperty({
|
695 | name: 'backgroundImage',
|
696 | cssName: 'background-image',
|
697 | valueChanged: (target, oldValue, newValue) => {
|
698 | target.backgroundInternal = target.backgroundInternal.withImage(newValue);
|
699 | },
|
700 | equalityComparer: (value1, value2) => {
|
701 | if (value1 instanceof LinearGradient && value2 instanceof LinearGradient) {
|
702 | return LinearGradient.equals(value1, value2);
|
703 | }
|
704 | else {
|
705 | return value1 === value2;
|
706 | }
|
707 | },
|
708 | valueConverter: (value) => {
|
709 | if (typeof value === 'string') {
|
710 | const parsed = parseBackground(value);
|
711 | if (parsed) {
|
712 | value = typeof parsed.value.image === 'object' ? LinearGradient.parse(parsed.value.image) : value;
|
713 | }
|
714 | }
|
715 | return value;
|
716 | },
|
717 | });
|
718 | backgroundImageProperty.register(Style);
|
719 | export const backgroundColorProperty = new CssAnimationProperty({
|
720 | name: 'backgroundColor',
|
721 | cssName: 'background-color',
|
722 | valueChanged: (target, oldValue, newValue) => {
|
723 | target.backgroundInternal = target.backgroundInternal.withColor(newValue);
|
724 | },
|
725 | equalityComparer: Color.equals,
|
726 | valueConverter: (value) => new Color(value),
|
727 | });
|
728 | backgroundColorProperty.register(Style);
|
729 | export const backgroundRepeatProperty = new CssProperty({
|
730 | name: 'backgroundRepeat',
|
731 | cssName: 'background-repeat',
|
732 | valueConverter: CoreTypes.BackgroundRepeat.parse,
|
733 | valueChanged: (target, oldValue, newValue) => {
|
734 | target.backgroundInternal = target.backgroundInternal.withRepeat(newValue);
|
735 | },
|
736 | });
|
737 | backgroundRepeatProperty.register(Style);
|
738 | export const backgroundSizeProperty = new CssProperty({
|
739 | name: 'backgroundSize',
|
740 | cssName: 'background-size',
|
741 | valueChanged: (target, oldValue, newValue) => {
|
742 | target.backgroundInternal = target.backgroundInternal.withSize(newValue);
|
743 | },
|
744 | });
|
745 | backgroundSizeProperty.register(Style);
|
746 | export const backgroundPositionProperty = new CssProperty({
|
747 | name: 'backgroundPosition',
|
748 | cssName: 'background-position',
|
749 | valueChanged: (target, oldValue, newValue) => {
|
750 | target.backgroundInternal = target.backgroundInternal.withPosition(newValue);
|
751 | },
|
752 | });
|
753 | backgroundPositionProperty.register(Style);
|
754 | function convertToBackgrounds(value) {
|
755 | if (typeof value === 'string') {
|
756 | const backgrounds = parseBackground(value).value;
|
757 | let backgroundColor = unsetValue;
|
758 | if (backgrounds.color) {
|
759 | backgroundColor = backgrounds.color instanceof Color ? backgrounds.color : new Color(backgrounds.color);
|
760 | }
|
761 | let backgroundImage;
|
762 | if (typeof backgrounds.image === 'object' && backgrounds.image) {
|
763 | backgroundImage = LinearGradient.parse(backgrounds.image);
|
764 | }
|
765 | else {
|
766 | backgroundImage = backgrounds.image || unsetValue;
|
767 | }
|
768 | const backgroundRepeat = backgrounds.repeat || unsetValue;
|
769 | const backgroundPosition = backgrounds.position ? backgrounds.position.text : unsetValue;
|
770 | return [
|
771 | [backgroundColorProperty, backgroundColor],
|
772 | [backgroundImageProperty, backgroundImage],
|
773 | [backgroundRepeatProperty, backgroundRepeat],
|
774 | [backgroundPositionProperty, backgroundPosition],
|
775 | ];
|
776 | }
|
777 | else {
|
778 | return [
|
779 | [backgroundColorProperty, unsetValue],
|
780 | [backgroundImageProperty, unsetValue],
|
781 | [backgroundRepeatProperty, unsetValue],
|
782 | [backgroundPositionProperty, unsetValue],
|
783 | ];
|
784 | }
|
785 | }
|
786 | function parseBorderColor(value) {
|
787 | const result = {
|
788 | top: undefined,
|
789 | right: undefined,
|
790 | bottom: undefined,
|
791 | left: undefined,
|
792 | };
|
793 | if (value.indexOf('rgb') === 0 || value.indexOf('hsl') === 0) {
|
794 | result.top = result.right = result.bottom = result.left = new Color(value);
|
795 | return result;
|
796 | }
|
797 | const arr = value.split(/[ ,]+/);
|
798 | if (arr.length === 1) {
|
799 | const arr0 = new Color(arr[0]);
|
800 | result.top = arr0;
|
801 | result.right = arr0;
|
802 | result.bottom = arr0;
|
803 | result.left = arr0;
|
804 | }
|
805 | else if (arr.length === 2) {
|
806 | const arr0 = new Color(arr[0]);
|
807 | const arr1 = new Color(arr[1]);
|
808 | result.top = arr0;
|
809 | result.right = arr1;
|
810 | result.bottom = arr0;
|
811 | result.left = arr1;
|
812 | }
|
813 | else if (arr.length === 3) {
|
814 | const arr0 = new Color(arr[0]);
|
815 | const arr1 = new Color(arr[1]);
|
816 | const arr2 = new Color(arr[2]);
|
817 | result.top = arr0;
|
818 | result.right = arr1;
|
819 | result.bottom = arr2;
|
820 | result.left = arr1;
|
821 | }
|
822 | else if (arr.length === 4) {
|
823 | const arr0 = new Color(arr[0]);
|
824 | const arr1 = new Color(arr[1]);
|
825 | const arr2 = new Color(arr[2]);
|
826 | const arr3 = new Color(arr[3]);
|
827 | result.top = arr0;
|
828 | result.right = arr1;
|
829 | result.bottom = arr2;
|
830 | result.left = arr3;
|
831 | }
|
832 | else {
|
833 | throw new Error(`Expected 1, 2, 3 or 4 parameters. Actual: ${value}`);
|
834 | }
|
835 | return result;
|
836 | }
|
837 |
|
838 | const borderColorProperty = new ShorthandProperty({
|
839 | name: 'borderColor',
|
840 | cssName: 'border-color',
|
841 | getter: function () {
|
842 | if (Color.equals(this.borderTopColor, this.borderRightColor) && Color.equals(this.borderTopColor, this.borderBottomColor) && Color.equals(this.borderTopColor, this.borderLeftColor)) {
|
843 | return this.borderTopColor;
|
844 | }
|
845 | else {
|
846 | return `${this.borderTopColor} ${this.borderRightColor} ${this.borderBottomColor} ${this.borderLeftColor}`;
|
847 | }
|
848 | },
|
849 | converter: function (value) {
|
850 | if (typeof value === 'string') {
|
851 | const fourColors = parseBorderColor(value);
|
852 | return [
|
853 | [borderTopColorProperty, fourColors.top],
|
854 | [borderRightColorProperty, fourColors.right],
|
855 | [borderBottomColorProperty, fourColors.bottom],
|
856 | [borderLeftColorProperty, fourColors.left],
|
857 | ];
|
858 | }
|
859 | else {
|
860 | return [
|
861 | [borderTopColorProperty, value],
|
862 | [borderRightColorProperty, value],
|
863 | [borderBottomColorProperty, value],
|
864 | [borderLeftColorProperty, value],
|
865 | ];
|
866 | }
|
867 | },
|
868 | });
|
869 | borderColorProperty.register(Style);
|
870 | export const borderTopColorProperty = new CssProperty({
|
871 | name: 'borderTopColor',
|
872 | cssName: 'border-top-color',
|
873 | valueChanged: (target, oldValue, newValue) => {
|
874 | target.backgroundInternal = target.backgroundInternal.withBorderTopColor(newValue);
|
875 | },
|
876 | equalityComparer: Color.equals,
|
877 | valueConverter: (value) => new Color(value),
|
878 | });
|
879 | borderTopColorProperty.register(Style);
|
880 | export const borderRightColorProperty = new CssProperty({
|
881 | name: 'borderRightColor',
|
882 | cssName: 'border-right-color',
|
883 | valueChanged: (target, oldValue, newValue) => {
|
884 | target.backgroundInternal = target.backgroundInternal.withBorderRightColor(newValue);
|
885 | },
|
886 | equalityComparer: Color.equals,
|
887 | valueConverter: (value) => new Color(value),
|
888 | });
|
889 | borderRightColorProperty.register(Style);
|
890 | export const borderBottomColorProperty = new CssProperty({
|
891 | name: 'borderBottomColor',
|
892 | cssName: 'border-bottom-color',
|
893 | valueChanged: (target, oldValue, newValue) => {
|
894 | target.backgroundInternal = target.backgroundInternal.withBorderBottomColor(newValue);
|
895 | },
|
896 | equalityComparer: Color.equals,
|
897 | valueConverter: (value) => new Color(value),
|
898 | });
|
899 | borderBottomColorProperty.register(Style);
|
900 | export const borderLeftColorProperty = new CssProperty({
|
901 | name: 'borderLeftColor',
|
902 | cssName: 'border-left-color',
|
903 | valueChanged: (target, oldValue, newValue) => {
|
904 | target.backgroundInternal = target.backgroundInternal.withBorderLeftColor(newValue);
|
905 | },
|
906 | equalityComparer: Color.equals,
|
907 | valueConverter: (value) => new Color(value),
|
908 | });
|
909 | borderLeftColorProperty.register(Style);
|
910 |
|
911 | const borderWidthProperty = new ShorthandProperty({
|
912 | name: 'borderWidth',
|
913 | cssName: 'border-width',
|
914 | getter: function () {
|
915 | if (Length.equals(this.borderTopWidth, this.borderRightWidth) && Length.equals(this.borderTopWidth, this.borderBottomWidth) && Length.equals(this.borderTopWidth, this.borderLeftWidth)) {
|
916 | return this.borderTopWidth;
|
917 | }
|
918 | else {
|
919 | return `${Length.convertToString(this.borderTopWidth)} ${Length.convertToString(this.borderRightWidth)} ${Length.convertToString(this.borderBottomWidth)} ${Length.convertToString(this.borderLeftWidth)}`;
|
920 | }
|
921 | },
|
922 | converter: function (value) {
|
923 | if (typeof value === 'string' && value !== 'auto') {
|
924 | const borderWidths = parseThickness(value);
|
925 | return [
|
926 | [borderTopWidthProperty, borderWidths.top],
|
927 | [borderRightWidthProperty, borderWidths.right],
|
928 | [borderBottomWidthProperty, borderWidths.bottom],
|
929 | [borderLeftWidthProperty, borderWidths.left],
|
930 | ];
|
931 | }
|
932 | else {
|
933 | return [
|
934 | [borderTopWidthProperty, value],
|
935 | [borderRightWidthProperty, value],
|
936 | [borderBottomWidthProperty, value],
|
937 | [borderLeftWidthProperty, value],
|
938 | ];
|
939 | }
|
940 | },
|
941 | });
|
942 | borderWidthProperty.register(Style);
|
943 | export const borderTopWidthProperty = new CssProperty({
|
944 | name: 'borderTopWidth',
|
945 | cssName: 'border-top-width',
|
946 | defaultValue: CoreTypes.zeroLength,
|
947 | affectsLayout: global.isIOS,
|
948 | equalityComparer: Length.equals,
|
949 | valueChanged: (target, oldValue, newValue) => {
|
950 | const value = Length.toDevicePixels(newValue, 0);
|
951 | if (!isNonNegativeFiniteNumber(value)) {
|
952 | throw new Error(`border-top-width should be Non-Negative Finite number. Value: ${value}`);
|
953 | }
|
954 | const view = target.viewRef.get();
|
955 | if (view) {
|
956 | view.effectiveBorderTopWidth = value;
|
957 | }
|
958 | else {
|
959 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
960 | }
|
961 | target.backgroundInternal = target.backgroundInternal.withBorderTopWidth(value);
|
962 | },
|
963 | valueConverter: Length.parse,
|
964 | });
|
965 | borderTopWidthProperty.register(Style);
|
966 | export const borderRightWidthProperty = new CssProperty({
|
967 | name: 'borderRightWidth',
|
968 | cssName: 'border-right-width',
|
969 | defaultValue: CoreTypes.zeroLength,
|
970 | affectsLayout: global.isIOS,
|
971 | equalityComparer: Length.equals,
|
972 | valueChanged: (target, oldValue, newValue) => {
|
973 | const value = Length.toDevicePixels(newValue, 0);
|
974 | if (!isNonNegativeFiniteNumber(value)) {
|
975 | throw new Error(`border-right-width should be Non-Negative Finite number. Value: ${value}`);
|
976 | }
|
977 | const view = target.viewRef.get();
|
978 | if (view) {
|
979 | view.effectiveBorderRightWidth = value;
|
980 | }
|
981 | else {
|
982 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
983 | }
|
984 | target.backgroundInternal = target.backgroundInternal.withBorderRightWidth(value);
|
985 | },
|
986 | valueConverter: Length.parse,
|
987 | });
|
988 | borderRightWidthProperty.register(Style);
|
989 | export const borderBottomWidthProperty = new CssProperty({
|
990 | name: 'borderBottomWidth',
|
991 | cssName: 'border-bottom-width',
|
992 | defaultValue: CoreTypes.zeroLength,
|
993 | affectsLayout: global.isIOS,
|
994 | equalityComparer: Length.equals,
|
995 | valueChanged: (target, oldValue, newValue) => {
|
996 | const value = Length.toDevicePixels(newValue, 0);
|
997 | if (!isNonNegativeFiniteNumber(value)) {
|
998 | throw new Error(`border-bottom-width should be Non-Negative Finite number. Value: ${value}`);
|
999 | }
|
1000 | const view = target.viewRef.get();
|
1001 | if (view) {
|
1002 | view.effectiveBorderBottomWidth = value;
|
1003 | }
|
1004 | else {
|
1005 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
1006 | }
|
1007 | target.backgroundInternal = target.backgroundInternal.withBorderBottomWidth(value);
|
1008 | },
|
1009 | valueConverter: Length.parse,
|
1010 | });
|
1011 | borderBottomWidthProperty.register(Style);
|
1012 | export const borderLeftWidthProperty = new CssProperty({
|
1013 | name: 'borderLeftWidth',
|
1014 | cssName: 'border-left-width',
|
1015 | defaultValue: CoreTypes.zeroLength,
|
1016 | affectsLayout: global.isIOS,
|
1017 | equalityComparer: Length.equals,
|
1018 | valueChanged: (target, oldValue, newValue) => {
|
1019 | const value = Length.toDevicePixels(newValue, 0);
|
1020 | if (!isNonNegativeFiniteNumber(value)) {
|
1021 | throw new Error(`border-left-width should be Non-Negative Finite number. Value: ${value}`);
|
1022 | }
|
1023 | const view = target.viewRef.get();
|
1024 | if (view) {
|
1025 | view.effectiveBorderLeftWidth = value;
|
1026 | }
|
1027 | else {
|
1028 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
1029 | }
|
1030 | target.backgroundInternal = target.backgroundInternal.withBorderLeftWidth(value);
|
1031 | },
|
1032 | valueConverter: Length.parse,
|
1033 | });
|
1034 | borderLeftWidthProperty.register(Style);
|
1035 |
|
1036 | const borderRadiusProperty = new ShorthandProperty({
|
1037 | name: 'borderRadius',
|
1038 | cssName: 'border-radius',
|
1039 | getter: function () {
|
1040 | if (Length.equals(this.borderTopLeftRadius, this.borderTopRightRadius) && Length.equals(this.borderTopLeftRadius, this.borderBottomRightRadius) && Length.equals(this.borderTopLeftRadius, this.borderBottomLeftRadius)) {
|
1041 | return this.borderTopLeftRadius;
|
1042 | }
|
1043 | return `${Length.convertToString(this.borderTopLeftRadius)} ${Length.convertToString(this.borderTopRightRadius)} ${Length.convertToString(this.borderBottomRightRadius)} ${Length.convertToString(this.borderBottomLeftRadius)}`;
|
1044 | },
|
1045 | converter: function (value) {
|
1046 | if (typeof value === 'string') {
|
1047 | const borderRadius = parseThickness(value);
|
1048 | return [
|
1049 | [borderTopLeftRadiusProperty, borderRadius.top],
|
1050 | [borderTopRightRadiusProperty, borderRadius.right],
|
1051 | [borderBottomRightRadiusProperty, borderRadius.bottom],
|
1052 | [borderBottomLeftRadiusProperty, borderRadius.left],
|
1053 | ];
|
1054 | }
|
1055 | else {
|
1056 | return [
|
1057 | [borderTopLeftRadiusProperty, value],
|
1058 | [borderTopRightRadiusProperty, value],
|
1059 | [borderBottomRightRadiusProperty, value],
|
1060 | [borderBottomLeftRadiusProperty, value],
|
1061 | ];
|
1062 | }
|
1063 | },
|
1064 | });
|
1065 | borderRadiusProperty.register(Style);
|
1066 | export const borderTopLeftRadiusProperty = new CssProperty({
|
1067 | name: 'borderTopLeftRadius',
|
1068 | cssName: 'border-top-left-radius',
|
1069 | defaultValue: 0,
|
1070 | affectsLayout: global.isIOS,
|
1071 | valueChanged: (target, oldValue, newValue) => {
|
1072 | const value = Length.toDevicePixels(newValue, 0);
|
1073 | if (!isNonNegativeFiniteNumber(value)) {
|
1074 | throw new Error(`border-top-left-radius should be Non-Negative Finite number. Value: ${value}`);
|
1075 | }
|
1076 | target.backgroundInternal = target.backgroundInternal.withBorderTopLeftRadius(value);
|
1077 | },
|
1078 | valueConverter: Length.parse,
|
1079 | equalityComparer: Length.equals,
|
1080 | });
|
1081 | borderTopLeftRadiusProperty.register(Style);
|
1082 | export const borderTopRightRadiusProperty = new CssProperty({
|
1083 | name: 'borderTopRightRadius',
|
1084 | cssName: 'border-top-right-radius',
|
1085 | defaultValue: 0,
|
1086 | affectsLayout: global.isIOS,
|
1087 | valueChanged: (target, oldValue, newValue) => {
|
1088 | const value = Length.toDevicePixels(newValue, 0);
|
1089 | if (!isNonNegativeFiniteNumber(value)) {
|
1090 | throw new Error(`border-top-right-radius should be Non-Negative Finite number. Value: ${value}`);
|
1091 | }
|
1092 | target.backgroundInternal = target.backgroundInternal.withBorderTopRightRadius(value);
|
1093 | },
|
1094 | valueConverter: Length.parse,
|
1095 | equalityComparer: Length.equals,
|
1096 | });
|
1097 | borderTopRightRadiusProperty.register(Style);
|
1098 | export const borderBottomRightRadiusProperty = new CssProperty({
|
1099 | name: 'borderBottomRightRadius',
|
1100 | cssName: 'border-bottom-right-radius',
|
1101 | defaultValue: 0,
|
1102 | affectsLayout: global.isIOS,
|
1103 | valueChanged: (target, oldValue, newValue) => {
|
1104 | const value = Length.toDevicePixels(newValue, 0);
|
1105 | if (!isNonNegativeFiniteNumber(value)) {
|
1106 | throw new Error(`border-bottom-right-radius should be Non-Negative Finite number. Value: ${value}`);
|
1107 | }
|
1108 | target.backgroundInternal = target.backgroundInternal.withBorderBottomRightRadius(value);
|
1109 | },
|
1110 | valueConverter: Length.parse,
|
1111 | equalityComparer: Length.equals,
|
1112 | });
|
1113 | borderBottomRightRadiusProperty.register(Style);
|
1114 | export const borderBottomLeftRadiusProperty = new CssProperty({
|
1115 | name: 'borderBottomLeftRadius',
|
1116 | cssName: 'border-bottom-left-radius',
|
1117 | defaultValue: 0,
|
1118 | affectsLayout: global.isIOS,
|
1119 | valueChanged: (target, oldValue, newValue) => {
|
1120 | const value = Length.toDevicePixels(newValue, 0);
|
1121 | if (!isNonNegativeFiniteNumber(value)) {
|
1122 | throw new Error(`border-bottom-left-radius should be Non-Negative Finite number. Value: ${value}`);
|
1123 | }
|
1124 | target.backgroundInternal = target.backgroundInternal.withBorderBottomLeftRadius(value);
|
1125 | },
|
1126 | valueConverter: Length.parse,
|
1127 | equalityComparer: Length.equals,
|
1128 | });
|
1129 | borderBottomLeftRadiusProperty.register(Style);
|
1130 | const boxShadowProperty = new CssProperty({
|
1131 | name: 'boxShadow',
|
1132 | cssName: 'box-shadow',
|
1133 | valueChanged: (target, oldValue, newValue) => {
|
1134 | target.backgroundInternal = target.backgroundInternal.withBoxShadow(newValue
|
1135 | ? {
|
1136 | inset: newValue.inset,
|
1137 | offsetX: Length.toDevicePixels(newValue.offsetX, 0),
|
1138 | offsetY: Length.toDevicePixels(newValue.offsetY, 0),
|
1139 | blurRadius: Length.toDevicePixels(newValue.blurRadius, 0),
|
1140 | spreadRadius: Length.toDevicePixels(newValue.spreadRadius, 0),
|
1141 | color: newValue.color,
|
1142 | }
|
1143 | : null);
|
1144 | },
|
1145 | valueConverter: (value) => {
|
1146 | return parseCSSShadow(value);
|
1147 | },
|
1148 | });
|
1149 | boxShadowProperty.register(Style);
|
1150 | function isNonNegativeFiniteNumber(value) {
|
1151 | return isFinite(value) && !isNaN(value) && value >= 0;
|
1152 | }
|
1153 | const supportedPaths = ['rect', 'circle', 'ellipse', 'polygon', 'inset'];
|
1154 | function isClipPathValid(value) {
|
1155 | if (!value) {
|
1156 | return true;
|
1157 | }
|
1158 | const functionName = value.substring(0, value.indexOf('(')).trim();
|
1159 | return supportedPaths.indexOf(functionName) !== -1;
|
1160 | }
|
1161 | export const clipPathProperty = new CssProperty({
|
1162 | name: 'clipPath',
|
1163 | cssName: 'clip-path',
|
1164 | valueChanged: (target, oldValue, newValue) => {
|
1165 | if (!isClipPathValid(newValue)) {
|
1166 | throw new Error('clip-path is not valid.');
|
1167 | }
|
1168 | target.backgroundInternal = target.backgroundInternal.withClipPath(newValue);
|
1169 | },
|
1170 | });
|
1171 | clipPathProperty.register(Style);
|
1172 | function isFloatValueConverter(value) {
|
1173 | const newValue = parseFloat(value);
|
1174 | if (isNaN(newValue)) {
|
1175 | throw new Error(`Invalid value: ${newValue}`);
|
1176 | }
|
1177 | return newValue;
|
1178 | }
|
1179 | export const zIndexProperty = new CssProperty({
|
1180 | name: 'zIndex',
|
1181 | cssName: 'z-index',
|
1182 | valueConverter: isFloatValueConverter,
|
1183 | });
|
1184 | zIndexProperty.register(Style);
|
1185 | function opacityConverter(value) {
|
1186 | const newValue = parseFloat(value);
|
1187 | if (!isNaN(newValue) && 0 <= newValue && newValue <= 1) {
|
1188 | return newValue;
|
1189 | }
|
1190 | throw new Error(`Opacity should be between [0, 1]. Value: ${newValue}`);
|
1191 | }
|
1192 | export const opacityProperty = new CssAnimationProperty({
|
1193 | name: 'opacity',
|
1194 | cssName: 'opacity',
|
1195 | defaultValue: 1,
|
1196 | valueConverter: opacityConverter,
|
1197 | });
|
1198 | opacityProperty.register(Style);
|
1199 | export const colorProperty = new InheritedCssProperty({
|
1200 | name: 'color',
|
1201 | cssName: 'color',
|
1202 | equalityComparer: Color.equals,
|
1203 | valueConverter: (v) => new Color(v),
|
1204 | });
|
1205 | colorProperty.register(Style);
|
1206 | export const fontInternalProperty = new CssProperty({
|
1207 | name: 'fontInternal',
|
1208 | cssName: '_fontInternal',
|
1209 | });
|
1210 | fontInternalProperty.register(Style);
|
1211 | export const fontFamilyProperty = new InheritedCssProperty({
|
1212 | name: 'fontFamily',
|
1213 | cssName: 'font-family',
|
1214 | affectsLayout: global.isIOS,
|
1215 | valueChanged: (target, oldValue, newValue) => {
|
1216 | const currentFont = target.fontInternal || Font.default;
|
1217 | if (currentFont.fontFamily !== newValue) {
|
1218 | const newFont = currentFont.withFontFamily(newValue);
|
1219 | target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont;
|
1220 | }
|
1221 | },
|
1222 | });
|
1223 | fontFamilyProperty.register(Style);
|
1224 | export const fontScaleInternalProperty = new InheritedCssProperty({
|
1225 | name: 'fontScaleInternal',
|
1226 | cssName: '_fontScaleInternal',
|
1227 | defaultValue: 1.0,
|
1228 | valueConverter: (v) => parseFloat(v),
|
1229 | });
|
1230 | fontScaleInternalProperty.register(Style);
|
1231 | export const fontSizeProperty = new InheritedCssProperty({
|
1232 | name: 'fontSize',
|
1233 | cssName: 'font-size',
|
1234 | affectsLayout: global.isIOS,
|
1235 | valueChanged: (target, oldValue, newValue) => {
|
1236 | if (target.viewRef['handleFontSize'] === true) {
|
1237 | return;
|
1238 | }
|
1239 | const currentFont = target.fontInternal || Font.default;
|
1240 | if (currentFont.fontSize !== newValue) {
|
1241 | const newFont = currentFont.withFontSize(newValue);
|
1242 | target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont;
|
1243 | }
|
1244 | },
|
1245 | valueConverter: (v) => parseFloat(v),
|
1246 | });
|
1247 | fontSizeProperty.register(Style);
|
1248 | export const fontStyleProperty = new InheritedCssProperty({
|
1249 | name: 'fontStyle',
|
1250 | cssName: 'font-style',
|
1251 | affectsLayout: global.isIOS,
|
1252 | defaultValue: FontStyle.NORMAL,
|
1253 | valueConverter: FontStyle.parse,
|
1254 | valueChanged: (target, oldValue, newValue) => {
|
1255 | const currentFont = target.fontInternal || Font.default;
|
1256 | if (currentFont.fontStyle !== newValue) {
|
1257 | const newFont = currentFont.withFontStyle(newValue);
|
1258 | target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont;
|
1259 | }
|
1260 | },
|
1261 | });
|
1262 | fontStyleProperty.register(Style);
|
1263 | export const fontWeightProperty = new InheritedCssProperty({
|
1264 | name: 'fontWeight',
|
1265 | cssName: 'font-weight',
|
1266 | affectsLayout: global.isIOS,
|
1267 | defaultValue: FontWeight.NORMAL,
|
1268 | valueConverter: FontWeight.parse,
|
1269 | valueChanged: (target, oldValue, newValue) => {
|
1270 | const currentFont = target.fontInternal || Font.default;
|
1271 | if (currentFont.fontWeight !== newValue) {
|
1272 | const newFont = currentFont.withFontWeight(newValue);
|
1273 | target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont;
|
1274 | }
|
1275 | },
|
1276 | });
|
1277 | fontWeightProperty.register(Style);
|
1278 | const fontProperty = new ShorthandProperty({
|
1279 | name: 'font',
|
1280 | cssName: 'font',
|
1281 | getter: function () {
|
1282 | return `${this.fontStyle} ${this.fontWeight} ${this.fontSize} ${this.fontFamily}`;
|
1283 | },
|
1284 | converter: function (value) {
|
1285 | if (value === unsetValue) {
|
1286 | return [
|
1287 | [fontStyleProperty, unsetValue],
|
1288 | [fontWeightProperty, unsetValue],
|
1289 | [fontSizeProperty, unsetValue],
|
1290 | [fontFamilyProperty, unsetValue],
|
1291 | ];
|
1292 | }
|
1293 | else {
|
1294 | const font = parseFont(value);
|
1295 | const fontSize = parseFloat(font.fontSize);
|
1296 | return [
|
1297 | [fontStyleProperty, font.fontStyle],
|
1298 | [fontWeightProperty, font.fontWeight],
|
1299 | [fontSizeProperty, fontSize],
|
1300 | [fontFamilyProperty, font.fontFamily],
|
1301 | ];
|
1302 | }
|
1303 | },
|
1304 | });
|
1305 | fontProperty.register(Style);
|
1306 | export const fontVariationSettingsProperty = new InheritedCssProperty({
|
1307 | name: 'fontVariationSettings',
|
1308 | cssName: 'font-variation-settings',
|
1309 | affectsLayout: global.isIOS,
|
1310 | valueChanged: (target, oldValue, newValue) => {
|
1311 | const currentFont = target.fontInternal || Font.default;
|
1312 | if (currentFont.fontVariationSettings !== newValue) {
|
1313 | const newFont = currentFont.withFontVariationSettings(newValue);
|
1314 | target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont;
|
1315 | }
|
1316 | },
|
1317 | valueConverter: (value) => {
|
1318 | return FontVariationSettings.parse(value);
|
1319 | },
|
1320 | });
|
1321 | fontVariationSettingsProperty.register(Style);
|
1322 | export const visibilityProperty = new CssProperty({
|
1323 | name: 'visibility',
|
1324 | cssName: 'visibility',
|
1325 | defaultValue: CoreTypes.Visibility.visible,
|
1326 | affectsLayout: global.isIOS,
|
1327 | valueConverter: CoreTypes.Visibility.parse,
|
1328 | valueChanged: (target, oldValue, newValue) => {
|
1329 | const view = target.viewRef.get();
|
1330 | if (view) {
|
1331 | view.isCollapsed = newValue === CoreTypes.Visibility.collapse;
|
1332 | }
|
1333 | else {
|
1334 | Trace.write(`${newValue} not set to view's property because ".viewRef" is cleared`, Trace.categories.Style, Trace.messageType.warn);
|
1335 | }
|
1336 | },
|
1337 | });
|
1338 | visibilityProperty.register(Style);
|
1339 | export const androidElevationProperty = new CssProperty({
|
1340 | name: 'androidElevation',
|
1341 | cssName: 'android-elevation',
|
1342 | valueConverter: parseFloat,
|
1343 | });
|
1344 | androidElevationProperty.register(Style);
|
1345 | export const androidDynamicElevationOffsetProperty = new CssProperty({
|
1346 | name: 'androidDynamicElevationOffset',
|
1347 | cssName: 'android-dynamic-elevation-offset',
|
1348 | valueConverter: parseFloat,
|
1349 | });
|
1350 | androidDynamicElevationOffsetProperty.register(Style);
|
1351 |
|
\ | No newline at end of file |