wuira 0.2.1 → 0.2.3

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/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ with the release date and highlight breaking changes explicitly.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.2.3 — 2026-10-15
11
+
12
+ ### Added
13
+
14
+ - Tooltip attachment in Cell decorations, inherited by all visual components.
15
+ Route hover to native GameTooltip or another native tooltip with custom text,
16
+ extra lines or a callback using native spell, item, unit, hyperlink and action APIs.
17
+ - Motion-only hover input preserves click-through behavior and existing scripts;
18
+ owned tooltips close on leave, hide and disposal without changing secure actions.
19
+
20
+ ## 0.2.2 — 2026-10-15
21
+
22
+ ### Added
23
+
24
+ - Event-driven EditModeLayouts source with native layout metadata, active layout,
25
+ availability status and disposable change subscriptions. Layout keys use native
26
+ type, interface style and name; indices are not persistent IDs.
27
+
28
+ ### Fixed
29
+
30
+ - Keep generated spell files in LF format on Windows checkouts so deterministic
31
+ generation checks compare identical bytes across operating systems.
32
+
10
33
  ## 0.2.1 — 2026-10-15
11
34
 
12
35
  ### Changed
package/README.md CHANGED
@@ -507,6 +507,23 @@ event listener when its owning feature ends. Support is capability-checked;
507
507
  actual timing still needs validation in Forever. The adapter follows the
508
508
  [Forever swing API definition](https://github.com/Gethe/wow-ui-source/blob/bd2470aed543f72697a044e989285b6c83e63f73/Interface/AddOns/Blizzard_APIDocumentationGenerated/SwingTimerDocumentation.lua).
509
509
 
510
+ ### Native Edit Mode layouts
511
+
512
+ ```ts
513
+ import { EditModeLayouts } from "wuira/datasources";
514
+
515
+ const layouts = new EditModeLayouts();
516
+ console.log(layouts.Active?.name);
517
+ const stop = layouts.onChange(snapshot => console.log(snapshot.active?.key));
518
+ // On teardown: stop(); layouts.Dispose();
519
+ ```
520
+
521
+ `Layouts` lists native indices, names, types and derived keys. `Value` is a detached
522
+ snapshot; native events refresh it without polling. Check `Status` for availability.
523
+ Forever exposes an index rather than a persistent UUID: `key` combines type,
524
+ interface style and name, survives index shifts, and changes on rename. These keys
525
+ describe local game layouts, not portable layout exports.
526
+
510
527
  ### Group members
511
528
 
512
529
  ```ts
@@ -593,6 +610,30 @@ not install event listeners or poll automatically. The current adapter requires
593
610
  in-game verification. Unit tokens refer to snapshot time and are not persistent
594
611
  caster identities. No Paladin-specific spell catalog or filtering is built in.
595
612
 
613
+ ### Native tooltips
614
+
615
+ `Tooltip` attaches to any Cell through decorations. It forwards hover to the
616
+ game's native tooltip API and creates no visual regions or update loop.
617
+
618
+ ```tsx
619
+ import { Cell, Tooltip } from "wuira/ui";
620
+
621
+ <Cell width={40} height={40} decorations={{
622
+ tooltip: new Tooltip({ text: "My tooltip", lines: [{ text: "Extra details" }] }),
623
+ }} />;
624
+
625
+ <Cell width={40} height={40} decorations={{
626
+ tooltip: new Tooltip({ show: tooltip => tooltip.SetSpellByID(635) }),
627
+ }} />;
628
+ ```
629
+
630
+ The same attachment works on SecureButton. Call SetItemByID, SetUnit, SetHyperlink
631
+ or SetAction inside `show` for other native content. `text` may be a function
632
+ evaluated on hover; `show` may return false to suppress display. Optional `target`
633
+ selects another native tooltip, and `anchor` defaults to ANCHOR_RIGHT.
634
+ Hover preserves click-through and existing scripts. Leave, hide and unmount close
635
+ only a tooltip still owned by that Cell. Use one decoration instance per Cell.
636
+
596
637
  ### Visual styling
597
638
 
598
639
  <!-- Screenshot: a dark Cell with a thin colored border. -->
@@ -0,0 +1,33 @@
1
+ import { DataSource } from "./DataSource";
2
+ export type EditModeLayoutsStatus = "ready" | "unavailable" | "restricted";
3
+ export interface EditModeLayout {
4
+ /** Native one-based index. Deleting another layout can change this number. */
5
+ readonly index: number;
6
+ readonly name: string;
7
+ readonly type: number;
8
+ readonly interfaceStyle?: number;
9
+ /** Derived from native type, interface style and name; changes on rename. */
10
+ readonly key: string;
11
+ }
12
+ export interface EditModeLayoutSnapshot {
13
+ readonly layouts: readonly EditModeLayout[];
14
+ readonly active?: EditModeLayout;
15
+ }
16
+ /** Read-only native Edit Mode layouts, refreshed by events without polling. */
17
+ export declare class EditModeLayouts extends DataSource<EditModeLayoutSnapshot> {
18
+ private snapshot;
19
+ private status;
20
+ private readonly listeners;
21
+ private readonly frame;
22
+ private disposed;
23
+ constructor();
24
+ get Status(): EditModeLayoutsStatus;
25
+ get Layouts(): readonly EditModeLayout[];
26
+ get Active(): EditModeLayout | undefined;
27
+ protected HasSecretFields(): boolean;
28
+ protected GetValue(): EditModeLayoutSnapshot;
29
+ /** No initial emission. Availability changes also notify observers. */
30
+ onChange(listener: (value: EditModeLayoutSnapshot) => void): () => void;
31
+ private Refresh;
32
+ Dispose(): void;
33
+ }
@@ -0,0 +1,689 @@
1
+ --[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
2
+ -- Lua Library inline imports
3
+ local function __TS__Class(self)
4
+ local c = {prototype = {}}
5
+ c.prototype.__index = c.prototype
6
+ c.prototype.constructor = c
7
+ return c
8
+ end
9
+
10
+ local function __TS__ClassExtends(target, base)
11
+ target.____super = base
12
+ local staticMetatable = setmetatable({__index = base}, base)
13
+ setmetatable(target, staticMetatable)
14
+ local baseMetatable = getmetatable(base)
15
+ if baseMetatable then
16
+ if type(baseMetatable.__index) == "function" then
17
+ staticMetatable.__index = baseMetatable.__index
18
+ end
19
+ if type(baseMetatable.__newindex) == "function" then
20
+ staticMetatable.__newindex = baseMetatable.__newindex
21
+ end
22
+ end
23
+ setmetatable(target.prototype, base.prototype)
24
+ if type(base.prototype.__index) == "function" then
25
+ target.prototype.__index = base.prototype.__index
26
+ end
27
+ if type(base.prototype.__newindex) == "function" then
28
+ target.prototype.__newindex = base.prototype.__newindex
29
+ end
30
+ if type(base.prototype.__tostring) == "function" then
31
+ target.prototype.__tostring = base.prototype.__tostring
32
+ end
33
+ end
34
+
35
+ local __TS__Symbol, Symbol
36
+ do
37
+ local symbolMetatable = {__tostring = function(self)
38
+ return ("Symbol(" .. (self.description or "")) .. ")"
39
+ end}
40
+ function __TS__Symbol(description)
41
+ return setmetatable({description = description}, symbolMetatable)
42
+ end
43
+ Symbol = {
44
+ asyncDispose = __TS__Symbol("Symbol.asyncDispose"),
45
+ dispose = __TS__Symbol("Symbol.dispose"),
46
+ iterator = __TS__Symbol("Symbol.iterator"),
47
+ hasInstance = __TS__Symbol("Symbol.hasInstance"),
48
+ species = __TS__Symbol("Symbol.species"),
49
+ toStringTag = __TS__Symbol("Symbol.toStringTag")
50
+ }
51
+ end
52
+
53
+ local __TS__Iterator
54
+ do
55
+ local function iteratorGeneratorStep(self)
56
+ local co = self.____coroutine
57
+ local status, value = coroutine.resume(co)
58
+ if not status then
59
+ error(value, 0)
60
+ end
61
+ if coroutine.status(co) == "dead" then
62
+ return
63
+ end
64
+ return true, value
65
+ end
66
+ local function iteratorIteratorStep(self)
67
+ local result = self:next()
68
+ if result.done then
69
+ return
70
+ end
71
+ return true, result.value
72
+ end
73
+ local function iteratorStringStep(self, index)
74
+ index = index + 1
75
+ if index > #self then
76
+ return
77
+ end
78
+ return index, string.sub(self, index, index)
79
+ end
80
+ function __TS__Iterator(iterable)
81
+ if type(iterable) == "string" then
82
+ return iteratorStringStep, iterable, 0
83
+ elseif iterable.____coroutine ~= nil then
84
+ return iteratorGeneratorStep, iterable
85
+ elseif iterable[Symbol.iterator] then
86
+ local iterator = iterable[Symbol.iterator](iterable)
87
+ return iteratorIteratorStep, iterator
88
+ else
89
+ return ipairs(iterable)
90
+ end
91
+ end
92
+ end
93
+
94
+ local function __TS__New(target, ...)
95
+ local instance = setmetatable({}, target.prototype)
96
+ instance:____constructor(...)
97
+ return instance
98
+ end
99
+
100
+ local Set
101
+ do
102
+ Set = __TS__Class()
103
+ Set.name = "Set"
104
+ function Set.prototype.____constructor(self, values)
105
+ self[Symbol.toStringTag] = "Set"
106
+ self.size = 0
107
+ self.nextKey = {}
108
+ self.previousKey = {}
109
+ if values == nil then
110
+ return
111
+ end
112
+ local iterable = values
113
+ if iterable[Symbol.iterator] then
114
+ local iterator = iterable[Symbol.iterator](iterable)
115
+ while true do
116
+ local result = iterator:next()
117
+ if result.done then
118
+ break
119
+ end
120
+ self:add(result.value)
121
+ end
122
+ else
123
+ local array = values
124
+ for ____, value in ipairs(array) do
125
+ self:add(value)
126
+ end
127
+ end
128
+ end
129
+ function Set.prototype.add(self, value)
130
+ local isNewValue = not self:has(value)
131
+ if isNewValue then
132
+ self.size = self.size + 1
133
+ end
134
+ if self.firstKey == nil then
135
+ self.firstKey = value
136
+ self.lastKey = value
137
+ elseif isNewValue then
138
+ self.nextKey[self.lastKey] = value
139
+ self.previousKey[value] = self.lastKey
140
+ self.lastKey = value
141
+ end
142
+ return self
143
+ end
144
+ function Set.prototype.clear(self)
145
+ self.nextKey = {}
146
+ self.previousKey = {}
147
+ self.firstKey = nil
148
+ self.lastKey = nil
149
+ self.size = 0
150
+ end
151
+ function Set.prototype.delete(self, value)
152
+ local contains = self:has(value)
153
+ if contains then
154
+ self.size = self.size - 1
155
+ local next = self.nextKey[value]
156
+ local previous = self.previousKey[value]
157
+ if next ~= nil and previous ~= nil then
158
+ self.nextKey[previous] = next
159
+ self.previousKey[next] = previous
160
+ elseif next ~= nil then
161
+ self.firstKey = next
162
+ self.previousKey[next] = nil
163
+ elseif previous ~= nil then
164
+ self.lastKey = previous
165
+ self.nextKey[previous] = nil
166
+ else
167
+ self.firstKey = nil
168
+ self.lastKey = nil
169
+ end
170
+ self.nextKey[value] = nil
171
+ self.previousKey[value] = nil
172
+ end
173
+ return contains
174
+ end
175
+ function Set.prototype.forEach(self, callback)
176
+ for ____, key in __TS__Iterator(self:keys()) do
177
+ callback(nil, key, key, self)
178
+ end
179
+ end
180
+ function Set.prototype.has(self, value)
181
+ return self.nextKey[value] ~= nil or self.lastKey == value
182
+ end
183
+ Set.prototype[Symbol.iterator] = function(self)
184
+ return self:values()
185
+ end
186
+ function Set.prototype.entries(self)
187
+ local function getFirstKey()
188
+ return self.firstKey
189
+ end
190
+ local nextKey = self.nextKey
191
+ local key
192
+ local started = false
193
+ return {
194
+ [Symbol.iterator] = function(self)
195
+ return self
196
+ end,
197
+ next = function(self)
198
+ if not started then
199
+ started = true
200
+ key = getFirstKey(nil)
201
+ else
202
+ key = nextKey[key]
203
+ end
204
+ return {done = not key, value = {key, key}}
205
+ end
206
+ }
207
+ end
208
+ function Set.prototype.keys(self)
209
+ local function getFirstKey()
210
+ return self.firstKey
211
+ end
212
+ local nextKey = self.nextKey
213
+ local key
214
+ local started = false
215
+ return {
216
+ [Symbol.iterator] = function(self)
217
+ return self
218
+ end,
219
+ next = function(self)
220
+ if not started then
221
+ started = true
222
+ key = getFirstKey(nil)
223
+ else
224
+ key = nextKey[key]
225
+ end
226
+ return {done = not key, value = key}
227
+ end
228
+ }
229
+ end
230
+ function Set.prototype.values(self)
231
+ local function getFirstKey()
232
+ return self.firstKey
233
+ end
234
+ local nextKey = self.nextKey
235
+ local key
236
+ local started = false
237
+ return {
238
+ [Symbol.iterator] = function(self)
239
+ return self
240
+ end,
241
+ next = function(self)
242
+ if not started then
243
+ started = true
244
+ key = getFirstKey(nil)
245
+ else
246
+ key = nextKey[key]
247
+ end
248
+ return {done = not key, value = key}
249
+ end
250
+ }
251
+ end
252
+ function Set.prototype.union(self, other)
253
+ local result = __TS__New(Set, self)
254
+ for ____, item in __TS__Iterator(other) do
255
+ result:add(item)
256
+ end
257
+ return result
258
+ end
259
+ function Set.prototype.intersection(self, other)
260
+ local result = __TS__New(Set)
261
+ for ____, item in __TS__Iterator(self) do
262
+ if other:has(item) then
263
+ result:add(item)
264
+ end
265
+ end
266
+ return result
267
+ end
268
+ function Set.prototype.difference(self, other)
269
+ local result = __TS__New(Set, self)
270
+ for ____, item in __TS__Iterator(other) do
271
+ result:delete(item)
272
+ end
273
+ return result
274
+ end
275
+ function Set.prototype.symmetricDifference(self, other)
276
+ local result = __TS__New(Set, self)
277
+ for ____, item in __TS__Iterator(other) do
278
+ if self:has(item) then
279
+ result:delete(item)
280
+ else
281
+ result:add(item)
282
+ end
283
+ end
284
+ return result
285
+ end
286
+ function Set.prototype.isSubsetOf(self, other)
287
+ for ____, item in __TS__Iterator(self) do
288
+ if not other:has(item) then
289
+ return false
290
+ end
291
+ end
292
+ return true
293
+ end
294
+ function Set.prototype.isSupersetOf(self, other)
295
+ for ____, item in __TS__Iterator(other) do
296
+ if not self:has(item) then
297
+ return false
298
+ end
299
+ end
300
+ return true
301
+ end
302
+ function Set.prototype.isDisjointFrom(self, other)
303
+ for ____, item in __TS__Iterator(self) do
304
+ if other:has(item) then
305
+ return false
306
+ end
307
+ end
308
+ return true
309
+ end
310
+ Set[Symbol.species] = Set
311
+ end
312
+
313
+ local function __TS__ObjectAssign(target, ...)
314
+ local sources = {...}
315
+ for i = 1, #sources do
316
+ local source = sources[i]
317
+ if type(source) == "table" then
318
+ for key in pairs(source) do
319
+ target[key] = source[key]
320
+ end
321
+ end
322
+ end
323
+ return target
324
+ end
325
+
326
+ local function __TS__ArrayMap(self, callbackfn, thisArg)
327
+ local result = {}
328
+ for i = 1, #self do
329
+ result[i] = callbackfn(thisArg, self[i], i - 1, self)
330
+ end
331
+ return result
332
+ end
333
+
334
+ local function __TS__ArrayIsArray(value)
335
+ return type(value) == "table" and (value[1] ~= nil or next(value) == nil)
336
+ end
337
+
338
+ local function __TS__NumberIsFinite(value)
339
+ return type(value) == "number" and value == value and value ~= math.huge and value ~= -math.huge
340
+ end
341
+
342
+ local function __TS__NumberIsInteger(value)
343
+ return __TS__NumberIsFinite(value) and math.floor(value) == value
344
+ end
345
+
346
+ local __TS__ArrayFrom
347
+ do
348
+ local function arrayLikeStep(self, index)
349
+ index = index + 1
350
+ if index > self.length then
351
+ return
352
+ end
353
+ return index, self[index]
354
+ end
355
+ local function arrayLikeIterator(arr)
356
+ if type(arr.length) == "number" then
357
+ return arrayLikeStep, arr, 0
358
+ end
359
+ return __TS__Iterator(arr)
360
+ end
361
+ function __TS__ArrayFrom(arrayLike, mapFn, thisArg)
362
+ local result = {}
363
+ if mapFn == nil then
364
+ for ____, v in arrayLikeIterator(arrayLike) do
365
+ result[#result + 1] = v
366
+ end
367
+ else
368
+ local i = 0
369
+ for ____, v in arrayLikeIterator(arrayLike) do
370
+ local ____mapFn_3 = mapFn
371
+ local ____thisArg_1 = thisArg
372
+ local ____v_2 = v
373
+ local ____i_0 = i
374
+ i = ____i_0 + 1
375
+ result[#result + 1] = ____mapFn_3(____thisArg_1, ____v_2, ____i_0)
376
+ end
377
+ end
378
+ return result
379
+ end
380
+ end
381
+
382
+ local function __TS__CloneDescriptor(____bindingPattern0)
383
+ local value
384
+ local writable
385
+ local set
386
+ local get
387
+ local configurable
388
+ local enumerable
389
+ enumerable = ____bindingPattern0.enumerable
390
+ configurable = ____bindingPattern0.configurable
391
+ get = ____bindingPattern0.get
392
+ set = ____bindingPattern0.set
393
+ writable = ____bindingPattern0.writable
394
+ value = ____bindingPattern0.value
395
+ local descriptor = {enumerable = enumerable == true, configurable = configurable == true}
396
+ local hasGetterOrSetter = get ~= nil or set ~= nil
397
+ local hasValueOrWritableAttribute = writable ~= nil or value ~= nil
398
+ if hasGetterOrSetter and hasValueOrWritableAttribute then
399
+ error("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute.", 0)
400
+ end
401
+ if get or set then
402
+ descriptor.get = get
403
+ descriptor.set = set
404
+ else
405
+ descriptor.value = value
406
+ descriptor.writable = writable == true
407
+ end
408
+ return descriptor
409
+ end
410
+
411
+ local __TS__DescriptorGet
412
+ do
413
+ local getmetatable = _G.getmetatable
414
+ local ____rawget = _G.rawget
415
+ function __TS__DescriptorGet(self, metatable, key)
416
+ while metatable do
417
+ local rawResult = ____rawget(metatable, key)
418
+ if rawResult ~= nil then
419
+ return rawResult
420
+ end
421
+ local descriptors = ____rawget(metatable, "_descriptors")
422
+ if descriptors then
423
+ local descriptor = descriptors[key]
424
+ if descriptor ~= nil then
425
+ if descriptor.get then
426
+ return descriptor.get(self)
427
+ end
428
+ return descriptor.value
429
+ end
430
+ end
431
+ metatable = getmetatable(metatable)
432
+ end
433
+ end
434
+ end
435
+
436
+ local __TS__DescriptorSet
437
+ do
438
+ local getmetatable = _G.getmetatable
439
+ local ____rawget = _G.rawget
440
+ local rawset = _G.rawset
441
+ function __TS__DescriptorSet(self, metatable, key, value)
442
+ while metatable do
443
+ local descriptors = ____rawget(metatable, "_descriptors")
444
+ if descriptors then
445
+ local descriptor = descriptors[key]
446
+ if descriptor ~= nil then
447
+ if descriptor.set then
448
+ descriptor.set(self, value)
449
+ else
450
+ if descriptor.writable == false then
451
+ error(
452
+ ((("Cannot assign to read only property '" .. key) .. "' of object '") .. tostring(self)) .. "'",
453
+ 0
454
+ )
455
+ end
456
+ descriptor.value = value
457
+ end
458
+ return
459
+ end
460
+ end
461
+ metatable = getmetatable(metatable)
462
+ end
463
+ rawset(self, key, value)
464
+ end
465
+ end
466
+
467
+ local __TS__SetDescriptor
468
+ do
469
+ local getmetatable = _G.getmetatable
470
+ local function descriptorIndex(self, key)
471
+ return __TS__DescriptorGet(
472
+ self,
473
+ getmetatable(self),
474
+ key
475
+ )
476
+ end
477
+ local function descriptorNewIndex(self, key, value)
478
+ return __TS__DescriptorSet(
479
+ self,
480
+ getmetatable(self),
481
+ key,
482
+ value
483
+ )
484
+ end
485
+ function __TS__SetDescriptor(target, key, desc, isPrototype)
486
+ if isPrototype == nil then
487
+ isPrototype = false
488
+ end
489
+ local ____isPrototype_0
490
+ if isPrototype then
491
+ ____isPrototype_0 = target
492
+ else
493
+ ____isPrototype_0 = getmetatable(target)
494
+ end
495
+ local metatable = ____isPrototype_0
496
+ if not metatable then
497
+ metatable = {}
498
+ setmetatable(target, metatable)
499
+ end
500
+ if not isPrototype and not rawget(metatable, "_isOwnDescriptorMetatable") then
501
+ local instanceMetatable = {}
502
+ instanceMetatable._isOwnDescriptorMetatable = true
503
+ setmetatable(instanceMetatable, metatable)
504
+ setmetatable(target, instanceMetatable)
505
+ metatable = instanceMetatable
506
+ end
507
+ local value = rawget(target, key)
508
+ if value ~= nil then
509
+ rawset(target, key, nil)
510
+ end
511
+ if not rawget(metatable, "_descriptors") then
512
+ metatable._descriptors = {}
513
+ end
514
+ metatable._descriptors[key] = __TS__CloneDescriptor(desc)
515
+ metatable.__index = descriptorIndex
516
+ metatable.__newindex = descriptorNewIndex
517
+ end
518
+ end
519
+ -- End of Lua Library inline imports
520
+ local ____exports = {}
521
+ local ____DataSource = require("datasources.DataSource")
522
+ local DataSource = ____DataSource.DataSource
523
+ --- Read-only native Edit Mode layouts, refreshed by events without polling.
524
+ ____exports.EditModeLayouts = __TS__Class()
525
+ local EditModeLayouts = ____exports.EditModeLayouts
526
+ EditModeLayouts.name = "EditModeLayouts"
527
+ __TS__ClassExtends(EditModeLayouts, DataSource)
528
+ function EditModeLayouts.prototype.____constructor(self)
529
+ DataSource.prototype.____constructor(self, {layouts = {}})
530
+ self.snapshot = {layouts = {}}
531
+ self.status = "unavailable"
532
+ self.listeners = __TS__New(Set)
533
+ self.disposed = false
534
+ self.frame = CreateFrame("Frame", nil, UIParent)
535
+ for ____, event in ipairs({"EDIT_MODE_LAYOUTS_UPDATED", "PLAYER_ENTERING_WORLD", "PLAYER_SPECIALIZATION_CHANGED"}) do
536
+ self.frame:RegisterEvent(event)
537
+ end
538
+ self.frame:SetScript(
539
+ "OnEvent",
540
+ function(_frame, event, value)
541
+ local ____self_Refresh_1 = self.Refresh
542
+ local ____temp_0
543
+ if event == "EDIT_MODE_LAYOUTS_UPDATED" then
544
+ ____temp_0 = value
545
+ else
546
+ ____temp_0 = nil
547
+ end
548
+ return ____self_Refresh_1(self, ____temp_0)
549
+ end
550
+ )
551
+ self:Refresh()
552
+ end
553
+ function EditModeLayouts.prototype.HasSecretFields(self)
554
+ return self.status == "restricted"
555
+ end
556
+ function EditModeLayouts.prototype.GetValue(self)
557
+ local ____TS__ArrayMap_result_3 = __TS__ArrayMap(
558
+ self.snapshot.layouts,
559
+ function(____, layout) return __TS__ObjectAssign({}, layout) end
560
+ )
561
+ local ____temp_2
562
+ if self.snapshot.active == nil then
563
+ ____temp_2 = nil
564
+ else
565
+ ____temp_2 = __TS__ObjectAssign({}, self.snapshot.active)
566
+ end
567
+ return {layouts = ____TS__ArrayMap_result_3, active = ____temp_2}
568
+ end
569
+ function EditModeLayouts.prototype.onChange(self, listener)
570
+ if self.disposed then
571
+ error("EditModeLayouts: source is disposed")
572
+ end
573
+ self.listeners:add(listener)
574
+ return function()
575
+ self.listeners:delete(listener)
576
+ end
577
+ end
578
+ function EditModeLayouts.prototype.Refresh(self, value)
579
+ local function signature()
580
+ local ____temp_6 = ((self.status .. "|") .. table.concat(
581
+ __TS__ArrayMap(
582
+ self.snapshot.layouts,
583
+ function(____, layout) return (((tostring(layout.index) .. ":") .. tostring(#layout.key)) .. ":") .. layout.key end
584
+ ),
585
+ "|"
586
+ )) .. "|active:"
587
+ local ____opt_4 = self.snapshot.active
588
+ return ____temp_6 .. tostring(____opt_4 and ____opt_4.index or "")
589
+ end
590
+ local previous = signature(nil)
591
+ self.snapshot = {layouts = {}}
592
+ self.status = "unavailable"
593
+ if value ~= nil or (C_EditMode and C_EditMode.GetLayouts) ~= nil then
594
+ do
595
+ local function ____catch()
596
+ self.status = "unavailable"
597
+ end
598
+ local ____try = pcall(function()
599
+ local ____value_13 = value
600
+ if ____value_13 == nil then
601
+ local ____opt_9 = C_EditMode and C_EditMode.GetLayouts
602
+ ____value_13 = ____opt_9 and ____opt_9()
603
+ end
604
+ local native = ____value_13
605
+ if issecretvalue(native) then
606
+ self.status = "restricted"
607
+ elseif native ~= nil then
608
+ if issecretvalue(native.layouts) or issecretvalue(native.activeLayout) then
609
+ self.status = "restricted"
610
+ elseif __TS__ArrayIsArray(native.layouts) and __TS__NumberIsInteger(native.activeLayout) and native.activeLayout > 0 then
611
+ local layouts = {}
612
+ self.status = "ready"
613
+ do
614
+ local offset = 0
615
+ while offset < #native.layouts do
616
+ local layout = native.layouts[offset + 1]
617
+ if issecretvalue(layout) or issecretvalue(layout.layoutName) or issecretvalue(layout.layoutType) or issecretvalue(layout.interfaceStyle) then
618
+ self.status = "restricted"
619
+ break
620
+ end
621
+ if type(layout.layoutName) ~= "string" or not __TS__NumberIsInteger(layout.layoutType) or layout.interfaceStyle ~= nil and not __TS__NumberIsInteger(layout.interfaceStyle) then
622
+ self.status = "unavailable"
623
+ break
624
+ end
625
+ layouts[#layouts + 1] = {
626
+ index = offset + 1,
627
+ name = layout.layoutName,
628
+ type = layout.layoutType,
629
+ interfaceStyle = layout.interfaceStyle,
630
+ key = (((tostring(layout.layoutType) .. ":") .. tostring(layout.interfaceStyle or "")) .. ":") .. layout.layoutName
631
+ }
632
+ offset = offset + 1
633
+ end
634
+ end
635
+ if self.status == "ready" then
636
+ self.snapshot = {layouts = layouts, active = layouts[native.activeLayout]}
637
+ end
638
+ end
639
+ end
640
+ end)
641
+ if not ____try then
642
+ ____catch()
643
+ end
644
+ end
645
+ end
646
+ if signature(nil) ~= previous and (self.status ~= "ready" or self:CanEmit(self:GetValue())) then
647
+ for ____, listener in ipairs(__TS__ArrayFrom(self.listeners)) do
648
+ listener(
649
+ nil,
650
+ self:GetValue()
651
+ )
652
+ end
653
+ end
654
+ end
655
+ function EditModeLayouts.prototype.Dispose(self)
656
+ if self.disposed then
657
+ return
658
+ end
659
+ self.disposed = true
660
+ self.listeners:clear()
661
+ self.frame:UnregisterAllEvents()
662
+ self.frame:SetScript("OnEvent", nil)
663
+ self.frame:Hide()
664
+ end
665
+ __TS__SetDescriptor(
666
+ EditModeLayouts.prototype,
667
+ "Status",
668
+ {get = function(self)
669
+ return self.status
670
+ end},
671
+ true
672
+ )
673
+ __TS__SetDescriptor(
674
+ EditModeLayouts.prototype,
675
+ "Layouts",
676
+ {get = function(self)
677
+ return self.Value.layouts
678
+ end},
679
+ true
680
+ )
681
+ __TS__SetDescriptor(
682
+ EditModeLayouts.prototype,
683
+ "Active",
684
+ {get = function(self)
685
+ return self.Value.active
686
+ end},
687
+ true
688
+ )
689
+ return ____exports
@@ -1,5 +1,7 @@
1
1
  /// <reference path="../platform/forever.d.ts" />
2
2
  export { DataSource } from "./DataSource";
3
+ export { EditModeLayouts } from "./EditModeLayouts";
4
+ export type { EditModeLayout, EditModeLayoutSnapshot, EditModeLayoutsStatus } from "./EditModeLayouts";
3
5
  export { ProgressSource } from "./ProgressSource";
4
6
  export { HP, Mana, Unit, DataSources, HealthPrediction } from "./Resources";
5
7
  export { Aura, AuraSource, Buffs, Debuffs } from "./Auras";
@@ -4,6 +4,10 @@ do
4
4
  local ____DataSource = require("datasources.DataSource")
5
5
  ____exports.DataSource = ____DataSource.DataSource
6
6
  end
7
+ do
8
+ local ____EditModeLayouts = require("datasources.EditModeLayouts")
9
+ ____exports.EditModeLayouts = ____EditModeLayouts.EditModeLayouts
10
+ end
7
11
  do
8
12
  local ____ProgressSource = require("datasources.ProgressSource")
9
13
  ____exports.ProgressSource = ____ProgressSource.ProgressSource
@@ -50,9 +50,12 @@ interface wuiraNativeFrame {
50
50
  SetFrameLevel(level: number): void;
51
51
  CreateFontString(name: undefined, layer: "OVERLAY"): wuiraNativeFontString;
52
52
  EnableMouse(enabled: boolean): void;
53
+ EnableMouseMotion(enabled: boolean): void;
54
+ IsMouseMotionEnabled(): boolean;
53
55
  RegisterEvent(event: string): void;
54
56
  UnregisterAllEvents(): void;
55
57
  SetScript(script: "OnEvent", handler: ((this: void, frame: wuiraNativeFrame, event: string, unit?: string) => void) | undefined): void;
58
+ SetScript<TArgs extends unknown[]>(script: "OnEvent", handler: ((this: void, frame: wuiraNativeFrame, event: string, ...args: TArgs) => void) | undefined): void;
56
59
  SetScript(script: "OnUpdate", handler: ((this: void, frame: wuiraNativeFrame, elapsed: number) => void) | undefined): void;
57
60
  GetWidth(): number;
58
61
  GetHeight(): number;
@@ -38,5 +38,6 @@ function ____exports.renderDecorations(self, item, tree)
38
38
  end
39
39
  ____attach_16(nil, ____temp_15)
40
40
  attach(nil, decorations and decorations.glow)
41
+ attach(nil, decorations and decorations.tooltip)
41
42
  end
42
43
  return ____exports
@@ -1,8 +1,10 @@
1
1
  import type { Background } from "./Background";
2
2
  import type { Border } from "./Border";
3
3
  import type { Glow } from "./Glow";
4
+ import type { Tooltip } from "./Tooltip";
4
5
  export interface Decorations {
5
6
  background?: Background;
6
7
  border?: Border;
7
8
  glow?: Glow;
9
+ tooltip?: Tooltip;
8
10
  }
@@ -0,0 +1,50 @@
1
+ import type { Color } from "../../types/Color";
2
+ import { Decoration } from "./Decoration";
3
+ export type TooltipAnchor = "ANCHOR_RIGHT" | "ANCHOR_LEFT" | "ANCHOR_TOP" | "ANCHOR_BOTTOM" | "ANCHOR_CURSOR" | "ANCHOR_NONE";
4
+ /** Native GameTooltip surface passed to callbacks; no custom tooltip renderer. */
5
+ export interface NativeTooltip {
6
+ SetOwner(owner: wuiraNativeFrame, anchor: TooltipAnchor): void;
7
+ IsOwned(owner: wuiraNativeFrame): boolean;
8
+ ClearLines(): void;
9
+ SetText(text: string, r?: number, g?: number, b?: number, alpha?: number, wrap?: boolean): void;
10
+ AddLine(text: string, r?: number, g?: number, b?: number, wrap?: boolean): void;
11
+ SetSpellByID(id: number): void;
12
+ SetItemByID(id: number): void;
13
+ SetUnit(unit: string): void;
14
+ SetHyperlink(link: string): void;
15
+ SetAction(slot: number): void;
16
+ Show(): void;
17
+ Hide(): void;
18
+ }
19
+ export interface TooltipLine {
20
+ text: string;
21
+ color?: Color;
22
+ wrap?: boolean;
23
+ }
24
+ interface TooltipOptions {
25
+ anchor?: TooltipAnchor;
26
+ /** Defaults to the game's shared GameTooltip; resolved on hover. */
27
+ target?: NativeTooltip | (() => NativeTooltip | undefined);
28
+ lines?: readonly TooltipLine[];
29
+ }
30
+ export type TooltipProps = TooltipOptions & ({
31
+ text: string | (() => string | undefined);
32
+ color?: Color;
33
+ wrap?: boolean;
34
+ show?: never;
35
+ } | {
36
+ show: (tooltip: NativeTooltip, owner: wuiraNativeFrame) => void | false;
37
+ text?: never;
38
+ });
39
+ /** Attaches hover routing to a Cell. Creates no frames, textures or update loop. */
40
+ export declare class Tooltip extends Decoration {
41
+ readonly props: TooltipProps;
42
+ private binding?;
43
+ private shown?;
44
+ constructor(props: TooltipProps);
45
+ protected draw(frame: wuiraNativeFrame): void;
46
+ private Show;
47
+ private Hide;
48
+ dispose(): void;
49
+ }
50
+ export {};
@@ -0,0 +1,218 @@
1
+ --[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
2
+ -- Lua Library inline imports
3
+ local function __TS__Class(self)
4
+ local c = {prototype = {}}
5
+ c.prototype.__index = c.prototype
6
+ c.prototype.constructor = c
7
+ return c
8
+ end
9
+
10
+ local function __TS__ClassExtends(target, base)
11
+ target.____super = base
12
+ local staticMetatable = setmetatable({__index = base}, base)
13
+ setmetatable(target, staticMetatable)
14
+ local baseMetatable = getmetatable(base)
15
+ if baseMetatable then
16
+ if type(baseMetatable.__index) == "function" then
17
+ staticMetatable.__index = baseMetatable.__index
18
+ end
19
+ if type(baseMetatable.__newindex) == "function" then
20
+ staticMetatable.__newindex = baseMetatable.__newindex
21
+ end
22
+ end
23
+ setmetatable(target.prototype, base.prototype)
24
+ if type(base.prototype.__index) == "function" then
25
+ target.prototype.__index = base.prototype.__index
26
+ end
27
+ if type(base.prototype.__newindex) == "function" then
28
+ target.prototype.__newindex = base.prototype.__newindex
29
+ end
30
+ if type(base.prototype.__tostring) == "function" then
31
+ target.prototype.__tostring = base.prototype.__tostring
32
+ end
33
+ end
34
+
35
+ local function __TS__CountVarargs(...)
36
+ return select("#", ...)
37
+ end
38
+
39
+ local function __TS__SparseArrayNew(...)
40
+ local sparseArray = {...}
41
+ sparseArray.sparseLength = __TS__CountVarargs(...)
42
+ return sparseArray
43
+ end
44
+
45
+ local function __TS__SparseArrayPush(sparseArray, ...)
46
+ local args = {...}
47
+ local argsLen = __TS__CountVarargs(...)
48
+ local listLen = sparseArray.sparseLength
49
+ for i = 1, argsLen do
50
+ sparseArray[listLen + i] = args[i]
51
+ end
52
+ sparseArray.sparseLength = listLen + argsLen
53
+ end
54
+
55
+ local function __TS__SparseArraySpread(sparseArray)
56
+ local _unpack = unpack or table.unpack
57
+ return _unpack(sparseArray, 1, sparseArray.sparseLength)
58
+ end
59
+ -- End of Lua Library inline imports
60
+ local ____exports = {}
61
+ local ____Decoration = require("ui.decorations.Decoration")
62
+ local Decoration = ____Decoration.Decoration
63
+ --- Attaches hover routing to a Cell. Creates no frames, textures or update loop.
64
+ ____exports.Tooltip = __TS__Class()
65
+ local Tooltip = ____exports.Tooltip
66
+ Tooltip.name = "Tooltip"
67
+ __TS__ClassExtends(Tooltip, Decoration)
68
+ function Tooltip.prototype.____constructor(self, props)
69
+ Decoration.prototype.____constructor(self)
70
+ self.props = props
71
+ end
72
+ function Tooltip.prototype.draw(self, frame)
73
+ local binding = {
74
+ frame = frame,
75
+ motion = frame:IsMouseMotionEnabled()
76
+ }
77
+ self.binding = binding
78
+ binding.enter = function() return self:Show(frame) end
79
+ binding.leave = function() return self:Hide() end
80
+ frame:HookScript(
81
+ "OnEnter",
82
+ function()
83
+ local ____opt_0 = binding.enter
84
+ return ____opt_0 and ____opt_0(binding)
85
+ end
86
+ )
87
+ frame:HookScript(
88
+ "OnLeave",
89
+ function()
90
+ local ____opt_2 = binding.leave
91
+ return ____opt_2 and ____opt_2(binding)
92
+ end
93
+ )
94
+ frame:HookScript(
95
+ "OnHide",
96
+ function()
97
+ local ____opt_4 = binding.leave
98
+ return ____opt_4 and ____opt_4(binding)
99
+ end
100
+ )
101
+ if not binding.motion then
102
+ frame:EnableMouseMotion(true)
103
+ end
104
+ end
105
+ function Tooltip.prototype.Show(self, owner)
106
+ self:Hide()
107
+ local target = self.props.target
108
+ local ____temp_6
109
+ if type(target) == "function" then
110
+ ____temp_6 = target(nil)
111
+ else
112
+ ____temp_6 = target or GameTooltip
113
+ end
114
+ local tooltip = ____temp_6
115
+ if tooltip == nil then
116
+ return
117
+ end
118
+ tooltip:SetOwner(owner, self.props.anchor or "ANCHOR_RIGHT")
119
+ self.shown = {tooltip = tooltip, owner = owner}
120
+ do
121
+ local function ____catch(failure)
122
+ self:Hide()
123
+ error(failure, 0)
124
+ end
125
+ local ____try, ____hasReturned, ____returnValue = pcall(function()
126
+ tooltip:ClearLines()
127
+ if self.props.show ~= nil then
128
+ if self.props:show(tooltip, owner) == false then
129
+ self:Hide()
130
+ return true
131
+ end
132
+ else
133
+ local ____temp_7
134
+ if type(self.props.text) == "function" then
135
+ ____temp_7 = self.props:text()
136
+ else
137
+ ____temp_7 = self.props.text
138
+ end
139
+ local text = ____temp_7
140
+ if text == nil then
141
+ self:Hide()
142
+ return true
143
+ end
144
+ local color = self.props.color or ({r = 1, g = 1, b = 1})
145
+ local ____tooltip_SetText_10 = tooltip.SetText
146
+ local ____array_9 = __TS__SparseArrayNew(
147
+ text,
148
+ color.r,
149
+ color.g,
150
+ color.b,
151
+ color.a or 1
152
+ )
153
+ local ____self_props_wrap_8 = self.props.wrap
154
+ if ____self_props_wrap_8 == nil then
155
+ ____self_props_wrap_8 = true
156
+ end
157
+ __TS__SparseArrayPush(____array_9, ____self_props_wrap_8)
158
+ ____tooltip_SetText_10(
159
+ tooltip,
160
+ __TS__SparseArraySpread(____array_9)
161
+ )
162
+ end
163
+ for ____, line in ipairs(self.props.lines or ({})) do
164
+ local color = line.color or ({r = 1, g = 1, b = 1})
165
+ local ____tooltip_AddLine_13 = tooltip.AddLine
166
+ local ____array_12 = __TS__SparseArrayNew(line.text, color.r, color.g, color.b)
167
+ local ____line_wrap_11 = line.wrap
168
+ if ____line_wrap_11 == nil then
169
+ ____line_wrap_11 = true
170
+ end
171
+ __TS__SparseArrayPush(____array_12, ____line_wrap_11)
172
+ ____tooltip_AddLine_13(
173
+ tooltip,
174
+ __TS__SparseArraySpread(____array_12)
175
+ )
176
+ end
177
+ tooltip:Show()
178
+ end)
179
+ if not ____try then
180
+ ____hasReturned, ____returnValue = ____catch(____hasReturned)
181
+ end
182
+ if ____hasReturned then
183
+ return ____returnValue
184
+ end
185
+ end
186
+ end
187
+ function Tooltip.prototype.Hide(self)
188
+ local shown = self.shown
189
+ self.shown = nil
190
+ if shown ~= nil and shown.tooltip:IsOwned(shown.owner) then
191
+ shown.tooltip:Hide()
192
+ end
193
+ end
194
+ function Tooltip.prototype.dispose(self)
195
+ local binding = self.binding
196
+ self.binding = nil
197
+ if binding ~= nil then
198
+ binding.enter = nil
199
+ binding.leave = nil
200
+ local ____temp_17 = not binding.motion
201
+ if ____temp_17 then
202
+ local ____InCombatLockdown_result_16 = InCombatLockdown()
203
+ if ____InCombatLockdown_result_16 then
204
+ local ____this_15
205
+ ____this_15 = binding.frame
206
+ local ____opt_14 = ____this_15.IsProtected
207
+ ____InCombatLockdown_result_16 = (____opt_14 and ____opt_14(____this_15)) == true
208
+ end
209
+ ____temp_17 = not ____InCombatLockdown_result_16
210
+ end
211
+ if ____temp_17 then
212
+ binding.frame:EnableMouseMotion(false)
213
+ end
214
+ end
215
+ self:Hide()
216
+ Decoration.prototype.dispose(self)
217
+ end
218
+ return ____exports
package/lib/ui/index.d.ts CHANGED
@@ -24,6 +24,8 @@ export { ProgressBar } from "./ProgressBar";
24
24
  export { Background } from "./decorations/Background";
25
25
  export { Border } from "./decorations/Border";
26
26
  export { Glow } from "./decorations/Glow";
27
+ export { Tooltip } from "./decorations/Tooltip";
28
+ export type { TooltipProps, TooltipAnchor, TooltipLine, NativeTooltip } from "./decorations/Tooltip";
27
29
  export { Label } from "./Label";
28
30
  export type { LabelProps } from "./Label";
29
31
  export { Icon } from "./Icon";
package/lib/ui/index.lua CHANGED
@@ -68,6 +68,10 @@ do
68
68
  local ____Glow = require("ui.decorations.Glow")
69
69
  ____exports.Glow = ____Glow.Glow
70
70
  end
71
+ do
72
+ local ____Tooltip = require("ui.decorations.Tooltip")
73
+ ____exports.Tooltip = ____Tooltip.Tooltip
74
+ end
71
75
  do
72
76
  local ____Label = require("ui.Label")
73
77
  ____exports.Label = ____Label.Label
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wuira",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "author": {
5
5
  "name": "Kerim Cetinbas",
6
6
  "email": "a.cetinbas@icloud.com"