@rokkit/core 1.0.0-next.96 → 1.0.0-next.98
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 +10 -10
- package/src/calendar.js +8 -0
- package/src/constants.js +6 -0
- package/src/index.js +11 -6
- package/src/mapped-list.js +5 -7
- package/src/mapping.js +24 -19
- package/src/nested.js +12 -2
- package/src/theme.js +9 -1
- package/src/ticks.js +4 -8
- package/src/types.js +15 -1
- package/src/parser.js +0 -110
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/core",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.98",
|
|
4
4
|
"description": "Core components, actions and stores for svelte apps.",
|
|
5
5
|
"author": "Jerry Thomas <me@jerrythomas.name>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@sveltejs/vite-plugin-svelte": "^3.1.
|
|
16
|
-
"@testing-library/svelte": "^5.1
|
|
17
|
-
"@types/ramda": "^0.30.
|
|
15
|
+
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
|
16
|
+
"@testing-library/svelte": "^5.2.1",
|
|
17
|
+
"@types/ramda": "^0.30.2",
|
|
18
18
|
"@vitest/coverage-v8": "^1.6.0",
|
|
19
19
|
"@vitest/ui": "~1.6.0",
|
|
20
|
-
"jsdom": "^24.1.
|
|
21
|
-
"svelte": "^4.2.
|
|
22
|
-
"typescript": "^5.
|
|
23
|
-
"vite": "^5.
|
|
20
|
+
"jsdom": "^24.1.3",
|
|
21
|
+
"svelte": "^4.2.19",
|
|
22
|
+
"typescript": "^5.6.2",
|
|
23
|
+
"vite": "^5.4.6",
|
|
24
24
|
"vitest": "~1.6.0",
|
|
25
|
-
"shared-config": "1.0.0-next.
|
|
26
|
-
"validators": "1.0.0-next.
|
|
25
|
+
"shared-config": "1.0.0-next.98",
|
|
26
|
+
"validators": "1.0.0-next.98"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"src/**/*.js",
|
package/src/calendar.js
CHANGED
|
@@ -10,6 +10,14 @@ export const weekdays = [
|
|
|
10
10
|
'Saturday'
|
|
11
11
|
]
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Get the days in the month.
|
|
15
|
+
*
|
|
16
|
+
* @param {Date} value
|
|
17
|
+
* @param {Array} holidays
|
|
18
|
+
* @param {boolean} fixed
|
|
19
|
+
* @returns {import('./types').CalendarDay[]}
|
|
20
|
+
*/
|
|
13
21
|
export function getCalendarDays(value, holidays = [], fixed = false) {
|
|
14
22
|
const month = getMonth(value)
|
|
15
23
|
const year = getYear(value)
|
package/src/constants.js
CHANGED
|
@@ -97,6 +97,12 @@ export const defaultThemeMapping = {
|
|
|
97
97
|
info: 'cyan'
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Generate a state icon mapping from a list of icon names
|
|
102
|
+
*
|
|
103
|
+
* @param {string[]} icons
|
|
104
|
+
* @returns {import('./types').StateIcons}
|
|
105
|
+
*/
|
|
100
106
|
export function stateIconsFromNames(icons) {
|
|
101
107
|
return icons
|
|
102
108
|
.map((k) => [...k.split('-')])
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
// skipcq: JS-E1004 - Needed for exposing JS Doc types
|
|
1
2
|
export * from './types'
|
|
3
|
+
// skipcq: JS-E1004 - Needed for exposing all constants
|
|
2
4
|
export * from './constants'
|
|
3
|
-
export
|
|
5
|
+
export { flattenNestedList, findValueFromPath } from './nested'
|
|
6
|
+
// skipcq: JS-E1004 - Needed for exposing all functions
|
|
4
7
|
export * from './mapping'
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
8
|
+
export { mappedList } from './mapped-list'
|
|
9
|
+
export { getLineTypes } from './connector'
|
|
10
|
+
export { generateTicks } from './ticks'
|
|
11
|
+
export { weekdays, getCalendarDays } from './calendar'
|
|
12
|
+
// skipcq: JS-E1004 - Needed for exposing all functions
|
|
9
13
|
export * from './utils'
|
|
14
|
+
// skipcq: JS-E1004 - Needed for exposing all functions
|
|
10
15
|
export * from './theme'
|
|
11
|
-
|
|
16
|
+
// skipcq: JS-E1004 - Needed for exposing all functions
|
|
12
17
|
export * from './string'
|
package/src/mapped-list.js
CHANGED
|
@@ -28,6 +28,7 @@ function findInChildren(item, index, fields, value, attr, position) {
|
|
|
28
28
|
index
|
|
29
29
|
])
|
|
30
30
|
}
|
|
31
|
+
return null
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
34
|
* Traverses the tree to find an item by value.
|
|
@@ -186,7 +187,6 @@ function getNextSiblingOrAncestor(position, items, fields) {
|
|
|
186
187
|
let children = parent.item[parent.fields.children]
|
|
187
188
|
if (index < children.length - 1) {
|
|
188
189
|
index += 1
|
|
189
|
-
|
|
190
190
|
const sibling = findItemByIndexArray([...position.slice(0, -1), index], items, fields)
|
|
191
191
|
return { item: sibling.item, position: sibling.position, fields }
|
|
192
192
|
} else {
|
|
@@ -197,12 +197,10 @@ function getNextSiblingOrAncestor(position, items, fields) {
|
|
|
197
197
|
parent = findItemByIndexArray(position, items, fields)
|
|
198
198
|
children = parent.item[parent.fields.children]
|
|
199
199
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
fields
|
|
205
|
-
}
|
|
200
|
+
return {
|
|
201
|
+
item: children[index + 1],
|
|
202
|
+
position: [...position, index + 1],
|
|
203
|
+
fields
|
|
206
204
|
}
|
|
207
205
|
}
|
|
208
206
|
}
|
package/src/mapping.js
CHANGED
|
@@ -5,7 +5,7 @@ import { toString, isObject } from './utils'
|
|
|
5
5
|
* Get the component to be used to render the item.
|
|
6
6
|
* If the component is null or undefined, it will return the default component.
|
|
7
7
|
*
|
|
8
|
-
* @param {object|string}
|
|
8
|
+
* @param {object|string} value
|
|
9
9
|
* @param {import('./types.js').FieldMapping} fields
|
|
10
10
|
* @param {import('./types.js').ComponentMap} using
|
|
11
11
|
*/
|
|
@@ -18,24 +18,34 @@ export function getComponent(value, fields, using) {
|
|
|
18
18
|
/**
|
|
19
19
|
* Get the icon for the item. If the icon is an object, it will use the state to determine which icon to use.
|
|
20
20
|
*
|
|
21
|
-
* @param {object|string}
|
|
21
|
+
* @param {object|string} value
|
|
22
22
|
* @param {import('./types.js').FieldMapping} fields
|
|
23
|
+
* @returns {string}
|
|
23
24
|
*/
|
|
24
25
|
export function getIcon(value, fields = defaultFields) {
|
|
25
26
|
if (fields.icon === undefined || typeof (value ?? '') !== 'object') return null
|
|
26
|
-
|
|
27
|
-
const name =
|
|
28
|
-
typeof value[fields.icon] === 'object'
|
|
29
|
-
? value[fields.icon][value[fields.state]]
|
|
30
|
-
: value[fields.icon]
|
|
27
|
+
|
|
28
|
+
const name = getIconFromObject(value, fields)
|
|
31
29
|
return fields.iconPrefix ? [fields.iconPrefix, name].join('-') : name
|
|
32
30
|
}
|
|
33
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Get the icon for the item. If the icon is an object, it will use the state to determine which icon to use.
|
|
34
|
+
*
|
|
35
|
+
* @param {object} value
|
|
36
|
+
* @param {import('./types.js').FieldMapping} fields
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function getIconFromObject(value, fields) {
|
|
40
|
+
if (typeof value[fields.icon] === 'object') return value[fields.icon][value[fields.state]]
|
|
41
|
+
return value[fields.icon]
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
/**
|
|
35
45
|
* Get the value for the item. If the value is an object,
|
|
36
46
|
* it will use the field mapping to determine which attribute to get.
|
|
37
47
|
*
|
|
38
|
-
* @param {*}
|
|
48
|
+
* @param {*} node
|
|
39
49
|
* @param {import('./types').FieldMapping} fields
|
|
40
50
|
* @returns {*}
|
|
41
51
|
*/
|
|
@@ -54,11 +64,6 @@ export function getValue(node, fields = defaultFields) {
|
|
|
54
64
|
export function getText(node, fields = defaultFields) {
|
|
55
65
|
const value = isObject(node) ? node[fields.text] : node
|
|
56
66
|
return value
|
|
57
|
-
// return value != null
|
|
58
|
-
// ? isObject(value)
|
|
59
|
-
// ? JSON.stringify(value, null, 2)
|
|
60
|
-
// : value.toString()
|
|
61
|
-
// : value
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
/**
|
|
@@ -67,8 +72,8 @@ export function getText(node, fields = defaultFields) {
|
|
|
67
72
|
*
|
|
68
73
|
* @param {*} node
|
|
69
74
|
* @param {import('./types').FieldMapping} fields
|
|
70
|
-
* @param {
|
|
71
|
-
* @returns {
|
|
75
|
+
* @param {Function} formatter
|
|
76
|
+
* @returns {Function}
|
|
72
77
|
*/
|
|
73
78
|
export function getFormattedText(node, fields = defaultFields, formatter = toString) {
|
|
74
79
|
const value = isObject(node) ? node[fields.text] : node
|
|
@@ -79,7 +84,7 @@ export function getFormattedText(node, fields = defaultFields, formatter = toStr
|
|
|
79
84
|
}
|
|
80
85
|
/**
|
|
81
86
|
* Gets the attribute from the node
|
|
82
|
-
* @param {*}
|
|
87
|
+
* @param {*} node
|
|
83
88
|
* @param {string} attr
|
|
84
89
|
* @returns {*}
|
|
85
90
|
*/
|
|
@@ -89,7 +94,7 @@ export function getAttribute(node, attr) {
|
|
|
89
94
|
/**
|
|
90
95
|
* Check if the current item is a parent
|
|
91
96
|
*
|
|
92
|
-
* @param {*}
|
|
97
|
+
* @param {*} item
|
|
93
98
|
* @param {import('./types').FieldMapping} fields
|
|
94
99
|
* @returns {boolean}
|
|
95
100
|
*/
|
|
@@ -105,7 +110,7 @@ export function hasChildren(item, fields) {
|
|
|
105
110
|
/**
|
|
106
111
|
* Check if the current item is a parent and is expanded
|
|
107
112
|
*
|
|
108
|
-
* @param {*}
|
|
113
|
+
* @param {*} item
|
|
109
114
|
* @param {import('./types').FieldMapping} fields
|
|
110
115
|
* @returns {boolean}
|
|
111
116
|
*/
|
|
@@ -121,7 +126,7 @@ export function isExpanded(item, fields) {
|
|
|
121
126
|
/**
|
|
122
127
|
* Verify if at least one item has children
|
|
123
128
|
*
|
|
124
|
-
* @param {Array<*>}
|
|
129
|
+
* @param {Array<*>} items
|
|
125
130
|
* @param {import('./types').FieldMapping} fields
|
|
126
131
|
* @returns {boolean}
|
|
127
132
|
*/
|
package/src/nested.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { omit } from 'ramda'
|
|
2
2
|
import { defaultFields } from './constants'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Flattens a nested list of items
|
|
6
|
+
*
|
|
7
|
+
* @param {Array} items
|
|
8
|
+
* @param {import('./types).FieldMapping} fields
|
|
9
|
+
* @param {number} level
|
|
10
|
+
* @returns {Array}
|
|
11
|
+
*/
|
|
4
12
|
export function flattenNestedList(items, fields = defaultFields, level = 0) {
|
|
5
13
|
fields = { ...defaultFields, ...fields }
|
|
6
14
|
let data = []
|
|
@@ -20,9 +28,11 @@ export function flattenNestedList(items, fields = defaultFields, level = 0) {
|
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
/**
|
|
23
|
-
*
|
|
31
|
+
* Matches a path slug to a value in the menu
|
|
24
32
|
*
|
|
25
|
-
* @param {string}
|
|
33
|
+
* @param {string} slug
|
|
34
|
+
* @param {Array} data
|
|
35
|
+
* @param {import('./types').FieldMapping} fields
|
|
26
36
|
* @returns {any}
|
|
27
37
|
*/
|
|
28
38
|
export function findValueFromPath(slug, data, fields) {
|
package/src/theme.js
CHANGED
|
@@ -11,6 +11,7 @@ const modifiers = {
|
|
|
11
11
|
* Generate shades for a color using css varuable
|
|
12
12
|
*
|
|
13
13
|
* @param {string} name
|
|
14
|
+
* @param {string} modifier
|
|
14
15
|
* @returns
|
|
15
16
|
*/
|
|
16
17
|
export function shadesOf(name, modifier = 'none') {
|
|
@@ -36,6 +37,13 @@ export function shadesOf(name, modifier = 'none') {
|
|
|
36
37
|
)
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Generate shades for a color using css varuable
|
|
42
|
+
*
|
|
43
|
+
* @param {string} name
|
|
44
|
+
* @param {string} modifier
|
|
45
|
+
* @returns {object}
|
|
46
|
+
*/
|
|
39
47
|
export function stateColors(name, modifier = 'none') {
|
|
40
48
|
const fn = modifier in modifiers ? modifiers[modifier] : modifiers.none
|
|
41
49
|
return {
|
|
@@ -75,7 +83,7 @@ export function themeColors(modifier = 'none') {
|
|
|
75
83
|
* @param {'light' | 'dark'} mode - The theme mode for which the mappings are being created.
|
|
76
84
|
* @param {function(number): string} valueCondition - A function that takes a shade value and returns the color value
|
|
77
85
|
* based on the condition appropriate for light or dark mode.
|
|
78
|
-
* @returns {{import('./types'}.
|
|
86
|
+
* @returns {{import('./types'}.ShadeMapping[]>} An array of objects, where each object contains key, value, and mode
|
|
79
87
|
* properties corresponding to a CSS custom property definition.
|
|
80
88
|
*/
|
|
81
89
|
function createShadeMappings(variant, mode, valueCondition) {
|
package/src/ticks.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generates an array of tick marks for a range of values.
|
|
3
3
|
*
|
|
4
|
-
* @param {number} lowerBound
|
|
5
|
-
* @param {number} upperBound
|
|
4
|
+
* @param {number} lowerBound - The lower bound of the range.
|
|
5
|
+
* @param {number} upperBound - The upper bound of the range.
|
|
6
6
|
* @param {number} [minorTickStep=upperBound-lowerBound] - The step size for minor ticks.
|
|
7
|
-
* @param {number} [majorTickStep=1]
|
|
8
|
-
* @returns {
|
|
7
|
+
* @param {number} [majorTickStep=1] - The step size for major ticks.
|
|
8
|
+
* @returns {import('./types').TickMark[]>} An array of tick mark objects.
|
|
9
9
|
*/
|
|
10
10
|
export function generateTicks(
|
|
11
11
|
lowerBound,
|
|
@@ -13,10 +13,6 @@ export function generateTicks(
|
|
|
13
13
|
minorTickStep = upperBound - lowerBound,
|
|
14
14
|
majorTickStep = 1
|
|
15
15
|
) {
|
|
16
|
-
// lowerBound = +lowerBound
|
|
17
|
-
// upperBound = +upperBound
|
|
18
|
-
// minorTickStep = +minorTickStep
|
|
19
|
-
// majorTickStep = +majorTickStep
|
|
20
16
|
const length = 1 + Math.ceil((upperBound - lowerBound) / minorTickStep)
|
|
21
17
|
return Array.from({ length }, (_, i) => {
|
|
22
18
|
const value = i === length - 1 ? upperBound : lowerBound + minorTickStep * i
|
package/src/types.js
CHANGED
|
@@ -96,6 +96,20 @@
|
|
|
96
96
|
*/
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
* @typedef {
|
|
99
|
+
* @typedef {Object} TickMark
|
|
100
|
+
* @property {number} value - The value of the tick mark.
|
|
101
|
+
* @property {string} label - The label for the tick mark.
|
|
102
|
+
* @property {boolean} major - Indicates if the tick mark is a major tick.
|
|
100
103
|
*/
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @typedef {Object} CalendarDay
|
|
107
|
+
* @property {number} day - The day of the month.
|
|
108
|
+
* @property {number} offset - indicates the offset for positioning
|
|
109
|
+
* @property {date} date - Datevalue for the day.
|
|
110
|
+
* @property {string} text - formatted text for the day.
|
|
111
|
+
* @property {boolean} holiday - Indicates if the day is a holiday.
|
|
112
|
+
* @property {boolean} weekend - Indicates if the day is on the weekend.
|
|
113
|
+
*/
|
|
114
|
+
|
|
101
115
|
export default {}
|
package/src/parser.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Constructs a regular expression pattern for matching search filter strings.
|
|
3
|
-
* The pattern captures "column", "operator", and "value" groups for filter expressions.
|
|
4
|
-
* Supported operators include common comparison and wildcard operators.
|
|
5
|
-
*
|
|
6
|
-
* Examples of valid expressions this regex can match:
|
|
7
|
-
* - "username:john_doe"
|
|
8
|
-
* - "age>30"
|
|
9
|
-
* - "status!=active"
|
|
10
|
-
* - "title~\"Product Manager\""
|
|
11
|
-
* - "completed!~*yes"
|
|
12
|
-
*
|
|
13
|
-
* The "column" group matches one or more word characters.
|
|
14
|
-
* The "operator" group matches one among the specified comparison or wildcard operators:
|
|
15
|
-
* :, >, <, >=, <=, =<, =>, =, !=, ~, ~*, !~, !~*.
|
|
16
|
-
* The "value" group matches either a double-quoted string or a single unquoted word
|
|
17
|
-
* that doesn't include whitespace or any of the operator characters.
|
|
18
|
-
*
|
|
19
|
-
* @returns {RegExp} - The regular expression pattern to identify search filter elements.
|
|
20
|
-
*/
|
|
21
|
-
export function getRegex() {
|
|
22
|
-
const column = '[\\w]+'
|
|
23
|
-
const operator = ':|>|<|>=|<=|=<|=>|=|!=|~|~\\*|!~|!~\\*'
|
|
24
|
-
const value = '("[^"]+"|[^\\s=:<>!~*]+)'
|
|
25
|
-
|
|
26
|
-
const pattern = `(?<group>((?<column>${column})\\s?(?<operator>${operator})\\s?)(?<value>${value}))`
|
|
27
|
-
|
|
28
|
-
return new RegExp(pattern, 'gm')
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Parses a search string and extracts filters based on a predefined regular expression pattern.
|
|
33
|
-
* Each filter captures column, operator, and value components. Any remaining text that
|
|
34
|
-
* does not fit the pattern is considered a general search term.
|
|
35
|
-
*
|
|
36
|
-
* @param {string} string - The search string to parse for filters.
|
|
37
|
-
* @returns {Object[]} - An array of filter objects each containing the column, operator, and value,
|
|
38
|
-
* plus an additional object for general search if there is remaining text.
|
|
39
|
-
*/
|
|
40
|
-
export function parseFilters(string) {
|
|
41
|
-
const results = []
|
|
42
|
-
const regex = getRegex()
|
|
43
|
-
|
|
44
|
-
// Split the string into an array of tokens
|
|
45
|
-
const tokens = string.matchAll(regex)
|
|
46
|
-
let search = string
|
|
47
|
-
|
|
48
|
-
// Iterate over the tokens
|
|
49
|
-
for (const token of tokens) {
|
|
50
|
-
// Extract the named groups from the token
|
|
51
|
-
let { group, column, operator, value } = token.groups
|
|
52
|
-
search = search.replace(group, '').trim()
|
|
53
|
-
|
|
54
|
-
operator = replaceOperators(operator)
|
|
55
|
-
value = processValue(value, operator)
|
|
56
|
-
|
|
57
|
-
if (column && value) results.push({ column, operator, value })
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (search.length > 0) {
|
|
61
|
-
results.push({
|
|
62
|
-
operator: '~*',
|
|
63
|
-
value: new RegExp(removeQuotes(search), 'i')
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return results
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Replaces various shorthand operators with their standardized equivalent for filtering.
|
|
72
|
-
*
|
|
73
|
-
* @param {string} operator - The operator to be replaced.
|
|
74
|
-
* @returns {string} - The replaced operator string.
|
|
75
|
-
*/
|
|
76
|
-
function replaceOperators(operator) {
|
|
77
|
-
return operator.replace(':', '~*').replace('=>', '>=').replace('=<', '<=')
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Processes the filter value provided, converting it into a format suitable for filtering.
|
|
82
|
-
* If the operator includes a tilde (~), the value is converted to a RegExp.
|
|
83
|
-
*
|
|
84
|
-
* @param {string|number} value - The value to be processed. It could be a string needing quote removal or a number that needs parsing.
|
|
85
|
-
* @param {string} operator - The operator that determines the type of processing to be applied to the value.
|
|
86
|
-
* @returns {string|number|RegExp} - The processed value, which may be a RegExp if the operator involves pattern matching.
|
|
87
|
-
*/
|
|
88
|
-
function processValue(value, operator) {
|
|
89
|
-
// If the value can be parsed as an integer, do so. Otherwise, strip quotes if present.
|
|
90
|
-
|
|
91
|
-
value = !isNaN(parseInt(value)) ? parseInt(value) : removeQuotes(value)
|
|
92
|
-
// If the operator includes a tilde (~), treat the value as a regular expression,
|
|
93
|
-
// case-insensitive if '*' is present, sensitive otherwise.
|
|
94
|
-
if (operator.includes('~')) {
|
|
95
|
-
value = operator.includes('*') ? new RegExp(value, 'i') : new RegExp(value)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return value
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Removes double quotes from the start and end of a string.
|
|
103
|
-
*
|
|
104
|
-
* @param {string} str - The input string from which the surrounding quotes should be removed.
|
|
105
|
-
* @returns {string} - The unquoted string.
|
|
106
|
-
*/
|
|
107
|
-
function removeQuotes(str) {
|
|
108
|
-
const quoteMatch = str.match(/^"([^"]+)"$/)
|
|
109
|
-
return quoteMatch ? quoteMatch[1] : str
|
|
110
|
-
}
|