@skyscanner/backpack-web 41.14.0 → 41.15.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/bpk-component-bottom-sheet/src/BpkBottomSheet.d.ts +1 -1
- package/bpk-component-bottom-sheet/src/BpkBottomSheet.js +11 -4
- package/bpk-component-layout/index.d.ts +18 -0
- package/bpk-component-layout/index.js +29 -0
- package/bpk-component-layout/src/BpkBox.d.ts +3 -0
- package/bpk-component-layout/src/BpkBox.js +35 -0
- package/bpk-component-layout/src/BpkFlex.d.ts +3 -0
- package/bpk-component-layout/src/BpkFlex.js +53 -0
- package/bpk-component-layout/src/BpkGrid.d.ts +3 -0
- package/bpk-component-layout/src/BpkGrid.js +59 -0
- package/bpk-component-layout/src/BpkGridItem.d.ts +3 -0
- package/bpk-component-layout/src/BpkGridItem.js +47 -0
- package/bpk-component-layout/src/BpkProvider.d.ts +14 -0
- package/bpk-component-layout/src/BpkProvider.js +42 -0
- package/bpk-component-layout/src/BpkStack.constant.d.ts +2 -0
- package/bpk-component-layout/src/BpkStack.constant.js +22 -0
- package/bpk-component-layout/src/BpkStack.d.ts +5 -0
- package/bpk-component-layout/src/BpkStack.js +61 -0
- package/bpk-component-layout/src/BpkVessel.d.ts +46 -0
- package/bpk-component-layout/src/BpkVessel.js +72 -0
- package/bpk-component-layout/src/commonProps.d.ts +86 -0
- package/bpk-component-layout/src/commonProps.js +1 -0
- package/bpk-component-layout/src/theme.d.ts +36 -0
- package/bpk-component-layout/src/theme.js +229 -0
- package/bpk-component-layout/src/tokenUtils.d.ts +108 -0
- package/bpk-component-layout/src/tokenUtils.js +323 -0
- package/bpk-component-layout/src/tokens.d.ts +96 -0
- package/bpk-component-layout/src/tokens.js +138 -0
- package/bpk-component-layout/src/types.d.ts +236 -0
- package/bpk-component-layout/src/types.js +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Backpack - Skyscanner's Design System
|
|
3
|
+
*
|
|
4
|
+
* Copyright 2016 Skyscanner Ltd
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import StackOptionKeys from "./BpkStack.constant";
|
|
20
|
+
import { getSpacingValue } from "./theme";
|
|
21
|
+
import { BpkBreakpointToChakraKey, isValidSpacingValue, isValidSizeValue, isValidPositionValue, isPercentage } from "./tokens";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Allowlisted, component-scoped prop groups that are eligible for Backpack responsive value
|
|
25
|
+
* processing (Backpack breakpoint keys -> Chakra breakpoint keys).
|
|
26
|
+
*
|
|
27
|
+
* NOTE:
|
|
28
|
+
* - Spacing/size/position props are processed separately via `processBpkProps` and therefore
|
|
29
|
+
* are intentionally NOT included here.
|
|
30
|
+
* - These groups are meant to keep the responsive surface predictable per component, while
|
|
31
|
+
* avoiding duplicated per-component breakpoint mapping logic.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export const BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT = {
|
|
35
|
+
BpkBox: {
|
|
36
|
+
container: [
|
|
37
|
+
// Display
|
|
38
|
+
'display',
|
|
39
|
+
// Flex container props
|
|
40
|
+
'flexDirection', 'flexWrap', 'justifyContent', 'alignItems', 'alignContent',
|
|
41
|
+
// Grid container props
|
|
42
|
+
'gridTemplateColumns', 'gridTemplateRows', 'gridTemplateAreas', 'gridAutoFlow', 'gridAutoRows', 'gridAutoColumns'],
|
|
43
|
+
item: [
|
|
44
|
+
// Flex item props
|
|
45
|
+
'flex', 'flexGrow', 'flexShrink', 'flexBasis', 'order', 'alignSelf', 'justifySelf',
|
|
46
|
+
// Grid item placement props (useful on Box when composing grids)
|
|
47
|
+
'gridColumn', 'gridRow']
|
|
48
|
+
},
|
|
49
|
+
// Note: BpkFlex maps its public API props to these Chakra keys.
|
|
50
|
+
BpkFlex: {
|
|
51
|
+
container: ['flexDirection', 'justifyContent', 'alignItems', 'flexWrap'],
|
|
52
|
+
item: ['flexGrow', 'flexShrink', 'flexBasis']
|
|
53
|
+
},
|
|
54
|
+
// Note: BpkGrid maps its public API props to these Chakra keys.
|
|
55
|
+
BpkGrid: {
|
|
56
|
+
container: ['justifyContent', 'alignItems', 'gridTemplateColumns', 'gridTemplateRows', 'gridTemplateAreas', 'gridAutoFlow', 'gridAutoRows', 'gridAutoColumns'],
|
|
57
|
+
item: [
|
|
58
|
+
// Used when placing the grid itself within a parent grid.
|
|
59
|
+
'gridColumn', 'gridRow']
|
|
60
|
+
},
|
|
61
|
+
// Note: BpkStack uses Chakra Stack option prop names directly.
|
|
62
|
+
BpkStack: {
|
|
63
|
+
container: StackOptionKeys
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
export const BPK_RESPONSIVE_PROP_KEYS_BY_COMPONENT = {
|
|
67
|
+
BpkBox: [...BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkBox.container, ...(BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkBox.item ?? [])],
|
|
68
|
+
BpkFlex: [...BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkFlex.container, ...(BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkFlex.item ?? [])],
|
|
69
|
+
BpkGrid: [...BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkGrid.container, ...(BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkGrid.item ?? [])],
|
|
70
|
+
BpkStack: [...BPK_RESPONSIVE_PROP_GROUPS_BY_COMPONENT.BpkStack.container]
|
|
71
|
+
};
|
|
72
|
+
function filterToAllowlist(props, allowlist) {
|
|
73
|
+
const allowed = new Set(allowlist);
|
|
74
|
+
const result = {};
|
|
75
|
+
Object.keys(props).forEach(key => {
|
|
76
|
+
if (allowed.has(key) && props[key] !== undefined) {
|
|
77
|
+
result[key] = props[key];
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Process a component's props in one place:
|
|
85
|
+
* - strip className/style
|
|
86
|
+
* - process spacing/size/position props (including breakpoint mapping + token conversion)
|
|
87
|
+
* - process allowlisted non-spacing responsive layout props (breakpoint mapping only)
|
|
88
|
+
*
|
|
89
|
+
* The allowlist is grouped by component via `BPK_RESPONSIVE_PROP_KEYS_BY_COMPONENT`.
|
|
90
|
+
*
|
|
91
|
+
* @param {T} props - The component props to process.
|
|
92
|
+
* @param {ProcessBpkComponentPropsOptions} options - Component processing options (allowlist group + mapping).
|
|
93
|
+
* @returns {Record<string, any>} The processed props ready to pass to Chakra primitives.
|
|
94
|
+
*/
|
|
95
|
+
export function processBpkComponentProps(props, options) {
|
|
96
|
+
const processed = processBpkProps(props);
|
|
97
|
+
const allowlist = BPK_RESPONSIVE_PROP_KEYS_BY_COMPONENT[options.component];
|
|
98
|
+
const responsiveSource = options.responsiveProps ? filterToAllowlist(options.responsiveProps, allowlist) : filterToAllowlist(processed, allowlist);
|
|
99
|
+
if (Object.keys(responsiveSource).length === 0) {
|
|
100
|
+
return processed;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Ensure allowlisted layout props do NOT fall through unprocessed (e.g. array responsive values).
|
|
104
|
+
// These props must be provided via the responsive processing pipeline only.
|
|
105
|
+
const cleanedProcessed = {
|
|
106
|
+
...processed
|
|
107
|
+
};
|
|
108
|
+
allowlist.forEach(key => {
|
|
109
|
+
if (key in cleanedProcessed) {
|
|
110
|
+
delete cleanedProcessed[key];
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const responsiveProcessed = processResponsiveProps(responsiveSource, options.propNameMap);
|
|
114
|
+
|
|
115
|
+
// Remove keys that ended up as `undefined` (e.g. array responsive values are rejected).
|
|
116
|
+
Object.keys(responsiveProcessed).forEach(key => {
|
|
117
|
+
if (responsiveProcessed[key] === undefined) {
|
|
118
|
+
delete responsiveProcessed[key];
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
...cleanedProcessed,
|
|
123
|
+
...responsiveProcessed
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Converts Backpack spacing token to Chakra UI compatible value
|
|
129
|
+
* Returns the actual spacing value from the theme, not a token path
|
|
130
|
+
*
|
|
131
|
+
* @param {string} value - Backpack spacing token (e.g., 'bpk-spacing-base') or percentage
|
|
132
|
+
* @returns {string} The actual spacing value in rem or the percentage string
|
|
133
|
+
*/
|
|
134
|
+
export function convertBpkSpacingToChakra(value) {
|
|
135
|
+
if (isPercentage(value)) {
|
|
136
|
+
return value; // Percentages pass through
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Look up the actual spacing value from the theme
|
|
140
|
+
const spacingValue = getSpacingValue(value);
|
|
141
|
+
if (spacingValue !== undefined) {
|
|
142
|
+
return spacingValue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Fallback: if token not found, return the value as-is (will cause a warning)
|
|
146
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
147
|
+
// eslint-disable-next-line no-console
|
|
148
|
+
console.warn(`Spacing token "${value}" not found in theme. Returning as-is.`);
|
|
149
|
+
}
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Recursively processes responsive values (arrays or objects) to validate and convert tokens
|
|
155
|
+
*
|
|
156
|
+
* @param {*} value - The value to process (could be string, array, or object)
|
|
157
|
+
* @param {Function} converter - Function to convert valid tokens to actual values
|
|
158
|
+
* @param {Function} validator - Function to validate if a token is allowed
|
|
159
|
+
* @param {string} propName - The name of the prop being processed (for warning messages)
|
|
160
|
+
* @returns {*} The processed value with tokens converted, or undefined for invalid tokens
|
|
161
|
+
*/
|
|
162
|
+
export function normalizeResponsiveObject(value) {
|
|
163
|
+
const normalized = {};
|
|
164
|
+
Object.entries(value).forEach(([key, val]) => {
|
|
165
|
+
if (key === 'base') {
|
|
166
|
+
normalized.base = val;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const chakraKey = BpkBreakpointToChakraKey[key];
|
|
170
|
+
if (chakraKey) {
|
|
171
|
+
normalized[chakraKey] = val;
|
|
172
|
+
} else if (process.env.NODE_ENV !== 'production') {
|
|
173
|
+
// eslint-disable-next-line no-console
|
|
174
|
+
console.warn(`Unknown breakpoint "${key}" used in responsive prop. ` + 'Use Backpack breakpoint tokens such as mobile, tablet or desktop.');
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
return normalized;
|
|
178
|
+
}
|
|
179
|
+
export function processResponsiveValue(value, converter, validator, propName) {
|
|
180
|
+
if (value === undefined || value === null) {
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
183
|
+
if (Array.isArray(value)) {
|
|
184
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
185
|
+
// eslint-disable-next-line no-console
|
|
186
|
+
console.warn(`Array-based responsive values are not supported for prop "${propName}". ` + `Please use Backpack breakpoint keys instead.`);
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
if (typeof value === 'object') {
|
|
191
|
+
const normalized = normalizeResponsiveObject(value);
|
|
192
|
+
const result = {};
|
|
193
|
+
Object.keys(normalized).forEach(key => {
|
|
194
|
+
const processedValue = processResponsiveValue(normalized[key], converter, validator, propName);
|
|
195
|
+
if (processedValue !== undefined) {
|
|
196
|
+
result[key] = processedValue;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
200
|
+
}
|
|
201
|
+
const strValue = String(value);
|
|
202
|
+
if (!validator(strValue)) {
|
|
203
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
204
|
+
// eslint-disable-next-line no-console
|
|
205
|
+
console.warn(`Invalid value "${strValue}" for prop "${propName}". ` + `Only Backpack tokens are allowed.`);
|
|
206
|
+
}
|
|
207
|
+
return undefined; // Invalid values are removed
|
|
208
|
+
}
|
|
209
|
+
return converter(strValue);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Validates and converts spacing props for Chakra UI
|
|
214
|
+
* Handles all spacing-related properties including padding, margin, gap, size, border radius and position
|
|
215
|
+
*
|
|
216
|
+
* @param {T} props - Component props object
|
|
217
|
+
* @returns {Record<string, any>} Processed props with spacing tokens converted to actual values
|
|
218
|
+
*/
|
|
219
|
+
export function processSpacingProps(props) {
|
|
220
|
+
const spacingKeys = [
|
|
221
|
+
// Padding props
|
|
222
|
+
'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'paddingStart', 'paddingEnd', 'paddingInline',
|
|
223
|
+
// Margin props
|
|
224
|
+
'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft', 'marginStart', 'marginEnd', 'marginInline',
|
|
225
|
+
// Gap and spacing
|
|
226
|
+
'gap', 'spacing', 'rowGap', 'columnGap',
|
|
227
|
+
// Size props
|
|
228
|
+
'width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight',
|
|
229
|
+
// Position props
|
|
230
|
+
'top', 'right', 'bottom', 'left'];
|
|
231
|
+
const processed = {
|
|
232
|
+
...props
|
|
233
|
+
};
|
|
234
|
+
const sizeKeys = ['width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight'];
|
|
235
|
+
const positionKeys = ['top', 'right', 'bottom', 'left'];
|
|
236
|
+
spacingKeys.forEach(key => {
|
|
237
|
+
if (key in processed && processed[key] !== undefined) {
|
|
238
|
+
const isSizeProp = sizeKeys.includes(key);
|
|
239
|
+
const isPositionProp = positionKeys.includes(key);
|
|
240
|
+
let converter;
|
|
241
|
+
if (isSizeProp || isPositionProp) {
|
|
242
|
+
converter = v => v;
|
|
243
|
+
} else {
|
|
244
|
+
converter = convertBpkSpacingToChakra;
|
|
245
|
+
}
|
|
246
|
+
let validator;
|
|
247
|
+
if (isSizeProp) {
|
|
248
|
+
validator = isValidSizeValue;
|
|
249
|
+
} else if (isPositionProp) {
|
|
250
|
+
validator = isValidPositionValue;
|
|
251
|
+
} else {
|
|
252
|
+
validator = isValidSpacingValue;
|
|
253
|
+
}
|
|
254
|
+
const processedValue = processResponsiveValue(processed[key], converter, validator, key);
|
|
255
|
+
if (processedValue !== undefined) {
|
|
256
|
+
processed[key] = processedValue;
|
|
257
|
+
} else {
|
|
258
|
+
delete processed[key];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
return processed;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Processes all props to convert Backpack tokens to Chakra UI format
|
|
267
|
+
* Also explicitly removes className and style to prevent ad-hoc overrides
|
|
268
|
+
*
|
|
269
|
+
* Processing order:
|
|
270
|
+
* 1. Remove className & style
|
|
271
|
+
* 2. Process spacing props (includes position)
|
|
272
|
+
*
|
|
273
|
+
* @param {T} props - Component props object
|
|
274
|
+
* @returns {Record<string, any>} Processed props with tokens converted and disallowed props removed
|
|
275
|
+
*/
|
|
276
|
+
export function processBpkProps(props) {
|
|
277
|
+
// Explicitly remove className and style to prevent style overrides
|
|
278
|
+
const {
|
|
279
|
+
className,
|
|
280
|
+
style,
|
|
281
|
+
...cleanProps
|
|
282
|
+
} = props;
|
|
283
|
+
if (className !== undefined && process.env.NODE_ENV !== 'production') {
|
|
284
|
+
// eslint-disable-next-line no-console
|
|
285
|
+
console.warn('className prop is not allowed on Backpack layout components. ' + 'It has been removed to maintain design system consistency.');
|
|
286
|
+
}
|
|
287
|
+
if (style !== undefined && process.env.NODE_ENV !== 'production') {
|
|
288
|
+
// eslint-disable-next-line no-console
|
|
289
|
+
console.warn('style prop is not allowed on Backpack layout components. ' + 'It has been removed to maintain design system consistency.');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Process spacing props (includes position)
|
|
293
|
+
return processSpacingProps(cleanProps);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Processes responsive props that are simple string/enum values (non-spacing)
|
|
298
|
+
* using Backpack breakpoint keys. Array syntax is rejected as in spacing.
|
|
299
|
+
*
|
|
300
|
+
* @param {*} value - The value to process
|
|
301
|
+
* @param {string} propName - The name of the prop being processed
|
|
302
|
+
* @returns {*} The processed value with breakpoint keys mapped to Chakra keys
|
|
303
|
+
*/
|
|
304
|
+
export function processResponsiveStringProp(value, propName) {
|
|
305
|
+
return processResponsiveValue(value, v => v, () => true, propName);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Processes a collection of responsive props.
|
|
310
|
+
* @param {Record<string, any>} props - Object containing prop values.
|
|
311
|
+
* @param {Record<string, string>} propNameMap - Map of prop name to CSS/Chakra property name (for error messages and mapping).
|
|
312
|
+
* @returns {Record<string, any>} Processed props object.
|
|
313
|
+
*/
|
|
314
|
+
export function processResponsiveProps(props, propNameMap) {
|
|
315
|
+
const processed = {};
|
|
316
|
+
Object.keys(props).forEach(key => {
|
|
317
|
+
if (props[key] !== undefined) {
|
|
318
|
+
const targetPropName = propNameMap ? propNameMap[key] || key : key;
|
|
319
|
+
processed[targetPropName] = processResponsiveStringProp(props[key], targetPropName);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
return processed;
|
|
323
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backpack Design Tokens for Layout Components
|
|
3
|
+
*
|
|
4
|
+
* This file provides token mappings from Backpack design tokens to Chakra UI theme.
|
|
5
|
+
* All tokens are sourced from @skyscanner/bpk-foundations-web
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Backpack Spacing Tokens
|
|
9
|
+
* Use these constants to ensure type safety when passing spacing props
|
|
10
|
+
*/
|
|
11
|
+
export declare const BpkSpacing: {
|
|
12
|
+
readonly None: "bpk-spacing-none";
|
|
13
|
+
readonly XS: "bpk-spacing-xs";
|
|
14
|
+
readonly SM: "bpk-spacing-sm";
|
|
15
|
+
readonly Base: "bpk-spacing-base";
|
|
16
|
+
readonly MD: "bpk-spacing-md";
|
|
17
|
+
readonly LG: "bpk-spacing-lg";
|
|
18
|
+
readonly XL: "bpk-spacing-xl";
|
|
19
|
+
readonly XXL: "bpk-spacing-xxl";
|
|
20
|
+
};
|
|
21
|
+
export type BpkSpacingToken = typeof BpkSpacing[keyof typeof BpkSpacing];
|
|
22
|
+
/**
|
|
23
|
+
* Backpack Breakpoint Tokens
|
|
24
|
+
* Use these constants to ensure type safety when defining responsive overrides
|
|
25
|
+
* These map to the simplified keys defined in the Chakra theme
|
|
26
|
+
*/
|
|
27
|
+
export declare const BpkBreakpoint: {
|
|
28
|
+
readonly SmallMobile: "small-mobile";
|
|
29
|
+
readonly Mobile: "mobile";
|
|
30
|
+
readonly SmallTablet: "small-tablet";
|
|
31
|
+
readonly Tablet: "tablet";
|
|
32
|
+
readonly Desktop: "desktop";
|
|
33
|
+
};
|
|
34
|
+
export type BpkBreakpointToken = typeof BpkBreakpoint[keyof typeof BpkBreakpoint];
|
|
35
|
+
export type ChakraBreakpointKey = 'base' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
36
|
+
export declare const BpkBreakpointToChakraKey: Record<BpkBreakpointToken, ChakraBreakpointKey>;
|
|
37
|
+
/**
|
|
38
|
+
* Helper type for values that can be Backpack tokens or percentages
|
|
39
|
+
* but NOT px/rem values
|
|
40
|
+
*/
|
|
41
|
+
export type BpkSpacingValue = BpkSpacingToken | `${number}%`;
|
|
42
|
+
export type BpkBreakpointValue = BpkBreakpointToken;
|
|
43
|
+
/**
|
|
44
|
+
* Helper type for size props that can use rem, percentages or semantic values.
|
|
45
|
+
* This is intentionally separate from BpkSpacingValue to avoid encouraging
|
|
46
|
+
* spacing tokens for explicit sizes.
|
|
47
|
+
*/
|
|
48
|
+
export type BpkSizeValue = `${number}rem` | `${number}%` | 'auto' | 'full' | 'fit-content';
|
|
49
|
+
/**
|
|
50
|
+
* Helper type for position props that can use rem or percentages.
|
|
51
|
+
* We intentionally do not allow semantic values like 'auto' here.
|
|
52
|
+
*/
|
|
53
|
+
export type BpkPositionValue = `${number}rem` | `${number}%`;
|
|
54
|
+
/**
|
|
55
|
+
* Helper type for flex-basis prop that can use rem, percentages or semantic values.
|
|
56
|
+
* Excludes 'px' values to enforce design system constraints.
|
|
57
|
+
*/
|
|
58
|
+
export type BpkBasisValue = `${number}rem` | `${number}%` | 'auto' | 'content' | 'fit-content' | 'max-content' | 'min-content' | 'initial' | 'inherit';
|
|
59
|
+
/**
|
|
60
|
+
* Helper type for responsive values based on Backpack breakpoints.
|
|
61
|
+
*
|
|
62
|
+
* We intentionally only support:
|
|
63
|
+
* - a single scalar value (non-responsive)
|
|
64
|
+
* - an object keyed by Backpack breakpoint tokens (and optional base)
|
|
65
|
+
*
|
|
66
|
+
* We do NOT support array-based responsive values in the public API.
|
|
67
|
+
*/
|
|
68
|
+
export type BpkResponsiveValue<T> = T | Partial<Record<BpkBreakpointToken | 'base', T>>;
|
|
69
|
+
/**
|
|
70
|
+
* Validates if a value is a percentage string
|
|
71
|
+
*
|
|
72
|
+
* @param {string} value - The value to validate
|
|
73
|
+
* @returns {boolean} True if the value is a valid percentage string
|
|
74
|
+
*/
|
|
75
|
+
export declare function isPercentage(value: string): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Validates if a spacing value is valid (token or percentage)
|
|
78
|
+
*
|
|
79
|
+
* @param {string} value - The spacing value to validate
|
|
80
|
+
* @returns {boolean} True if the value is a valid Backpack spacing token or percentage
|
|
81
|
+
*/
|
|
82
|
+
export declare function isValidSpacingValue(value: string): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Validates if a size value is valid
|
|
85
|
+
*
|
|
86
|
+
* @param {string} value - The size value to validate
|
|
87
|
+
* @returns {boolean} True if the value is a valid rem/percentage/semantic size
|
|
88
|
+
*/
|
|
89
|
+
export declare function isValidSizeValue(value: string): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Validates if a position value is valid
|
|
92
|
+
*
|
|
93
|
+
* @param {string} value - The position value to validate
|
|
94
|
+
* @returns {boolean} True if the value is a valid rem or percentage
|
|
95
|
+
*/
|
|
96
|
+
export declare function isValidPositionValue(value: string): boolean;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Backpack - Skyscanner's Design System
|
|
3
|
+
*
|
|
4
|
+
* Copyright 2016 Skyscanner Ltd
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Backpack Design Tokens for Layout Components
|
|
21
|
+
*
|
|
22
|
+
* This file provides token mappings from Backpack design tokens to Chakra UI theme.
|
|
23
|
+
* All tokens are sourced from @skyscanner/bpk-foundations-web
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Backpack Spacing Tokens
|
|
28
|
+
* Use these constants to ensure type safety when passing spacing props
|
|
29
|
+
*/
|
|
30
|
+
export const BpkSpacing = {
|
|
31
|
+
None: 'bpk-spacing-none',
|
|
32
|
+
XS: 'bpk-spacing-xs',
|
|
33
|
+
SM: 'bpk-spacing-sm',
|
|
34
|
+
Base: 'bpk-spacing-base',
|
|
35
|
+
MD: 'bpk-spacing-md',
|
|
36
|
+
LG: 'bpk-spacing-lg',
|
|
37
|
+
XL: 'bpk-spacing-xl',
|
|
38
|
+
XXL: 'bpk-spacing-xxl'
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Backpack Breakpoint Tokens
|
|
42
|
+
* Use these constants to ensure type safety when defining responsive overrides
|
|
43
|
+
* These map to the simplified keys defined in the Chakra theme
|
|
44
|
+
*/
|
|
45
|
+
export const BpkBreakpoint = {
|
|
46
|
+
SmallMobile: 'small-mobile',
|
|
47
|
+
Mobile: 'mobile',
|
|
48
|
+
SmallTablet: 'small-tablet',
|
|
49
|
+
Tablet: 'tablet',
|
|
50
|
+
Desktop: 'desktop'
|
|
51
|
+
};
|
|
52
|
+
export const BpkBreakpointToChakraKey = {
|
|
53
|
+
// Keep this mapping in sync with the breakpoints configured in `theme.ts`.
|
|
54
|
+
// `base` is reserved for "default value" and is not a breakpoint token.
|
|
55
|
+
'small-mobile': 'sm',
|
|
56
|
+
mobile: 'md',
|
|
57
|
+
'small-tablet': 'lg',
|
|
58
|
+
tablet: 'xl',
|
|
59
|
+
desktop: '2xl'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Helper type for values that can be Backpack tokens or percentages
|
|
64
|
+
* but NOT px/rem values
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Helper type for size props that can use rem, percentages or semantic values.
|
|
69
|
+
* This is intentionally separate from BpkSpacingValue to avoid encouraging
|
|
70
|
+
* spacing tokens for explicit sizes.
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Helper type for position props that can use rem or percentages.
|
|
75
|
+
* We intentionally do not allow semantic values like 'auto' here.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Helper type for flex-basis prop that can use rem, percentages or semantic values.
|
|
80
|
+
* Excludes 'px' values to enforce design system constraints.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Helper type for responsive values based on Backpack breakpoints.
|
|
85
|
+
*
|
|
86
|
+
* We intentionally only support:
|
|
87
|
+
* - a single scalar value (non-responsive)
|
|
88
|
+
* - an object keyed by Backpack breakpoint tokens (and optional base)
|
|
89
|
+
*
|
|
90
|
+
* We do NOT support array-based responsive values in the public API.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validates if a value is a percentage string
|
|
95
|
+
*
|
|
96
|
+
* @param {string} value - The value to validate
|
|
97
|
+
* @returns {boolean} True if the value is a valid percentage string
|
|
98
|
+
*/
|
|
99
|
+
export function isPercentage(value) {
|
|
100
|
+
return /^\d+(\.\d+)?%$/.test(value);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Validates if a spacing value is valid (token or percentage)
|
|
105
|
+
*
|
|
106
|
+
* @param {string} value - The spacing value to validate
|
|
107
|
+
* @returns {boolean} True if the value is a valid Backpack spacing token or percentage
|
|
108
|
+
*/
|
|
109
|
+
export function isValidSpacingValue(value) {
|
|
110
|
+
return Object.values(BpkSpacing).includes(value) || isPercentage(value);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validates if a size value is valid
|
|
115
|
+
*
|
|
116
|
+
* @param {string} value - The size value to validate
|
|
117
|
+
* @returns {boolean} True if the value is a valid rem/percentage/semantic size
|
|
118
|
+
*/
|
|
119
|
+
export function isValidSizeValue(value) {
|
|
120
|
+
return /^-?\d+(\.\d+)?rem$/.test(value) ||
|
|
121
|
+
// rem values
|
|
122
|
+
isPercentage(value) ||
|
|
123
|
+
// percentage values
|
|
124
|
+
value === 'auto' || value === 'full' || value === 'fit-content';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Validates if a position value is valid
|
|
129
|
+
*
|
|
130
|
+
* @param {string} value - The position value to validate
|
|
131
|
+
* @returns {boolean} True if the value is a valid rem or percentage
|
|
132
|
+
*/
|
|
133
|
+
export function isValidPositionValue(value) {
|
|
134
|
+
return /^-?\d+(\.\d+)?rem$/.test(value) ||
|
|
135
|
+
// rem values
|
|
136
|
+
isPercentage(value) // percentage values
|
|
137
|
+
;
|
|
138
|
+
}
|