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

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,8 @@ 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 { getItemAtIndex, getIndexForItem } from "./mapped-items.js";
12
+ 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,15 @@ 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[];
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.106",
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,190 @@
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
+
9
+ constructor(fields = defaultFields, componentMap = {}) {
10
+ this.#updateFields(fields)
11
+ this.#updateComponentMap(componentMap)
12
+ }
13
+
14
+ #updateFields(fields) {
15
+ Object.keys(fields).forEach((key) => {
16
+ this.#fields[key] = fields[key]
17
+ })
18
+ this.hasIcon = has(this.#fields.icon)
19
+ this.hasImage = has(this.#fields.image)
20
+ this.hasText = has(this.#fields.text)
21
+ this.hasValue = has(this.#fields.value)
22
+ this.hasLabel = has(this.#fields.label)
23
+ this.hasComponent = has(this.#fields.component)
24
+ this.hasCurrency = has(this.#fields.currency)
25
+ this.withPrefix = (x) => [this.#fields.iconPrefix, x].join('-').replace(/^-+/g, '')
26
+ this.excludeFlags = omit([
27
+ this.#fields.isDeleted,
28
+ this.#fields.isHidden,
29
+ this.#fields.isSelected,
30
+ this.#fields.isFiltered,
31
+ this.#fields.isOpen
32
+ ])
33
+ }
34
+
35
+ #updateComponentMap(components) {
36
+ if (typeof components === 'object' && components) {
37
+ Object.keys(components).forEach((key) => {
38
+ this.#componentMap[key] = components[key]
39
+ })
40
+ }
21
41
  }
22
42
 
23
43
  get fields() {
24
- return this._fields
44
+ return this.#fields
25
45
  }
26
46
 
27
47
  set fields(fields) {
28
- this._fields = { ...defaultFields, ...fields }
48
+ this.#updateFields(fields)
29
49
  }
30
50
 
31
- get using() {
32
- return this._using
51
+ get componentMap() {
52
+ return this.#componentMap
33
53
  }
34
- set using(using) {
35
- this._using = using ?? {}
54
+
55
+ set componentMap(components) {
56
+ this.#updateComponentMap(components)
36
57
  }
37
58
 
38
59
  getComponent(value) {
39
- return getComponent(value, this.fields, this.using)
60
+ if (this.hasComponent(value))
61
+ return this.componentMap[value[this.fields.component]] ?? this.componentMap.default
62
+ return this.componentMap.default
40
63
  }
41
64
 
42
65
  getIcon(value) {
43
- return getIcon(value, this.fields)
66
+ if (!this.hasIcon(value)) return null
67
+ const icon = value[this.fields.icon]
68
+ if (isObject(icon)) return this.withPrefix(icon[value[this.fields.state]])
69
+ return this.withPrefix(icon)
70
+ }
71
+ getImage(value) {
72
+ return this.getAttribute(value, 'image')
44
73
  }
45
74
 
46
- getValue(node) {
47
- return getValue(node, this.fields)
75
+ getValue(value) {
76
+ if (this.hasValue(value)) {
77
+ return value[this.fields.value]
78
+ }
79
+ return value
48
80
  }
49
81
 
50
- getText(node) {
51
- return getText(node, this.fields)
82
+ getText(value) {
83
+ if (this.hasText(value)) {
84
+ return value[this.fields.text]
85
+ }
86
+ return typeof value === 'object' ? null : value
52
87
  }
53
88
 
54
- getAttribute(node, attr) {
55
- return getAttribute(node, attr)
89
+ getLabel(value) {
90
+ return this.getAttribute(value, 'label') ?? this.getText(value)
56
91
  }
57
92
 
58
- getFormattedText(node, formatter) {
59
- return getFormattedText(node, this.fields, formatter)
93
+ getAttribute(value, attr) {
94
+ if (has(attr, this.fields)) {
95
+ return has(this.fields[attr], value) ? value[this.fields[attr]] : null
96
+ }
97
+ return null
98
+ }
99
+
100
+ getFormattedText(value, formatter) {
101
+ const text = this.getText(value)
102
+ if (isNil(text)) return ''
103
+
104
+ if (typeof formatter !== 'function') return text.toString()
105
+
106
+ if (this.hasCurrency(value)) {
107
+ return formatter(text, this.getAttribute(value, 'currency'))
108
+ }
109
+ return formatter(text)
60
110
  }
61
111
 
62
112
  hasChildren(item) {
63
- return hasChildren(item, this.fields)
113
+ return (
114
+ !isNil(item) &&
115
+ has(this.fields.children, item) &&
116
+ Array.isArray(item[this.fields.children]) &&
117
+ item[this.fields.children].length > 0
118
+ )
64
119
  }
65
120
 
66
121
  isExpanded(item) {
67
- return isExpanded(item, this.fields)
122
+ if (this.hasChildren(item)) {
123
+ return has(this.fields.isOpen, item) && item[this.fields.isOpen]
124
+ }
125
+ return false
68
126
  }
69
127
 
70
- isNested(items) {
71
- return Array.isArray(items) && items.some((item) => this.hasChildren(item))
128
+ isHidden(item) {
129
+ return has(this.fields.isHidden, item) && item[this.fields.isHidden]
72
130
  }
73
131
 
74
- hasImage(item) {
75
- return has(item, this.fields.image)
132
+ isNested(items) {
133
+ return Array.isArray(items) && items.some((item) => this.hasChildren(item))
76
134
  }
77
135
 
78
- hasIcon(item) {
79
- return has(item, this.fields.icon)
136
+ toggleVisibility(items, visible) {
137
+ items.forEach((item) => {
138
+ item[this.fields.isHidden] = !visible
139
+ if (this.hasChildren(item)) {
140
+ this.toggleVisibility(item[this.fields.children], visible && item[this.fields.isOpen])
141
+ }
142
+ })
143
+ }
144
+
145
+ toggleExpansion(item) {
146
+ if (this.hasChildren(item)) {
147
+ item[this.fields.isOpen] = !item[this.fields.isOpen]
148
+ this.toggleVisibility(item[this.fields.children], item[this.fields.isOpen])
149
+ }
150
+ }
151
+
152
+ getChildren(item) {
153
+ return this.hasChildren(item) ? item[this.fields.children] : []
154
+ }
155
+
156
+ /**
157
+ * Finds children by an index path
158
+ *
159
+ * @param {Array<Object>} items
160
+ * @param {Array<number>} path
161
+ * @returns {Array<Object>}
162
+ */
163
+ getChildrenByPath(items, path = []) {
164
+ const result = path.reduce(
165
+ (children, index) => children?.[index]?.[this.fields.children],
166
+ items
167
+ )
168
+
169
+ if (result === undefined) throw new Error('Invalid path')
170
+ return result
171
+ }
172
+
173
+ /**
174
+ * Finds an item by an index path
175
+ *
176
+ * @param {Array<Object>} items
177
+ * @param {Array<number>} path
178
+ * @returns {Object|null}
179
+ */
180
+ getItemByPath(items, path = []) {
181
+ // skipcq: JS-W1042 default undefined is needed
182
+ const result = path.reduce(
183
+ (item, index, i) => (i === 0 ? items?.[index] : item?.[this.fields.children]?.[index]),
184
+ undefined
185
+ )
186
+
187
+ if (result === undefined) throw new Error('Invalid path')
188
+ return result
80
189
  }
81
190
  }
package/src/index.js CHANGED
@@ -8,6 +8,11 @@ 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'
@@ -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
@@ -83,3 +83,21 @@ export function scaledPath(size, x) {
83
83
  if (Array.isArray(x)) return x.map((v) => scaledPath(size, v)).join(' ')
84
84
  return typeof x === 'number' ? x * size : x
85
85
  }
86
+
87
+ /**
88
+ * Gets a key string from path
89
+ * @param {string[]} path
90
+ * @returns {string}
91
+ */
92
+ export function getKeyFromPath(path) {
93
+ return path.join('-')
94
+ }
95
+
96
+ /**
97
+ * Gets a path array from key string
98
+ * @param {string} key
99
+ * @returns {string[]}
100
+ */
101
+ export function getPathFromKey(key) {
102
+ return key.split('-').map(Number)
103
+ }