boxwood 2.11.0 → 2.13.0
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/package.json +1 -1
- package/ui/grid/index.js +9 -4
- package/ui/markdown/index.js +107 -9
- package/ui/markdown/utilities/parseCustomTag.js +198 -0
- package/ui/markdown/utilities/replaceVariables.js +80 -0
- package/ui/normalize.js +32 -17
package/package.json
CHANGED
package/ui/grid/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { component, css, Div } = require("../..")
|
|
2
2
|
const { normalizeGap, normalizeBreakpoint } = require("../normalize")
|
|
3
|
+
const { toNumber } = require("../normalize")
|
|
3
4
|
|
|
4
5
|
const BREAKPOINTS = {
|
|
5
6
|
xl: "1199px",
|
|
@@ -15,6 +16,9 @@ function Grid(
|
|
|
15
16
|
gap = normalizeGap(gap)
|
|
16
17
|
breakpoint = normalizeBreakpoint(breakpoint)
|
|
17
18
|
|
|
19
|
+
// Normalize columns: convert string numbers to numbers
|
|
20
|
+
columns = toNumber(columns)
|
|
21
|
+
|
|
18
22
|
const styleObject = {
|
|
19
23
|
"box-sizing": "border-box",
|
|
20
24
|
display: "grid",
|
|
@@ -27,10 +31,11 @@ function Grid(
|
|
|
27
31
|
}),
|
|
28
32
|
...(typeof columns === "object" &&
|
|
29
33
|
Object.keys(columns).reduce((object, key) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
let value = toNumber(columns[key])
|
|
35
|
+
value =
|
|
36
|
+
typeof value === "number"
|
|
37
|
+
? `repeat(${value}, 1fr)`
|
|
38
|
+
: value
|
|
34
39
|
if (key === "default") {
|
|
35
40
|
object["grid-template-columns"] = value
|
|
36
41
|
} else if (typeof key === "string") {
|
package/ui/markdown/index.js
CHANGED
|
@@ -21,6 +21,11 @@ const {
|
|
|
21
21
|
} = require("../..")
|
|
22
22
|
|
|
23
23
|
const { format } = require("./utilities/format")
|
|
24
|
+
const {
|
|
25
|
+
parseCustomTag,
|
|
26
|
+
resolveAttributes,
|
|
27
|
+
} = require("./utilities/parseCustomTag")
|
|
28
|
+
const { replaceVariables } = require("./utilities/replaceVariables")
|
|
24
29
|
|
|
25
30
|
const ORDERED_LIST_REGEXP = /^\d+\.\s/
|
|
26
31
|
const UNORDERED_MARKERS = ["- ", "— ", "– ", "• "]
|
|
@@ -55,6 +60,19 @@ function Markdown(params, children) {
|
|
|
55
60
|
return null
|
|
56
61
|
}
|
|
57
62
|
|
|
63
|
+
const customComponents = params && params.components
|
|
64
|
+
const data = params && params.data
|
|
65
|
+
|
|
66
|
+
// Filter out special params that shouldn't be passed to HTML elements
|
|
67
|
+
const htmlParams = params
|
|
68
|
+
? Object.keys(params).reduce((acc, key) => {
|
|
69
|
+
if (key !== "components" && key !== "data") {
|
|
70
|
+
acc[key] = params[key]
|
|
71
|
+
}
|
|
72
|
+
return acc
|
|
73
|
+
}, {})
|
|
74
|
+
: {}
|
|
75
|
+
|
|
58
76
|
// First pass: detect code blocks before processing lines
|
|
59
77
|
const allLines = children.trim().split("\n")
|
|
60
78
|
const items = []
|
|
@@ -64,6 +82,68 @@ function Markdown(params, children) {
|
|
|
64
82
|
const line = allLines[i]
|
|
65
83
|
const trimmed = line.trim()
|
|
66
84
|
|
|
85
|
+
// Check for custom component tags first
|
|
86
|
+
const customTag = parseCustomTag(line, customComponents)
|
|
87
|
+
if (customTag) {
|
|
88
|
+
if (customTag.type === "custom-component" && customTag.selfClosing) {
|
|
89
|
+
// Self-closing custom component
|
|
90
|
+
items.push({
|
|
91
|
+
type: "custom-component",
|
|
92
|
+
tagName: customTag.tagName,
|
|
93
|
+
component: customTag.component,
|
|
94
|
+
attributes: customTag.attributes,
|
|
95
|
+
selfClosing: true,
|
|
96
|
+
indent: line.length - line.trimStart().length,
|
|
97
|
+
})
|
|
98
|
+
i++
|
|
99
|
+
continue
|
|
100
|
+
} else if (customTag.type === "custom-component-open") {
|
|
101
|
+
// Opening tag of a custom component
|
|
102
|
+
const openIndent = line.length - line.trimStart().length
|
|
103
|
+
const tagName = customTag.tagName
|
|
104
|
+
const componentFn = customTag.component
|
|
105
|
+
const attributes = customTag.attributes
|
|
106
|
+
const contentLines = []
|
|
107
|
+
i++ // Move past opening tag
|
|
108
|
+
|
|
109
|
+
// Collect content until closing tag
|
|
110
|
+
let depth = 1
|
|
111
|
+
while (i < allLines.length && depth > 0) {
|
|
112
|
+
const contentLine = allLines[i]
|
|
113
|
+
const contentTag = parseCustomTag(contentLine, customComponents)
|
|
114
|
+
|
|
115
|
+
if (contentTag && contentTag.tagName === tagName) {
|
|
116
|
+
if (contentTag.type === "custom-component-open") {
|
|
117
|
+
depth++
|
|
118
|
+
contentLines.push(contentLine)
|
|
119
|
+
} else if (contentTag.type === "custom-component-close") {
|
|
120
|
+
depth--
|
|
121
|
+
if (depth === 0) {
|
|
122
|
+
// Found matching closing tag
|
|
123
|
+
i++
|
|
124
|
+
break
|
|
125
|
+
}
|
|
126
|
+
contentLines.push(contentLine)
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
contentLines.push(contentLine)
|
|
130
|
+
}
|
|
131
|
+
i++
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
items.push({
|
|
135
|
+
type: "custom-component",
|
|
136
|
+
tagName,
|
|
137
|
+
component: componentFn,
|
|
138
|
+
attributes,
|
|
139
|
+
content: contentLines.join("\n"),
|
|
140
|
+
selfClosing: false,
|
|
141
|
+
indent: openIndent,
|
|
142
|
+
})
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
67
147
|
// Check for code block start
|
|
68
148
|
if (trimmed.startsWith("```")) {
|
|
69
149
|
const language = trimmed.substring(3).trim()
|
|
@@ -169,7 +249,8 @@ function Markdown(params, children) {
|
|
|
169
249
|
}
|
|
170
250
|
|
|
171
251
|
// Process current item at the correct indent level
|
|
172
|
-
const
|
|
252
|
+
const processedContent = replaceVariables(item.content, data)
|
|
253
|
+
const content = [format(processedContent, INLINE_COMPONENTS)]
|
|
173
254
|
currentIndex++
|
|
174
255
|
|
|
175
256
|
// Check if next item is a nested list
|
|
@@ -180,11 +261,11 @@ function Markdown(params, children) {
|
|
|
180
261
|
currentIndex = nestedResult.nextIndex
|
|
181
262
|
}
|
|
182
263
|
|
|
183
|
-
list.push(Li(
|
|
264
|
+
list.push(Li(htmlParams, content))
|
|
184
265
|
}
|
|
185
266
|
|
|
186
267
|
const listElement =
|
|
187
|
-
parentListType === "ul" ? Ul(
|
|
268
|
+
parentListType === "ul" ? Ul(htmlParams, list) : Ol(htmlParams, list)
|
|
188
269
|
|
|
189
270
|
return { list: listElement, nextIndex: currentIndex }
|
|
190
271
|
}
|
|
@@ -192,7 +273,20 @@ function Markdown(params, children) {
|
|
|
192
273
|
while (i < items.length) {
|
|
193
274
|
const item = items[i]
|
|
194
275
|
|
|
195
|
-
if (item.type === "
|
|
276
|
+
if (item.type === "custom-component") {
|
|
277
|
+
// Handle custom component
|
|
278
|
+
const resolvedAttrs = resolveAttributes(item.attributes, data)
|
|
279
|
+
|
|
280
|
+
if (item.selfClosing) {
|
|
281
|
+
// Self-closing component
|
|
282
|
+
nodes.push(item.component(resolvedAttrs))
|
|
283
|
+
} else {
|
|
284
|
+
// Component with children - recursively process markdown content
|
|
285
|
+
const childContent = item.content ? Markdown(params, item.content) : []
|
|
286
|
+
nodes.push(item.component(resolvedAttrs, childContent))
|
|
287
|
+
}
|
|
288
|
+
i++
|
|
289
|
+
} else if (item.type === "li") {
|
|
196
290
|
const result = parseList(i, item.indent)
|
|
197
291
|
nodes.push(result.list)
|
|
198
292
|
i = result.nextIndex
|
|
@@ -204,26 +298,30 @@ function Markdown(params, children) {
|
|
|
204
298
|
i++
|
|
205
299
|
}
|
|
206
300
|
|
|
301
|
+
const content = replaceVariables(lines.join("\n"), data)
|
|
207
302
|
nodes.push(
|
|
208
303
|
Blockquote(
|
|
209
|
-
|
|
210
|
-
P(
|
|
304
|
+
htmlParams,
|
|
305
|
+
P(htmlParams, format(content, INLINE_COMPONENTS)),
|
|
211
306
|
),
|
|
212
307
|
)
|
|
213
308
|
} else if (item.type === "hr") {
|
|
214
|
-
nodes.push(Hr(
|
|
309
|
+
nodes.push(Hr(htmlParams))
|
|
215
310
|
i++
|
|
216
311
|
} else if (item.type === "pre") {
|
|
217
312
|
// Code blocks - wrap in <pre><code>
|
|
218
313
|
const codeParams = item.language
|
|
219
314
|
? { class: `language-${item.language}` }
|
|
220
315
|
: {}
|
|
221
|
-
nodes.push(Pre(
|
|
316
|
+
nodes.push(Pre(htmlParams, Code(codeParams, item.content)))
|
|
222
317
|
i++
|
|
223
318
|
} else {
|
|
224
319
|
const { type, content } = item
|
|
225
320
|
const Component = COMPONENTS[type] || P
|
|
226
|
-
|
|
321
|
+
const processedContent = replaceVariables(content, data)
|
|
322
|
+
nodes.push(
|
|
323
|
+
Component(htmlParams, format(processedContent, INLINE_COMPONENTS)),
|
|
324
|
+
)
|
|
227
325
|
i++
|
|
228
326
|
}
|
|
229
327
|
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a custom component tag from markdown
|
|
3
|
+
* Supports both <Component attr="value"> and <Component attr={variable}>
|
|
4
|
+
* @param {string} line - The line to parse
|
|
5
|
+
* @param {Object} customComponents - Map of component names to functions
|
|
6
|
+
* @returns {Object|null} - Parsed tag info or null if not a custom tag
|
|
7
|
+
*/
|
|
8
|
+
function parseCustomTag(line, customComponents) {
|
|
9
|
+
if (!customComponents || typeof customComponents !== "object") {
|
|
10
|
+
return null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const trimmed = line.trim()
|
|
14
|
+
|
|
15
|
+
// Check for self-closing tag: <ComponentName ... /> (space before / is optional)
|
|
16
|
+
const selfClosingMatch = trimmed.match(
|
|
17
|
+
/^<([A-Za-z][A-Za-z0-9-]*)(\s+[^>]*)?\s*\/>\s*$/,
|
|
18
|
+
)
|
|
19
|
+
if (selfClosingMatch) {
|
|
20
|
+
const [, tagName, attributesStr] = selfClosingMatch
|
|
21
|
+
const component = customComponents[tagName]
|
|
22
|
+
if (component) {
|
|
23
|
+
const attributes = parseAttributes(attributesStr || "")
|
|
24
|
+
return {
|
|
25
|
+
type: "custom-component",
|
|
26
|
+
tagName,
|
|
27
|
+
component,
|
|
28
|
+
attributes,
|
|
29
|
+
selfClosing: true,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check for opening tag: <ComponentName ...>
|
|
35
|
+
const openTagMatch = trimmed.match(
|
|
36
|
+
/^<([A-Za-z][A-Za-z0-9-]*)(\s+[^>]*)?>\s*$/,
|
|
37
|
+
)
|
|
38
|
+
if (openTagMatch) {
|
|
39
|
+
const [, tagName, attributesStr] = openTagMatch
|
|
40
|
+
const component = customComponents[tagName]
|
|
41
|
+
if (component) {
|
|
42
|
+
const attributes = parseAttributes(attributesStr || "")
|
|
43
|
+
return {
|
|
44
|
+
type: "custom-component-open",
|
|
45
|
+
tagName,
|
|
46
|
+
component,
|
|
47
|
+
attributes,
|
|
48
|
+
selfClosing: false,
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check for closing tag: </ComponentName>
|
|
54
|
+
const closeTagMatch = trimmed.match(/^<\/([A-Za-z][A-Za-z0-9-]*)>\s*$/)
|
|
55
|
+
if (closeTagMatch) {
|
|
56
|
+
const [, tagName] = closeTagMatch
|
|
57
|
+
const component = customComponents[tagName]
|
|
58
|
+
if (component) {
|
|
59
|
+
return {
|
|
60
|
+
type: "custom-component-close",
|
|
61
|
+
tagName,
|
|
62
|
+
component,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse attributes from a tag string
|
|
72
|
+
* Supports: attr="value", attr='value', attr={variable}, attr
|
|
73
|
+
* @param {string} attributesStr - The attributes string
|
|
74
|
+
* @returns {Object} - Parsed attributes
|
|
75
|
+
*/
|
|
76
|
+
function parseAttributes(attributesStr) {
|
|
77
|
+
const attributes = {}
|
|
78
|
+
if (!attributesStr || !attributesStr.trim()) {
|
|
79
|
+
return attributes
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const str = attributesStr.trim()
|
|
83
|
+
let i = 0
|
|
84
|
+
|
|
85
|
+
while (i < str.length) {
|
|
86
|
+
// Skip whitespace
|
|
87
|
+
while (i < str.length && /\s/.test(str[i])) {
|
|
88
|
+
i++
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (i >= str.length) break
|
|
92
|
+
|
|
93
|
+
// Parse attribute name
|
|
94
|
+
let nameStart = i
|
|
95
|
+
while (i < str.length && /[a-zA-Z0-9-_:]/.test(str[i])) {
|
|
96
|
+
i++
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const name = str.substring(nameStart, i)
|
|
100
|
+
if (!name) break
|
|
101
|
+
|
|
102
|
+
// Skip whitespace after name
|
|
103
|
+
while (i < str.length && /\s/.test(str[i])) {
|
|
104
|
+
i++
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check for =
|
|
108
|
+
if (i >= str.length || str[i] !== "=") {
|
|
109
|
+
// Boolean attribute
|
|
110
|
+
attributes[name] = true
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
i++ // Skip =
|
|
115
|
+
|
|
116
|
+
// Skip whitespace after =
|
|
117
|
+
while (i < str.length && /\s/.test(str[i])) {
|
|
118
|
+
i++
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (i >= str.length) break
|
|
122
|
+
|
|
123
|
+
// Parse value
|
|
124
|
+
const quote = str[i]
|
|
125
|
+
|
|
126
|
+
if (quote === '"' || quote === "'") {
|
|
127
|
+
// Quoted string
|
|
128
|
+
i++ // Skip opening quote
|
|
129
|
+
let value = ""
|
|
130
|
+
while (i < str.length && str[i] !== quote) {
|
|
131
|
+
if (str[i] === "\\" && i + 1 < str.length) {
|
|
132
|
+
// Escaped character - add the next character literally
|
|
133
|
+
value += str[i + 1]
|
|
134
|
+
i += 2
|
|
135
|
+
} else {
|
|
136
|
+
value += str[i]
|
|
137
|
+
i++
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
attributes[name] = value
|
|
141
|
+
if (i < str.length) i++ // Skip closing quote
|
|
142
|
+
} else if (str[i] === "{") {
|
|
143
|
+
// Variable reference
|
|
144
|
+
i++ // Skip {
|
|
145
|
+
const valueStart = i
|
|
146
|
+
while (i < str.length && str[i] !== "}") {
|
|
147
|
+
i++
|
|
148
|
+
}
|
|
149
|
+
const variableName = str.substring(valueStart, i).trim()
|
|
150
|
+
attributes[name] = { __variable: variableName }
|
|
151
|
+
if (i < str.length) i++ // Skip }
|
|
152
|
+
} else {
|
|
153
|
+
// Unquoted value (until space or end)
|
|
154
|
+
const valueStart = i
|
|
155
|
+
while (i < str.length && !/\s/.test(str[i])) {
|
|
156
|
+
i++
|
|
157
|
+
}
|
|
158
|
+
const value = str.substring(valueStart, i)
|
|
159
|
+
attributes[name] = value
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return attributes
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Resolve attributes by replacing variable references with actual values
|
|
168
|
+
* @param {Object} attributes - Attributes with possible variable references
|
|
169
|
+
* @param {Object} data - Data object containing variable values
|
|
170
|
+
* @returns {Object} - Resolved attributes
|
|
171
|
+
*/
|
|
172
|
+
function resolveAttributes(attributes, data) {
|
|
173
|
+
if (!attributes || typeof attributes !== "object") {
|
|
174
|
+
return {}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const resolved = {}
|
|
178
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
179
|
+
if (value && typeof value === "object" && value.__variable) {
|
|
180
|
+
// Resolve variable
|
|
181
|
+
const variableName = value.__variable
|
|
182
|
+
resolved[key] =
|
|
183
|
+
data && data[variableName] !== undefined
|
|
184
|
+
? data[variableName]
|
|
185
|
+
: undefined
|
|
186
|
+
} else {
|
|
187
|
+
resolved[key] = value
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return resolved
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = {
|
|
195
|
+
parseCustomTag,
|
|
196
|
+
parseAttributes,
|
|
197
|
+
resolveAttributes,
|
|
198
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replace {variableName} placeholders in text with actual values from data
|
|
3
|
+
* @param {string} text - Text containing variable placeholders
|
|
4
|
+
* @param {Object} data - Data object with variable values
|
|
5
|
+
* @returns {string|Array} - Text with variables replaced, or array if mixed content
|
|
6
|
+
*/
|
|
7
|
+
function replaceVariables(text, data) {
|
|
8
|
+
if (!text || typeof text !== "string") {
|
|
9
|
+
return text
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (!data || typeof data !== "object") {
|
|
13
|
+
return text
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check if text contains any variables
|
|
17
|
+
if (!text.includes("{") || !text.includes("}")) {
|
|
18
|
+
return text
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = []
|
|
22
|
+
let i = 0
|
|
23
|
+
let lastIndex = 0
|
|
24
|
+
|
|
25
|
+
while (i < text.length) {
|
|
26
|
+
if (text[i] === "\\" && text[i + 1] === "{") {
|
|
27
|
+
// Escaped opening brace
|
|
28
|
+
result.push(text.substring(lastIndex, i))
|
|
29
|
+
result.push("{")
|
|
30
|
+
i += 2
|
|
31
|
+
lastIndex = i
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (text[i] === "{") {
|
|
36
|
+
const closeIndex = text.indexOf("}", i + 1)
|
|
37
|
+
|
|
38
|
+
if (closeIndex !== -1) {
|
|
39
|
+
// Found a variable placeholder
|
|
40
|
+
const variableName = text.substring(i + 1, closeIndex).trim()
|
|
41
|
+
|
|
42
|
+
if (variableName) {
|
|
43
|
+
// Add text before the variable
|
|
44
|
+
if (i > lastIndex) {
|
|
45
|
+
result.push(text.substring(lastIndex, i))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Add the variable value
|
|
49
|
+
const value = data[variableName]
|
|
50
|
+
if (value !== undefined && value !== null) {
|
|
51
|
+
result.push(String(value))
|
|
52
|
+
} else {
|
|
53
|
+
// Variable not found, keep the placeholder
|
|
54
|
+
result.push(text.substring(i, closeIndex + 1))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
i = closeIndex + 1
|
|
58
|
+
lastIndex = i
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
i++
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add remaining text
|
|
68
|
+
if (lastIndex < text.length) {
|
|
69
|
+
result.push(text.substring(lastIndex))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// If no substitutions were made, return original text
|
|
73
|
+
if (result.length === 0) {
|
|
74
|
+
return text
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result.join("")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { replaceVariables }
|
package/ui/normalize.js
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts string numbers to integers
|
|
3
|
+
* @param {string|number} value - The value to convert
|
|
4
|
+
* @returns {number|string} - Integer if string is numeric, otherwise original value
|
|
5
|
+
*/
|
|
6
|
+
function toNumber(value) {
|
|
7
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
8
|
+
return parseInt(value, 10)
|
|
9
|
+
}
|
|
10
|
+
return value
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts number or string number to pixel value
|
|
15
|
+
* @param {string|number} value - The value to convert
|
|
16
|
+
* @returns {string|number} - Value with px suffix if numeric, otherwise original value
|
|
17
|
+
*/
|
|
18
|
+
function toPixels(value) {
|
|
19
|
+
value = toNumber(value)
|
|
20
|
+
if (typeof value === "number") {
|
|
21
|
+
return `${value}px`
|
|
22
|
+
}
|
|
23
|
+
return value
|
|
24
|
+
}
|
|
25
|
+
|
|
1
26
|
function normalizeFlex(align) {
|
|
2
27
|
switch (align) {
|
|
3
28
|
case "start":
|
|
@@ -22,15 +47,12 @@ function normalizeGap(gap) {
|
|
|
22
47
|
if (!gap) {
|
|
23
48
|
return "1rem"
|
|
24
49
|
}
|
|
25
|
-
|
|
26
|
-
return `${gap}px`
|
|
27
|
-
}
|
|
28
|
-
|
|
50
|
+
|
|
29
51
|
if (GAP_MAP.hasOwnProperty(gap)) {
|
|
30
52
|
return GAP_MAP[gap]
|
|
31
53
|
}
|
|
32
54
|
|
|
33
|
-
return gap
|
|
55
|
+
return toPixels(gap)
|
|
34
56
|
}
|
|
35
57
|
|
|
36
58
|
const BREAKPOINT_MAP = {
|
|
@@ -42,20 +64,14 @@ const BREAKPOINT_MAP = {
|
|
|
42
64
|
}
|
|
43
65
|
|
|
44
66
|
function normalizeBreakpoint(breakpoint) {
|
|
45
|
-
if (typeof breakpoint === "number") {
|
|
46
|
-
return `${breakpoint}px`
|
|
47
|
-
}
|
|
48
67
|
if (BREAKPOINT_MAP.hasOwnProperty(breakpoint)) {
|
|
49
68
|
return BREAKPOINT_MAP[breakpoint]
|
|
50
69
|
}
|
|
51
|
-
return breakpoint
|
|
70
|
+
return toPixels(breakpoint)
|
|
52
71
|
}
|
|
53
72
|
|
|
54
73
|
function normalizeWidth(width) {
|
|
55
|
-
|
|
56
|
-
return `${width}px`
|
|
57
|
-
}
|
|
58
|
-
return width
|
|
74
|
+
return toPixels(width)
|
|
59
75
|
}
|
|
60
76
|
|
|
61
77
|
const SPACING_MAP = {
|
|
@@ -68,16 +84,15 @@ const SPACING_MAP = {
|
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
function normalizeSpacing(spacing) {
|
|
71
|
-
if (typeof spacing === "number") {
|
|
72
|
-
return `${spacing}px`
|
|
73
|
-
}
|
|
74
87
|
if (SPACING_MAP.hasOwnProperty(spacing)) {
|
|
75
88
|
return SPACING_MAP[spacing]
|
|
76
89
|
}
|
|
77
|
-
return spacing
|
|
90
|
+
return toPixels(spacing)
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
module.exports = {
|
|
94
|
+
toNumber,
|
|
95
|
+
toPixels,
|
|
81
96
|
normalizeFlex,
|
|
82
97
|
normalizeGap,
|
|
83
98
|
normalizeBreakpoint,
|