@crockery/fellowship-components 0.1.0 → 0.3.0

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.
Files changed (54) hide show
  1. package/README.md +7 -2
  2. package/RELEASING.md +9 -45
  3. package/dist/chunks/data-source-C-Gs7kRl.js +2 -0
  4. package/dist/chunks/decorate-B_ZTN9li.js.map +1 -1
  5. package/dist/chunks/dungeon-cast-browser-Cyh519NI.js +705 -0
  6. package/dist/chunks/dungeon-cast-browser-Cyh519NI.js.map +1 -0
  7. package/dist/chunks/{dungeon-map-IPN6sEDj.js → dungeon-map-xFaPasVr.js} +113 -138
  8. package/dist/chunks/dungeon-map-xFaPasVr.js.map +1 -0
  9. package/dist/chunks/{talent-calculator-Ch1Pbbba.js → talent-calculator-CIPNbqK5.js} +84 -114
  10. package/dist/chunks/talent-calculator-CIPNbqK5.js.map +1 -0
  11. package/dist/data/cast-data-source.d.ts +5 -0
  12. package/dist/data/cast-data-source.d.ts.map +1 -0
  13. package/dist/data/data-source.d.ts +5 -16
  14. package/dist/data/data-source.d.ts.map +1 -1
  15. package/dist/data/package-cast-data-source.d.ts +8 -0
  16. package/dist/data/package-cast-data-source.d.ts.map +1 -0
  17. package/dist/data/package-data-source.d.ts +3 -1
  18. package/dist/data/package-data-source.d.ts.map +1 -1
  19. package/dist/data/package-talent-data-source.d.ts +2 -0
  20. package/dist/data/package-talent-data-source.d.ts.map +1 -1
  21. package/dist/data/talent-data-source.d.ts +4 -10
  22. package/dist/data/talent-data-source.d.ts.map +1 -1
  23. package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts +36 -0
  24. package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts.map +1 -0
  25. package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts +2 -0
  26. package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts.map +1 -0
  27. package/dist/dungeon-cast-browser/index.d.ts +4 -0
  28. package/dist/dungeon-cast-browser/index.d.ts.map +1 -0
  29. package/dist/dungeon-cast-browser/render-model.d.ts +5 -0
  30. package/dist/dungeon-cast-browser/render-model.d.ts.map +1 -0
  31. package/dist/dungeon-cast-browser/types.d.ts +60 -0
  32. package/dist/dungeon-cast-browser/types.d.ts.map +1 -0
  33. package/dist/dungeon-cast-browser.js +7 -0
  34. package/dist/dungeon-cast-browser.js.map +1 -0
  35. package/dist/dungeon-map.js +1 -1
  36. package/dist/index.d.ts +6 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +8 -5
  39. package/dist/react/dungeon-cast-browser.d.ts +12 -0
  40. package/dist/react/dungeon-cast-browser.d.ts.map +1 -0
  41. package/dist/react/dungeon-cast-browser.js +19 -0
  42. package/dist/react/dungeon-cast-browser.js.map +1 -0
  43. package/dist/react/dungeon-map.js +1 -1
  44. package/dist/react/index.d.ts +2 -0
  45. package/dist/react/index.d.ts.map +1 -1
  46. package/dist/react/talent-calculator.js +1 -1
  47. package/dist/react.js +4 -3
  48. package/dist/talent-calculator.js +1 -1
  49. package/package.json +35 -25
  50. package/src/dungeon-cast-browser/README.md +22 -0
  51. package/dist/chunks/data-source-CoIFvV72.js +0 -10
  52. package/dist/chunks/data-source-CoIFvV72.js.map +0 -1
  53. package/dist/chunks/dungeon-map-IPN6sEDj.js.map +0 -1
  54. package/dist/chunks/talent-calculator-Ch1Pbbba.js.map +0 -1
@@ -0,0 +1,705 @@
1
+ import { t as e } from "./data-source-C-Gs7kRl.js";
2
+ import { n as t, r as n, t as r } from "./decorate-B_ZTN9li.js";
3
+ import { createPackageFellowshipDataClient as i } from "@crockery/fellowship-data/client";
4
+ import { LitElement as a, css as o, html as s, nothing as c } from "lit";
5
+ import { customElement as l, property as u, state as d } from "lit/decorators.js";
6
+ import { repeat as f } from "lit/directives/repeat.js";
7
+ //#region src/data/package-cast-data-source.ts
8
+ var p = (e = {}) => {
9
+ let t = i(e);
10
+ return {
11
+ loadDungeonCasts: (e, n) => t.loadDungeonCasts(e, n),
12
+ resolveAssetUrl: (e) => t.resolveAssetUrl(e)
13
+ };
14
+ }, m = o`
15
+ :host {
16
+ --fellowship-cast-canvas: var(--_fellowship-field-canvas);
17
+ --fellowship-cast-surface: var(--_fellowship-field-surface);
18
+ --fellowship-cast-raised: var(--_fellowship-field-surface-raised);
19
+ --fellowship-cast-ink: var(--_fellowship-field-ink);
20
+ --fellowship-cast-muted: var(--_fellowship-field-muted);
21
+ --fellowship-cast-tertiary: var(--_fellowship-field-tertiary);
22
+ --fellowship-cast-border: var(--_fellowship-field-border);
23
+ --fellowship-cast-border-strong: var(--_fellowship-field-border-strong);
24
+ --fellowship-cast-interrupt: var(--_fellowship-field-accent);
25
+ --fellowship-cast-stop: var(--_fellowship-field-brass);
26
+ --fellowship-cast-focus: var(--_fellowship-field-focus);
27
+ --fellowship-cast-font-family: var(--_fellowship-field-font-family);
28
+ --fellowship-cast-tactical-font-family: var(--_fellowship-field-mono-font-family);
29
+
30
+ display: block;
31
+ inline-size: 100%;
32
+ min-block-size: 320px;
33
+ color: var(--fellowship-cast-ink);
34
+ font-family: var(--fellowship-cast-font-family);
35
+ container-type: inline-size;
36
+ -webkit-font-smoothing: antialiased;
37
+ }
38
+
39
+ :host([hidden]) {
40
+ display: none;
41
+ }
42
+
43
+ :host([fill]) {
44
+ min-block-size: 0;
45
+ block-size: 100%;
46
+ overflow: hidden;
47
+ }
48
+
49
+ *,
50
+ *::before,
51
+ *::after {
52
+ box-sizing: border-box;
53
+ }
54
+
55
+ .browser {
56
+ min-block-size: inherit;
57
+ overflow: hidden;
58
+ border: 1px solid var(--fellowship-cast-border);
59
+ border-radius: calc(var(--_fellowship-field-radius) + 2px);
60
+ background: var(--fellowship-cast-canvas);
61
+ }
62
+
63
+ :host([fill]) .browser {
64
+ display: grid;
65
+ grid-template-rows: auto minmax(0, 1fr);
66
+ min-block-size: 0;
67
+ block-size: 100%;
68
+ }
69
+
70
+ .toolbar {
71
+ display: grid;
72
+ grid-template-columns: minmax(0, 1fr) auto;
73
+ gap: 12px;
74
+ align-items: end;
75
+ padding: 16px;
76
+ border-block-end: 1px solid var(--fellowship-cast-border);
77
+ background: var(--fellowship-cast-surface);
78
+ }
79
+
80
+ .eyebrow,
81
+ .summary,
82
+ .enemy-id,
83
+ .ability-id,
84
+ .capability,
85
+ .marker {
86
+ font-family: var(--fellowship-cast-tactical-font-family);
87
+ }
88
+
89
+ .eyebrow {
90
+ margin: 0 0 4px;
91
+ color: var(--fellowship-cast-interrupt);
92
+ font-size: 10px;
93
+ font-weight: 600;
94
+ letter-spacing: 0.12em;
95
+ text-transform: uppercase;
96
+ }
97
+
98
+ h2 {
99
+ margin: 0;
100
+ font-size: clamp(18px, 3cqi, 24px);
101
+ font-weight: 600;
102
+ letter-spacing: 0.01em;
103
+ line-height: 1.08;
104
+ }
105
+
106
+ .summary {
107
+ margin: 6px 0 0;
108
+ color: var(--fellowship-cast-tertiary);
109
+ font-size: 11px;
110
+ font-variant-numeric: tabular-nums;
111
+ letter-spacing: 0.04em;
112
+ }
113
+
114
+ .search-label {
115
+ display: grid;
116
+ gap: 4px;
117
+ min-inline-size: min(240px, 38cqi);
118
+ color: var(--fellowship-cast-muted);
119
+ font-size: 11px;
120
+ font-weight: 600;
121
+ letter-spacing: 0.04em;
122
+ }
123
+
124
+ .search-input {
125
+ min-block-size: 44px;
126
+ inline-size: 100%;
127
+ border: 1px solid var(--fellowship-cast-border-strong);
128
+ border-radius: var(--_fellowship-field-radius);
129
+ background: var(--_fellowship-field-inset);
130
+ color: var(--fellowship-cast-ink);
131
+ font: inherit;
132
+ font-size: 14px;
133
+ padding-inline: 12px;
134
+ }
135
+
136
+ .search-input::placeholder {
137
+ color: var(--fellowship-cast-tertiary);
138
+ }
139
+
140
+ .search-input:focus-visible,
141
+ .cast-row:focus-visible,
142
+ .retry-button:focus-visible {
143
+ outline: 2px solid var(--fellowship-cast-focus);
144
+ outline-offset: 2px;
145
+ }
146
+
147
+ .enemy-list {
148
+ display: grid;
149
+ }
150
+
151
+ :host([fill]) .enemy-list {
152
+ min-block-size: 0;
153
+ align-content: start;
154
+ overflow-y: auto;
155
+ overscroll-behavior: contain;
156
+ }
157
+
158
+ .enemy {
159
+ min-inline-size: 0;
160
+ border-block-end: 1px solid var(--fellowship-cast-border);
161
+ }
162
+
163
+ .enemy:last-child {
164
+ border-block-end: 0;
165
+ }
166
+
167
+ .enemy-header {
168
+ display: grid;
169
+ grid-template-columns: 4px minmax(0, 1fr) auto;
170
+ gap: 12px;
171
+ align-items: center;
172
+ min-block-size: 52px;
173
+ padding: 8px 16px;
174
+ background: color-mix(in srgb, var(--fellowship-cast-raised) 72%, transparent);
175
+ }
176
+
177
+ .enemy-rule {
178
+ align-self: stretch;
179
+ background: var(--fellowship-cast-interrupt);
180
+ }
181
+
182
+ .enemy-name {
183
+ overflow: hidden;
184
+ margin: 0;
185
+ font-size: 15px;
186
+ font-weight: 600;
187
+ line-height: 1.15;
188
+ text-overflow: ellipsis;
189
+ white-space: nowrap;
190
+ }
191
+
192
+ .enemy-id {
193
+ overflow: hidden;
194
+ margin: 3px 0 0;
195
+ color: var(--fellowship-cast-tertiary);
196
+ font-size: 10px;
197
+ text-overflow: ellipsis;
198
+ white-space: nowrap;
199
+ }
200
+
201
+ .enemy-count {
202
+ color: var(--fellowship-cast-muted);
203
+ font-family: var(--fellowship-cast-tactical-font-family);
204
+ font-size: 10px;
205
+ font-variant-numeric: tabular-nums;
206
+ }
207
+
208
+ .cast-list {
209
+ display: grid;
210
+ margin: 0;
211
+ padding: 0;
212
+ list-style: none;
213
+ }
214
+
215
+ .cast-list > li + li {
216
+ border-block-start: 1px solid var(--fellowship-cast-border);
217
+ }
218
+
219
+ .cast-row {
220
+ position: relative;
221
+ display: grid;
222
+ grid-template-columns: 40px minmax(0, 1fr) auto auto;
223
+ gap: 12px;
224
+ align-items: center;
225
+ inline-size: 100%;
226
+ min-block-size: 64px;
227
+ padding: 8px 16px 8px 28px;
228
+ border: 0;
229
+ background: transparent;
230
+ color: inherit;
231
+ font: inherit;
232
+ text-align: start;
233
+ cursor: pointer;
234
+ transition: background-color 140ms cubic-bezier(0.23, 1, 0.32, 1);
235
+ }
236
+
237
+ .cast-row:not(:disabled):hover {
238
+ background: color-mix(in srgb, var(--fellowship-cast-interrupt) 7%, transparent);
239
+ }
240
+
241
+ .cast-row[aria-pressed="true"] {
242
+ background: color-mix(in srgb, var(--fellowship-cast-interrupt) 11%, transparent);
243
+ }
244
+
245
+ .cast-row[aria-pressed="true"]::before {
246
+ position: absolute;
247
+ inset: 12px auto 12px 0;
248
+ inline-size: 3px;
249
+ background: var(--fellowship-cast-interrupt);
250
+ content: "";
251
+ }
252
+
253
+ .cast-row:disabled {
254
+ color: var(--fellowship-cast-tertiary);
255
+ cursor: not-allowed;
256
+ }
257
+
258
+ .ability-image,
259
+ .ability-fallback {
260
+ inline-size: 40px;
261
+ block-size: 40px;
262
+ border: 1px solid var(--fellowship-cast-border-strong);
263
+ border-radius: 4px;
264
+ background: var(--fellowship-cast-raised);
265
+ }
266
+
267
+ .ability-image {
268
+ display: block;
269
+ object-fit: cover;
270
+ }
271
+
272
+ .ability-fallback {
273
+ display: grid;
274
+ place-items: center;
275
+ color: var(--fellowship-cast-muted);
276
+ font-family: var(--fellowship-cast-tactical-font-family);
277
+ font-size: 11px;
278
+ font-weight: 600;
279
+ }
280
+
281
+ .ability-image[hidden],
282
+ .ability-fallback[hidden] {
283
+ display: none;
284
+ }
285
+
286
+ .ability-copy {
287
+ min-inline-size: 0;
288
+ }
289
+
290
+ .ability-name {
291
+ display: block;
292
+ overflow: hidden;
293
+ font-size: 14px;
294
+ font-weight: 600;
295
+ line-height: 1.15;
296
+ text-overflow: ellipsis;
297
+ white-space: nowrap;
298
+ }
299
+
300
+ .ability-id {
301
+ display: block;
302
+ overflow: hidden;
303
+ margin-block-start: 4px;
304
+ color: var(--fellowship-cast-tertiary);
305
+ font-size: 10px;
306
+ text-overflow: ellipsis;
307
+ white-space: nowrap;
308
+ }
309
+
310
+ .capability {
311
+ min-inline-size: 102px;
312
+ padding: 5px 8px;
313
+ border: 1px solid currentColor;
314
+ border-radius: 999px;
315
+ color: var(--fellowship-cast-interrupt);
316
+ font-size: 10px;
317
+ font-weight: 600;
318
+ letter-spacing: 0.04em;
319
+ text-align: center;
320
+ white-space: nowrap;
321
+ }
322
+
323
+ .capability[data-kind="varies"] {
324
+ border-style: dashed;
325
+ }
326
+
327
+ .capability[data-kind="stop"] {
328
+ color: var(--fellowship-cast-stop);
329
+ }
330
+
331
+ .capability[data-kind="none"] {
332
+ color: var(--fellowship-cast-tertiary);
333
+ }
334
+
335
+ .marker {
336
+ min-inline-size: 72px;
337
+ color: var(--fellowship-cast-stop);
338
+ font-size: 10px;
339
+ font-weight: 600;
340
+ letter-spacing: 0.06em;
341
+ text-align: end;
342
+ text-transform: uppercase;
343
+ }
344
+
345
+ .marker[data-empty="true"] {
346
+ visibility: hidden;
347
+ }
348
+
349
+ .status {
350
+ display: grid;
351
+ min-block-size: 280px;
352
+ padding: 32px;
353
+ place-items: center;
354
+ text-align: center;
355
+ }
356
+
357
+ :host([fill]) .status {
358
+ min-block-size: 0;
359
+ }
360
+
361
+ .status-card {
362
+ display: grid;
363
+ max-inline-size: 360px;
364
+ gap: 8px;
365
+ justify-items: center;
366
+ }
367
+
368
+ .status-mark {
369
+ inline-size: 32px;
370
+ block-size: 4px;
371
+ margin-block-end: 8px;
372
+ background: var(--fellowship-cast-interrupt);
373
+ }
374
+
375
+ .status-title {
376
+ margin: 0;
377
+ font-size: 18px;
378
+ font-weight: 600;
379
+ }
380
+
381
+ .status-copy {
382
+ margin: 0;
383
+ color: var(--fellowship-cast-muted);
384
+ font-size: 14px;
385
+ line-height: 1.45;
386
+ }
387
+
388
+ .retry-button {
389
+ min-block-size: 44px;
390
+ margin-block-start: 8px;
391
+ padding-inline: 16px;
392
+ border: 1px solid var(--fellowship-cast-interrupt);
393
+ border-radius: var(--_fellowship-field-radius);
394
+ background: transparent;
395
+ color: var(--fellowship-cast-ink);
396
+ font: inherit;
397
+ font-weight: 600;
398
+ cursor: pointer;
399
+ }
400
+
401
+ @container (max-width: 560px) {
402
+ .toolbar {
403
+ grid-template-columns: 1fr;
404
+ }
405
+
406
+ .search-label {
407
+ min-inline-size: 0;
408
+ }
409
+
410
+ .cast-row {
411
+ grid-template-columns: 40px minmax(0, 1fr) auto;
412
+ padding-inline: 16px;
413
+ }
414
+
415
+ .marker {
416
+ position: absolute;
417
+ inset: 5px 8px auto auto;
418
+ min-inline-size: 0;
419
+ font-size: 8px;
420
+ }
421
+
422
+ .ability-id,
423
+ .enemy-id {
424
+ display: none;
425
+ }
426
+
427
+ .capability {
428
+ min-inline-size: 0;
429
+ padding-inline: 6px;
430
+ }
431
+ }
432
+
433
+ @media (prefers-reduced-motion: reduce) {
434
+ .cast-row {
435
+ transition: none;
436
+ }
437
+ }
438
+ `, h = ({ enemyId: e, abilityId: t }) => `${e}\u0000${t}`, g = (e) => e.images.find(({ size: e }) => e === "200") ?? e.images[0] ?? null, _ = (e, t, n) => t ? "Interrupt varies" : e ? "Interrupt" : n ? "Stop" : "No response", v = (e, t) => {
439
+ let n = /* @__PURE__ */ new Set(), r = [];
440
+ for (let i of e.dungeon.enemies) {
441
+ if (n.has(i.id)) continue;
442
+ n.add(i.id);
443
+ let a = e.enemies.get(i.id);
444
+ if (!a) continue;
445
+ let o = (e.abilitiesByEnemy.get(a.id) ?? []).filter((e) => e.castInterruptibility !== void 0).map((e) => {
446
+ let n = e.castInterruptibility !== "never", r = e.castInterruptibility === "sometimes", i = a.canBeControlled, o = g(e), s = null;
447
+ if (o) try {
448
+ s = t.resolveAssetUrl(o);
449
+ } catch {
450
+ s = null;
451
+ }
452
+ return {
453
+ identity: {
454
+ enemyId: a.id,
455
+ abilityId: e.id
456
+ },
457
+ ability: e,
458
+ enemy: a,
459
+ canInterrupt: n,
460
+ interruptVaries: r,
461
+ canStop: i,
462
+ actionable: n || i,
463
+ capabilityLabel: _(n, r, i),
464
+ imageUrl: s
465
+ };
466
+ });
467
+ o.some(({ actionable: e }) => e) && r.push({
468
+ enemy: a,
469
+ casts: o
470
+ });
471
+ }
472
+ return {
473
+ dungeon: e.dungeon,
474
+ enemies: r,
475
+ castCount: r.reduce((e, t) => e + t.casts.length, 0)
476
+ };
477
+ }, y = {
478
+ bubbles: !0,
479
+ composed: !0
480
+ }, b = class extends a {
481
+ constructor(...e) {
482
+ super(...e), this.dungeonId = "", this.selectedCast = null, this.configuredCasts = [], this.configuredFilter = "all", this.fill = !1, this.assetBaseUrl = null, this.dataSource = null, this._status = "empty", this._error = null, this._model = null, this._query = "", this.#e = null, this.#t = 0, this.#d = (e) => {
483
+ this._query = e.currentTarget.value;
484
+ }, this.#f = (e) => {
485
+ let t = e.currentTarget;
486
+ t.hidden = !0;
487
+ let n = t.nextElementSibling;
488
+ n instanceof HTMLElement && (n.hidden = !1);
489
+ }, this.#p = (e) => {
490
+ let t = e.key === "ArrowDown" || e.key === "ArrowRight" ? 1 : e.key === "ArrowUp" || e.key === "ArrowLeft" ? -1 : 0;
491
+ if (t === 0 && e.key !== "Home" && e.key !== "End") return;
492
+ let n = [...this.renderRoot.querySelectorAll(".cast-row:not(:disabled)")];
493
+ if (n.length === 0) return;
494
+ let r = n.indexOf(e.currentTarget);
495
+ n[e.key === "Home" ? 0 : e.key === "End" ? n.length - 1 : (r + t + n.length) % n.length]?.focus(), e.preventDefault();
496
+ };
497
+ }
498
+ static {
499
+ this.styles = [n, m];
500
+ }
501
+ #e;
502
+ #t;
503
+ connectedCallback() {
504
+ super.connectedCallback(), t(this.ownerDocument);
505
+ }
506
+ disconnectedCallback() {
507
+ this.#e?.abort(), super.disconnectedCallback();
508
+ }
509
+ updated(e) {
510
+ super.updated(e), (e.has("dungeonId") || e.has("dataSource") || e.has("assetBaseUrl")) && queueMicrotask(() => {
511
+ this.isConnected && this.#n();
512
+ });
513
+ }
514
+ retry() {
515
+ this.#n();
516
+ }
517
+ get renderModel() {
518
+ return this._model;
519
+ }
520
+ render() {
521
+ if (this._status !== "ready" || !this._model) return s`<section class="browser" aria-label="Dungeon cast browser">${this.#o()}</section>`;
522
+ let e = this.#r(this._model);
523
+ return s`
524
+ <section class="browser" aria-label=${`${this._model.dungeon.name ?? this._model.dungeon.id} casts`}>
525
+ <header class="toolbar" part="toolbar">
526
+ <div>
527
+ <p class="eyebrow">Dungeon cast index</p>
528
+ <h2>${this._model.dungeon.name ?? this._model.dungeon.id}</h2>
529
+ <p class="summary">
530
+ ${this._model.enemies.length} enemies · ${this._model.castCount} cast-bearing abilities
531
+ </p>
532
+ </div>
533
+ <label class="search-label">
534
+ Search casts
535
+ <input
536
+ class="search-input"
537
+ type="search"
538
+ placeholder="Enemy or ability"
539
+ .value=${this._query}
540
+ @input=${this.#d}
541
+ />
542
+ </label>
543
+ </header>
544
+ ${e.length > 0 ? s`<div class="enemy-list" part="enemy-list">${f(e, ({ enemy: e }) => e.id, (e) => this.#i(e))}</div>` : this.#s()}
545
+ </section>
546
+ `;
547
+ }
548
+ async #n() {
549
+ let t = this.dungeonId.trim(), n = ++this.#t;
550
+ if (this.#e?.abort(), this.#e = new AbortController(), this._model = null, this._error = null, this._query = "", !t) {
551
+ this._status = "empty";
552
+ return;
553
+ }
554
+ this._status = "loading";
555
+ let r = this.dataSource ?? p(this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {});
556
+ try {
557
+ let i = await r.loadDungeonCasts(t, this.#e.signal);
558
+ if (n !== this.#t || this.#e.signal.aborted) return;
559
+ if (i.manifest.schemaVersion !== 1) throw new e("unsupported-schema", `Unsupported fellowship-data schema version: ${String(i.manifest.schemaVersion)}.`);
560
+ let a = v(i, r);
561
+ this._model = a, this._status = "ready", this.dispatchEvent(new CustomEvent("fellowship-cast-browser-ready", {
562
+ ...y,
563
+ detail: {
564
+ model: a,
565
+ data: i
566
+ }
567
+ }));
568
+ } catch (t) {
569
+ if (n !== this.#t || this.#e.signal.aborted) return;
570
+ let r = {
571
+ code: t instanceof e ? t.code : t.code ?? "data-load-failed",
572
+ message: t instanceof Error ? t.message : String(t),
573
+ ...t instanceof Error && t.cause !== void 0 ? { cause: t.cause } : {}
574
+ };
575
+ this._error = r, this._status = "error", this.dispatchEvent(new CustomEvent("fellowship-cast-browser-error", {
576
+ ...y,
577
+ detail: r
578
+ }));
579
+ }
580
+ }
581
+ #r(e) {
582
+ let t = this._query.trim().toLocaleLowerCase(), n = this.configuredFilter === "configured" || this.configuredFilter === "unconfigured" ? this.configuredFilter : "all";
583
+ return e.enemies.flatMap((e) => {
584
+ let r = `${e.enemy.name ?? ""} ${e.enemy.id}`.toLocaleLowerCase().includes(t), i = e.casts.filter((e) => {
585
+ if (!(!t || r || `${e.ability.name ?? ""} ${e.ability.id}`.toLocaleLowerCase().includes(t))) return !1;
586
+ let i = this.configuredCasts.some((t) => this.#l(t, e.identity));
587
+ return n === "all" || n === "configured" && i || n === "unconfigured" && !i;
588
+ });
589
+ return i.length > 0 ? [{
590
+ ...e,
591
+ casts: i
592
+ }] : [];
593
+ });
594
+ }
595
+ #i(e) {
596
+ return s`
597
+ <article class="enemy" part="enemy">
598
+ <header class="enemy-header" part="enemy-header">
599
+ <span class="enemy-rule" aria-hidden="true"></span>
600
+ <div>
601
+ <h3 class="enemy-name">${e.enemy.name ?? e.enemy.id}</h3>
602
+ <p class="enemy-id">${e.enemy.id}</p>
603
+ </div>
604
+ <span class="enemy-count">${e.casts.length} ${e.casts.length === 1 ? "cast" : "casts"}</span>
605
+ </header>
606
+ <ul class="cast-list">
607
+ ${f(e.casts, ({ identity: e }) => h(e), (e) => s`<li>${this.#a(e)}</li>`)}
608
+ </ul>
609
+ </article>
610
+ `;
611
+ }
612
+ #a(e) {
613
+ let t = this.#l(this.selectedCast, e.identity), n = this.configuredCasts.some((t) => this.#l(t, e.identity)), r = e.interruptVaries ? "varies" : e.canInterrupt ? "interrupt" : e.canStop ? "stop" : "none", i = (e.ability.name ?? e.ability.id).split(/\s+/).map((e) => e[0]).join("").slice(0, 2).toUpperCase();
614
+ return s`
615
+ <button
616
+ class="cast-row"
617
+ part="cast"
618
+ type="button"
619
+ ?disabled=${!e.actionable}
620
+ aria-pressed=${String(t)}
621
+ data-configured=${String(n)}
622
+ @click=${() => this.#u(e)}
623
+ @keydown=${this.#p}
624
+ >
625
+ ${e.imageUrl ? s`<img
626
+ class="ability-image"
627
+ part="cast-icon"
628
+ src=${e.imageUrl}
629
+ alt=""
630
+ @error=${this.#f}
631
+ /><span
632
+ class="ability-fallback"
633
+ part="cast-icon-fallback"
634
+ aria-hidden="true"
635
+ hidden
636
+ >${i}</span
637
+ >` : s`<span class="ability-fallback" part="cast-icon-fallback" aria-hidden="true">${i}</span>`}
638
+ <span class="ability-copy">
639
+ <span class="ability-name">${e.ability.name ?? e.ability.id}</span>
640
+ <span class="ability-id">${e.ability.id}</span>
641
+ </span>
642
+ <span class="capability" data-kind=${r}>${e.capabilityLabel}</span>
643
+ <span class="marker" data-empty=${String(!n)}>${n ? "Configured" : c}</span>
644
+ </button>
645
+ `;
646
+ }
647
+ #o() {
648
+ return this._status === "loading" ? this.#c("Loading cast index", "Joining dungeon enemies with their cast data.") : this._status === "error" ? s`
649
+ <div class="status" role="alert">
650
+ <div class="status-card">
651
+ <span class="status-mark" aria-hidden="true"></span>
652
+ <h2 class="status-title">Cast data unavailable</h2>
653
+ <p class="status-copy">${this._error?.message ?? "The dungeon cast index could not be loaded."}</p>
654
+ <button class="retry-button" type="button" @click=${this.retry}>Retry</button>
655
+ </div>
656
+ </div>
657
+ ` : this._status === "ready" ? this.#c("No actionable casts", "This dungeon has no enemies with casts that can be interrupted or stopped.") : this.#c("Select a dungeon", "Choose a dungeon to inspect enemy casts.");
658
+ }
659
+ #s() {
660
+ return this.#c("No matching casts", `No enemy or ability matches “${this._query.trim()}”.`);
661
+ }
662
+ #c(e, t) {
663
+ return s`
664
+ <div class="status">
665
+ <div class="status-card">
666
+ <span class="status-mark" aria-hidden="true"></span>
667
+ <h2 class="status-title">${e}</h2>
668
+ <p class="status-copy">${t}</p>
669
+ </div>
670
+ </div>
671
+ `;
672
+ }
673
+ #l(e, t) {
674
+ return e?.enemyId === t.enemyId && e.abilityId === t.abilityId;
675
+ }
676
+ #u(e) {
677
+ e.actionable && this.dispatchEvent(new CustomEvent("fellowship-cast-selected", {
678
+ ...y,
679
+ detail: { cast: e }
680
+ }));
681
+ }
682
+ #d;
683
+ #f;
684
+ #p;
685
+ };
686
+ r([u({
687
+ type: String,
688
+ attribute: "dungeon-id"
689
+ })], b.prototype, "dungeonId", void 0), r([u({ attribute: !1 })], b.prototype, "selectedCast", void 0), r([u({ attribute: !1 })], b.prototype, "configuredCasts", void 0), r([u({
690
+ type: String,
691
+ attribute: "configured-filter"
692
+ })], b.prototype, "configuredFilter", void 0), r([u({
693
+ type: Boolean,
694
+ reflect: !0
695
+ })], b.prototype, "fill", void 0), r([u({
696
+ type: String,
697
+ attribute: "asset-base-url"
698
+ })], b.prototype, "assetBaseUrl", void 0), r([u({ attribute: !1 })], b.prototype, "dataSource", void 0), r([d()], b.prototype, "_status", void 0), r([d()], b.prototype, "_error", void 0), r([d()], b.prototype, "_model", void 0), r([d()], b.prototype, "_query", void 0), b = r([l("fellowship-dungeon-cast-browser")], b);
699
+ var x = () => {
700
+ globalThis.customElements !== void 0 && !globalThis.customElements.get("fellowship-dungeon-cast-browser") && globalThis.customElements.define("fellowship-dungeon-cast-browser", b);
701
+ };
702
+ //#endregion
703
+ export { p as a, v as i, x as n, h as r, b as t };
704
+
705
+ //# sourceMappingURL=dungeon-cast-browser-Cyh519NI.js.map