@rokkit/core 1.0.0-next.39 → 1.0.0-next.41
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 +5 -6
- package/src/index.js +1 -0
- package/src/mapped-list.js +237 -0
- package/src/mapping.js +25 -0
- package/src/ticks.js +20 -14
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/core",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.41",
|
|
4
4
|
"description": "Core components, actions and stores for svelte apps.",
|
|
5
5
|
"author": "Jerry Thomas <me@jerrythomas.name>",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"main": "index.js",
|
|
7
|
+
"main": "src/index.js",
|
|
8
8
|
"svelte": "src/index.js",
|
|
9
9
|
"module": "src/index.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
@@ -23,13 +23,12 @@
|
|
|
23
23
|
"validators": "latest",
|
|
24
24
|
"vite": "^4.4.7",
|
|
25
25
|
"vitest": "~0.33.0",
|
|
26
|
-
"shared-config": "1.0.0-next.
|
|
26
|
+
"shared-config": "1.0.0-next.41"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"src/**/*.js",
|
|
30
30
|
"src/**/*.svelte",
|
|
31
|
-
"!
|
|
32
|
-
"!src/**/*.spec.js"
|
|
31
|
+
"!spec"
|
|
33
32
|
],
|
|
34
33
|
"exports": {
|
|
35
34
|
"./src": "./src",
|
|
@@ -53,6 +52,6 @@
|
|
|
53
52
|
"test": "vitest",
|
|
54
53
|
"coverage": "vitest run --coverage",
|
|
55
54
|
"latest": "pnpm upgrade --latest && pnpm test:ci",
|
|
56
|
-
"release": "
|
|
55
|
+
"release": "pnpm publish --access public"
|
|
57
56
|
}
|
|
58
57
|
}
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { isExpanded, hasChildren, getAttribute } from './mapping'
|
|
2
|
+
import { equals } from 'ramda'
|
|
3
|
+
/**
|
|
4
|
+
* Traverses the tree to find an item by value.
|
|
5
|
+
* @param {Array} items - The items array.
|
|
6
|
+
* @param {Object} fields - The fields mapping.
|
|
7
|
+
* @param {any} value - The value to find.
|
|
8
|
+
* @param {Array} position - The current position in the tree.
|
|
9
|
+
* @returns {Object} The found item, or null if not found.
|
|
10
|
+
*/
|
|
11
|
+
export function findItemByValue(
|
|
12
|
+
value,
|
|
13
|
+
items,
|
|
14
|
+
fields,
|
|
15
|
+
attr = null,
|
|
16
|
+
position = []
|
|
17
|
+
) {
|
|
18
|
+
for (let i = 0; i < items.length; i++) {
|
|
19
|
+
const item = items[i]
|
|
20
|
+
|
|
21
|
+
if (attr) {
|
|
22
|
+
if (getAttribute(item, attr) === value) {
|
|
23
|
+
return { item, position: position.concat(i), fields }
|
|
24
|
+
}
|
|
25
|
+
} else if (equals(item, value)) {
|
|
26
|
+
return { item, position: position.concat(i), fields }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// If the item has children, recurse into them.
|
|
30
|
+
if (hasChildren(item, fields)) {
|
|
31
|
+
const found = findItemByValue(
|
|
32
|
+
value,
|
|
33
|
+
item[fields.children],
|
|
34
|
+
fields.fields ?? fields,
|
|
35
|
+
attr,
|
|
36
|
+
position.concat(i)
|
|
37
|
+
)
|
|
38
|
+
if (found) return found
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// If the item was not found, return null.
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets an item from an items array using an index array.
|
|
48
|
+
* @param {Array} indices - The index array.
|
|
49
|
+
* @param {Array} items - The items array.
|
|
50
|
+
* @param {Object} fields - The fields configuration.
|
|
51
|
+
* @returns {Object} The item.
|
|
52
|
+
*/
|
|
53
|
+
export function findItemByIndexArray(indices, items, fields) {
|
|
54
|
+
let item = items[indices[0]]
|
|
55
|
+
let levelFields = fields
|
|
56
|
+
for (let level = 1; level < indices.length; level++) {
|
|
57
|
+
if (hasChildren(item, levelFields)) {
|
|
58
|
+
item = item[levelFields.children][indices[level]]
|
|
59
|
+
levelFields = levelFields.fields ?? levelFields
|
|
60
|
+
} else {
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { item, position: indices, fields: levelFields }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
*
|
|
69
|
+
* @param {Array<integer>} position
|
|
70
|
+
* @param {Array<*>} items
|
|
71
|
+
* @param {import('@rokkit/core').FieldMapping} fields
|
|
72
|
+
* @returns
|
|
73
|
+
*/
|
|
74
|
+
export function findNearestItemBefore(position, items, fields) {
|
|
75
|
+
if (position.length == 0) return null
|
|
76
|
+
if (items.length === 0) return null
|
|
77
|
+
if ((position ?? []).length === 0)
|
|
78
|
+
return { item: items[0], position: [0], fields }
|
|
79
|
+
|
|
80
|
+
let index = position[position.length - 1]
|
|
81
|
+
if (index > 0) {
|
|
82
|
+
index -= 1
|
|
83
|
+
if (position.length == 1) {
|
|
84
|
+
return findLastVisibleChild(items[index], [index], fields)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sibling = findItemByIndexArray(
|
|
88
|
+
[...position.slice(0, -1), index],
|
|
89
|
+
items,
|
|
90
|
+
fields
|
|
91
|
+
)
|
|
92
|
+
return findLastVisibleChild(sibling.item, sibling.position, sibling.fields)
|
|
93
|
+
} else {
|
|
94
|
+
return findItemByIndexArray(position.slice(0, -1), items, fields)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
*
|
|
100
|
+
* @param {*} parent
|
|
101
|
+
* @param {Array<integer>} position
|
|
102
|
+
* @param {import('@rokkit/core').FieldMapping} fields
|
|
103
|
+
* @returns
|
|
104
|
+
*/
|
|
105
|
+
export function findLastVisibleChild(parent, position, fields) {
|
|
106
|
+
if (isExpanded(parent, fields)) {
|
|
107
|
+
const children = parent[fields.children]
|
|
108
|
+
return findLastVisibleChild(
|
|
109
|
+
children[children.length - 1],
|
|
110
|
+
position.concat(children.length - 1),
|
|
111
|
+
fields.fields ?? fields
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
return { item: parent, position, fields }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Returns the next item in the tree.
|
|
119
|
+
* @param {Array} position - The position of the current item.
|
|
120
|
+
* @param {Array} items - The items in the tree.
|
|
121
|
+
* @param {Object} fields - The fields mapping.
|
|
122
|
+
* @returns {Object|null} The next item or null if there is none.
|
|
123
|
+
*/
|
|
124
|
+
export function findNearestItemAfter(position, items, fields) {
|
|
125
|
+
if (items.length === 0) return null
|
|
126
|
+
if ((position ?? []).length === 0)
|
|
127
|
+
return { item: items[0], position: [0], fields }
|
|
128
|
+
|
|
129
|
+
let current = findItemByIndexArray(position, items, fields)
|
|
130
|
+
|
|
131
|
+
if (isExpanded(current.item, current.fields)) {
|
|
132
|
+
return getFirstChild(current, position)
|
|
133
|
+
} else if (position.length === 1) {
|
|
134
|
+
return getNextSiblingAtRoot(position, items, fields)
|
|
135
|
+
} else {
|
|
136
|
+
return getNextSiblingOrAncestor(position, items, fields)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Returns the next child of the current item.
|
|
142
|
+
* @param {Object} current - The current item.
|
|
143
|
+
* @param {Array} position - The position of the current item.
|
|
144
|
+
* @returns {Object} The next child.
|
|
145
|
+
*/
|
|
146
|
+
function getFirstChild(current, position) {
|
|
147
|
+
return {
|
|
148
|
+
item: current.item[current.fields.children][0],
|
|
149
|
+
position: position.concat(0),
|
|
150
|
+
fields: current.fields.fields ?? current.fields
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Returns the next sibling of the current item at the root level.
|
|
156
|
+
* @param {Array} position - The position of the current item.
|
|
157
|
+
* @param {Array} items - The items in the tree.
|
|
158
|
+
* @param {Object} fields - The fields mapping.
|
|
159
|
+
* @returns {Object|null} The next sibling or null if there is none.
|
|
160
|
+
*/
|
|
161
|
+
function getNextSiblingAtRoot(position, items, fields) {
|
|
162
|
+
if (position[0] < items.length - 1) {
|
|
163
|
+
return {
|
|
164
|
+
item: items[position[0] + 1],
|
|
165
|
+
position: [position[0] + 1],
|
|
166
|
+
fields
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return null
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Returns the next sibling of the current item or the next item in the ancestor.
|
|
174
|
+
* @param {Array} position - The position of the current item.
|
|
175
|
+
* @param {Array} items - The items in the tree.
|
|
176
|
+
* @param {Object} fields - The fields mapping.
|
|
177
|
+
* @returns {Object|null} The next sibling or ancestor or null if there is none.
|
|
178
|
+
*/
|
|
179
|
+
function getNextSiblingOrAncestor(position, items, fields) {
|
|
180
|
+
let index = position[position.length - 1]
|
|
181
|
+
let parent = findItemByIndexArray(position.slice(0, -1), items, fields)
|
|
182
|
+
|
|
183
|
+
let children = parent.item[parent.fields.children]
|
|
184
|
+
if (index < children.length - 1) {
|
|
185
|
+
index += 1
|
|
186
|
+
|
|
187
|
+
const sibling = findItemByIndexArray(
|
|
188
|
+
[...position.slice(0, -1), index],
|
|
189
|
+
items,
|
|
190
|
+
fields
|
|
191
|
+
)
|
|
192
|
+
return { item: sibling.item, position: sibling.position, fields }
|
|
193
|
+
} else {
|
|
194
|
+
while (index === children.length - 1) {
|
|
195
|
+
index = position[position.length - 1]
|
|
196
|
+
position = position.slice(0, -1)
|
|
197
|
+
if (position.length === 0) return null
|
|
198
|
+
parent = findItemByIndexArray(position, items, fields)
|
|
199
|
+
children = parent.item[parent.fields.children]
|
|
200
|
+
}
|
|
201
|
+
if (index < children.length - 1) {
|
|
202
|
+
return {
|
|
203
|
+
item: children[index + 1],
|
|
204
|
+
position: [...position, index + 1],
|
|
205
|
+
fields
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Creates a mapped list from an items array and a fields mapping.
|
|
213
|
+
* @param {Array<Object>} items - The items array.
|
|
214
|
+
* @param {import('@rokkit/core').FieldMapping} fields - The fields mapping.
|
|
215
|
+
* @returns {Object} The mapped list.
|
|
216
|
+
*/
|
|
217
|
+
export function mappedList(items, fields) {
|
|
218
|
+
const findByValue = (value) => findItemByValue(value, items, fields)
|
|
219
|
+
const findByAttribute = (value, attr) =>
|
|
220
|
+
findItemByValue(value, items, fields, attr)
|
|
221
|
+
const findByIndexArray = (index) => findItemByIndexArray(index, items, fields)
|
|
222
|
+
const previous = (position) => findNearestItemBefore(position, items, fields)
|
|
223
|
+
const next = (position) => findNearestItemAfter(position, items, fields)
|
|
224
|
+
|
|
225
|
+
const update = (newItems, newFields) => {
|
|
226
|
+
items = newItems
|
|
227
|
+
fields = newFields
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
findByValue,
|
|
231
|
+
findByAttribute,
|
|
232
|
+
findByIndexArray,
|
|
233
|
+
previous,
|
|
234
|
+
next,
|
|
235
|
+
update
|
|
236
|
+
}
|
|
237
|
+
}
|
package/src/mapping.js
CHANGED
|
@@ -29,16 +29,41 @@ export function getIcon(value, fields = defaultFields) {
|
|
|
29
29
|
: value[fields.icon]
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Get the value for the item. If the value is an object,
|
|
34
|
+
* it will use the field mapping to determine which attribute to get.
|
|
35
|
+
*
|
|
36
|
+
* @param {*} node
|
|
37
|
+
* @param {import('./types').FieldMapping} fields
|
|
38
|
+
* @returns {*}
|
|
39
|
+
*/
|
|
32
40
|
export function getValue(node, fields = defaultFields) {
|
|
33
41
|
return typeof node === 'object' && node !== null
|
|
34
42
|
? node[fields.value] ?? node[fields.text]
|
|
35
43
|
: node
|
|
36
44
|
}
|
|
37
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Get the text for the item. If the text is an object,
|
|
48
|
+
* it will use the field mapping to determine which attribute to get.
|
|
49
|
+
*
|
|
50
|
+
* @param {*} node
|
|
51
|
+
* @param {import('./types').FieldMapping} fields
|
|
52
|
+
* @returns {*}
|
|
53
|
+
*/
|
|
38
54
|
export function getText(node, fields = defaultFields) {
|
|
39
55
|
return typeof node === 'object' && node !== null ? node[fields.text] : node
|
|
40
56
|
}
|
|
41
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Gets the attribute from the node
|
|
60
|
+
* @param {*} node
|
|
61
|
+
* @param {string} attr
|
|
62
|
+
* @returns {*}
|
|
63
|
+
*/
|
|
64
|
+
export function getAttribute(node, attr) {
|
|
65
|
+
return typeof node === 'object' && node !== null ? node[attr] : null
|
|
66
|
+
}
|
|
42
67
|
/**
|
|
43
68
|
* Check if the current item is a parent
|
|
44
69
|
*
|
package/src/ticks.js
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates an array of tick marks for a range of values.
|
|
3
|
+
*
|
|
4
|
+
* @param {number} lowerBound - The lower bound of the range.
|
|
5
|
+
* @param {number} upperBound - The upper bound of the range.
|
|
6
|
+
* @param {number} [minorTickStep=upperBound-lowerBound] - The step size for minor ticks.
|
|
7
|
+
* @param {number} [majorTickStep=1] - The step size for major ticks.
|
|
8
|
+
* @returns {Array<{value: number, label: string, major: boolean}>} An array of tick mark objects.
|
|
9
|
+
*/
|
|
1
10
|
export function generateTicks(
|
|
2
11
|
lowerBound,
|
|
3
12
|
upperBound,
|
|
4
|
-
minorTickStep,
|
|
5
|
-
majorTickStep = 1
|
|
6
|
-
formatter = new Intl.NumberFormat('en-US', { CompactDisplay: 'short' })
|
|
13
|
+
minorTickStep = upperBound - lowerBound,
|
|
14
|
+
majorTickStep = 1
|
|
7
15
|
) {
|
|
8
|
-
minorTickStep = minorTickStep ? minorTickStep : upperBound - lowerBound
|
|
9
16
|
const length = 1 + Math.ceil((upperBound - lowerBound) / minorTickStep)
|
|
10
|
-
|
|
11
|
-
value
|
|
12
|
-
major
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return ticks
|
|
17
|
+
return Array.from({ length }, (_, i) => {
|
|
18
|
+
const value = i == length - 1 ? upperBound : lowerBound + minorTickStep * i
|
|
19
|
+
const major = i == 0 || i == length - 1 || i % majorTickStep == 0
|
|
20
|
+
return {
|
|
21
|
+
value,
|
|
22
|
+
label: major ? value : '',
|
|
23
|
+
major
|
|
24
|
+
}
|
|
25
|
+
})
|
|
20
26
|
}
|