@rokkit/core 1.0.0-next.13 → 1.0.0-next.131
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/dist/calendar.d.ts +10 -0
- package/dist/colors/index.d.ts +47 -0
- package/dist/connector.d.ts +8 -0
- package/dist/constants.d.ts +88 -0
- package/dist/events.d.ts +12 -0
- package/dist/field-mapper.d.ts +60 -0
- package/dist/index.d.ts +13 -0
- package/dist/key-event-map.d.ts +18 -0
- package/dist/mapped-items.d.ts +14 -0
- package/dist/mapping.d.ts +14 -0
- package/dist/nested.d.ts +9 -0
- package/dist/string.d.ts +59 -0
- package/dist/theme.d.ts +96 -0
- package/dist/ticks.d.ts +10 -0
- package/dist/types.d.ts +300 -0
- package/dist/utils.d.ts +107 -0
- package/dist/vite/icon-collections.d.ts +32 -0
- package/dist/vite/index.d.ts +1 -0
- package/package.json +25 -39
- package/src/calendar.js +44 -0
- package/src/colors/extra.json +16 -0
- package/src/colors/index.ts +24 -0
- package/src/colors/syntax.json +42 -0
- package/src/colors/tailwind.json +275 -0
- package/src/connector.js +34 -0
- package/src/constants.js +181 -107
- package/src/events.js +32 -0
- package/src/field-mapper.js +147 -0
- package/src/index.js +19 -27
- package/src/key-event-map.js +36 -0
- package/src/mapped-items.js +22 -0
- package/src/mapping.js +21 -0
- package/src/nested.js +28 -0
- package/src/string.js +97 -0
- package/src/theme.ts +192 -0
- package/src/ticks.js +26 -0
- package/src/types.js +160 -0
- package/src/utils.js +250 -0
- package/src/vite/icon-collections.js +73 -0
- package/src/vite/index.js +1 -0
- package/LICENSE +0 -21
- package/README.md +0 -1
- package/src/Accordion.svelte +0 -80
- package/src/Alerts.svelte +0 -39
- package/src/DropDown.svelte +0 -82
- package/src/DropSearch.svelte +0 -67
- package/src/EditableTabs.svelte +0 -31
- package/src/Icon.svelte +0 -15
- package/src/List-Discard.svelte +0 -48
- package/src/List.svelte +0 -65
- package/src/ListActions.svelte +0 -35
- package/src/NavTabs.svelte +0 -0
- package/src/NestedList.svelte +0 -77
- package/src/Overlay.svelte +0 -4
- package/src/PageNavigator.svelte +0 -94
- package/src/ResponsiveGrid.svelte +0 -73
- package/src/Scrollable.svelte +0 -8
- package/src/Searchable.svelte +0 -19
- package/src/Sidebar.svelte +0 -5
- package/src/Slider.svelte +0 -17
- package/src/SpinList.svelte +0 -48
- package/src/SplitPane.svelte +0 -109
- package/src/SplitView.svelte +0 -44
- package/src/Splitter.svelte +0 -95
- package/src/TabItem.svelte +0 -27
- package/src/TabItems.svelte +0 -34
- package/src/Tabs.svelte +0 -49
- package/src/Tree.svelte +0 -45
- package/src/actions/dismissable.js +0 -24
- package/src/actions/fillable.js +0 -114
- package/src/actions/hierarchy.js +0 -189
- package/src/actions/index.js +0 -7
- package/src/actions/navigable.js +0 -43
- package/src/actions/navigator.js +0 -179
- package/src/actions/pannable.js +0 -50
- package/src/actions/swipeable.js +0 -56
- package/src/actions/themeable.js +0 -23
- package/src/items/Collapsible.svelte +0 -51
- package/src/items/Connector.svelte +0 -26
- package/src/items/Link.svelte +0 -18
- package/src/items/Node.svelte +0 -52
- package/src/items/Pill.svelte +0 -19
- package/src/items/Separator.svelte +0 -1
- package/src/items/Summary.svelte +0 -27
- package/src/items/Text.svelte +0 -21
- package/src/items/index.js +0 -8
- package/src/list.js +0 -14
- package/src/mocks/Custom.svelte +0 -7
- package/src/mocks/index.js +0 -10
- package/src/stores/alerts.js +0 -3
- package/src/stores/index.js +0 -6
- package/src/stores/persist.js +0 -63
- package/src/stores/theme.js +0 -34
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { isNil, has, omit } from 'ramda'
|
|
2
|
+
import { DEFAULT_FIELDS } from './constants.js'
|
|
3
|
+
import { isObject } from './utils.js'
|
|
4
|
+
|
|
5
|
+
export class FieldMapper {
|
|
6
|
+
#fields = { ...DEFAULT_FIELDS }
|
|
7
|
+
#childMapper = null
|
|
8
|
+
|
|
9
|
+
constructor(fields = DEFAULT_FIELDS) {
|
|
10
|
+
this.#updateFields(fields)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#updateFields(fields) {
|
|
14
|
+
Object.keys(fields).forEach((key) => {
|
|
15
|
+
this.#fields[key] = fields[key]
|
|
16
|
+
})
|
|
17
|
+
this.hasIcon = has(this.#fields.icon)
|
|
18
|
+
this.hasImage = has(this.#fields.image)
|
|
19
|
+
this.hasValue = has(this.#fields.value)
|
|
20
|
+
this.hasLabel = has(this.#fields.label)
|
|
21
|
+
this.hasComponent = has(this.#fields.component)
|
|
22
|
+
this.hasCurrency = has(this.#fields.currency)
|
|
23
|
+
this.withPrefix = (x) => [this.#fields.iconPrefix, x].join('-').replace(/^-+/g, '')
|
|
24
|
+
this.excludeFlags = omit([
|
|
25
|
+
this.#fields.isDeleted,
|
|
26
|
+
this.#fields.isHidden,
|
|
27
|
+
this.#fields.isSelected,
|
|
28
|
+
this.#fields.isFiltered,
|
|
29
|
+
this.#fields.isOpen
|
|
30
|
+
])
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getChildMapper() {
|
|
34
|
+
if (!this.#childMapper) {
|
|
35
|
+
this.#childMapper = new FieldMapper(this.fields.fields ?? this.fields)
|
|
36
|
+
}
|
|
37
|
+
return this.#childMapper
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
prop(fieldName, value) {
|
|
43
|
+
if (isNil(value)) return null
|
|
44
|
+
|
|
45
|
+
if (typeof value === 'object') {
|
|
46
|
+
return value[this.fields[fieldName]]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return fieldName === 'label' ? value : null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Gets a mapped attribute from the original item
|
|
54
|
+
*
|
|
55
|
+
* @param {string} fieldName - Name of the field to get
|
|
56
|
+
* @returns {any|null} - The attribute value or null if not found
|
|
57
|
+
*/
|
|
58
|
+
get(fieldName, value, defaultValue = null) {
|
|
59
|
+
return this.prop(fieldName, value) ?? defaultValue
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get fields() {
|
|
63
|
+
return this.#fields
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
set fields(fields) {
|
|
67
|
+
this.#updateFields(fields)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getIcon(value) {
|
|
71
|
+
if (!this.hasIcon(value)) return null
|
|
72
|
+
const icon = value[this.fields.icon]
|
|
73
|
+
if (isObject(icon)) return this.withPrefix(icon[value[this.fields.state]])
|
|
74
|
+
return this.withPrefix(icon)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getValue(value) {
|
|
78
|
+
if (this.hasValue(value)) {
|
|
79
|
+
return value[this.fields.value]
|
|
80
|
+
}
|
|
81
|
+
return value
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getFormattedText(value, formatter) {
|
|
85
|
+
const text = this.get('label', value)
|
|
86
|
+
|
|
87
|
+
if (isNil(text)) return ''
|
|
88
|
+
|
|
89
|
+
if (typeof formatter !== 'function') return text.toString()
|
|
90
|
+
|
|
91
|
+
if (this.hasCurrency(value)) {
|
|
92
|
+
return formatter(text, this.get('currency', value))
|
|
93
|
+
}
|
|
94
|
+
return formatter(text)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
hasChildren(item) {
|
|
98
|
+
return (
|
|
99
|
+
!isNil(item) &&
|
|
100
|
+
has(this.fields.children, item) &&
|
|
101
|
+
Array.isArray(item[this.fields.children]) &&
|
|
102
|
+
item[this.fields.children].length > 0
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
isNested(items) {
|
|
107
|
+
return Array.isArray(items) && items.some((item) => this.hasChildren(item))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getChildren(item) {
|
|
111
|
+
return this.hasChildren(item) ? item[this.fields.children] : []
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Finds children by an index path
|
|
116
|
+
*
|
|
117
|
+
* @param {Array<Object>} items
|
|
118
|
+
* @param {Array<number>} path
|
|
119
|
+
* @returns {Array<Object>}
|
|
120
|
+
*/
|
|
121
|
+
getChildrenByPath(items, path = []) {
|
|
122
|
+
const result = path.reduce(
|
|
123
|
+
(children, index) => children?.[index]?.[this.fields.children],
|
|
124
|
+
items
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if (result === undefined) throw new Error('Invalid path')
|
|
128
|
+
return result
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Finds an item by an index path
|
|
133
|
+
*
|
|
134
|
+
* @param {Array<Object>} items
|
|
135
|
+
* @param {Array<number>} path
|
|
136
|
+
* @returns {Object|null}
|
|
137
|
+
*/
|
|
138
|
+
getItemByPath(items, path = []) {
|
|
139
|
+
const result = path.reduce(
|
|
140
|
+
(item, index, i) => (i === 0 ? items?.[index] : item?.[this.fields.children]?.[index]),
|
|
141
|
+
/* skipcq: JS-W1042 default undefined is needed */ undefined
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if (result === undefined) throw new Error('Invalid path')
|
|
145
|
+
return result
|
|
146
|
+
}
|
|
147
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
export * from './
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
//
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
18
|
-
export {
|
|
19
|
-
|
|
20
|
-
export { default as Sidebar } from './Sidebar.svelte'
|
|
21
|
-
export { default as SplitPane } from './SplitPane.svelte'
|
|
22
|
-
export { default as Splitter } from './Splitter.svelte'
|
|
23
|
-
export { default as SplitView } from './SplitView.svelte'
|
|
24
|
-
export { default as Overlay } from './Overlay.svelte'
|
|
25
|
-
|
|
26
|
-
export { default as PageNavigator } from './PageNavigator.svelte'
|
|
27
|
-
export { default as ResponsiveGrid } from './ResponsiveGrid.svelte'
|
|
1
|
+
// skipcq: JS-E1004 - Needed for exposing JS Doc types
|
|
2
|
+
export * from './types.js'
|
|
3
|
+
// skipcq: JS-E1004 - Needed for exposing all constants
|
|
4
|
+
export * from './constants.js'
|
|
5
|
+
// skipcq: JS-E1004 - Needed for exposing all functions
|
|
6
|
+
export * from './utils.js'
|
|
7
|
+
// skipcq: JS-E1004 - Needed for exposing all functions
|
|
8
|
+
export * from './nested.js'
|
|
9
|
+
// skipcq: JS-E1004 - Needed for exposing all functions
|
|
10
|
+
export * from './string.js'
|
|
11
|
+
// skipcq: JS-E1004 - Needed for exposing all functions
|
|
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 { getNestedFields } from './mapping.js'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { find, toPairs } from 'ramda'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Class to manage key event mappings.
|
|
5
|
+
*/
|
|
6
|
+
export class KeyEventMap {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.mapping = {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Add a new key mapping.
|
|
13
|
+
* @param {string} eventName - The event name.
|
|
14
|
+
* @param {Array<string>|RegExp} keys - The keys to match.
|
|
15
|
+
*/
|
|
16
|
+
add(eventName, keys) {
|
|
17
|
+
if (!Array.isArray(keys) && !(keys instanceof RegExp)) {
|
|
18
|
+
throw new Error('Keys must be an array or a regular expression')
|
|
19
|
+
}
|
|
20
|
+
this.mapping[eventName] = keys
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the event name for a given key.
|
|
25
|
+
* @param {string} key - The key to match.
|
|
26
|
+
* @returns {string|null} - The event name or null if no match is found.
|
|
27
|
+
*/
|
|
28
|
+
getEventForKey(key) {
|
|
29
|
+
|
|
30
|
+
const matchEvent = ([_, keys]) =>
|
|
31
|
+
(Array.isArray(keys) && keys.includes(key)) || (keys instanceof RegExp && keys.test(key))
|
|
32
|
+
|
|
33
|
+
const event = find(matchEvent, toPairs(this.mapping))
|
|
34
|
+
return event ? event[0] : null
|
|
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/mapping.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DEFAULT_FIELDS } from './constants.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gets the attribute from the node
|
|
5
|
+
* @param {*} node
|
|
6
|
+
* @param {string} attr
|
|
7
|
+
* @returns {*}
|
|
8
|
+
*/
|
|
9
|
+
export function getAttribute(node, attr) {
|
|
10
|
+
return typeof node === 'object' && node !== null && attr !== null ? node[attr] : null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fetches the fieldmapping for a child node
|
|
15
|
+
*
|
|
16
|
+
* @param {import('./types').FieldMapping} fields
|
|
17
|
+
* @returns {import('./types').FieldMapping}
|
|
18
|
+
*/
|
|
19
|
+
export function getNestedFields(fields) {
|
|
20
|
+
return { ...DEFAULT_FIELDS, ...(fields.fields ?? fields) }
|
|
21
|
+
}
|
package/src/nested.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { omit } from 'ramda'
|
|
2
|
+
import { DEFAULT_FIELDS } from './constants.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Flattens a nested list of items
|
|
6
|
+
*
|
|
7
|
+
* @param {Array} items
|
|
8
|
+
* @param {import('./types).FieldMapping} fields
|
|
9
|
+
* @param {number} level
|
|
10
|
+
* @returns {Array}
|
|
11
|
+
*/
|
|
12
|
+
export function flattenNestedList(items, fields = DEFAULT_FIELDS, level = 0) {
|
|
13
|
+
fields = { ...DEFAULT_FIELDS, ...fields }
|
|
14
|
+
let data = []
|
|
15
|
+
items.forEach((item) => {
|
|
16
|
+
const children = item[fields.children] ?? []
|
|
17
|
+
data = [
|
|
18
|
+
...data,
|
|
19
|
+
{
|
|
20
|
+
...omit([fields.children], item),
|
|
21
|
+
[fields.level]: level,
|
|
22
|
+
[fields.parent]: children.length > 0
|
|
23
|
+
},
|
|
24
|
+
...flattenNestedList(children, fields, level + 1)
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
return data
|
|
28
|
+
}
|
package/src/string.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { filter } from 'ramda'
|
|
2
|
+
/**
|
|
3
|
+
* Capitalizes the first letter of input string
|
|
4
|
+
*
|
|
5
|
+
* @param {String} str
|
|
6
|
+
* @returns {String}
|
|
7
|
+
*/
|
|
8
|
+
export function toInitCapCase(text) {
|
|
9
|
+
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Convert a hyphen separated string to PascalCase
|
|
14
|
+
*
|
|
15
|
+
* @param {String} text
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
export function toPascalCase(text) {
|
|
19
|
+
return text
|
|
20
|
+
.split('-')
|
|
21
|
+
.map((part) => toInitCapCase(part))
|
|
22
|
+
.join('')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Convert a PascalCase string to snake case with separator as hyphen
|
|
27
|
+
*
|
|
28
|
+
* @param {string} text
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
export function toHyphenCase(text) {
|
|
32
|
+
return text
|
|
33
|
+
.replace(/\s+/, '-')
|
|
34
|
+
.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
|
|
35
|
+
.replace(/^-/, '')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Simple comparison for two strings
|
|
40
|
+
*
|
|
41
|
+
* @param {String} a
|
|
42
|
+
* @param {String} b
|
|
43
|
+
* @returns
|
|
44
|
+
*/
|
|
45
|
+
export function compareStrings(a, b) {
|
|
46
|
+
return a > b ? 1 : a < b ? -1 : 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Sort by splitting hyphen separated strings while keeping strings with same number of parts together
|
|
51
|
+
*
|
|
52
|
+
* @param {String} a hyphen separates string
|
|
53
|
+
* @param {String} b hyphen separates string
|
|
54
|
+
* @param {string} separator - separator to split the string
|
|
55
|
+
* @returns {Number} -1, 0, 1 based on comparison
|
|
56
|
+
*/
|
|
57
|
+
export function sortByParts(a, b, separator = '-') {
|
|
58
|
+
const partsOfA = a.split(separator)
|
|
59
|
+
const partsOfB = b.split(separator)
|
|
60
|
+
|
|
61
|
+
let result = compareStrings(partsOfA[0], partsOfB[0])
|
|
62
|
+
if (result === 0) result = partsOfA.length - partsOfB.length
|
|
63
|
+
if (result === 0) result = compareStrings(a, b)
|
|
64
|
+
return result
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Generates a unique id from current timestamp
|
|
69
|
+
*
|
|
70
|
+
* @returns {String} timestamp based unique id
|
|
71
|
+
*/
|
|
72
|
+
export function uniqueId(prefix = '', separator = '-') {
|
|
73
|
+
const pair = prefix && prefix.length > 0 ? [prefix] : []
|
|
74
|
+
pair.push(Date.now().toString(36))
|
|
75
|
+
return pair.join(separator)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Removes undefined and null values from the input object.
|
|
80
|
+
*
|
|
81
|
+
* @param {Object} obj
|
|
82
|
+
* @returns {Object}
|
|
83
|
+
*/
|
|
84
|
+
export function compact(obj) {
|
|
85
|
+
return filter((x) => x !== undefined && x !== null, obj)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Converts an input number into it's hexadecimal representation, with optional left padded zeroes based on the `size`
|
|
90
|
+
*
|
|
91
|
+
* @param {number} value
|
|
92
|
+
* @param {number} size
|
|
93
|
+
* @returns
|
|
94
|
+
*/
|
|
95
|
+
export function toHexString(value, size = 2) {
|
|
96
|
+
return value.toString(16).padStart(size, '0')
|
|
97
|
+
}
|
package/src/theme.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { DEFAULT_THEME_MAPPING, defaultColors, TONE_MAP } from './constants'
|
|
4
|
+
import { shades } from './colors/index'
|
|
5
|
+
import { hex2rgb } from './utils'
|
|
6
|
+
|
|
7
|
+
const modifiers = {
|
|
8
|
+
hsl: (value) => `hsl(${value} / <alpha-value>)`,
|
|
9
|
+
rgb: (value) => `rgb(${value} / <alpha-value>)`,
|
|
10
|
+
none: (value) => value
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generate shades for a color using css varuable
|
|
15
|
+
*
|
|
16
|
+
* @param {string} name
|
|
17
|
+
* @param {string} modifier
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
20
|
+
export function shadesOf(name, modifier = 'none') {
|
|
21
|
+
const fn = modifier in modifiers ? modifiers[modifier] : modifiers.none
|
|
22
|
+
|
|
23
|
+
return shades.reduce(
|
|
24
|
+
(result, shade) => ({
|
|
25
|
+
...result,
|
|
26
|
+
[shade]: fn(`var(--color-${name}-${shade})`)
|
|
27
|
+
}),
|
|
28
|
+
{
|
|
29
|
+
DEFAULT: fn(`var(--color-${name}-500)`)
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generates color rules for a specific theme variant, for both light and dark modes.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} variant - The name of the variant to generate rules for.
|
|
38
|
+
* @param {Object} colors - The object containing color definitions.
|
|
39
|
+
* @param {Object} mapping - An object that maps variant names to color property names.
|
|
40
|
+
* @returns {import('./types').ShadeMappings} An array containing the color rules for both light and dark modes.
|
|
41
|
+
*/
|
|
42
|
+
function generateColorRules(variant, colors, mapping) {
|
|
43
|
+
return ['DEFAULT', ...shades].flatMap((shade) => [
|
|
44
|
+
{
|
|
45
|
+
key: shade === 'DEFAULT' ? `--color-${variant}` : `--color-${variant}-${shade}`,
|
|
46
|
+
value: hex2rgb(colors[mapping[variant]][`${shade}`])
|
|
47
|
+
}
|
|
48
|
+
])
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Constructs and returns the light and dark theme variants based on provided color mapping and color definitions.
|
|
53
|
+
*
|
|
54
|
+
* @param {Object} [mapping=DEFAULT_THEME_MAPPING] - An object mapping variant names to color property names.
|
|
55
|
+
* @param {Object} [colors=defaultColors] - The object containing default color definitions.
|
|
56
|
+
* @returns {Array<Array>} An array containing two arrays, one for the light theme variant and another for the dark theme.
|
|
57
|
+
*/
|
|
58
|
+
export function themeRules(mapping = DEFAULT_THEME_MAPPING, colors = defaultColors) {
|
|
59
|
+
mapping = { ...DEFAULT_THEME_MAPPING, ...mapping }
|
|
60
|
+
colors = { ...defaultColors, ...colors }
|
|
61
|
+
const variants = Object.keys(mapping)
|
|
62
|
+
const rules = variants
|
|
63
|
+
.flatMap((variant) => generateColorRules(variant, colors, mapping))
|
|
64
|
+
.reduce((acc, { key, value }) => ({ ...acc, [key]: value }), {})
|
|
65
|
+
|
|
66
|
+
return rules
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Generates UnoCSS shortcut definitions for semantic tones with bg, border, text.
|
|
71
|
+
* @param {string} name - Color name (e.g., 'primary')
|
|
72
|
+
* @returns {Array} Array of shortcut definitions
|
|
73
|
+
*/
|
|
74
|
+
export function semanticShortcuts(name) {
|
|
75
|
+
const prefixes = ['bg', 'border', 'border-l', 'border-r', 'border-t', 'border-b', 'text', 'ring', 'outline', 'from', 'to', 'divide']
|
|
76
|
+
const shortcuts = []
|
|
77
|
+
|
|
78
|
+
for (const [toneName, lightValue] of Object.entries(TONE_MAP)) {
|
|
79
|
+
const darkValue = 1000 - lightValue
|
|
80
|
+
|
|
81
|
+
for (const prefix of prefixes) {
|
|
82
|
+
// Variant-prefixed regex (e.g., hover:bg-primary-base)
|
|
83
|
+
const variantPattern = new RegExp(`^(.+):${prefix}-${name}-${toneName}(\/\\d+)?$`)
|
|
84
|
+
shortcuts.push([
|
|
85
|
+
variantPattern,
|
|
86
|
+
([, variant, end]) =>
|
|
87
|
+
`${variant}:${prefix}-${name}-${lightValue}${end || ''} ${variant}:dark:${prefix}-${name}-${darkValue}${end || ''}`
|
|
88
|
+
])
|
|
89
|
+
|
|
90
|
+
const opacityPattern = new RegExp(`^${prefix}-${name}-${toneName}(\/\\d+)?$`)
|
|
91
|
+
shortcuts.push([
|
|
92
|
+
opacityPattern,
|
|
93
|
+
([, end]) =>
|
|
94
|
+
`${prefix}-${name}-${lightValue}${end || ''} dark:${prefix}-${name}-${darkValue}${end || ''}`
|
|
95
|
+
])
|
|
96
|
+
|
|
97
|
+
// Exact static shortcut (e.g., bg-primary-base)
|
|
98
|
+
const exactPattern = `${prefix}-${name}-${toneName}`
|
|
99
|
+
shortcuts.push([
|
|
100
|
+
exactPattern,
|
|
101
|
+
`${prefix}-${name}-${lightValue} dark:${prefix}-${name}-${darkValue}`
|
|
102
|
+
])
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return shortcuts
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Generates "on-color" text shortcuts for readable text on colored backgrounds.
|
|
111
|
+
*
|
|
112
|
+
* - `text-on-{name}` → high contrast text for use on z5+ backgrounds (always light text)
|
|
113
|
+
* - `text-on-{name}-muted` → slightly muted but still readable on z5+ backgrounds
|
|
114
|
+
*
|
|
115
|
+
* @param {string} name - Color name (e.g., 'primary', 'surface')
|
|
116
|
+
* @returns {Array} Array of shortcut definitions
|
|
117
|
+
*/
|
|
118
|
+
export function contrastShortcuts(name) {
|
|
119
|
+
return [
|
|
120
|
+
[`text-on-${name}`, `text-${name}-50 dark:text-${name}-50`],
|
|
121
|
+
[`text-on-${name}-muted`, `text-${name}-100 dark:text-${name}-200`]
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Theme class for managing color palettes, mappings, and semantic shortcuts.
|
|
127
|
+
*/
|
|
128
|
+
export class Theme {
|
|
129
|
+
#colors
|
|
130
|
+
#mapping
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
*
|
|
134
|
+
* @param {import('./types.js').ColorTheme} param0
|
|
135
|
+
*/
|
|
136
|
+
constructor({ colors = defaultColors, mapping = DEFAULT_THEME_MAPPING } = {}) {
|
|
137
|
+
this.#colors = { ...defaultColors, ...colors }
|
|
138
|
+
this.#mapping = { ...DEFAULT_THEME_MAPPING, ...mapping }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get colors() {
|
|
142
|
+
return this.#colors
|
|
143
|
+
}
|
|
144
|
+
set colors(colors) {
|
|
145
|
+
this.#colors = { ...colors }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get mapping() {
|
|
149
|
+
return this.#mapping
|
|
150
|
+
}
|
|
151
|
+
set mapping(mapping) {
|
|
152
|
+
this.#mapping = { ...mapping }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
mapVariant(color, variant) {
|
|
156
|
+
return Object.keys(color).reduce(
|
|
157
|
+
(acc, key) => ({
|
|
158
|
+
...acc,
|
|
159
|
+
[key]:
|
|
160
|
+
key === 'DEFAULT'
|
|
161
|
+
? `rgba(var(--color-${variant}),<alpha-value>)`
|
|
162
|
+
: `rgba(var(--color-${variant}-${key}),<alpha-value>)`
|
|
163
|
+
}),
|
|
164
|
+
{}
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getColorRules(mapping = null) {
|
|
169
|
+
const variants = Object.entries({ ...this.#mapping, ...mapping })
|
|
170
|
+
return variants.reduce(
|
|
171
|
+
(acc, [variant, key]) => ({
|
|
172
|
+
...acc,
|
|
173
|
+
[variant]: this.mapVariant(this.#colors[key], variant)
|
|
174
|
+
}),
|
|
175
|
+
{}
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
getPalette(mapping = null) {
|
|
180
|
+
const useMapping = { ...this.#mapping, ...mapping }
|
|
181
|
+
const useColors = { ...defaultColors, ...this.#colors }
|
|
182
|
+
const variants = Object.keys(useMapping)
|
|
183
|
+
const rules = variants
|
|
184
|
+
.flatMap((variant) => generateColorRules(variant, useColors, useMapping))
|
|
185
|
+
.reduce((acc, { key, value }) => ({ ...acc, [key]: value }), {})
|
|
186
|
+
return rules
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
getShortcuts(name) {
|
|
190
|
+
return [...semanticShortcuts(name), ...contrastShortcuts(name)]
|
|
191
|
+
}
|
|
192
|
+
}
|
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
|
+
}
|