@rokkit/core 1.0.0-next.100
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 +21 -0
- package/README.md +1 -0
- package/package.json +59 -0
- package/src/calendar.js +44 -0
- package/src/colors/index.js +19 -0
- package/src/colors/syntax.json +42 -0
- package/src/colors/tailwind.json +289 -0
- package/src/connector.js +29 -0
- package/src/constants.js +118 -0
- package/src/index.js +17 -0
- package/src/mapped-list.js +233 -0
- package/src/mapping.js +138 -0
- package/src/nested.js +56 -0
- package/src/string.js +97 -0
- package/src/theme.js +197 -0
- package/src/ticks.js +26 -0
- package/src/types.js +115 -0
- package/src/utils.js +72 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { defaultFields } from './constants'
|
|
2
|
+
import { isExpanded, hasChildren, getAttribute } from './mapping'
|
|
3
|
+
import { equals } from 'ramda'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks if a specifc attribute of an item matches a value.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} item - The item.
|
|
9
|
+
* @param {string} attr - The attribute to check.
|
|
10
|
+
*/
|
|
11
|
+
function isMatch(item, attr, value) {
|
|
12
|
+
const itemValue = attr ? getAttribute(item, attr) : item
|
|
13
|
+
return equals(itemValue, value)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Traverses the tree to find an item by value.
|
|
18
|
+
* @param {Array} items - The items array.
|
|
19
|
+
* @param {Object} fields - The fields mapping.
|
|
20
|
+
* @param {any} value - The value to find.
|
|
21
|
+
* @param {Array} position - The current position in the tree.
|
|
22
|
+
* @returns {Object} The found item, or null if not found.
|
|
23
|
+
*/
|
|
24
|
+
function findInChildren(item, index, fields, value, attr, position) {
|
|
25
|
+
if (hasChildren(item, fields)) {
|
|
26
|
+
return findItemByValue(value, item[fields.children], fields.fields ?? fields, attr, [
|
|
27
|
+
...position,
|
|
28
|
+
index
|
|
29
|
+
])
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Traverses the tree to find an item by value.
|
|
35
|
+
* @param {Array} items - The items array.
|
|
36
|
+
* @param {Object} fields - The fields mapping.
|
|
37
|
+
* @param {any} value - The value to find.
|
|
38
|
+
* @param {Array} position - The current position in the tree.
|
|
39
|
+
* @returns {Object} The found item, or null if not found.
|
|
40
|
+
*/
|
|
41
|
+
export function findItemByValue(value, items, fields = defaultFields, attr = null, position = []) {
|
|
42
|
+
for (let i = 0; i < items.length; i++) {
|
|
43
|
+
if (isMatch(items[i], attr, value)) {
|
|
44
|
+
return { item: items[i], position: [...position, i], fields }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const foundInChildren = findInChildren(items[i], i, fields, value, attr, position)
|
|
48
|
+
if (foundInChildren) return foundInChildren
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Gets an item from an items array using an index array.
|
|
56
|
+
* @param {Array} indices - The index array.
|
|
57
|
+
* @param {Array} items - The items array.
|
|
58
|
+
* @param {Object} fields - The fields configuration.
|
|
59
|
+
* @returns {Object} The item.
|
|
60
|
+
*/
|
|
61
|
+
export function findItemByIndexArray(indices, items, fields) {
|
|
62
|
+
let item = items[indices[0]]
|
|
63
|
+
let levelFields = fields
|
|
64
|
+
for (let level = 1; level < indices.length; level++) {
|
|
65
|
+
if (hasChildren(item, levelFields)) {
|
|
66
|
+
item = item[levelFields.children][indices[level]]
|
|
67
|
+
levelFields = levelFields.fields ?? levelFields
|
|
68
|
+
} else {
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { item, position: indices, fields: levelFields }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @param {Array<integer>} position
|
|
78
|
+
* @param {Array<*>} items
|
|
79
|
+
* @param {import('./types').FieldMapping} fields
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
export function findNearestItemBefore(position, items, fields) {
|
|
83
|
+
if (items.length === 0) return null
|
|
84
|
+
if (position.length === 0) return { item: items[0], position: [0], fields }
|
|
85
|
+
|
|
86
|
+
let index = position[position.length - 1]
|
|
87
|
+
let result = null
|
|
88
|
+
if (index > 0) {
|
|
89
|
+
index -= 1
|
|
90
|
+
if (position.length === 1) {
|
|
91
|
+
return findLastVisibleChild(items[index], [index], fields)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const sibling = findItemByIndexArray([...position.slice(0, -1), index], items, fields)
|
|
95
|
+
result = findLastVisibleChild(sibling.item, sibling.position, sibling.fields)
|
|
96
|
+
} else {
|
|
97
|
+
result = findItemByIndexArray(position.slice(0, -1), items, fields)
|
|
98
|
+
}
|
|
99
|
+
return result
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Returns the next sibling of the current item.
|
|
104
|
+
*
|
|
105
|
+
* @param {*} parent
|
|
106
|
+
* @param {Array<integer>} position
|
|
107
|
+
* @param {import('./types').FieldMapping} fields
|
|
108
|
+
* @returns
|
|
109
|
+
*/
|
|
110
|
+
export function findLastVisibleChild(parent, position, fields) {
|
|
111
|
+
if (isExpanded(parent, fields)) {
|
|
112
|
+
const children = parent[fields.children]
|
|
113
|
+
return findLastVisibleChild(
|
|
114
|
+
children[children.length - 1],
|
|
115
|
+
position.concat(children.length - 1),
|
|
116
|
+
fields.fields ?? fields
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
return { item: parent, position, fields }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Returns the next item in the tree.
|
|
124
|
+
* @param {Array} position - The position of the current item.
|
|
125
|
+
* @param {Array} items - The items in the tree.
|
|
126
|
+
* @param {Object} fields - The fields mapping.
|
|
127
|
+
* @returns {Object|null} The next item or null if there is none.
|
|
128
|
+
*/
|
|
129
|
+
export function findNearestItemAfter(position, items, fields) {
|
|
130
|
+
if (items.length === 0) return null
|
|
131
|
+
if (position.length === 0) return { item: items[0], position: [0], fields }
|
|
132
|
+
|
|
133
|
+
const current = findItemByIndexArray(position, items, fields)
|
|
134
|
+
let result = null
|
|
135
|
+
if (isExpanded(current.item, current.fields)) {
|
|
136
|
+
result = getFirstChild(current, position)
|
|
137
|
+
} else if (position.length === 1) {
|
|
138
|
+
result = getNextSiblingAtRoot(position, items, fields)
|
|
139
|
+
} else {
|
|
140
|
+
result = getNextSiblingOrAncestor(position, items, fields)
|
|
141
|
+
}
|
|
142
|
+
return result
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns the next child of the current item.
|
|
147
|
+
* @param {Object} current - The current item.
|
|
148
|
+
* @param {Array} position - The position of the current item.
|
|
149
|
+
* @returns {Object} The next child.
|
|
150
|
+
*/
|
|
151
|
+
function getFirstChild(current, position) {
|
|
152
|
+
return {
|
|
153
|
+
item: current.item[current.fields.children][0],
|
|
154
|
+
position: position.concat(0),
|
|
155
|
+
fields: current.fields.fields ?? current.fields
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Returns the next sibling of the current item at the root level.
|
|
161
|
+
* @param {Array} position - The position of the current item.
|
|
162
|
+
* @param {Array} items - The items in the tree.
|
|
163
|
+
* @param {Object} fields - The fields mapping.
|
|
164
|
+
* @returns {Object|null} The next sibling or null if there is none.
|
|
165
|
+
*/
|
|
166
|
+
function getNextSiblingAtRoot(position, items, fields) {
|
|
167
|
+
if (position[0] < items.length - 1) {
|
|
168
|
+
return {
|
|
169
|
+
item: items[position[0] + 1],
|
|
170
|
+
position: [position[0] + 1],
|
|
171
|
+
fields
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Returns the next sibling of the current item or the next item in the ancestor.
|
|
179
|
+
* @param {Array} position - The position of the current item.
|
|
180
|
+
* @param {Array} items - The items in the tree.
|
|
181
|
+
* @param {Object} fields - The fields mapping.
|
|
182
|
+
* @returns {Object|null} The next sibling or ancestor or null if there is none.
|
|
183
|
+
*/
|
|
184
|
+
function getNextSiblingOrAncestor(position, items, fields) {
|
|
185
|
+
let index = position[position.length - 1]
|
|
186
|
+
let parent = findItemByIndexArray(position.slice(0, -1), items, fields)
|
|
187
|
+
let children = parent.item[parent.fields.children]
|
|
188
|
+
if (index < children.length - 1) {
|
|
189
|
+
index += 1
|
|
190
|
+
const sibling = findItemByIndexArray([...position.slice(0, -1), index], items, fields)
|
|
191
|
+
return { item: sibling.item, position: sibling.position, fields }
|
|
192
|
+
} else {
|
|
193
|
+
while (index === children.length - 1) {
|
|
194
|
+
index = position[position.length - 1]
|
|
195
|
+
position = position.slice(0, -1)
|
|
196
|
+
if (position.length === 0) return null
|
|
197
|
+
parent = findItemByIndexArray(position, items, fields)
|
|
198
|
+
children = parent.item[parent.fields.children]
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
item: children[index + 1],
|
|
202
|
+
position: [...position, index + 1],
|
|
203
|
+
fields
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Creates a mapped list from an items array and a fields mapping.
|
|
210
|
+
* @param {Array<Object>} items - The items array.
|
|
211
|
+
* @param {import('./types').FieldMapping} fields - The fields mapping.
|
|
212
|
+
* @returns {Object} The mapped list.
|
|
213
|
+
*/
|
|
214
|
+
export function mappedList(items, fields) {
|
|
215
|
+
const findByValue = (value) => findItemByValue(value, items, fields)
|
|
216
|
+
const findByAttribute = (value, attr) => findItemByValue(value, items, fields, attr)
|
|
217
|
+
const findByIndexArray = (index) => findItemByIndexArray(index, items, fields)
|
|
218
|
+
const previous = (position) => findNearestItemBefore(position, items, fields)
|
|
219
|
+
const next = (position) => findNearestItemAfter(position, items, fields)
|
|
220
|
+
|
|
221
|
+
const update = (newItems, newFields) => {
|
|
222
|
+
items = newItems
|
|
223
|
+
fields = newFields
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
findByValue,
|
|
227
|
+
findByAttribute,
|
|
228
|
+
findByIndexArray,
|
|
229
|
+
previous,
|
|
230
|
+
next,
|
|
231
|
+
update
|
|
232
|
+
}
|
|
233
|
+
}
|
package/src/mapping.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { defaultFields } from './constants'
|
|
2
|
+
import { toString, isObject } from './utils'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get the component to be used to render the item.
|
|
6
|
+
* If the component is null or undefined, it will return the default component.
|
|
7
|
+
*
|
|
8
|
+
* @param {object|string} value
|
|
9
|
+
* @param {import('./types.js').FieldMapping} fields
|
|
10
|
+
* @param {import('./types.js').ComponentMap} using
|
|
11
|
+
*/
|
|
12
|
+
export function getComponent(value, fields, using) {
|
|
13
|
+
return fields.component && isObject(value)
|
|
14
|
+
? using[value[fields.component]] ?? using.default
|
|
15
|
+
: using.default
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
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
|
+
*
|
|
21
|
+
* @param {object|string} value
|
|
22
|
+
* @param {import('./types.js').FieldMapping} fields
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
export function getIcon(value, fields = defaultFields) {
|
|
26
|
+
if (fields.icon === undefined || typeof (value ?? '') !== 'object') return null
|
|
27
|
+
|
|
28
|
+
const name = getIconFromObject(value, fields)
|
|
29
|
+
return fields.iconPrefix ? [fields.iconPrefix, name].join('-') : name
|
|
30
|
+
}
|
|
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
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the value for the item. If the value is an object,
|
|
46
|
+
* it will use the field mapping to determine which attribute to get.
|
|
47
|
+
*
|
|
48
|
+
* @param {*} node
|
|
49
|
+
* @param {import('./types').FieldMapping} fields
|
|
50
|
+
* @returns {*}
|
|
51
|
+
*/
|
|
52
|
+
export function getValue(node, fields = defaultFields) {
|
|
53
|
+
return typeof node === 'object' && node !== null ? node[fields.value] ?? node[fields.text] : node
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get the text for the item. If the text is an object,
|
|
58
|
+
* it will use the field mapping to determine which attribute to get.
|
|
59
|
+
*
|
|
60
|
+
* @param {*} node
|
|
61
|
+
* @param {import('./types').FieldMapping} fields
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
export function getText(node, fields = defaultFields) {
|
|
65
|
+
const value = isObject(node) ? node[fields.text] : node
|
|
66
|
+
return value
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the formatted text for the item. If the text is an object, use the field mapping to determine
|
|
71
|
+
* which attribute to get currency. Use the formatter or identity function to format the text.
|
|
72
|
+
*
|
|
73
|
+
* @param {*} node
|
|
74
|
+
* @param {import('./types').FieldMapping} fields
|
|
75
|
+
* @param {Function} formatter
|
|
76
|
+
* @returns {Function}
|
|
77
|
+
*/
|
|
78
|
+
export function getFormattedText(node, fields = defaultFields, formatter = toString) {
|
|
79
|
+
const value = isObject(node) ? node[fields.text] : node
|
|
80
|
+
const currency = getAttribute(node, fields.currency)
|
|
81
|
+
const formatValue = typeof formatter === 'function' ? formatter : toString
|
|
82
|
+
|
|
83
|
+
return currency ? formatValue(value, currency) : formatValue(value)
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Gets the attribute from the node
|
|
87
|
+
* @param {*} node
|
|
88
|
+
* @param {string} attr
|
|
89
|
+
* @returns {*}
|
|
90
|
+
*/
|
|
91
|
+
export function getAttribute(node, attr) {
|
|
92
|
+
return typeof node === 'object' && node !== null && attr !== null ? node[attr] : null
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if the current item is a parent
|
|
96
|
+
*
|
|
97
|
+
* @param {*} item
|
|
98
|
+
* @param {import('./types').FieldMapping} fields
|
|
99
|
+
* @returns {boolean}
|
|
100
|
+
*/
|
|
101
|
+
export function hasChildren(item, fields) {
|
|
102
|
+
return (
|
|
103
|
+
item !== null &&
|
|
104
|
+
typeof item === 'object' &&
|
|
105
|
+
fields.children in item &&
|
|
106
|
+
Array.isArray(item[fields.children])
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Check if the current item is a parent and is expanded
|
|
112
|
+
*
|
|
113
|
+
* @param {*} item
|
|
114
|
+
* @param {import('./types').FieldMapping} fields
|
|
115
|
+
* @returns {boolean}
|
|
116
|
+
*/
|
|
117
|
+
export function isExpanded(item, fields) {
|
|
118
|
+
if (item === null) return false
|
|
119
|
+
if (!hasChildren(item, fields)) return false
|
|
120
|
+
if (fields.isOpen in item) {
|
|
121
|
+
return item[fields.isOpen]
|
|
122
|
+
}
|
|
123
|
+
return false
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Verify if at least one item has children
|
|
128
|
+
*
|
|
129
|
+
* @param {Array<*>} items
|
|
130
|
+
* @param {import('./types').FieldMapping} fields
|
|
131
|
+
* @returns {boolean}
|
|
132
|
+
*/
|
|
133
|
+
export function isNested(items, fields) {
|
|
134
|
+
for (let i = 0; i < items.length; i++) {
|
|
135
|
+
if (hasChildren(items[i], fields)) return true
|
|
136
|
+
}
|
|
137
|
+
return false
|
|
138
|
+
}
|
package/src/nested.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { omit } from 'ramda'
|
|
2
|
+
import { defaultFields } from './constants'
|
|
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
|
+
*/
|
|
12
|
+
export function flattenNestedList(items, fields = defaultFields, level = 0) {
|
|
13
|
+
fields = { ...defaultFields, ...fields }
|
|
14
|
+
let data = []
|
|
15
|
+
items.forEach((item) => {
|
|
16
|
+
const children = item[fields.children] ?? []
|
|
17
|
+
data = [
|
|
18
|
+
...data,
|
|
19
|
+
{
|
|
20
|
+
...omit([fields.children], item),
|
|
21
|
+
[fields.level]: level,
|
|
22
|
+
[fields.parent]: children.length > 0
|
|
23
|
+
},
|
|
24
|
+
...flattenNestedList(children, fields, level + 1)
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
return data
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Matches a path slug to a value in the menu
|
|
32
|
+
*
|
|
33
|
+
* @param {string} slug
|
|
34
|
+
* @param {Array} data
|
|
35
|
+
* @param {import('./types').FieldMapping} fields
|
|
36
|
+
* @returns {any}
|
|
37
|
+
*/
|
|
38
|
+
export function findValueFromPath(slug, data, fields) {
|
|
39
|
+
fields = { ...defaultFields, ...fields }
|
|
40
|
+
const keys = slug.split('/')
|
|
41
|
+
let items = data
|
|
42
|
+
let value = null
|
|
43
|
+
|
|
44
|
+
keys.forEach((key, index) => {
|
|
45
|
+
const match = items.find((item) => item[fields.key] === key)
|
|
46
|
+
if (match) {
|
|
47
|
+
if (index < keys.length - 1) {
|
|
48
|
+
match[fields.isOpen] = true
|
|
49
|
+
items = match[fields.children]
|
|
50
|
+
} else {
|
|
51
|
+
value = match
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
return value
|
|
56
|
+
}
|
package/src/string.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { filter } from 'ramda'
|
|
2
|
+
/**
|
|
3
|
+
* Capitalizes the first letter of input string
|
|
4
|
+
*
|
|
5
|
+
* @param {String} str
|
|
6
|
+
* @returns {String}
|
|
7
|
+
*/
|
|
8
|
+
export function toInitCapCase(text) {
|
|
9
|
+
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Convert a hyphen separated string to PascalCase
|
|
14
|
+
*
|
|
15
|
+
* @param {String} text
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
export function toPascalCase(text) {
|
|
19
|
+
return text
|
|
20
|
+
.split('-')
|
|
21
|
+
.map((part) => toInitCapCase(part))
|
|
22
|
+
.join('')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Convert a PascalCase string to snake case with separator as hyphen
|
|
27
|
+
*
|
|
28
|
+
* @param {string} text
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
export function toHyphenCase(text) {
|
|
32
|
+
return text
|
|
33
|
+
.replace(/\s+/, '-')
|
|
34
|
+
.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
|
|
35
|
+
.replace(/^-/, '')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sort by splitting hyphen separated strings while keeping strings with same number of parts together
|
|
40
|
+
*
|
|
41
|
+
* @param {String} a hyphen separates string
|
|
42
|
+
* @param {String} b hyphen separates string
|
|
43
|
+
* @param {string} separator - separator to split the string
|
|
44
|
+
* @returns {Number} -1, 0, 1 based on comparison
|
|
45
|
+
*/
|
|
46
|
+
export function sortByParts(a, b, separator = '-') {
|
|
47
|
+
const partsOfA = a.split(separator)
|
|
48
|
+
const partsOfB = b.split(separator)
|
|
49
|
+
|
|
50
|
+
let result = compareStrings(partsOfA[0], partsOfB[0])
|
|
51
|
+
if (result === 0) result = partsOfA.length - partsOfB.length
|
|
52
|
+
if (result === 0) result = compareStrings(a, b)
|
|
53
|
+
return result
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Simple comparison for two strings
|
|
58
|
+
*
|
|
59
|
+
* @param {String} a
|
|
60
|
+
* @param {String} b
|
|
61
|
+
* @returns
|
|
62
|
+
*/
|
|
63
|
+
export function compareStrings(a, b) {
|
|
64
|
+
return a > b ? 1 : a < b ? -1 : 0
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Generates a unique id from current timestamp
|
|
69
|
+
*
|
|
70
|
+
* @returns {String} timestamp based unique id
|
|
71
|
+
*/
|
|
72
|
+
export function uniqueId(prefix = '', separator = '-') {
|
|
73
|
+
const pair = prefix && prefix.length > 0 ? [prefix] : []
|
|
74
|
+
pair.push(Date.now().toString(36))
|
|
75
|
+
return pair.join(separator)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Removes undefined and null values from the input object.
|
|
80
|
+
*
|
|
81
|
+
* @param {Object} obj
|
|
82
|
+
* @returns {Object}
|
|
83
|
+
*/
|
|
84
|
+
export function compact(obj) {
|
|
85
|
+
return filter((x) => x !== undefined && x !== null, obj)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Converts an input number into it's hexadecimal representation, with optional left padded zeroes based on the `size`
|
|
90
|
+
*
|
|
91
|
+
* @param {number} value
|
|
92
|
+
* @param {number} size
|
|
93
|
+
* @returns
|
|
94
|
+
*/
|
|
95
|
+
export function toHexString(value, size = 2) {
|
|
96
|
+
return value.toString(16).padStart(size, '0')
|
|
97
|
+
}
|