@octane-xplat/nativescript-mason-core 0.0.1 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +198 -2
- package/artifact-inventory.json +774 -0
- package/box-shadow.d.ts +10 -0
- package/box-shadow.js +48 -0
- package/common.d.ts +485 -0
- package/common.js +2659 -0
- package/css-colors.d.ts +17 -0
- package/css-colors.js +54 -0
- package/css-environment.d.ts +49 -0
- package/css-environment.js +309 -0
- package/css-filter.d.ts +17 -0
- package/css-filter.js +140 -0
- package/css-keyframes.d.ts +41 -0
- package/css-keyframes.js +437 -0
- package/css-math.d.ts +34 -0
- package/css-math.js +300 -0
- package/css-shorthands.d.ts +65 -0
- package/css-shorthands.js +673 -0
- package/css-transform.d.ts +6 -0
- package/css-transform.js +100 -0
- package/diagnostics.d.ts +51 -0
- package/diagnostics.js +52 -0
- package/display-density.d.ts +9 -0
- package/display-density.js +20 -0
- package/fonts.d.ts +20 -0
- package/fonts.js +88 -0
- package/foreign-layout.d.ts +6 -0
- package/foreign-layout.js +151 -0
- package/gradient-stops.d.ts +25 -0
- package/gradient-stops.js +112 -0
- package/index.android.d.ts +11 -0
- package/index.android.js +9 -0
- package/index.d.ts +12 -0
- package/index.ios.d.ts +11 -0
- package/index.ios.js +9 -0
- package/ios-host-README.md +124 -0
- package/layout-types.d.ts +192 -0
- package/mask.d.ts +54 -0
- package/mask.js +26 -0
- package/native-environment.android.d.ts +3 -0
- package/native-environment.android.js +69 -0
- package/native-environment.d.ts +3 -0
- package/native-environment.ios.d.ts +3 -0
- package/native-environment.ios.js +65 -0
- package/native-types-README.md +51 -0
- package/package.json +47 -6
- package/panel-helpers.d.ts +3 -0
- package/panel-helpers.js +8 -0
- package/platforms/android/MasonCore.aar +0 -0
- package/platforms/android/include.gradle +6 -0
- package/platforms/android/manifest.json +73 -0
- package/platforms/ios/FontManager.LICENSE +201 -0
- package/platforms/ios/MasonCore.xcframework/Info.plist +43 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Headers/Mason.h +25 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Headers/MasonCore-Swift.h +1739 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Headers/mason_native.h +601 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Info.plist +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/MasonCore +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Modules/MasonCore.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Modules/MasonCore.swiftmodule/arm64-apple-ios.abi.json +100095 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Modules/MasonCore.swiftmodule/arm64-apple-ios.private.swiftinterface +3174 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Modules/MasonCore.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Modules/MasonCore.swiftmodule/arm64-apple-ios.swiftinterface +3174 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/Modules/module.modulemap +9 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/SDWebImage_SDWebImage.bundle/Info.plist +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64/MasonCore.framework/SDWebImage_SDWebImage.bundle/PrivacyInfo.xcprivacy +23 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Headers/Mason.h +25 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Headers/MasonCore-Swift.h +1739 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Headers/mason_native.h +601 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Info.plist +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/MasonCore +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Modules/MasonCore.swiftmodule/Project/arm64-apple-ios-simulator.swiftsourceinfo +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Modules/MasonCore.swiftmodule/arm64-apple-ios-simulator.abi.json +100095 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Modules/MasonCore.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +3174 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Modules/MasonCore.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Modules/MasonCore.swiftmodule/arm64-apple-ios-simulator.swiftinterface +3174 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/Modules/module.modulemap +9 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/SDWebImage_SDWebImage.bundle/Info.plist +0 -0
- package/platforms/ios/MasonCore.xcframework/ios-arm64-simulator/MasonCore.framework/SDWebImage_SDWebImage.bundle/PrivacyInfo.xcprivacy +23 -0
- package/platforms/ios/Package.resolved +24 -0
- package/platforms/ios/SDWebImage.LICENSE +20 -0
- package/platforms/ios/build.xcconfig +4 -0
- package/platforms/ios/device-manifest.json +213 -0
- package/platforms/ios/include/FontManager/NSCFontDescriptors.h +36 -0
- package/platforms/ios/include/FontManager/NSCFontFace.h +98 -0
- package/platforms/ios/include/FontManager/NSCFontFaceSet.h +73 -0
- package/platforms/ios/include/FontManager/NSCFontManagerUmbrella.h +7 -0
- package/platforms/ios/include/FontManager/NSCFontParser.h +22 -0
- package/platforms/ios/include/FontManager/NSCFontRegex.h +4 -0
- package/platforms/ios/include/FontManager/NSCFontResolver.h +43 -0
- package/platforms/ios/include/FontManager/NSCFontStyle.h +27 -0
- package/platforms/ios/include/FontManager/NSCFontTypes.h +41 -0
- package/platforms/ios/include/SDWebImage/NSButton+WebCache.h +340 -0
- package/platforms/ios/include/SDWebImage/NSData+ImageContentType.h +63 -0
- package/platforms/ios/include/SDWebImage/NSImage+Compatibility.h +67 -0
- package/platforms/ios/include/SDWebImage/SDAnimatedImage.h +136 -0
- package/platforms/ios/include/SDWebImage/SDAnimatedImagePlayer.h +113 -0
- package/platforms/ios/include/SDWebImage/SDAnimatedImageRep.h +33 -0
- package/platforms/ios/include/SDWebImage/SDAnimatedImageView+WebCache.h +168 -0
- package/platforms/ios/include/SDWebImage/SDAnimatedImageView.h +125 -0
- package/platforms/ios/include/SDWebImage/SDCallbackQueue.h +59 -0
- package/platforms/ios/include/SDWebImage/SDDiskCache.h +146 -0
- package/platforms/ios/include/SDWebImage/SDGraphicsImageRenderer.h +84 -0
- package/platforms/ios/include/SDWebImage/SDImageAPNGCoder.h +19 -0
- package/platforms/ios/include/SDWebImage/SDImageAWebPCoder.h +23 -0
- package/platforms/ios/include/SDWebImage/SDImageCache.h +482 -0
- package/platforms/ios/include/SDWebImage/SDImageCacheConfig.h +153 -0
- package/platforms/ios/include/SDWebImage/SDImageCacheDefine.h +179 -0
- package/platforms/ios/include/SDWebImage/SDImageCachesManager.h +81 -0
- package/platforms/ios/include/SDWebImage/SDImageCoder.h +347 -0
- package/platforms/ios/include/SDWebImage/SDImageCoderHelper.h +251 -0
- package/platforms/ios/include/SDWebImage/SDImageCodersManager.h +58 -0
- package/platforms/ios/include/SDWebImage/SDImageFrame.h +44 -0
- package/platforms/ios/include/SDWebImage/SDImageGIFCoder.h +22 -0
- package/platforms/ios/include/SDWebImage/SDImageGraphics.h +28 -0
- package/platforms/ios/include/SDWebImage/SDImageHEICCoder.h +25 -0
- package/platforms/ios/include/SDWebImage/SDImageIOAnimatedCoder.h +65 -0
- package/platforms/ios/include/SDWebImage/SDImageIOCoder.h +30 -0
- package/platforms/ios/include/SDWebImage/SDImageLoader.h +146 -0
- package/platforms/ios/include/SDWebImage/SDImageLoadersManager.h +40 -0
- package/platforms/ios/include/SDWebImage/SDImageTransformer.h +283 -0
- package/platforms/ios/include/SDWebImage/SDMemoryCache.h +78 -0
- package/platforms/ios/include/SDWebImage/SDWebImage.h +91 -0
- package/platforms/ios/include/SDWebImage/SDWebImageCacheKeyFilter.h +35 -0
- package/platforms/ios/include/SDWebImage/SDWebImageCacheSerializer.h +39 -0
- package/platforms/ios/include/SDWebImage/SDWebImageCompat.h +102 -0
- package/platforms/ios/include/SDWebImage/SDWebImageDefine.h +420 -0
- package/platforms/ios/include/SDWebImage/SDWebImageDownloader.h +320 -0
- package/platforms/ios/include/SDWebImage/SDWebImageDownloaderConfig.h +113 -0
- package/platforms/ios/include/SDWebImage/SDWebImageDownloaderDecryptor.h +52 -0
- package/platforms/ios/include/SDWebImage/SDWebImageDownloaderOperation.h +191 -0
- package/platforms/ios/include/SDWebImage/SDWebImageDownloaderRequestModifier.h +72 -0
- package/platforms/ios/include/SDWebImage/SDWebImageDownloaderResponseModifier.h +72 -0
- package/platforms/ios/include/SDWebImage/SDWebImageError.h +33 -0
- package/platforms/ios/include/SDWebImage/SDWebImageIndicator.h +119 -0
- package/platforms/ios/include/SDWebImage/SDWebImageManager.h +290 -0
- package/platforms/ios/include/SDWebImage/SDWebImageOperation.h +27 -0
- package/platforms/ios/include/SDWebImage/SDWebImageOptionsProcessor.h +78 -0
- package/platforms/ios/include/SDWebImage/SDWebImagePrefetcher.h +168 -0
- package/platforms/ios/include/SDWebImage/SDWebImageTransition.h +131 -0
- package/platforms/ios/include/SDWebImage/UIButton+WebCache.h +402 -0
- package/platforms/ios/include/SDWebImage/UIImage+ExtendedCacheData.h +24 -0
- package/platforms/ios/include/SDWebImage/UIImage+ForceDecode.h +52 -0
- package/platforms/ios/include/SDWebImage/UIImage+GIF.h +26 -0
- package/platforms/ios/include/SDWebImage/UIImage+MemoryCacheCost.h +27 -0
- package/platforms/ios/include/SDWebImage/UIImage+Metadata.h +104 -0
- package/platforms/ios/include/SDWebImage/UIImage+MultiFormat.h +81 -0
- package/platforms/ios/include/SDWebImage/UIImage+Transform.h +164 -0
- package/platforms/ios/include/SDWebImage/UIImageView+HighlightedWebCache.h +142 -0
- package/platforms/ios/include/SDWebImage/UIImageView+WebCache.h +209 -0
- package/platforms/ios/include/SDWebImage/UIView+WebCache.h +131 -0
- package/platforms/ios/include/SDWebImage/UIView+WebCacheOperation.h +52 -0
- package/platforms/ios/include/SDWebImage/UIView+WebCacheState.h +63 -0
- package/platforms/ios/include/module.modulemap +2 -0
- package/platforms/ios/manifest.json +132 -0
- package/platforms/ios/simulator-manifest.json +213 -0
- package/plugin-README.md +234 -0
- package/properties.d.ts +154 -0
- package/properties.js +2456 -0
- package/pseudo.d.ts +9 -0
- package/pseudo.js +57 -0
- package/scroll/index.android.d.ts +15 -0
- package/scroll/index.android.js +72 -0
- package/scroll/index.d.ts +5 -0
- package/scroll/index.ios.d.ts +18 -0
- package/scroll/index.ios.js +106 -0
- package/style.d.ts +768 -0
- package/style.js +7167 -0
- package/symbols.d.ts +25 -0
- package/symbols.js +41 -0
- package/tree/index.android.d.ts +11 -0
- package/tree/index.android.js +42 -0
- package/tree/index.d.ts +15 -0
- package/tree/index.ios.d.ts +26 -0
- package/tree/index.ios.js +53 -0
- package/typings/android-declarations.d.ts +4 -0
- package/typings/android.d.ts +9730 -0
- package/typings/manifest.json +45 -0
- package/typings/native.d.ts +5 -0
- package/typings/objc!FontManager.d.ts +389 -0
- package/typings/objc!MasonCore.d.ts +2472 -0
- package/typings/objc!SDWebImage.d.ts +3378 -0
- package/units.d.ts +29 -0
- package/units.js +44 -0
- package/utils/index.android.d.ts +211 -0
- package/utils/index.android.js +595 -0
- package/utils/index.ios.d.ts +222 -0
- package/utils/index.ios.js +790 -0
- package/view/index.android.d.ts +13 -0
- package/view/index.android.js +74 -0
- package/view/index.d.ts +2 -0
- package/view/index.ios.d.ts +24 -0
- package/view/index.ios.js +157 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
import { unsetValue } from '@nativescript/core';
|
|
2
|
+
export function tokenizeCss(value) {
|
|
3
|
+
const tokens = [];
|
|
4
|
+
let current = '';
|
|
5
|
+
let depth = 0;
|
|
6
|
+
let quote = '';
|
|
7
|
+
let escaped = false;
|
|
8
|
+
const flush = () => {
|
|
9
|
+
if (current) {
|
|
10
|
+
tokens.push(current);
|
|
11
|
+
current = '';
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
for (const ch of value) {
|
|
15
|
+
if (quote) {
|
|
16
|
+
current += ch;
|
|
17
|
+
if (escaped) {
|
|
18
|
+
escaped = false;
|
|
19
|
+
}
|
|
20
|
+
else if (ch === '\\') {
|
|
21
|
+
escaped = true;
|
|
22
|
+
}
|
|
23
|
+
else if (ch === quote) {
|
|
24
|
+
quote = '';
|
|
25
|
+
}
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (ch === '"' || ch === "'") {
|
|
29
|
+
quote = ch;
|
|
30
|
+
}
|
|
31
|
+
else if (ch === '(') {
|
|
32
|
+
depth++;
|
|
33
|
+
}
|
|
34
|
+
else if (ch === ')') {
|
|
35
|
+
if (depth === 0) {
|
|
36
|
+
throw new Error(`Unbalanced ")" in "${value}"`);
|
|
37
|
+
}
|
|
38
|
+
depth--;
|
|
39
|
+
}
|
|
40
|
+
else if (depth === 0 && /\s/.test(ch)) {
|
|
41
|
+
flush();
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
else if (depth === 0 && (ch === ',' || ch === '/')) {
|
|
45
|
+
flush();
|
|
46
|
+
tokens.push(ch);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
current += ch;
|
|
50
|
+
}
|
|
51
|
+
if (depth !== 0 || quote) {
|
|
52
|
+
throw new Error(`Unterminated "${quote || '('}" in "${value}"`);
|
|
53
|
+
}
|
|
54
|
+
flush();
|
|
55
|
+
return tokens;
|
|
56
|
+
}
|
|
57
|
+
export function cssLength(css, parse) {
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
parsed = parse(css);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
parsed = undefined;
|
|
64
|
+
}
|
|
65
|
+
if (typeof parsed === 'number') {
|
|
66
|
+
return { value: parsed, unit: 'dip', css };
|
|
67
|
+
}
|
|
68
|
+
if (parsed && typeof parsed === 'object') {
|
|
69
|
+
return { ...parsed, css };
|
|
70
|
+
}
|
|
71
|
+
return { value: 0, unit: 'dip', css };
|
|
72
|
+
}
|
|
73
|
+
export function isCssLength(value) {
|
|
74
|
+
return typeof value?.css === 'string';
|
|
75
|
+
}
|
|
76
|
+
const KEBAB_SEGMENT = /-([a-z])/g;
|
|
77
|
+
/** "background-color" -> "backgroundColor". */
|
|
78
|
+
export function toCamelCase(prop) {
|
|
79
|
+
return prop.replace(KEBAB_SEGMENT, (_, c) => c.toUpperCase());
|
|
80
|
+
}
|
|
81
|
+
const LENGTH = /^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?([a-z]+|%)?$/i;
|
|
82
|
+
const FUNCTION = /^[a-z-]+\(.*\)$/is;
|
|
83
|
+
function isLength(token) {
|
|
84
|
+
return LENGTH.test(token) || FUNCTION.test(token);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The space-separated values of a 1–4 value shorthand. Core's own splitter
|
|
88
|
+
* also accepts commas between them, so they are skipped rather than rejected.
|
|
89
|
+
*/
|
|
90
|
+
function values(value, accepts) {
|
|
91
|
+
const tokens = tokenizeCss(value).filter((token) => token !== ',');
|
|
92
|
+
for (const token of tokens) {
|
|
93
|
+
if (!accepts(token)) {
|
|
94
|
+
throw new Error(`Unexpected "${token}" in "${value}"`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return tokens;
|
|
98
|
+
}
|
|
99
|
+
function expandSides(parts, value) {
|
|
100
|
+
switch (parts.length) {
|
|
101
|
+
case 1:
|
|
102
|
+
return [parts[0], parts[0], parts[0], parts[0]];
|
|
103
|
+
case 2:
|
|
104
|
+
return [parts[0], parts[1], parts[0], parts[1]];
|
|
105
|
+
case 3:
|
|
106
|
+
return [parts[0], parts[1], parts[2], parts[1]];
|
|
107
|
+
case 4:
|
|
108
|
+
return [parts[0], parts[1], parts[2], parts[3]];
|
|
109
|
+
default:
|
|
110
|
+
throw new Error(`Expected 1 to 4 values in "${value}"`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function sides(accepts) {
|
|
114
|
+
return (value) => expandSides(values(value, accepts), value);
|
|
115
|
+
}
|
|
116
|
+
export const splitMargin = sides((token) => token === 'auto' || isLength(token));
|
|
117
|
+
export const splitPadding = sides(isLength);
|
|
118
|
+
export const BORDER_WIDTH_KEYWORDS = new Map([
|
|
119
|
+
['thin', '1'],
|
|
120
|
+
['medium', '3'],
|
|
121
|
+
['thick', '5'],
|
|
122
|
+
]);
|
|
123
|
+
export const splitBorderWidth = sides((token) => BORDER_WIDTH_KEYWORDS.has(token) || isLength(token));
|
|
124
|
+
export const splitBorderColor = sides((token) => token !== '/');
|
|
125
|
+
/** A logical axis shorthand (`margin-inline: <start> [<end>]`): start and end. */
|
|
126
|
+
function startEnd(accepts) {
|
|
127
|
+
return (value) => {
|
|
128
|
+
const parts = values(value, accepts);
|
|
129
|
+
if (parts.length < 1 || parts.length > 2) {
|
|
130
|
+
throw new Error(`Expected 1 or 2 values in "${value}"`);
|
|
131
|
+
}
|
|
132
|
+
return [parts[0], parts[1] ?? parts[0]];
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export const splitMarginStartEnd = startEnd((token) => token === 'auto' || isLength(token));
|
|
136
|
+
export const splitPaddingStartEnd = startEnd(isLength);
|
|
137
|
+
export const splitInsetStartEnd = startEnd((token) => token === 'auto' || isLength(token));
|
|
138
|
+
export const splitBorderWidthStartEnd = startEnd((token) => BORDER_WIDTH_KEYWORDS.has(token) || isLength(token));
|
|
139
|
+
export const splitBorderColorStartEnd = startEnd((token) => token !== '/');
|
|
140
|
+
/** A logical property that maps onto one physical longhand: the value as is. */
|
|
141
|
+
export function passThrough(value) {
|
|
142
|
+
return [value];
|
|
143
|
+
}
|
|
144
|
+
export const BORDER_STYLES = new Set(['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset']);
|
|
145
|
+
export const splitBorderStyle = sides((token) => BORDER_STYLES.has(token.toLowerCase()));
|
|
146
|
+
export const splitBorderStyleStartEnd = startEnd((token) => BORDER_STYLES.has(token.toLowerCase()));
|
|
147
|
+
const LENGTH_FUNCTION = /^(calc|min|max|clamp)\(/i;
|
|
148
|
+
/**
|
|
149
|
+
* Values for omitted `border` parts, matching the native border parsers:
|
|
150
|
+
* `medium`, `solid` (CSS: `none`) and black (CSS: `currentcolor`).
|
|
151
|
+
*/
|
|
152
|
+
const BORDER_OMITTED = { width: '3', style: 'solid', color: 'black' };
|
|
153
|
+
/** `<width> || <style> || <color>`, in any order, each at most once. */
|
|
154
|
+
function parseBorderParts(value, maxWidths) {
|
|
155
|
+
const widths = [];
|
|
156
|
+
let style;
|
|
157
|
+
let color;
|
|
158
|
+
for (const token of tokenizeCss(value)) {
|
|
159
|
+
const lower = token.toLowerCase();
|
|
160
|
+
if (BORDER_STYLES.has(lower)) {
|
|
161
|
+
if (style !== undefined) {
|
|
162
|
+
throw new Error(`Two border styles in "${value}"`);
|
|
163
|
+
}
|
|
164
|
+
style = lower;
|
|
165
|
+
}
|
|
166
|
+
else if (BORDER_WIDTH_KEYWORDS.has(lower) || LENGTH.test(token) || LENGTH_FUNCTION.test(token)) {
|
|
167
|
+
if (widths.length === maxWidths) {
|
|
168
|
+
throw new Error(`Too many border widths in "${value}"`);
|
|
169
|
+
}
|
|
170
|
+
widths.push(BORDER_WIDTH_KEYWORDS.get(lower) ?? token);
|
|
171
|
+
}
|
|
172
|
+
else if (token === ',' || token === '/' || color !== undefined) {
|
|
173
|
+
throw new Error(`Unexpected "${token}" in border "${value}"`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
color = token;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!widths.length && style === undefined && color === undefined) {
|
|
180
|
+
throw new Error(`Empty border "${value}"`);
|
|
181
|
+
}
|
|
182
|
+
// A border with no style has a computed width of 0, so it takes no room.
|
|
183
|
+
const hidden = style === 'none' || style === 'hidden';
|
|
184
|
+
return {
|
|
185
|
+
widths: hidden ? ['0'] : widths.length ? widths : [BORDER_OMITTED.width],
|
|
186
|
+
style: style ?? BORDER_OMITTED.style,
|
|
187
|
+
color: color === undefined || color.toLowerCase() === 'currentcolor' ? BORDER_OMITTED.color : color,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/** `border-<side>` as its width, style and color; omitted parts take BORDER_OMITTED. */
|
|
191
|
+
export function splitBorderSide(value) {
|
|
192
|
+
const { widths, style, color } = parseBorderParts(value, 1);
|
|
193
|
+
return [widths[0], style, color];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* `border` as four widths, four styles and four colors (top, right, bottom, left).
|
|
197
|
+
* Accepts up to four widths, as `border-width` does.
|
|
198
|
+
*/
|
|
199
|
+
export function splitBorder(value) {
|
|
200
|
+
const { widths, style, color } = parseBorderParts(value, 4);
|
|
201
|
+
return [...expandSides(widths, value), style, style, style, style, color, color, color, color];
|
|
202
|
+
}
|
|
203
|
+
export function splitBorderRadius(value) {
|
|
204
|
+
const tokens = values(value, (token) => token === '/' || isLength(token));
|
|
205
|
+
const slash = tokens.indexOf('/');
|
|
206
|
+
const horizontal = slash === -1 ? tokens : tokens.slice(0, slash);
|
|
207
|
+
const vertical = slash === -1 ? undefined : tokens.slice(slash + 1);
|
|
208
|
+
if (vertical && (vertical.length === 0 || vertical.includes('/'))) {
|
|
209
|
+
throw new Error(`Expected one "/" between radii in "${value}"`);
|
|
210
|
+
}
|
|
211
|
+
const h = expandSides(horizontal, value);
|
|
212
|
+
const v = vertical ? expandSides(vertical, value) : undefined;
|
|
213
|
+
return h.map((radius, i) => (v ? `${radius} ${v[i]}` : radius));
|
|
214
|
+
}
|
|
215
|
+
export function parseCornerRadius(value) {
|
|
216
|
+
const [h = '0', v = h] = tokenizeCss(value);
|
|
217
|
+
return [h, v];
|
|
218
|
+
}
|
|
219
|
+
// The same few radius strings repeat across a tree, so parse each once. Callers
|
|
220
|
+
// get copies: views keep and mutate their corner arrays.
|
|
221
|
+
const cornersCache = new Map();
|
|
222
|
+
export function borderRadiusCorners(value) {
|
|
223
|
+
let corners = cornersCache.get(value);
|
|
224
|
+
if (!corners) {
|
|
225
|
+
corners = splitBorderRadius(value).map(parseCornerRadius);
|
|
226
|
+
if (cornersCache.size >= 256)
|
|
227
|
+
cornersCache.clear();
|
|
228
|
+
cornersCache.set(value, corners);
|
|
229
|
+
}
|
|
230
|
+
return corners.map(([h, v]) => [h, v]);
|
|
231
|
+
}
|
|
232
|
+
export function composeBorderRadius(corners) {
|
|
233
|
+
const horizontal = corners.map(([h]) => h).join(' ');
|
|
234
|
+
if (corners.every(([h, v]) => h === v)) {
|
|
235
|
+
return horizontal;
|
|
236
|
+
}
|
|
237
|
+
return `${horizontal} / ${corners.map(([, v]) => v).join(' ')}`;
|
|
238
|
+
}
|
|
239
|
+
const IMAGE_FUNCTION = /^(url|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(/i;
|
|
240
|
+
const REPEAT_SINGLE = new Set(['repeat-x', 'repeat-y']);
|
|
241
|
+
const REPEAT = new Set(['repeat', 'space', 'round', 'no-repeat']);
|
|
242
|
+
const ATTACHMENT = new Set(['scroll', 'fixed', 'local']);
|
|
243
|
+
const BOX = new Set(['border-box', 'padding-box', 'content-box']);
|
|
244
|
+
const POSITION_KEYWORD = new Set(['left', 'right', 'top', 'bottom', 'center']);
|
|
245
|
+
const SIZE_KEYWORD = new Set(['auto', 'cover', 'contain']);
|
|
246
|
+
function isColorCandidate(token) {
|
|
247
|
+
return token !== '/' && token !== 'none' && token !== 'text' && !IMAGE_FUNCTION.test(token) && !LENGTH.test(token) && ![REPEAT_SINGLE, REPEAT, ATTACHMENT, BOX, POSITION_KEYWORD, SIZE_KEYWORD].some((keywords) => keywords.has(token));
|
|
248
|
+
}
|
|
249
|
+
const LAYER_INITIAL = { image: 'none', repeat: 'repeat', position: '0% 0%', size: 'auto', clip: 'border-box', origin: 'padding-box', attachment: 'scroll' };
|
|
250
|
+
function parseBackgroundLayer(tokens, value, isFinal) {
|
|
251
|
+
const layer = {};
|
|
252
|
+
let boxes = 0;
|
|
253
|
+
// `text` is a clip value only; a box keyword beside it is the origin.
|
|
254
|
+
let clipsToText = false;
|
|
255
|
+
const invalid = () => new Error(`Unexpected "${tokens.join(' ')}" in background "${value}"`);
|
|
256
|
+
let i = 0;
|
|
257
|
+
const run = (accepts, max) => {
|
|
258
|
+
const start = i;
|
|
259
|
+
while (i < tokens.length && i - start < max && accepts(tokens[i])) {
|
|
260
|
+
i++;
|
|
261
|
+
}
|
|
262
|
+
return tokens.slice(start, i).join(' ');
|
|
263
|
+
};
|
|
264
|
+
while (i < tokens.length) {
|
|
265
|
+
const token = tokens[i];
|
|
266
|
+
if (layer.image === undefined && (token === 'none' || IMAGE_FUNCTION.test(token))) {
|
|
267
|
+
layer.image = token;
|
|
268
|
+
i++;
|
|
269
|
+
}
|
|
270
|
+
else if (layer.repeat === undefined && REPEAT_SINGLE.has(token)) {
|
|
271
|
+
layer.repeat = token;
|
|
272
|
+
i++;
|
|
273
|
+
}
|
|
274
|
+
else if (layer.repeat === undefined && REPEAT.has(token)) {
|
|
275
|
+
layer.repeat = run((t) => REPEAT.has(t), 2);
|
|
276
|
+
}
|
|
277
|
+
else if (layer.attachment === undefined && ATTACHMENT.has(token)) {
|
|
278
|
+
layer.attachment = token;
|
|
279
|
+
i++;
|
|
280
|
+
}
|
|
281
|
+
else if (!clipsToText && token === 'text') {
|
|
282
|
+
clipsToText = true;
|
|
283
|
+
i++;
|
|
284
|
+
}
|
|
285
|
+
else if (boxes < (clipsToText ? 1 : 2) && BOX.has(token)) {
|
|
286
|
+
if (boxes === 0) {
|
|
287
|
+
layer.origin = token;
|
|
288
|
+
}
|
|
289
|
+
layer.clip = token;
|
|
290
|
+
boxes++;
|
|
291
|
+
i++;
|
|
292
|
+
}
|
|
293
|
+
else if (layer.position === undefined && (POSITION_KEYWORD.has(token) || LENGTH.test(token))) {
|
|
294
|
+
layer.position = run((t) => POSITION_KEYWORD.has(t) || LENGTH.test(t), 4);
|
|
295
|
+
if (tokens[i] === '/') {
|
|
296
|
+
i++;
|
|
297
|
+
const size = run((t) => SIZE_KEYWORD.has(t) || LENGTH.test(t), 2).split(' ');
|
|
298
|
+
if (!size[0] || (size.length === 2 && size.some((t) => t === 'cover' || t === 'contain'))) {
|
|
299
|
+
throw invalid();
|
|
300
|
+
}
|
|
301
|
+
layer.size = size.join(' ');
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else if (isFinal && layer.color === undefined && isColorCandidate(token)) {
|
|
305
|
+
layer.color = token;
|
|
306
|
+
i++;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
throw invalid();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (clipsToText) {
|
|
313
|
+
if (boxes > 1) {
|
|
314
|
+
throw invalid();
|
|
315
|
+
}
|
|
316
|
+
layer.clip = 'text';
|
|
317
|
+
}
|
|
318
|
+
return layer;
|
|
319
|
+
}
|
|
320
|
+
export function splitBackground(value) {
|
|
321
|
+
const layerTokens = [[]];
|
|
322
|
+
for (const token of tokenizeCss(value)) {
|
|
323
|
+
if (token === ',') {
|
|
324
|
+
layerTokens.push([]);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
layerTokens[layerTokens.length - 1].push(token);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const layers = layerTokens.map((tokens, i) => {
|
|
331
|
+
if (tokens.length === 0) {
|
|
332
|
+
throw new Error(`Empty layer in background "${value}"`);
|
|
333
|
+
}
|
|
334
|
+
return parseBackgroundLayer(tokens, value, i === layerTokens.length - 1);
|
|
335
|
+
});
|
|
336
|
+
const perLayer = (part, read) => {
|
|
337
|
+
const parts = layers.map(read);
|
|
338
|
+
return parts.some((p) => p !== undefined) ? parts.map((p) => p ?? LAYER_INITIAL[part]).join(', ') : unsetValue;
|
|
339
|
+
};
|
|
340
|
+
return [layers[layers.length - 1].color ?? unsetValue, perLayer('image', (layer) => layer.image), perLayer('repeat', (layer) => layer.repeat), perLayer('position', (layer) => layer.position), perLayer('size', (layer) => layer.size), perLayer('clip', (layer) => layer.clip), perLayer('origin', (layer) => layer.origin), perLayer('attachment', (layer) => layer.attachment)];
|
|
341
|
+
}
|
|
342
|
+
const MASK_BOX = new Set(['border-box', 'padding-box', 'content-box', 'margin-box', 'fill-box', 'stroke-box', 'view-box']);
|
|
343
|
+
const MASK_COMPOSITE = new Set(['add', 'subtract', 'intersect', 'exclude']);
|
|
344
|
+
const MASK_MODE = new Set(['alpha', 'luminance', 'match-source']);
|
|
345
|
+
const MASK_INITIAL = { image: 'none', position: '0% 0%', size: 'auto', repeat: 'repeat', origin: 'border-box', clip: 'border-box', composite: 'add', mode: 'match-source' };
|
|
346
|
+
// Like a background layer, without a colour or attachment, with a compositing operator and a
|
|
347
|
+
// masking mode, and with `no-clip` as a clip box.
|
|
348
|
+
function parseMaskLayer(tokens, value) {
|
|
349
|
+
const layer = {};
|
|
350
|
+
let boxes = 0;
|
|
351
|
+
let noClip = false;
|
|
352
|
+
const invalid = () => new Error(`Unexpected "${tokens.join(' ')}" in mask "${value}"`);
|
|
353
|
+
let i = 0;
|
|
354
|
+
const run = (accepts, max) => {
|
|
355
|
+
const start = i;
|
|
356
|
+
while (i < tokens.length && i - start < max && accepts(tokens[i])) {
|
|
357
|
+
i++;
|
|
358
|
+
}
|
|
359
|
+
return tokens.slice(start, i).join(' ');
|
|
360
|
+
};
|
|
361
|
+
while (i < tokens.length) {
|
|
362
|
+
const token = tokens[i];
|
|
363
|
+
if (layer.image === undefined && (token === 'none' || IMAGE_FUNCTION.test(token))) {
|
|
364
|
+
layer.image = token;
|
|
365
|
+
i++;
|
|
366
|
+
}
|
|
367
|
+
else if (layer.repeat === undefined && REPEAT_SINGLE.has(token)) {
|
|
368
|
+
layer.repeat = token;
|
|
369
|
+
i++;
|
|
370
|
+
}
|
|
371
|
+
else if (layer.repeat === undefined && REPEAT.has(token)) {
|
|
372
|
+
layer.repeat = run((t) => REPEAT.has(t), 2);
|
|
373
|
+
}
|
|
374
|
+
else if (MASK_BOX.has(token) && (noClip ? layer.origin === undefined : boxes < 2)) {
|
|
375
|
+
if (layer.origin === undefined) {
|
|
376
|
+
layer.origin = token;
|
|
377
|
+
}
|
|
378
|
+
if (!noClip) {
|
|
379
|
+
layer.clip = token;
|
|
380
|
+
}
|
|
381
|
+
boxes++;
|
|
382
|
+
i++;
|
|
383
|
+
}
|
|
384
|
+
else if (token === 'no-clip' && !noClip && boxes < 2) {
|
|
385
|
+
// One box and `no-clip`, either way round: the box is the origin.
|
|
386
|
+
layer.clip = token;
|
|
387
|
+
noClip = true;
|
|
388
|
+
i++;
|
|
389
|
+
}
|
|
390
|
+
else if (layer.composite === undefined && MASK_COMPOSITE.has(token)) {
|
|
391
|
+
layer.composite = token;
|
|
392
|
+
i++;
|
|
393
|
+
}
|
|
394
|
+
else if (layer.mode === undefined && MASK_MODE.has(token)) {
|
|
395
|
+
layer.mode = token;
|
|
396
|
+
i++;
|
|
397
|
+
}
|
|
398
|
+
else if (layer.position === undefined && (POSITION_KEYWORD.has(token) || LENGTH.test(token))) {
|
|
399
|
+
layer.position = run((t) => POSITION_KEYWORD.has(t) || LENGTH.test(t), 4);
|
|
400
|
+
if (tokens[i] === '/') {
|
|
401
|
+
i++;
|
|
402
|
+
const size = run((t) => SIZE_KEYWORD.has(t) || LENGTH.test(t), 2).split(' ');
|
|
403
|
+
if (!size[0] || (size.length === 2 && size.some((t) => t === 'cover' || t === 'contain'))) {
|
|
404
|
+
throw invalid();
|
|
405
|
+
}
|
|
406
|
+
layer.size = size.join(' ');
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
throw invalid();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return layer;
|
|
414
|
+
}
|
|
415
|
+
/** `mask` into image, position, size, repeat, origin, clip, composite and mode, per layer. */
|
|
416
|
+
export function splitMask(value) {
|
|
417
|
+
const layerTokens = [[]];
|
|
418
|
+
for (const token of tokenizeCss(value)) {
|
|
419
|
+
if (token === ',') {
|
|
420
|
+
layerTokens.push([]);
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
layerTokens[layerTokens.length - 1].push(token);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const layers = layerTokens.map((tokens) => {
|
|
427
|
+
if (tokens.length === 0) {
|
|
428
|
+
throw new Error(`Empty layer in mask "${value}"`);
|
|
429
|
+
}
|
|
430
|
+
return parseMaskLayer(tokens, value);
|
|
431
|
+
});
|
|
432
|
+
// A shorthand sets every longhand, so whatever a layer leaves out gets its initial value.
|
|
433
|
+
const perLayer = (part) => layers.map((layer) => layer[part] ?? MASK_INITIAL[part]).join(', ');
|
|
434
|
+
return [perLayer('image'), perLayer('position'), perLayer('size'), perLayer('repeat'), perLayer('origin'), perLayer('clip'), perLayer('composite'), perLayer('mode')];
|
|
435
|
+
}
|
|
436
|
+
// -webkit-mask-composite uses canvas-style operator names. These are the ones with a standard
|
|
437
|
+
// equivalent for how a layer combines with the layers below it.
|
|
438
|
+
const WEBKIT_MASK_COMPOSITE = {
|
|
439
|
+
'source-over': 'add',
|
|
440
|
+
'source-in': 'intersect',
|
|
441
|
+
'source-out': 'subtract',
|
|
442
|
+
xor: 'exclude',
|
|
443
|
+
'destination-in': 'intersect',
|
|
444
|
+
};
|
|
445
|
+
// Older WebKit box keywords for -webkit-mask-origin/-clip.
|
|
446
|
+
const WEBKIT_MASK_BOX = { border: 'border-box', padding: 'padding-box', content: 'content-box' };
|
|
447
|
+
/** A -webkit-mask-composite list as `mask-composite`; operators with no equivalent become `add`. */
|
|
448
|
+
export function webkitMaskComposite(value) {
|
|
449
|
+
return splitLayerList(value)
|
|
450
|
+
.map((part) => (MASK_COMPOSITE.has(part) ? part : (WEBKIT_MASK_COMPOSITE[part] ?? 'add')))
|
|
451
|
+
.join(', ');
|
|
452
|
+
}
|
|
453
|
+
/** A -webkit-mask-origin or -clip list as the standard property. */
|
|
454
|
+
export function webkitMaskBox(value) {
|
|
455
|
+
return splitLayerList(value)
|
|
456
|
+
.map((part) => WEBKIT_MASK_BOX[part] ?? part)
|
|
457
|
+
.join(', ');
|
|
458
|
+
}
|
|
459
|
+
function splitLayerList(value) {
|
|
460
|
+
const parts = [''];
|
|
461
|
+
for (const token of tokenizeCss(value)) {
|
|
462
|
+
if (token === ',') {
|
|
463
|
+
parts.push('');
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
parts[parts.length - 1] += (parts[parts.length - 1] ? ' ' : '') + token;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return parts.map((part) => part.trim());
|
|
470
|
+
}
|
|
471
|
+
/** `-webkit-mask`: `mask` with WebKit's composite operators and box keywords. */
|
|
472
|
+
export function splitWebkitMask(value) {
|
|
473
|
+
const standard = tokenizeCss(value)
|
|
474
|
+
.map((token) => WEBKIT_MASK_COMPOSITE[token] ?? WEBKIT_MASK_BOX[token] ?? token)
|
|
475
|
+
.join(' ');
|
|
476
|
+
return splitMask(standard);
|
|
477
|
+
}
|
|
478
|
+
const MASK_BORDER_REPEAT = new Set(['stretch', 'repeat', 'round', 'space']);
|
|
479
|
+
const MASK_BORDER_MODE = new Set(['alpha', 'luminance']);
|
|
480
|
+
const NUMBER_OR_PERCENT = /^[+]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?%?$/i;
|
|
481
|
+
/**
|
|
482
|
+
* `mask-border` (and `-webkit-mask-box-image`) into source, slice, width, outset, repeat and mode:
|
|
483
|
+
* `<source> || <slice> [ / <width>? [ / <outset> ]? ]? || <repeat> || <mode>`.
|
|
484
|
+
*/
|
|
485
|
+
export function splitMaskBorder(value) {
|
|
486
|
+
const tokens = tokenizeCss(value);
|
|
487
|
+
const invalid = () => new Error(`Unexpected "${tokens.join(' ')}" in mask-border "${value}"`);
|
|
488
|
+
let source;
|
|
489
|
+
let slice;
|
|
490
|
+
let width;
|
|
491
|
+
let outset;
|
|
492
|
+
let repeat;
|
|
493
|
+
let mode;
|
|
494
|
+
let i = 0;
|
|
495
|
+
const run = (accepts, max) => {
|
|
496
|
+
const start = i;
|
|
497
|
+
while (i < tokens.length && i - start < max && accepts(tokens[i])) {
|
|
498
|
+
i++;
|
|
499
|
+
}
|
|
500
|
+
return tokens.slice(start, i).join(' ');
|
|
501
|
+
};
|
|
502
|
+
while (i < tokens.length) {
|
|
503
|
+
const token = tokens[i];
|
|
504
|
+
if (source === undefined && (token === 'none' || IMAGE_FUNCTION.test(token))) {
|
|
505
|
+
source = token;
|
|
506
|
+
i++;
|
|
507
|
+
}
|
|
508
|
+
else if (slice === undefined && (NUMBER_OR_PERCENT.test(token) || token === 'fill')) {
|
|
509
|
+
slice = run((t) => NUMBER_OR_PERCENT.test(t) || t === 'fill', 5);
|
|
510
|
+
if (slice.split(' ').filter((t) => t === 'fill').length > 1) {
|
|
511
|
+
throw invalid();
|
|
512
|
+
}
|
|
513
|
+
if (tokens[i] === '/') {
|
|
514
|
+
i++;
|
|
515
|
+
width = run((t) => t === 'auto' || LENGTH.test(t), 4) || undefined;
|
|
516
|
+
if (tokens[i] === '/') {
|
|
517
|
+
i++;
|
|
518
|
+
outset = run((t) => LENGTH.test(t), 4);
|
|
519
|
+
if (!outset) {
|
|
520
|
+
throw invalid();
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else if (width === undefined) {
|
|
524
|
+
throw invalid();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else if (repeat === undefined && MASK_BORDER_REPEAT.has(token)) {
|
|
529
|
+
repeat = run((t) => MASK_BORDER_REPEAT.has(t), 2);
|
|
530
|
+
}
|
|
531
|
+
else if (mode === undefined && MASK_BORDER_MODE.has(token)) {
|
|
532
|
+
mode = token;
|
|
533
|
+
i++;
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
throw invalid();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return [source ?? 'none', slice ?? '0', width ?? 'auto', outset ?? '0', repeat ?? 'stretch', mode ?? 'alpha'];
|
|
540
|
+
}
|
|
541
|
+
function keywordList(value, allowed, max) {
|
|
542
|
+
const tokens = tokenizeCss(value);
|
|
543
|
+
if (tokens.length === 0 || tokens.length > max || !tokens.every((token) => allowed.has(token))) {
|
|
544
|
+
throw new Error(`Expected 1 to ${max} of ${[...allowed].join(', ')} in "${value}"`);
|
|
545
|
+
}
|
|
546
|
+
return tokens;
|
|
547
|
+
}
|
|
548
|
+
const OVERFLOW = new Set(['visible', 'hidden', 'clip', 'scroll', 'auto']);
|
|
549
|
+
export function splitOverflow(value) {
|
|
550
|
+
const [x, y = x] = keywordList(value, OVERFLOW, 2);
|
|
551
|
+
return [x, y];
|
|
552
|
+
}
|
|
553
|
+
const FLEX_DIRECTION = new Set(['row', 'row-reverse', 'column', 'column-reverse']);
|
|
554
|
+
const FLEX_WRAP = new Set(['nowrap', 'wrap', 'wrap-reverse']);
|
|
555
|
+
export function splitFlexFlow(value) {
|
|
556
|
+
const tokens = keywordList(value, new Set([...FLEX_DIRECTION, ...FLEX_WRAP]), 2);
|
|
557
|
+
const direction = tokens.filter((token) => FLEX_DIRECTION.has(token));
|
|
558
|
+
const wrap = tokens.filter((token) => FLEX_WRAP.has(token));
|
|
559
|
+
if (direction.length > 1 || wrap.length > 1) {
|
|
560
|
+
throw new Error(`Expected at most one direction and one wrap in "${value}"`);
|
|
561
|
+
}
|
|
562
|
+
return [direction[0] ?? unsetValue, wrap[0] ?? unsetValue];
|
|
563
|
+
}
|
|
564
|
+
const FLEX_FACTOR = /^\+?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i;
|
|
565
|
+
const FLEX_BASIS_KEYWORD = new Set(['auto', 'content', 'min-content', 'max-content', 'fit-content']);
|
|
566
|
+
export function splitFlex(value) {
|
|
567
|
+
const tokens = tokenizeCss(value);
|
|
568
|
+
if (tokens.length === 1 && tokens[0] === 'none') {
|
|
569
|
+
return ['0', '0', 'auto'];
|
|
570
|
+
}
|
|
571
|
+
if (tokens.length === 1 && tokens[0] === 'auto') {
|
|
572
|
+
return ['1', '1', 'auto'];
|
|
573
|
+
}
|
|
574
|
+
let grow;
|
|
575
|
+
let shrink;
|
|
576
|
+
let basis;
|
|
577
|
+
let i = 0;
|
|
578
|
+
const takeFactors = () => {
|
|
579
|
+
if (grow === undefined && FLEX_FACTOR.test(tokens[i] ?? '')) {
|
|
580
|
+
grow = tokens[i++];
|
|
581
|
+
if (FLEX_FACTOR.test(tokens[i] ?? '')) {
|
|
582
|
+
shrink = tokens[i++];
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
const takeBasis = () => {
|
|
587
|
+
if (basis === undefined && i < tokens.length && (FLEX_BASIS_KEYWORD.has(tokens[i]) || (isLength(tokens[i]) && !tokens[i].startsWith('-')))) {
|
|
588
|
+
basis = tokens[i++];
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
takeFactors();
|
|
592
|
+
takeBasis();
|
|
593
|
+
takeFactors();
|
|
594
|
+
if (i !== tokens.length || i === 0) {
|
|
595
|
+
throw new Error(`Expected [<grow> <shrink>?] || <basis> in flex "${value}"`);
|
|
596
|
+
}
|
|
597
|
+
return [grow ?? '1', shrink ?? '1', basis ?? '0%'];
|
|
598
|
+
}
|
|
599
|
+
export function splitGap(value) {
|
|
600
|
+
const tokens = values(value, (token) => token === 'normal' || isLength(token));
|
|
601
|
+
if (tokens.length === 0 || tokens.length > 2) {
|
|
602
|
+
throw new Error(`Expected 1 or 2 values in gap "${value}"`);
|
|
603
|
+
}
|
|
604
|
+
const [row, column = row] = tokens;
|
|
605
|
+
return [row, column];
|
|
606
|
+
}
|
|
607
|
+
const ALIGNMENT_PREFIX = new Set(['safe', 'unsafe', 'first', 'last']);
|
|
608
|
+
/** `place-items` / `place-content` / `place-self`: the align value, then the justify value, which defaults to it. */
|
|
609
|
+
export function splitPlace(value) {
|
|
610
|
+
const tokens = tokenizeCss(value);
|
|
611
|
+
const parts = [];
|
|
612
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
613
|
+
// `safe center` and `last baseline` are one value.
|
|
614
|
+
if (ALIGNMENT_PREFIX.has(tokens[i]) && i + 1 < tokens.length) {
|
|
615
|
+
parts.push(`${tokens[i]} ${tokens[++i]}`);
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
parts.push(tokens[i]);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (parts.length === 0 || parts.length > 2) {
|
|
622
|
+
throw new Error(`Expected 1 or 2 values in "${value}"`);
|
|
623
|
+
}
|
|
624
|
+
const [align, justify = align] = parts;
|
|
625
|
+
return [align, justify];
|
|
626
|
+
}
|
|
627
|
+
const LIST_STYLE_POSITIONS = new Set(['inside', 'outside']);
|
|
628
|
+
/**
|
|
629
|
+
* Splits `list-style` into type and position, in any order. An image is accepted
|
|
630
|
+
* and dropped; omitted parts reset to `disc` and `outside`.
|
|
631
|
+
*/
|
|
632
|
+
export function splitListStyle(value) {
|
|
633
|
+
let type;
|
|
634
|
+
let position;
|
|
635
|
+
let image = false;
|
|
636
|
+
let nones = 0;
|
|
637
|
+
for (const token of tokenizeCss(value)) {
|
|
638
|
+
const lower = token.toLowerCase();
|
|
639
|
+
if (lower === 'none') {
|
|
640
|
+
nones++;
|
|
641
|
+
}
|
|
642
|
+
else if (LIST_STYLE_POSITIONS.has(lower)) {
|
|
643
|
+
if (position !== undefined) {
|
|
644
|
+
throw new Error(`Two list-style positions in "${value}"`);
|
|
645
|
+
}
|
|
646
|
+
position = lower;
|
|
647
|
+
}
|
|
648
|
+
else if (IMAGE_FUNCTION.test(token)) {
|
|
649
|
+
if (image) {
|
|
650
|
+
throw new Error(`Two list-style images in "${value}"`);
|
|
651
|
+
}
|
|
652
|
+
image = true;
|
|
653
|
+
}
|
|
654
|
+
else if (token === ',' || token === '/' || type !== undefined) {
|
|
655
|
+
throw new Error(`Unexpected "${token}" in list-style "${value}"`);
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
type = lower;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
// `none` fills whichever of type and image is still open, type first.
|
|
662
|
+
const open = (type === undefined ? 1 : 0) + (image ? 0 : 1);
|
|
663
|
+
if (nones > open) {
|
|
664
|
+
throw new Error(`Too many "none" in list-style "${value}"`);
|
|
665
|
+
}
|
|
666
|
+
if (nones > 0 && type === undefined) {
|
|
667
|
+
type = 'none';
|
|
668
|
+
}
|
|
669
|
+
if (type === undefined && position === undefined && !image && nones === 0) {
|
|
670
|
+
throw new Error(`Empty list-style "${value}"`);
|
|
671
|
+
}
|
|
672
|
+
return [type ?? 'disc', position ?? 'outside'];
|
|
673
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type Matrix2D = [number, number, number, number, number, number];
|
|
2
|
+
export declare function parseAngle(token: string): number;
|
|
3
|
+
export declare function parseCssTransform(value: string, toDip: (token: string) => number | undefined, size: {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}): Matrix2D | null;
|