@rokkit/data 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 +50 -0
- package/package.json +56 -0
- package/src/aggregators.js +24 -0
- package/src/constants.js +72 -0
- package/src/convert.js +36 -0
- package/src/dataset.js +428 -0
- package/src/filter.js +57 -0
- package/src/formatter.js +56 -0
- package/src/hierarchy.js +136 -0
- package/src/index.js +7 -0
- package/src/infer.js +263 -0
- package/src/list.js +232 -0
- package/src/metadata.js +106 -0
- package/src/model.js +100 -0
- package/src/parser.js +111 -0
- package/src/renamer.js +98 -0
- package/src/rollup.js +138 -0
- package/src/types.js +173 -0
- package/src/utils.js +36 -0
- package/src/view.js +181 -0
package/src/rollup.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { omit, pick, mergeLeft, map, difference, uniq, pipe } from 'ramda'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Groups data by specified keys.
|
|
5
|
+
*
|
|
6
|
+
* @param {Array} data - An array of data points.
|
|
7
|
+
* @param {Array} groupByKeys - Keys to group the data by.
|
|
8
|
+
* @param {Array} summaries - An array of summary objects to define aggregation operations.
|
|
9
|
+
* @returns {Object} Grouped data object with aggregation placeholders.
|
|
10
|
+
*/
|
|
11
|
+
export function groupDataByKeys(data, groupByKeys, summaries) {
|
|
12
|
+
const keysExtractor = pick(groupByKeys)
|
|
13
|
+
const groupedData = data.reduce((grouped, row) => {
|
|
14
|
+
const key = JSON.stringify(keysExtractor(row))
|
|
15
|
+
if (!grouped[key]) {
|
|
16
|
+
grouped[key] = { ...keysExtractor(row), ...initialValues(summaries) }
|
|
17
|
+
}
|
|
18
|
+
addToSummaries(grouped[key], row, summaries)
|
|
19
|
+
return grouped
|
|
20
|
+
}, {})
|
|
21
|
+
|
|
22
|
+
return Object.values(groupedData)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sets initial values for summaries.
|
|
27
|
+
*
|
|
28
|
+
* @param {Array} summaries - An array of summary objects.
|
|
29
|
+
* @returns {Object} An object with keys for each summary initialized to empty arrays.
|
|
30
|
+
*/
|
|
31
|
+
export function initialValues(summaries) {
|
|
32
|
+
const fields = summaries.map(({ reducers }) => reducers.map((r) => r.field)).flat()
|
|
33
|
+
return fields.reduce((acc, name) => ({ ...acc, [name]: [] }), {})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Adds data to summary aggregation arrays.
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} group - The group object to add data to.
|
|
40
|
+
* @param {Object} row - The data row being processed.
|
|
41
|
+
* @param {Array} summaries - An array of summary objects.
|
|
42
|
+
*/
|
|
43
|
+
export function addToSummaries(group, row, summaries) {
|
|
44
|
+
summaries.forEach(({ mapper, reducers }) => {
|
|
45
|
+
const value = mapper(row)
|
|
46
|
+
reducers.forEach((r) => {
|
|
47
|
+
group[r.field].push(value)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fills grouped data with aligned data if specified in configuration.
|
|
54
|
+
*
|
|
55
|
+
* @param {Object} groupedData - The grouped data object.
|
|
56
|
+
* @param {Object} config - Configuration object containing alignment settings.
|
|
57
|
+
* @returns {Array} Aligned data array.
|
|
58
|
+
*/
|
|
59
|
+
export function fillAlignedData(groupedData, config, fillRowsFunc) {
|
|
60
|
+
// const fillRowsFunc = getAlignGenerator(df.data, config)
|
|
61
|
+
const { actual_flag, children } = config
|
|
62
|
+
return Object.values(groupedData).map((row) => ({
|
|
63
|
+
...row,
|
|
64
|
+
[children]: [
|
|
65
|
+
...row[children].map((value) => ({ ...value, [actual_flag]: 1 })),
|
|
66
|
+
...fillRowsFunc(row[children])
|
|
67
|
+
]
|
|
68
|
+
}))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Performs aggregation operations on grouped data.
|
|
73
|
+
*
|
|
74
|
+
* @param {Array} dataArray - An array of grouped data with aggregation arrays.
|
|
75
|
+
* @param {Array} summaries - An array of summary objects with reducer functions.
|
|
76
|
+
* @returns {Array} Aggregated data array with computed summary values.
|
|
77
|
+
*/
|
|
78
|
+
export function aggregateData(dataArray, summaries) {
|
|
79
|
+
const fields = summaries.map(({ name, reducers }) => reducers.map((r) => ({ name, ...r }))).flat()
|
|
80
|
+
return dataArray.map((row) => ({
|
|
81
|
+
...row,
|
|
82
|
+
...fields.reduce(
|
|
83
|
+
(acc, { field, formula }) => ({
|
|
84
|
+
...acc,
|
|
85
|
+
[field]: formula(row[field])
|
|
86
|
+
}),
|
|
87
|
+
{}
|
|
88
|
+
)
|
|
89
|
+
}))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a generator that produces missing rows in a dataset based on specified columns.
|
|
94
|
+
* The function determines all unique combinations of the specified columns in config.align_by
|
|
95
|
+
* and returns a generator function to create the missing combinations, potentially with default
|
|
96
|
+
* values for other columns.
|
|
97
|
+
* @param {Array<Object>} data - The array of objects representing the dataset.
|
|
98
|
+
* @param {Object} config - The configuration object.
|
|
99
|
+
*
|
|
100
|
+
* @returns {Function} A generator function that when called, produces the missing rows.
|
|
101
|
+
*/
|
|
102
|
+
export function getAlignGenerator(data, config) {
|
|
103
|
+
const { align_by, group_by, actual_flag } = config
|
|
104
|
+
const template = { ...omit([...align_by, ...group_by], config.template), [actual_flag]: 0 }
|
|
105
|
+
const subset = pipe(map(pick(align_by)), uniq)(data)
|
|
106
|
+
|
|
107
|
+
return pipe(map(pick(align_by)), uniq, difference(subset), map(mergeLeft(template)))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Returns an aggregator object with a mapper and reducer function.
|
|
112
|
+
*
|
|
113
|
+
* @param {Array<string>} from - The key or keys to aggregate.
|
|
114
|
+
* @param {Function} using - The aggregation function.
|
|
115
|
+
* @returns {import('./types').SummaryConfig} - An object with a mapper and reducer function.
|
|
116
|
+
*/
|
|
117
|
+
// export function getAggregator(from, into, using = identity) {
|
|
118
|
+
// // const mapper = pick(Array.isArray ? from : [from])
|
|
119
|
+
// return {
|
|
120
|
+
// mapper: pick(from),
|
|
121
|
+
// reducers: [{ field: into, formula: using }]
|
|
122
|
+
// }
|
|
123
|
+
// }
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Returns the default aggregator which rolls up the columns other than the group_by columns into a single object.
|
|
127
|
+
*
|
|
128
|
+
* @param {import('./types').Metadata} metadata - The metadata for the columns to be aggregated.
|
|
129
|
+
* @param {Object} config - The configuration used to build the aggregator.
|
|
130
|
+
*
|
|
131
|
+
* @returns {Object} An object containing the default aggregator for the specified metadata and configuration.
|
|
132
|
+
*/
|
|
133
|
+
// export function defaultAggregator(metadata, config) {
|
|
134
|
+
// const child = metadata.filter((col) => !config.group_by.includes(col.name))
|
|
135
|
+
// const keys = child.map((col) => col.name)
|
|
136
|
+
|
|
137
|
+
// return { ...getAggregator(keys, config.children), metadata: child }
|
|
138
|
+
// }
|
package/src/types.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('@rokkit/core').FieldMapping} FieldMapping
|
|
3
|
+
* @typedef {import('@rokkit/core').SortOptions} SortOptions
|
|
4
|
+
* @typedef {import('@rokkit/core').HorizontalAlignOptions} HorizontalAlignOptions
|
|
5
|
+
* @typedef {import('@rokkit/core').ActionTypes} ActionTypes
|
|
6
|
+
* @typedef {import('@rokkit/core').SelectionState} SelectionState
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} ColumnSorter
|
|
11
|
+
* @property {string} name
|
|
12
|
+
* @property {function} sorter
|
|
13
|
+
*
|
|
14
|
+
* @typedef {string|[string,boolean]|ColumnSorter} SortableColumn
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} ColumnAggregator
|
|
19
|
+
* @property {string} name
|
|
20
|
+
* @property {function} aggregator
|
|
21
|
+
* @property {string} suffix
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Options for joining two data sets
|
|
26
|
+
*
|
|
27
|
+
* @typedef OptionsToRenameKeys
|
|
28
|
+
* @property {string} [prefix] - Prefix to be added to each attribute
|
|
29
|
+
* @property {string} [suffix] - Suffix to be added to each attribute
|
|
30
|
+
* @property {string} [separator='_'] - Separator to be used when adding prefix or suffix. defaults to _
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Column metadata for the table.
|
|
35
|
+
*
|
|
36
|
+
* @typedef {Object} ColumnMetadata
|
|
37
|
+
*
|
|
38
|
+
* @property {string} name - The name of the column.
|
|
39
|
+
* @property {string} type - The data type of the column (e.g., "string", "number", "date").
|
|
40
|
+
* @property {FieldMapping} [field] - The field mapping for the column.
|
|
41
|
+
* @property {number} [digits=0] - The number of digits for numeric values (defaults to 0).
|
|
42
|
+
* @property {Function} formatter - A function to format the column value.
|
|
43
|
+
* @property {boolean} [sortable] - Indicates if the column is sortable (true/false).
|
|
44
|
+
* @property {SortOptions} [sortOrder] - The sort order of the column.
|
|
45
|
+
* @property {HorizontalAlignOptions} [align] - The alignment of the column content.
|
|
46
|
+
* @property {ActionTypes} [action] - Action attribute for action columns.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {Array<ColumnMetadata>} Metadata
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {Object<string,number} ColumnIndexMap
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Track the state of a row in the table.
|
|
59
|
+
*
|
|
60
|
+
* @typedef {Object} TreeTableNode
|
|
61
|
+
* @property {number} row - Reference to actual row in the data.
|
|
62
|
+
* @property {number} depth - The depth of the node in the hierarchy.
|
|
63
|
+
* @property {string} [value] - The value of the hierarchy node.
|
|
64
|
+
* @property {boolean} [isHidden] - Indicates whether the node is visible (true/false).
|
|
65
|
+
* @property {boolean} [isParent] - Indicates if this node is a parent (true/false).
|
|
66
|
+
* @property {boolean} [isExpanded] - Indicates whether the node is expanded (true/false).
|
|
67
|
+
* @property {number} [parent] - Reference to the parent node in the flat list.
|
|
68
|
+
* @property {SelectionState} [selected] - Indicates whether the node is selected (true/false/indeterminate).
|
|
69
|
+
* @property {Array<any>} children - Reference to the children nodes in the flat list.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Track the state of all rows in the table.
|
|
74
|
+
*
|
|
75
|
+
* @typedef {Object} TreeTable
|
|
76
|
+
* @property {TreeTableNode[]} rows - Flat list of hierarchy nodes.
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @typedef {Object} OverrideConfig
|
|
81
|
+
* @property {string} actual_flag - the field used for identifying filler rows in the data
|
|
82
|
+
* @property {string} children - the field used for collecting children of a node in rollup
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @typedef {Object} ReducerConfig
|
|
87
|
+
* @property {string} field - The target field to hold the reduced value
|
|
88
|
+
* @property {Function} using - The function to use for reducing the data
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @typedef {Object} SummaryConfig
|
|
93
|
+
* @property {Function} mapper - A function used to collect the data for the summary
|
|
94
|
+
* @property {Array<ReducerConfig} reducers - Array of reducer configurations
|
|
95
|
+
* @property {Metadata} [metadata] - Array of column metadata for the summary
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @typedef {Object} DataFrameConfig
|
|
100
|
+
* @property {Array<string>} group_by=[] - The columns to group by.
|
|
101
|
+
* @property {Array<string>} align_by=[] - The columns to be aligned during rollup.
|
|
102
|
+
* @property {Array<SummaryConfig>} summaries=[] - Array of summary configurations.
|
|
103
|
+
* @property {any} template={} - Template row to be used for filling in missing rows.
|
|
104
|
+
* @extends OverrideConfig
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @typedef {Function} SortBy
|
|
109
|
+
* @description Sort the DataFrame by the specified columns.
|
|
110
|
+
* @param {SortableColumn} columns - The columns to sort by.
|
|
111
|
+
* @returns {DataFrame} - The sorted DataFrame.
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* DataFrame-like object with data manipulation methods.
|
|
116
|
+
*
|
|
117
|
+
* @typedef {Object} DataFrame
|
|
118
|
+
* @property {Array<Object>} data - Array of objects representing the rows of data.
|
|
119
|
+
* @property {Metadata} metadata - Array of column metadata for the data.
|
|
120
|
+
* @property {DataFrameConfig} config - Configuration options for the DataFrame.
|
|
121
|
+
* @property {ColumnIndexMap} columns - A map of column names to their index in the metadata.
|
|
122
|
+
* @property {function(OverrideConfig):DataFrame} override - Method to override the configuration of the DataFrame.
|
|
123
|
+
* @property {function(...string):DataFrame} groupBy - Method to group the DataFrame by specified columns.
|
|
124
|
+
* @property {function(function(any)):DataFrame} where - Sets the filter function for the DataFrame
|
|
125
|
+
* @property {function():DataFrame} align - Method to align the DataFrame.
|
|
126
|
+
* @property {function():DataFrame} using - set the template to be used for filling in missing rows.
|
|
127
|
+
* @property {function():DataFrame} summarize - add a summarizer to be used during rollup
|
|
128
|
+
* @property {function(...SortableColumn):DataFrame} sortBy - Method to sort the DataFrame by specified columns.
|
|
129
|
+
* @property {function():DataFrame} join - perform join with another DataFrame returning a new DataFrmae. Defaults to innerJoin, other join types can be specified.
|
|
130
|
+
* @property {function():DataFrame} innerJoin - perform an ineer join with another DataFrame and return a new DataFrame.
|
|
131
|
+
* @property {function():DataFrame} leftJoin - perform a left join with another DataFrame and return a new DataFrame.
|
|
132
|
+
* @property {function():DataFrame} rightJoin - perform a right join with another DataFrame returning a new DataFrame.
|
|
133
|
+
* @property {function():DataFrame} fullJoin - perform a full join with another DataFrame returning a new DataFrame.
|
|
134
|
+
* @property {function():DataFrame} nestedJoin - perform a nested join with another DataFrame returning a new DataFrame.
|
|
135
|
+
* @property {function():DataFrame} rename - Method to rename the columns of the DataFrame.
|
|
136
|
+
* @property {function():DataFrame} drop - Method to drop the columns of the DataFrame.
|
|
137
|
+
* @property {function():DataFrame} delete - Method to delete the rows of the DataFrame.
|
|
138
|
+
* @property {function():DataFrame} update - Method to update the rows of the DataFrame.
|
|
139
|
+
* @property {function():DataFrame} union - Method to combine the rows of the DataFrame with another DataFrame.
|
|
140
|
+
* @property {function():DataFrame} minus - Method to remove the rows of the DataFrame which are present in another DataFrame.
|
|
141
|
+
* @property {function():DataFrame} intersect - Method to keep the rows of the DataFrame which are present in another DataFrame.
|
|
142
|
+
* @property {function():DataFrame} rollup - Method to rollup the DataFrame.
|
|
143
|
+
* @property {function():Arrat<Object>} select - Method to select the columns of the DataFrame.
|
|
144
|
+
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @typedef {inner|outer|full|nested} JoinType
|
|
149
|
+
*/
|
|
150
|
+
/**
|
|
151
|
+
* Options for joining two data sets
|
|
152
|
+
*
|
|
153
|
+
* @typedef JoinOptions
|
|
154
|
+
* @property {boolean} [inner] - Flag indicating inner join
|
|
155
|
+
* @property {JoinType} [type='inner] - The join type to use (inner, outer, full, nested).
|
|
156
|
+
* @property {string} [prefix] - prefix to be used for renaming keys in the second data set.
|
|
157
|
+
* @property {string} [suffix] - suffix to be used for renaming keys in the second data set.
|
|
158
|
+
* @property {string} [separator='_'] - separator to be used for renaming keys in the second data set.
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* DataView
|
|
163
|
+
* @typedef {Object} DataView
|
|
164
|
+
* @property {TreeTable} hierarchy - The state of the rows in the table.
|
|
165
|
+
* @property {Metadata} columns - The metadata for the columns in the table.
|
|
166
|
+
* @property {Function} sortBy - Sort the DataView by the specified columns.
|
|
167
|
+
* @property {Function} clearSort - Clear the sorting of the DataView.
|
|
168
|
+
* @property {Function} filter - Filter the DataView by the specified columns.
|
|
169
|
+
* @property {Function} toggle - toggle expand or collapse the DataView by the specified columns.
|
|
170
|
+
* @property {Function} select - select the DataView by the specified columns.
|
|
171
|
+
*/
|
|
172
|
+
|
|
173
|
+
export default {}
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a value is a date string
|
|
3
|
+
* @param {string} value
|
|
4
|
+
* @returns {boolean}
|
|
5
|
+
*/
|
|
6
|
+
function isDateString(value) {
|
|
7
|
+
return !isNaN(Date.parse(value))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function deriveTypeFromEmptyValue(value) {
|
|
11
|
+
if (value === undefined || value === null) return 'string'
|
|
12
|
+
return typeof value
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Infer the type of a value as array, date or the default type
|
|
16
|
+
* @param {any} value
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
function deriveArrayAndDateTypes(value) {
|
|
20
|
+
if (Array.isArray(value)) return 'array'
|
|
21
|
+
if (value instanceof Date) return 'date'
|
|
22
|
+
|
|
23
|
+
return deriveTypeFromEmptyValue(value)
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Derive the type of a value
|
|
27
|
+
* @param {any} value
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
export function typeOf(value) {
|
|
31
|
+
const type = deriveArrayAndDateTypes(value)
|
|
32
|
+
if (type === 'string' && isDateString(value)) return 'date'
|
|
33
|
+
if (type === 'number' && Number.isInteger(value)) return 'integer'
|
|
34
|
+
|
|
35
|
+
return type
|
|
36
|
+
}
|
package/src/view.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { omit } from 'ramda'
|
|
2
|
+
import { get, writable } from 'svelte/store'
|
|
3
|
+
import { deriveMetadata, deriveSortableColumn } from './infer'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
flattenNestedChildren,
|
|
7
|
+
removeChildren,
|
|
8
|
+
deriveHierarchy,
|
|
9
|
+
toggleExpansion
|
|
10
|
+
} from './hierarchy'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sorts a nested array of objects based on a group of sorters.
|
|
14
|
+
*
|
|
15
|
+
* @param {Array<Object>} elements - The array of objects to sort.
|
|
16
|
+
*/
|
|
17
|
+
function sortNested(elements, group) {
|
|
18
|
+
elements
|
|
19
|
+
.sort((a, b) => {
|
|
20
|
+
let result = 0
|
|
21
|
+
for (let i = 0; i < group.length && result === 0; i++) {
|
|
22
|
+
result = group[i].sorter(a, b)
|
|
23
|
+
}
|
|
24
|
+
return result
|
|
25
|
+
})
|
|
26
|
+
.forEach((x) => {
|
|
27
|
+
if (Array.isArray(x.children) && x.children.length > 0) {
|
|
28
|
+
sortNested(x.children, group)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Sorts a hierarchy based on a group of sorters.
|
|
35
|
+
*
|
|
36
|
+
* @param {Array<Object>} hierarchy - The hierarchy to sort.
|
|
37
|
+
* @param {Array<Array<string, boolean>>} sortGroup - The group of sorters to apply.
|
|
38
|
+
*/
|
|
39
|
+
export function groupSort(hierarchy, sortGroup) {
|
|
40
|
+
const group = sortGroup.map(deriveSortableColumn).map(({ name, sorter }) => ({
|
|
41
|
+
name,
|
|
42
|
+
sorter: (a, b) => sorter(a.row[name], b.row[name])
|
|
43
|
+
}))
|
|
44
|
+
|
|
45
|
+
removeChildren(hierarchy)
|
|
46
|
+
sortNested(hierarchy, group)
|
|
47
|
+
flattenNestedChildren(hierarchy)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sorts the hierarchy based on a specific field name and order.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} name - The field name to sort by.
|
|
54
|
+
* @param {boolean} [ascending=true] - Whether the sort should be in ascending order.
|
|
55
|
+
*/
|
|
56
|
+
const sortHierarchyBy = (store, sortGroup, name, ascending = true) => {
|
|
57
|
+
sortGroup = [...sortGroup, [name, ascending]]
|
|
58
|
+
// change the sort order in the metadata
|
|
59
|
+
const { columns, hierarchy } = get(store)
|
|
60
|
+
columns.forEach((column) => {
|
|
61
|
+
if (column.name === name) {
|
|
62
|
+
column.order = ascending ? 'ascending' : 'descending'
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
groupSort(hierarchy, sortGroup)
|
|
66
|
+
store.set({ columns, hierarchy })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Updates the selection state for all children of a node in the hierarchy.
|
|
71
|
+
*
|
|
72
|
+
* @param {Array<import('./types').Hierarchy>} hierarchy - The hierarchy to update.
|
|
73
|
+
* @param {number} index - The index of the node to update.
|
|
74
|
+
*/
|
|
75
|
+
function updateChildren(item) {
|
|
76
|
+
if (!item.children) return
|
|
77
|
+
|
|
78
|
+
item.children.forEach((child) => {
|
|
79
|
+
child.selected = item.selected
|
|
80
|
+
updateChildren(child)
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Determines the selected state of a group of children in the hierarchy.
|
|
86
|
+
* If all children are checked, returns 'checked'.
|
|
87
|
+
* If all children are unchecked, returns 'unchecked'.
|
|
88
|
+
* Otherwise, returns 'indeterminate'.
|
|
89
|
+
*
|
|
90
|
+
* @param {Array<Object>} children - The array of child objects with a 'selected' property.
|
|
91
|
+
* @returns {string} The determined selected state: 'checked', 'unchecked', or 'indeterminate'.
|
|
92
|
+
*/
|
|
93
|
+
export function determineSelectedState(children) {
|
|
94
|
+
const allChecked = children.every((child) => child.selected === 'checked')
|
|
95
|
+
const allUnchecked =
|
|
96
|
+
!allChecked && children.every((child) => !child.selected || child.selected === 'unchecked')
|
|
97
|
+
|
|
98
|
+
return allChecked ? 'checked' : allUnchecked ? 'unchecked' : 'indeterminate'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Updates the selection state of parent nodes in the hierarchy based on their children's states.
|
|
103
|
+
* It traverses up from the provided `index` position in the hierarchy array, ensuring each
|
|
104
|
+
* parent's selected state reflects whether all, none, or some of its children are selected.
|
|
105
|
+
*
|
|
106
|
+
* @param {Array<Object>} hierarchy - The hierarchy structure containing nodes with
|
|
107
|
+
* 'parent' and 'children' references.
|
|
108
|
+
* @param {number} index - The index of the node in the hierarchy array from where
|
|
109
|
+
* to start updating parent nodes.
|
|
110
|
+
*/
|
|
111
|
+
function updateParents(item) {
|
|
112
|
+
let parent = item.parent
|
|
113
|
+
|
|
114
|
+
while (parent) {
|
|
115
|
+
parent.selected = determineSelectedState(parent.children)
|
|
116
|
+
parent = parent.parent
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Toggles the selection state of a node in the hierarchy.
|
|
122
|
+
* Depending on whether the node to toggle is a parent or child,
|
|
123
|
+
* it will either select or deselect all children, or update the parent's selection state based on its children's states.
|
|
124
|
+
*
|
|
125
|
+
* @param {import('./types').Hierarchy} item - The node to toggle.
|
|
126
|
+
*
|
|
127
|
+
*/
|
|
128
|
+
export function toggleSelection(item) {
|
|
129
|
+
item.selected = item.selected === 'checked' ? 'unchecked' : 'checked'
|
|
130
|
+
|
|
131
|
+
updateParents(item)
|
|
132
|
+
updateChildren(item)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Toggles the expansion or selection state of a data element at the specified index.
|
|
137
|
+
*
|
|
138
|
+
* @param {import('svelte/store').Writable} store - The store object representing the view.
|
|
139
|
+
* @param {number} index - The index of the element in the hierarchy to expand or collapse.
|
|
140
|
+
* @param {string} mode - The mode to toggle, either 'expansion' or 'selection'.
|
|
141
|
+
*/
|
|
142
|
+
function toggleState(store, index, mode) {
|
|
143
|
+
const { hierarchy } = get(store)
|
|
144
|
+
if (mode === 'selection') toggleSelection(hierarchy[index])
|
|
145
|
+
else if (mode === 'expansion') toggleExpansion(hierarchy[index])
|
|
146
|
+
store.update((state) => ({ ...state, hierarchy }))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Creates a view object for managing data presentation with metadata and hierarchy structures,
|
|
151
|
+
* as well as providing methods for sorting, selection, and toggling of data expansion.
|
|
152
|
+
*
|
|
153
|
+
* @param {Array<Object>} data - The source data array to be presented by the view.
|
|
154
|
+
* @param {Object} options - The configuration options used for deriving metadata and hierarchy from the data.
|
|
155
|
+
* @returns {import('svelte/store').Writable} An object representing the view, providing methods to manipulate and query the presentation state.
|
|
156
|
+
*/
|
|
157
|
+
export function dataview(data, options) {
|
|
158
|
+
const store = writable({
|
|
159
|
+
columns: deriveMetadata(data, options),
|
|
160
|
+
hierarchy: deriveHierarchy(data, options)
|
|
161
|
+
})
|
|
162
|
+
let sortGroup = []
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
...omit(['set', 'update'], store),
|
|
166
|
+
// filter: noop,
|
|
167
|
+
/**
|
|
168
|
+
* Clears the applied sort order from the hierarchy.
|
|
169
|
+
*/
|
|
170
|
+
clearSort: () => {
|
|
171
|
+
sortGroup = []
|
|
172
|
+
store.update((state) => ({
|
|
173
|
+
...state,
|
|
174
|
+
columns: state.columns.map((column) => ({ ...column, order: 'none' }))
|
|
175
|
+
}))
|
|
176
|
+
},
|
|
177
|
+
sortBy: (name, ascending) => sortHierarchyBy(store, sortGroup, name, ascending),
|
|
178
|
+
select: (index) => toggleState(store, index, 'selection'),
|
|
179
|
+
toggle: (index) => toggleState(store, index, 'expansion')
|
|
180
|
+
}
|
|
181
|
+
}
|