@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 CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@rokkit/core",
3
- "version": "1.0.0-next.39",
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.39"
26
+ "shared-config": "1.0.0-next.41"
27
27
  },
28
28
  "files": [
29
29
  "src/**/*.js",
30
30
  "src/**/*.svelte",
31
- "!src/mocks",
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": "tsc && pnpm publish --access public"
55
+ "release": "pnpm publish --access public"
57
56
  }
58
57
  }
package/src/index.js CHANGED
@@ -2,6 +2,7 @@ import './types'
2
2
  export * from './constants'
3
3
  export * from './nested'
4
4
  export * from './mapping'
5
+ export * from './mapped-list'
5
6
  export * from './connector'
6
7
  export * from './ticks'
7
8
  export * from './calendar'
@@ -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
- const ticks = Array.from({ length }, (_, i) => ({
11
- value: i == length - 1 ? upperBound : lowerBound + minorTickStep * i,
12
- major: i == 0 || i == length - 1 || i % majorTickStep == 0
13
- })).map(({ value, major }) => ({
14
- value,
15
- label: major ? formatter.format(value) : '',
16
- major
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
  }