@rokkit/states 1.0.0-next.150 → 1.0.0-next.152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/alerts.svelte.js +12 -3
- package/src/derive.svelte.js +2 -2
- package/src/list-controller.svelte.js +1 -1
- package/src/messages.svelte.js +96 -30
- package/src/wrapper.svelte.js +2 -2
package/package.json
CHANGED
package/src/alerts.svelte.js
CHANGED
|
@@ -15,12 +15,21 @@ class AlertsStore {
|
|
|
15
15
|
*/
|
|
16
16
|
#scheduleTimer(id, timeout) {
|
|
17
17
|
if (timeout > 0) {
|
|
18
|
-
this.#timers.set(
|
|
18
|
+
this.#timers.set(
|
|
19
|
+
id,
|
|
20
|
+
setTimeout(() => this.dismiss(id), timeout)
|
|
21
|
+
)
|
|
19
22
|
}
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
push({
|
|
25
|
+
|
|
26
|
+
push({
|
|
27
|
+
type = 'info',
|
|
28
|
+
text,
|
|
29
|
+
dismissible = false,
|
|
30
|
+
timeout = dismissible ? 0 : 4000,
|
|
31
|
+
actions
|
|
32
|
+
} = {}) {
|
|
24
33
|
const id = crypto.randomUUID()
|
|
25
34
|
this.#items = [...this.#items, { id, type, text, dismissible, timeout, actions }]
|
|
26
35
|
this.#scheduleTimer(id, timeout)
|
package/src/derive.svelte.js
CHANGED
|
@@ -47,7 +47,7 @@ function collectVisibleNodes(items, fields, path, expandedKeys) {
|
|
|
47
47
|
return data
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
export function flatVisibleNodes(items, fields = DEFAULT_FIELDS, path = [], expandedKeys = null) {
|
|
52
52
|
if (!items || !Array.isArray(items)) return []
|
|
53
53
|
return collectVisibleNodes(items, fields, path, expandedKeys)
|
|
@@ -84,7 +84,7 @@ function makeLookupEntry(item, norm, fields) {
|
|
|
84
84
|
* @param {SvelteMap} lookup Accumulator map
|
|
85
85
|
* @param {{ item: *, index: number, fields: *, path: Array<number> }} ctx
|
|
86
86
|
*/
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
function visitLookupNode(lookup, ctx) {
|
|
89
89
|
const { item, index, fields, path } = ctx
|
|
90
90
|
const itemPath = [...path, index]
|
|
@@ -44,7 +44,7 @@ export class ListController {
|
|
|
44
44
|
* Process a single item for expanded key initialization.
|
|
45
45
|
* @private
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
#initExpandedItem(item, index, fields, path) {
|
|
49
49
|
if (item === null || item === undefined || typeof item !== 'object') return
|
|
50
50
|
const children = item[fields.children]
|
package/src/messages.svelte.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Default messages for UI components
|
|
2
|
+
* Default (English) messages for all UI components.
|
|
3
3
|
* @type {import('./types').Messages}
|
|
4
4
|
*/
|
|
5
5
|
const defaultMessages = {
|
|
@@ -29,7 +29,8 @@ const defaultMessages = {
|
|
|
29
29
|
next: 'Next slide',
|
|
30
30
|
slides: 'Slide navigation'
|
|
31
31
|
},
|
|
32
|
-
tabs: { add: 'Add tab', remove: 'Remove tab' },
|
|
32
|
+
tabs: { add: 'Add tab', remove: 'Remove tab', placeholder: 'Select a tab to view its content.' },
|
|
33
|
+
table: { empty: 'No data', sortAscending: 'Sort ascending', sortDescending: 'Sort descending' },
|
|
33
34
|
code: { copy: 'Copy code', copied: 'Copied!' },
|
|
34
35
|
range: { lower: 'Lower bound', upper: 'Upper bound', value: 'Value' },
|
|
35
36
|
search_: { clear: 'Clear search' },
|
|
@@ -47,53 +48,118 @@ const defaultMessages = {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
*
|
|
51
|
+
* Deep-merge `overrides` onto `base`. One level deep for nested objects.
|
|
52
|
+
* @param {Record<string, unknown>} base
|
|
53
|
+
* @param {Record<string, unknown>} overrides
|
|
54
|
+
* @returns {Record<string, unknown>}
|
|
51
55
|
*/
|
|
56
|
+
function deepMerge(base, overrides) {
|
|
57
|
+
const result = { ...base }
|
|
58
|
+
for (const key of Object.keys(overrides)) {
|
|
59
|
+
const isObj = (v) => typeof v === 'object' && v !== null
|
|
60
|
+
result[key] =
|
|
61
|
+
isObj(overrides[key]) && isObj(result[key])
|
|
62
|
+
? { ...result[key], ...overrides[key] }
|
|
63
|
+
: overrides[key]
|
|
64
|
+
}
|
|
65
|
+
return result
|
|
66
|
+
}
|
|
67
|
+
|
|
52
68
|
class MessagesStore {
|
|
53
|
-
|
|
54
|
-
#
|
|
69
|
+
#registry = {}
|
|
70
|
+
#locale = 'en'
|
|
71
|
+
|
|
72
|
+
// ─── Flat string keys ─────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
emptyList = $state(defaultMessages.emptyList)
|
|
75
|
+
emptyTree = $state(defaultMessages.emptyTree)
|
|
76
|
+
loading = $state(defaultMessages.loading)
|
|
77
|
+
noResults = $state(defaultMessages.noResults)
|
|
78
|
+
select = $state(defaultMessages.select)
|
|
79
|
+
search = $state(defaultMessages.search)
|
|
80
|
+
|
|
81
|
+
// ─── Nested component namespaces ──────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
list = $state({ ...defaultMessages.list })
|
|
84
|
+
tree = $state({ ...defaultMessages.tree })
|
|
85
|
+
toolbar = $state({ ...defaultMessages.toolbar })
|
|
86
|
+
menu = $state({ ...defaultMessages.menu })
|
|
87
|
+
toggle = $state({ ...defaultMessages.toggle })
|
|
88
|
+
rating = $state({ ...defaultMessages.rating })
|
|
89
|
+
stepper = $state({ ...defaultMessages.stepper })
|
|
90
|
+
breadcrumbs = $state({ ...defaultMessages.breadcrumbs })
|
|
91
|
+
carousel = $state({ ...defaultMessages.carousel })
|
|
92
|
+
tabs = $state({ ...defaultMessages.tabs })
|
|
93
|
+
table = $state({ ...defaultMessages.table })
|
|
94
|
+
code = $state({ ...defaultMessages.code })
|
|
95
|
+
range = $state({ ...defaultMessages.range })
|
|
96
|
+
search_ = $state({ ...defaultMessages.search_ })
|
|
97
|
+
filter = $state({ ...defaultMessages.filter })
|
|
98
|
+
grid = $state({ ...defaultMessages.grid })
|
|
99
|
+
uploadProgress = $state({ ...defaultMessages.uploadProgress })
|
|
100
|
+
floatingNav = $state({ ...defaultMessages.floatingNav })
|
|
101
|
+
mode = $state({ ...defaultMessages.mode })
|
|
102
|
+
|
|
103
|
+
// ─── Active locale ─────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
/** Currently active locale tag (read-only). */
|
|
106
|
+
get locale() {
|
|
107
|
+
return this.#locale
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ─── Internals ─────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
#apply() {
|
|
113
|
+
const overrides = this.#registry[this.#locale]
|
|
114
|
+
const computed = overrides
|
|
115
|
+
? /** @type {import('./types').Messages} */ (deepMerge(defaultMessages, overrides))
|
|
116
|
+
: structuredClone(defaultMessages)
|
|
117
|
+
Object.assign(this, computed)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ─── Public API ────────────────────────────────────────────────────────────
|
|
55
121
|
|
|
56
122
|
/**
|
|
57
|
-
*
|
|
58
|
-
* @
|
|
123
|
+
* Register translation overrides for a locale. Unspecified keys fall back to English defaults.
|
|
124
|
+
* @param {string} locale — BCP 47 tag or any identifier (e.g. 'de', 'fr-CA')
|
|
125
|
+
* @param {Partial<import('./types').Messages>} overrides
|
|
59
126
|
*/
|
|
60
|
-
|
|
61
|
-
|
|
127
|
+
register(locale, overrides) {
|
|
128
|
+
this.#registry = { ...this.#registry, [locale]: overrides }
|
|
129
|
+
this.#apply()
|
|
62
130
|
}
|
|
63
131
|
|
|
64
132
|
/**
|
|
65
|
-
*
|
|
66
|
-
* @param {
|
|
67
|
-
* @param {Record<string, unknown>} custom
|
|
68
|
-
* @param {string} key
|
|
133
|
+
* Switch to a previously registered locale. Use 'en' to restore English defaults.
|
|
134
|
+
* @param {string} locale
|
|
69
135
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
merged[key] = { ...merged[key], ...custom[key] }
|
|
74
|
-
} else {
|
|
75
|
-
merged[key] = custom[key]
|
|
76
|
-
}
|
|
136
|
+
setLocale(locale) {
|
|
137
|
+
this.#locale = locale
|
|
138
|
+
this.#apply()
|
|
77
139
|
}
|
|
78
140
|
|
|
79
141
|
/**
|
|
80
|
-
*
|
|
81
|
-
* @param {Partial<import('./types').Messages>}
|
|
142
|
+
* Apply one-off overrides without naming a locale (convenience / backward compat).
|
|
143
|
+
* @param {Partial<import('./types').Messages>} overrides
|
|
82
144
|
*/
|
|
83
|
-
set(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
this.#messages = merged
|
|
145
|
+
set(overrides) {
|
|
146
|
+
this.#registry = { ...this.#registry, _custom: overrides }
|
|
147
|
+
this.#locale = '_custom'
|
|
148
|
+
this.#apply()
|
|
89
149
|
}
|
|
90
150
|
|
|
91
151
|
/**
|
|
92
|
-
* Reset to
|
|
152
|
+
* Reset to English defaults and clear all registered locales.
|
|
93
153
|
*/
|
|
94
154
|
reset() {
|
|
95
|
-
this.#
|
|
155
|
+
this.#registry = {}
|
|
156
|
+
this.#locale = 'en'
|
|
157
|
+
this.#apply()
|
|
96
158
|
}
|
|
97
159
|
}
|
|
98
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Reactive messages store. Access strings directly: `messages.select`, `messages.tree.expand`.
|
|
163
|
+
* Configure locale: `messages.register('de', {...})` + `messages.setLocale('de')`.
|
|
164
|
+
*/
|
|
99
165
|
export const messages = new MessagesStore()
|
package/src/wrapper.svelte.js
CHANGED
|
@@ -125,7 +125,7 @@ export class Wrapper {
|
|
|
125
125
|
* At root level with no parent: no-op.
|
|
126
126
|
* When collapsible=false, skips closing the group but still moves focus to parent.
|
|
127
127
|
*/
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
collapse(_path) {
|
|
130
130
|
if (!this.#focusedKey) return
|
|
131
131
|
const node = this.flatView.find((n) => n.key === this.#focusedKey)
|
|
@@ -177,7 +177,7 @@ export class Wrapper {
|
|
|
177
177
|
* Unlike select(), this only applies to groups and never fires onselect.
|
|
178
178
|
* No-op when collapsible=false.
|
|
179
179
|
*/
|
|
180
|
-
|
|
180
|
+
|
|
181
181
|
toggle(path) {
|
|
182
182
|
if (!this.#collapsible) return
|
|
183
183
|
const key = path ?? this.#focusedKey
|