@rokkit/core 1.0.0-next.105 → 1.0.0-next.107

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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Get the days in the month.
3
+ *
4
+ * @param {Date} value
5
+ * @param {Array} holidays
6
+ * @param {boolean} fixed
7
+ * @returns {import('./types').CalendarDay[]}
8
+ */
9
+ export function getCalendarDays(value: Date, holidays?: any[], fixed?: boolean): import("./types").CalendarDay[];
10
+ export const weekdays: string[];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Constructs an array of line types for tree visualization
3
+ * @param {boolean} hasChildren - Whether the node has children
4
+ * @param {import('./types').LineType[]} parentTypes - Types from parent nodes
5
+ * @param {import('./types').LineType} position - Current position type
6
+ * @returns {import('./types').LineType[]} Array of line types
7
+ */
8
+ export function getLineTypes(hasChildren?: boolean, parentTypes?: import("./types").LineType[], position?: import("./types").LineType): import("./types").LineType[];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Creates an emitter object from the given properties.
3
+ *
4
+ * - Filters attributes that start with 'on' and are functions,
5
+ * - Returns an object with keys that are the event names (without the 'on' prefix)
6
+ * - If a default event is not present in the props, it will be set to a no-op function.
7
+ *
8
+ * @param {Object} props - The properties object to filter.
9
+ * @param {Array<string>} defaults - An array of default events.
10
+ * @returns {import('./types.js').EventHandlers} The emitter object.
11
+ */
12
+ export function createEmitter(props: Object, defaults?: Array<string>): import("./types.js").EventHandlers;
@@ -1,20 +1,62 @@
1
1
  export class FieldMapper {
2
- constructor(fields?: any, using?: {});
3
- _fields: {};
4
- _using: {};
5
- set fields(fields: {});
6
- get fields(): {};
7
- set using(using: {});
8
- get using(): {};
9
- getComponent(value: any): import("svelte").SvelteComponent<Record<string, any>, any, any>;
10
- getIcon(value: any): string;
11
- getValue(node: any): any;
12
- getText(node: any): string;
13
- getAttribute(node: any, attr: any): any;
14
- getFormattedText(node: any, formatter: any): Function;
15
- hasChildren(item: any): boolean;
16
- isExpanded(item: any): boolean;
2
+ constructor(fields?: any, componentMap?: {});
3
+ hasIcon: ((obj: unknown) => obj is {
4
+ [x: string]: unknown;
5
+ }) | undefined;
6
+ hasImage: ((obj: unknown) => obj is {
7
+ [x: string]: unknown;
8
+ }) | undefined;
9
+ hasText: ((obj: unknown) => obj is {
10
+ [x: string]: unknown;
11
+ }) | undefined;
12
+ hasValue: ((obj: unknown) => obj is {
13
+ [x: string]: unknown;
14
+ }) | undefined;
15
+ hasLabel: ((obj: unknown) => obj is {
16
+ [x: string]: unknown;
17
+ }) | undefined;
18
+ hasComponent: ((obj: unknown) => obj is {
19
+ [x: string]: unknown;
20
+ }) | undefined;
21
+ hasCurrency: ((obj: unknown) => obj is {
22
+ [x: string]: unknown;
23
+ }) | undefined;
24
+ withPrefix: ((x: any) => string) | undefined;
25
+ excludeFlags: (<U extends Partial<Record<any, any>>>(obj: any extends keyof U ? U : never) => any extends keyof U ? Omit<U, any> : never) | undefined;
26
+ set fields(fields: any);
27
+ get fields(): any;
28
+ set componentMap(components: {});
29
+ get componentMap(): {};
30
+ getComponent(value: any): any;
31
+ getIcon(value: any): string | null;
32
+ getImage(value: any): any;
33
+ getValue(value: any): any;
34
+ getText(value: any): any;
35
+ getLabel(value: any): any;
36
+ getAttribute(value: any, attr: any): any;
37
+ getFormattedText(value: any, formatter: any): any;
38
+ hasChildren(item: any): item is never;
39
+ isExpanded(item: any): false;
40
+ isHidden(item: any): unknown;
17
41
  isNested(items: any): boolean;
18
- hasImage(item: any): any;
19
- hasIcon(item: any): any;
42
+ toggleVisibility(items: any, visible: any): void;
43
+ toggleExpansion(item: any): void;
44
+ getChildren(item: any): never[];
45
+ /**
46
+ * Finds children by an index path
47
+ *
48
+ * @param {Array<Object>} items
49
+ * @param {Array<number>} path
50
+ * @returns {Array<Object>}
51
+ */
52
+ getChildrenByPath(items: Array<Object>, path?: Array<number>): Array<Object>;
53
+ /**
54
+ * Finds an item by an index path
55
+ *
56
+ * @param {Array<Object>} items
57
+ * @param {Array<number>} path
58
+ * @returns {Object|null}
59
+ */
60
+ getItemByPath(items: Array<Object>, path?: Array<number>): Object | null;
61
+ #private;
20
62
  }
package/dist/index.d.ts CHANGED
@@ -5,3 +5,9 @@ export * from "./nested.js";
5
5
  export * from "./string.js";
6
6
  export * from "./theme.js";
7
7
  export { FieldMapper } from "./field-mapper.js";
8
+ export { createEmitter } from "./events.js";
9
+ export { getLineTypes } from "./connector.js";
10
+ export { generateTicks } from "./ticks.js";
11
+ export { getValue } from "./mapping.js";
12
+ export { getItemAtIndex, getIndexForItem } from "./mapped-items.js";
13
+ export { weekdays, getCalendarDays } from "./calendar.js";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get an item at a specific index
3
+ * @param {Array<any>} items
4
+ * @param {any} index
5
+ * @returns
6
+ */
7
+ export function getItemAtIndex(items: Array<any>, index: any): any;
8
+ /**
9
+ * Get the index for an item in an array
10
+ * @param {Array<any} items
11
+ * @param {any} item
12
+ * @returns
13
+ */
14
+ export function getIndexForItem(items: Array<any>, item: any): number;
package/dist/string.d.ts CHANGED
@@ -26,7 +26,7 @@ export function toHyphenCase(text: string): string;
26
26
  * @param {String} b
27
27
  * @returns
28
28
  */
29
- export function compareStrings(a: string, b: string): 0 | 1 | -1;
29
+ export function compareStrings(a: string, b: string): 1 | 0 | -1;
30
30
  /**
31
31
  * Sort by splitting hyphen separated strings while keeping strings with same number of parts together
32
32
  *
package/dist/theme.d.ts CHANGED
@@ -25,6 +25,11 @@ export function shadesOf(name: string, modifier?: string): {
25
25
  * @returns {object}
26
26
  */
27
27
  export function stateColors(name: string, modifier?: string): object;
28
+ /**
29
+ *
30
+ * @param {string} modifier
31
+ * @returns
32
+ */
28
33
  export function themeColors(modifier?: string): {};
29
34
  /**
30
35
  * Generates contrast colors for light and dark modes based on a given palette. Each color variant in the
@@ -0,0 +1,10 @@
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 {import('./types').TickMark[]>} An array of tick mark objects.
9
+ */
10
+ export function generateTicks(lowerBound: number, upperBound: number, minorTickStep?: number, majorTickStep?: number): import("./types").TickMark[];
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  declare const _default: {};
2
2
  export default _default;
3
+ export type LineType = "child" | "last" | "sibling" | "empty" | "icon";
3
4
  /**
4
5
  * An object where keys are event names and values are event handler functions.
5
6
  */
package/dist/utils.d.ts CHANGED
@@ -47,3 +47,22 @@ export function iconShortcuts(icons: string[], collection: string, variants: str
47
47
  * @returns {string|number}
48
48
  */
49
49
  export function scaledPath(size: number, x: string | number): string | number;
50
+ /**
51
+ * Gets a key string from path
52
+ * @param {string[]} path
53
+ * @returns {string}
54
+ */
55
+ export function getKeyFromPath(path: string[]): string;
56
+ /**
57
+ * Gets a path array from key string
58
+ * @param {string} key
59
+ * @returns {string[]}
60
+ */
61
+ export function getPathFromKey(key: string): string[];
62
+ /**
63
+ * Get snippet function from an object
64
+ * @param {Object} obj
65
+ * @param {string} key
66
+ * @returns {Function|undefined}
67
+ */
68
+ export function getSnippet(obj: Object, key: string): Function | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/core",
3
- "version": "1.0.0-next.105",
3
+ "version": "1.0.0-next.107",
4
4
  "description": "Contains core utility functions and classes that can be used in various components.",
5
5
  "author": "Jerry Thomas <me@jerrythomas.name>",
6
6
  "license": "MIT",
@@ -28,6 +28,7 @@
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
+ "date-fns": "^4.1.0",
31
32
  "ramda": "^0.30.1"
32
33
  }
33
34
  }
@@ -0,0 +1,44 @@
1
+ import { getDay, getMonth, getYear, getDaysInMonth, format, isWeekend } from 'date-fns'
2
+
3
+ export const weekdays = [
4
+ 'Sunday',
5
+ 'Monday',
6
+ 'Tuesday',
7
+ 'Wednesday',
8
+ 'Thursday',
9
+ 'Friday',
10
+ 'Saturday'
11
+ ]
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
+ */
21
+ export function getCalendarDays(value, holidays = [], fixed = false) {
22
+ const month = getMonth(value)
23
+ const year = getYear(value)
24
+ const offset = getDay(new Date(year, month, 1)) + 1
25
+
26
+ holidays = holidays.map((x) => format(new Date(x), 'yyyy-MMM-dd'))
27
+ let days = Array.from({ length: getDaysInMonth(value) }, (_, i) => ({
28
+ day: i + 1,
29
+ offset: i === 0 ? offset : 0,
30
+ date: new Date(year, month, i + 1)
31
+ })).map((x) => ({
32
+ ...x,
33
+ text: format(x.date, 'yyyy-MMM-dd'),
34
+ weekend: isWeekend(x.date),
35
+ holiday: holidays.includes(format(x.date, 'yyyy-MMM-dd'))
36
+ }))
37
+
38
+ if (fixed && days[0].offset > 4) {
39
+ const n = 5 * 7 - days[0].offset
40
+ days = [...days.slice(n + 1), ...days.slice(0, n + 1)]
41
+ }
42
+
43
+ return days
44
+ }
@@ -0,0 +1,34 @@
1
+ const LINE_TYPES = {
2
+ CHILD: 'child',
3
+ LAST: 'last',
4
+ SIBLING: 'sibling',
5
+ EMPTY: 'empty',
6
+ ICON: 'icon'
7
+ }
8
+
9
+ const nextType = {
10
+ [LINE_TYPES.CHILD]: LINE_TYPES.SIBLING,
11
+ [LINE_TYPES.LAST]: LINE_TYPES.EMPTY,
12
+ [LINE_TYPES.SIBLING]: LINE_TYPES.SIBLING,
13
+ [LINE_TYPES.EMPTY]: LINE_TYPES.EMPTY
14
+ }
15
+
16
+ /**
17
+ * Constructs an array of line types for tree visualization
18
+ * @param {boolean} hasChildren - Whether the node has children
19
+ * @param {import('./types').LineType[]} parentTypes - Types from parent nodes
20
+ * @param {import('./types').LineType} position - Current position type
21
+ * @returns {import('./types').LineType[]} Array of line types
22
+ */
23
+ export function getLineTypes(hasChildren = false, parentTypes = [], position = LINE_TYPES.CHILD) {
24
+ return parentTypes
25
+ .reduce((acc, type, index) => {
26
+ // For all but the last parent type, convert to next type
27
+ if (index < parentTypes.length - 1) {
28
+ return [...acc, nextType[type]]
29
+ }
30
+ // For the last parent type, use the position
31
+ return [...acc, position]
32
+ }, [])
33
+ .concat(hasChildren ? LINE_TYPES.ICON : LINE_TYPES.EMPTY)
34
+ }
package/src/constants.js CHANGED
@@ -18,11 +18,16 @@ export const defaultFields = {
18
18
  props: 'props',
19
19
  target: 'target',
20
20
  state: 'state',
21
- isOpen: '_open',
22
- isDeleted: '_deleted',
23
21
  level: 'level',
24
22
  parent: 'parent',
25
- currency: 'currency'
23
+ currency: 'currency',
24
+ label: 'label',
25
+ /* flag fields */
26
+ isSelected: '_selected',
27
+ isHidden: '_hidden',
28
+ isOpen: '_open',
29
+ isDeleted: '_deleted',
30
+ isFiltered: '_filtered'
26
31
  }
27
32
 
28
33
  export const defaultIcons = [
package/src/events.js ADDED
@@ -0,0 +1,32 @@
1
+ import { noop } from './utils.js'
2
+
3
+ /**
4
+ * Creates an emitter object from the given properties.
5
+ *
6
+ * - Filters attributes that start with 'on' and are functions,
7
+ * - Returns an object with keys that are the event names (without the 'on' prefix)
8
+ * - If a default event is not present in the props, it will be set to a no-op function.
9
+ *
10
+ * @param {Object} props - The properties object to filter.
11
+ * @param {Array<string>} defaults - An array of default events.
12
+ * @returns {import('./types.js').EventHandlers} The emitter object.
13
+ */
14
+ export function createEmitter(props, defaults = []) {
15
+ const emit = {}
16
+
17
+ // Filter and add functions that start with 'on'
18
+ Object.entries(props)
19
+ .filter(([key, value]) => key.startsWith('on') && typeof value === 'function')
20
+ .forEach(([key, value]) => {
21
+ emit[key.slice(2)] = value
22
+ })
23
+
24
+ // Add default events with no-op function if not present
25
+ defaults.forEach((event) => {
26
+ if (!emit[event]) {
27
+ emit[event] = noop
28
+ }
29
+ })
30
+
31
+ return emit
32
+ }
@@ -1,81 +1,191 @@
1
- import { defaultFields } from './constants'
2
- import {
3
- getComponent,
4
- getIcon,
5
- getValue,
6
- getText,
7
- getAttribute,
8
- getFormattedText,
9
- hasChildren,
10
- isExpanded
11
- } from './mapping'
12
- import { has } from 'ramda'
1
+ import { defaultFields } from './constants.js'
2
+ import { isNil, has, omit } from 'ramda'
3
+ import { isObject } from './utils.js'
13
4
 
14
5
  export class FieldMapper {
15
- _fields = {}
16
- _using = {}
17
-
18
- constructor(fields = defaultFields, using = {}) {
19
- this.fields = fields
20
- this.using = using
6
+ #fields = { ...defaultFields }
7
+ #componentMap = {}
8
+ #childMapper = null
9
+
10
+ constructor(fields = defaultFields, componentMap = {}) {
11
+ this.#updateFields(fields)
12
+ this.#updateComponentMap(componentMap)
13
+ }
14
+
15
+ #updateFields(fields) {
16
+ Object.keys(fields).forEach((key) => {
17
+ this.#fields[key] = fields[key]
18
+ })
19
+ this.hasIcon = has(this.#fields.icon)
20
+ this.hasImage = has(this.#fields.image)
21
+ this.hasText = has(this.#fields.text)
22
+ this.hasValue = has(this.#fields.value)
23
+ this.hasLabel = has(this.#fields.label)
24
+ this.hasComponent = has(this.#fields.component)
25
+ this.hasCurrency = has(this.#fields.currency)
26
+ this.withPrefix = (x) => [this.#fields.iconPrefix, x].join('-').replace(/^-+/g, '')
27
+ this.excludeFlags = omit([
28
+ this.#fields.isDeleted,
29
+ this.#fields.isHidden,
30
+ this.#fields.isSelected,
31
+ this.#fields.isFiltered,
32
+ this.#fields.isOpen
33
+ ])
34
+ }
35
+
36
+ #updateComponentMap(components) {
37
+ if (typeof components === 'object' && components) {
38
+ Object.keys(components).forEach((key) => {
39
+ this.#componentMap[key] = components[key]
40
+ })
41
+ }
21
42
  }
22
43
 
23
44
  get fields() {
24
- return this._fields
45
+ return this.#fields
25
46
  }
26
47
 
27
48
  set fields(fields) {
28
- this._fields = { ...defaultFields, ...fields }
49
+ this.#updateFields(fields)
29
50
  }
30
51
 
31
- get using() {
32
- return this._using
52
+ get componentMap() {
53
+ return this.#componentMap
33
54
  }
34
- set using(using) {
35
- this._using = using ?? {}
55
+
56
+ set componentMap(components) {
57
+ this.#updateComponentMap(components)
36
58
  }
37
59
 
38
60
  getComponent(value) {
39
- return getComponent(value, this.fields, this.using)
61
+ if (this.hasComponent(value))
62
+ return this.componentMap[value[this.fields.component]] ?? this.componentMap.default
63
+ return this.componentMap.default
40
64
  }
41
65
 
42
66
  getIcon(value) {
43
- return getIcon(value, this.fields)
67
+ if (!this.hasIcon(value)) return null
68
+ const icon = value[this.fields.icon]
69
+ if (isObject(icon)) return this.withPrefix(icon[value[this.fields.state]])
70
+ return this.withPrefix(icon)
71
+ }
72
+ getImage(value) {
73
+ return this.getAttribute(value, 'image')
44
74
  }
45
75
 
46
- getValue(node) {
47
- return getValue(node, this.fields)
76
+ getValue(value) {
77
+ if (this.hasValue(value)) {
78
+ return value[this.fields.value]
79
+ }
80
+ return value
48
81
  }
49
82
 
50
- getText(node) {
51
- return getText(node, this.fields)
83
+ getText(value) {
84
+ if (this.hasText(value)) {
85
+ return value[this.fields.text]
86
+ }
87
+ return typeof value === 'object' ? null : value
52
88
  }
53
89
 
54
- getAttribute(node, attr) {
55
- return getAttribute(node, attr)
90
+ getLabel(value) {
91
+ return this.getAttribute(value, 'label') ?? this.getText(value)
56
92
  }
57
93
 
58
- getFormattedText(node, formatter) {
59
- return getFormattedText(node, this.fields, formatter)
94
+ getAttribute(value, attr) {
95
+ if (has(attr, this.fields)) {
96
+ return has(this.fields[attr], value) ? value[this.fields[attr]] : null
97
+ }
98
+ return null
99
+ }
100
+
101
+ getFormattedText(value, formatter) {
102
+ const text = this.getText(value)
103
+ if (isNil(text)) return ''
104
+
105
+ if (typeof formatter !== 'function') return text.toString()
106
+
107
+ if (this.hasCurrency(value)) {
108
+ return formatter(text, this.getAttribute(value, 'currency'))
109
+ }
110
+ return formatter(text)
60
111
  }
61
112
 
62
113
  hasChildren(item) {
63
- return hasChildren(item, this.fields)
114
+ return (
115
+ !isNil(item) &&
116
+ has(this.fields.children, item) &&
117
+ Array.isArray(item[this.fields.children]) &&
118
+ item[this.fields.children].length > 0
119
+ )
64
120
  }
65
121
 
66
122
  isExpanded(item) {
67
- return isExpanded(item, this.fields)
123
+ if (this.hasChildren(item)) {
124
+ return has(this.fields.isOpen, item) && item[this.fields.isOpen]
125
+ }
126
+ return false
68
127
  }
69
128
 
70
- isNested(items) {
71
- return Array.isArray(items) && items.some((item) => this.hasChildren(item))
129
+ isHidden(item) {
130
+ return has(this.fields.isHidden, item) && item[this.fields.isHidden]
72
131
  }
73
132
 
74
- hasImage(item) {
75
- return has(item, this.fields.image)
133
+ isNested(items) {
134
+ return Array.isArray(items) && items.some((item) => this.hasChildren(item))
76
135
  }
77
136
 
78
- hasIcon(item) {
79
- return has(item, this.fields.icon)
137
+ toggleVisibility(items, visible) {
138
+ items.forEach((item) => {
139
+ item[this.fields.isHidden] = !visible
140
+ if (this.hasChildren(item)) {
141
+ this.toggleVisibility(item[this.fields.children], visible && item[this.fields.isOpen])
142
+ }
143
+ })
144
+ }
145
+
146
+ toggleExpansion(item) {
147
+ if (this.hasChildren(item)) {
148
+ item[this.fields.isOpen] = !item[this.fields.isOpen]
149
+ this.toggleVisibility(item[this.fields.children], item[this.fields.isOpen])
150
+ }
151
+ }
152
+
153
+ getChildren(item) {
154
+ return this.hasChildren(item) ? item[this.fields.children] : []
155
+ }
156
+
157
+ /**
158
+ * Finds children by an index path
159
+ *
160
+ * @param {Array<Object>} items
161
+ * @param {Array<number>} path
162
+ * @returns {Array<Object>}
163
+ */
164
+ getChildrenByPath(items, path = []) {
165
+ const result = path.reduce(
166
+ (children, index) => children?.[index]?.[this.fields.children],
167
+ items
168
+ )
169
+
170
+ if (result === undefined) throw new Error('Invalid path')
171
+ return result
172
+ }
173
+
174
+ /**
175
+ * Finds an item by an index path
176
+ *
177
+ * @param {Array<Object>} items
178
+ * @param {Array<number>} path
179
+ * @returns {Object|null}
180
+ */
181
+ getItemByPath(items, path = []) {
182
+ // skipcq: JS-W1042 default undefined is needed
183
+ const result = path.reduce(
184
+ (item, index, i) => (i === 0 ? items?.[index] : item?.[this.fields.children]?.[index]),
185
+ undefined
186
+ )
187
+
188
+ if (result === undefined) throw new Error('Invalid path')
189
+ return result
80
190
  }
81
191
  }
package/src/index.js CHANGED
@@ -8,6 +8,12 @@ export * from './utils.js'
8
8
  export * from './nested.js'
9
9
  // skipcq: JS-E1004 - Needed for exposing all functions
10
10
  export * from './string.js'
11
- export { FieldMapper } from './field-mapper.js'
12
11
  // skipcq: JS-E1004 - Needed for exposing all functions
13
12
  export * from './theme.js'
13
+ export { FieldMapper } from './field-mapper.js'
14
+ export { getItemAtIndex, getIndexForItem } from './mapped-items.js'
15
+ export { createEmitter } from './events.js'
16
+ export { getLineTypes } from './connector.js'
17
+ export { weekdays, getCalendarDays } from './calendar.js'
18
+ export { generateTicks } from './ticks.js'
19
+ export { getValue } from './mapping.js'
@@ -1,4 +1,4 @@
1
- import * as R from 'ramda'
1
+ import { find, toPairs } from 'ramda'
2
2
 
3
3
  /**
4
4
  * Class to manage key event mappings.
@@ -26,10 +26,11 @@ export class KeyEventMap {
26
26
  * @returns {string|null} - The event name or null if no match is found.
27
27
  */
28
28
  getEventForKey(key) {
29
- const matchEvent = ([eventName, keys]) =>
29
+ // eslint-disable-next-line no-unused-vars
30
+ const matchEvent = ([_, keys]) =>
30
31
  (Array.isArray(keys) && keys.includes(key)) || (keys instanceof RegExp && keys.test(key))
31
32
 
32
- const event = R.find(matchEvent, R.toPairs(this.mapping))
33
+ const event = find(matchEvent, toPairs(this.mapping))
33
34
  return event ? event[0] : null
34
35
  }
35
36
  }
@@ -0,0 +1,22 @@
1
+ import { equals, isNil } from 'ramda'
2
+
3
+ /**
4
+ * Get an item at a specific index
5
+ * @param {Array<any>} items
6
+ * @param {any} index
7
+ * @returns
8
+ */
9
+ export function getItemAtIndex(items, index) {
10
+ if (isNil(index)) return null
11
+ return index >= 0 && index < items.length ? items[index] : null
12
+ }
13
+
14
+ /**
15
+ * Get the index for an item in an array
16
+ * @param {Array<any} items
17
+ * @param {any} item
18
+ * @returns
19
+ */
20
+ export function getIndexForItem(items, item) {
21
+ return items.findIndex((i) => equals(i, item))
22
+ }
package/src/theme.js CHANGED
@@ -53,6 +53,11 @@ export function stateColors(name, modifier = 'none') {
53
53
  }
54
54
  }
55
55
 
56
+ /**
57
+ *
58
+ * @param {string} modifier
59
+ * @returns
60
+ */
56
61
  export function themeColors(modifier = 'none') {
57
62
  const fn = modifier in modifiers ? modifiers[modifier] : modifiers.none
58
63
 
@@ -183,7 +188,10 @@ export function themeRules(name = 'rokkit', mapping = defaultThemeMapping, color
183
188
  const variants = Object.keys(mapping)
184
189
 
185
190
  const rules = variants.reduce(
186
- (acc, variant) => [...acc, ...generateColorRules(variant, colors, mapping)],
191
+ (acc, variant) => [
192
+ ...acc,
193
+ ...generateColorRules(variant, { ...defaultColors, ...colors }, mapping)
194
+ ],
187
195
  []
188
196
  )
189
197
 
package/src/ticks.js ADDED
@@ -0,0 +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 {import('./types').TickMark[]>} An array of tick mark objects.
9
+ */
10
+ export function generateTicks(
11
+ lowerBound,
12
+ upperBound,
13
+ minorTickStep = upperBound - lowerBound,
14
+ majorTickStep = 1
15
+ ) {
16
+ const length = 1 + Math.ceil((upperBound - lowerBound) / minorTickStep)
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
+ })
26
+ }
package/src/types.js CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {'child' | 'last' | 'sibling' | 'empty' | 'icon'} LineType
3
+ */
4
+
1
5
  /**
2
6
  * @typedef {Object.<string, Function>} EventHandlers
3
7
  * An object where keys are event names and values are event handler functions.
package/src/utils.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { has } from 'ramda'
2
+
1
3
  /**
2
4
  * Finds the closest ancestor of the given element that has the given attribute.
3
5
  *
@@ -83,3 +85,34 @@ export function scaledPath(size, x) {
83
85
  if (Array.isArray(x)) return x.map((v) => scaledPath(size, v)).join(' ')
84
86
  return typeof x === 'number' ? x * size : x
85
87
  }
88
+
89
+ /**
90
+ * Gets a key string from path
91
+ * @param {string[]} path
92
+ * @returns {string}
93
+ */
94
+ export function getKeyFromPath(path) {
95
+ return path.join('-')
96
+ }
97
+
98
+ /**
99
+ * Gets a path array from key string
100
+ * @param {string} key
101
+ * @returns {string[]}
102
+ */
103
+ export function getPathFromKey(key) {
104
+ return key.split('-').map(Number)
105
+ }
106
+
107
+ /**
108
+ * Get snippet function from an object
109
+ * @param {Object} obj
110
+ * @param {string} key
111
+ * @returns {Function|undefined}
112
+ */
113
+ export function getSnippet(obj, key) {
114
+ if (has(key, obj) && typeof obj[key] === 'function') {
115
+ return obj[key]
116
+ }
117
+ return undefined
118
+ }