@crockery/fellowship-components 0.1.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.
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/RELEASING.md +75 -0
- package/THIRD_PARTY_LICENSES.md +79 -0
- package/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
- package/dist/assets/ibm-plex-sans-condensed-latin-400-normal-DKyD6XsY.woff2 +0 -0
- package/dist/assets/ibm-plex-sans-condensed-latin-500-normal-BqaLkUoX.woff2 +0 -0
- package/dist/assets/ibm-plex-sans-condensed-latin-600-normal-CRd5VyFf.woff2 +0 -0
- package/dist/assets/ibm-plex-sans-condensed-latin-700-normal-D8r4s4aS.woff2 +0 -0
- package/dist/chunks/data-source-CoIFvV72.js +10 -0
- package/dist/chunks/data-source-CoIFvV72.js.map +1 -0
- package/dist/chunks/decorate-B_ZTN9li.js +92 -0
- package/dist/chunks/decorate-B_ZTN9li.js.map +1 -0
- package/dist/chunks/dungeon-map-IPN6sEDj.js +1144 -0
- package/dist/chunks/dungeon-map-IPN6sEDj.js.map +1 -0
- package/dist/chunks/talent-calculator-Ch1Pbbba.js +1086 -0
- package/dist/chunks/talent-calculator-Ch1Pbbba.js.map +1 -0
- package/dist/chunks/tooltip-DoQVSnOn.js +412 -0
- package/dist/chunks/tooltip-DoQVSnOn.js.map +1 -0
- package/dist/data/data-source.d.ts +17 -0
- package/dist/data/data-source.d.ts.map +1 -0
- package/dist/data/package-data-source.d.ts +6 -0
- package/dist/data/package-data-source.d.ts.map +1 -0
- package/dist/data/package-talent-data-source.d.ts +6 -0
- package/dist/data/package-talent-data-source.d.ts.map +1 -0
- package/dist/data/talent-data-source.d.ts +11 -0
- package/dist/data/talent-data-source.d.ts.map +1 -0
- package/dist/dungeon-map/dungeon-map.d.ts +56 -0
- package/dist/dungeon-map/dungeon-map.d.ts.map +1 -0
- package/dist/dungeon-map/dungeon-map.styles.d.ts +2 -0
- package/dist/dungeon-map/dungeon-map.styles.d.ts.map +1 -0
- package/dist/dungeon-map/index.d.ts +5 -0
- package/dist/dungeon-map/index.d.ts.map +1 -0
- package/dist/dungeon-map/layout.d.ts +3 -0
- package/dist/dungeon-map/layout.d.ts.map +1 -0
- package/dist/dungeon-map/render-model.d.ts +7 -0
- package/dist/dungeon-map/render-model.d.ts.map +1 -0
- package/dist/dungeon-map/types.d.ts +107 -0
- package/dist/dungeon-map/types.d.ts.map +1 -0
- package/dist/dungeon-map/viewport-controller.d.ts +13 -0
- package/dist/dungeon-map/viewport-controller.d.ts.map +1 -0
- package/dist/dungeon-map.js +7 -0
- package/dist/dungeon-map.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/react/dungeon-map.d.ts +22 -0
- package/dist/react/dungeon-map.d.ts.map +1 -0
- package/dist/react/dungeon-map.js +180 -0
- package/dist/react/dungeon-map.js.map +1 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/talent-calculator.d.ts +22 -0
- package/dist/react/talent-calculator.d.ts.map +1 -0
- package/dist/react/talent-calculator.js +71 -0
- package/dist/react/talent-calculator.js.map +1 -0
- package/dist/react/tooltip.d.ts +15 -0
- package/dist/react/tooltip.d.ts.map +1 -0
- package/dist/react/tooltip.js +26 -0
- package/dist/react/tooltip.js.map +1 -0
- package/dist/react.js +4 -0
- package/dist/styles/field-manual-fonts.d.ts +3 -0
- package/dist/styles/field-manual-fonts.d.ts.map +1 -0
- package/dist/styles/field-manual.styles.d.ts +3 -0
- package/dist/styles/field-manual.styles.d.ts.map +1 -0
- package/dist/talent-calculator/index.d.ts +5 -0
- package/dist/talent-calculator/index.d.ts.map +1 -0
- package/dist/talent-calculator/render-model.d.ts +5 -0
- package/dist/talent-calculator/render-model.d.ts.map +1 -0
- package/dist/talent-calculator/selection-state.d.ts +10 -0
- package/dist/talent-calculator/selection-state.d.ts.map +1 -0
- package/dist/talent-calculator/talent-calculator.d.ts +51 -0
- package/dist/talent-calculator/talent-calculator.d.ts.map +1 -0
- package/dist/talent-calculator/talent-calculator.styles.d.ts +2 -0
- package/dist/talent-calculator/talent-calculator.styles.d.ts.map +1 -0
- package/dist/talent-calculator/types.d.ts +92 -0
- package/dist/talent-calculator/types.d.ts.map +1 -0
- package/dist/talent-calculator.js +7 -0
- package/dist/talent-calculator.js.map +1 -0
- package/dist/tooltip/index.d.ts +6 -0
- package/dist/tooltip/index.d.ts.map +1 -0
- package/dist/tooltip/positioning.d.ts +23 -0
- package/dist/tooltip/positioning.d.ts.map +1 -0
- package/dist/tooltip/rich-text.d.ts +4 -0
- package/dist/tooltip/rich-text.d.ts.map +1 -0
- package/dist/tooltip/tooltip.d.ts +33 -0
- package/dist/tooltip/tooltip.d.ts.map +1 -0
- package/dist/tooltip/tooltip.styles.d.ts +2 -0
- package/dist/tooltip/tooltip.styles.d.ts.map +1 -0
- package/dist/tooltip/types.d.ts +19 -0
- package/dist/tooltip/types.d.ts.map +1 -0
- package/dist/tooltip.js +7 -0
- package/dist/tooltip.js.map +1 -0
- package/package.json +149 -0
- package/src/dungeon-map/README.md +168 -0
- package/src/talent-calculator/README.md +190 -0
- package/src/tooltip/README.md +113 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"talent-calculator-Ch1Pbbba.js","names":["#abortController","#flushRendererWarnings","#loadHeroTalents","#skipNextPropertyPointEvent","#emitPointsChange","#normalizeSelections","#reportOverBudget","#rendererFailures","#prepareTalentRenderContent","#emitSelectionChange","#renderHeader","#renderGrid","#renderStatus","#setTalentPoints","#renderTalent","#handleTalentActivation","#renderTalentContent","#pendingRendererWarnings","#renderDefaultTalentIcon","#handleImageError","#loadToken","#lastOverBudgetSignature","#reportWarnings","#setError"],"sources":["../../src/data/package-talent-data-source.ts","../../src/talent-calculator/render-model.ts","../../src/talent-calculator/selection-state.ts","../../src/talent-calculator/talent-calculator.styles.ts","../../src/talent-calculator/talent-calculator.ts"],"sourcesContent":["import { getAssetUrl } from \"@crockery/fellowship-data\";\nimport type {\n FellowshipDataManifest,\n Hero,\n ImageAssetRef,\n Talent,\n} from \"@crockery/fellowship-data\";\n\nimport { FellowshipDataError } from \"./data-source.js\";\nimport type { FellowshipTalentDataSource, TalentCalculatorData } from \"./talent-data-source.js\";\n\nexport interface PackageFellowshipTalentDataSourceOptions {\n readonly assetBaseUrl?: string;\n}\n\ninterface PackageTalentIndex {\n readonly manifest: FellowshipDataManifest;\n readonly heroes: ReadonlyMap<string, Hero>;\n readonly talentsByHero: ReadonlyMap<string, readonly Talent[]>;\n}\n\nlet packageTalentDataPromise: Promise<PackageTalentIndex> | undefined;\n\nconst throwIfAborted = (signal?: AbortSignal): void => {\n if (signal?.aborted) {\n throw new FellowshipDataError(\n \"aborted\",\n \"The hero talent data request was aborted.\",\n signal.reason,\n );\n }\n};\n\nconst loadPackageTalentData = (): Promise<PackageTalentIndex> => {\n packageTalentDataPromise ??= Promise.all([\n import(\"@crockery/fellowship-data/heroes\"),\n import(\"@crockery/fellowship-data/talents\"),\n import(\"@crockery/fellowship-data/manifest\"),\n ])\n .then(([heroModule, talentModule, manifestModule]) => {\n const manifest = manifestModule.default;\n if ((manifest as { schemaVersion: number }).schemaVersion !== 1) {\n throw new FellowshipDataError(\n \"unsupported-schema\",\n `Unsupported fellowship-data schema version: ${String(manifest.schemaVersion)}.`,\n );\n }\n\n const mutableTalentsByHero = new Map<string, Talent[]>();\n for (const talent of talentModule.default) {\n const talents = mutableTalentsByHero.get(talent.parentId) ?? [];\n talents.push(talent);\n mutableTalentsByHero.set(talent.parentId, talents);\n }\n\n return {\n manifest,\n heroes: new Map(heroModule.default.map((hero) => [hero.id, hero])),\n talentsByHero: new Map(\n [...mutableTalentsByHero].map(([heroId, talents]) => [heroId, talents] as const),\n ),\n };\n })\n .catch((error: unknown) => {\n packageTalentDataPromise = undefined;\n if (error instanceof FellowshipDataError) {\n throw error;\n }\n throw new FellowshipDataError(\n \"data-load-failed\",\n \"Unable to load hero talents from @crockery/fellowship-data.\",\n error,\n );\n });\n\n return packageTalentDataPromise;\n};\n\nexport const createPackageFellowshipTalentDataSource = (\n options: PackageFellowshipTalentDataSourceOptions = {},\n): FellowshipTalentDataSource => ({\n async loadHeroTalents(heroId: string, signal?: AbortSignal): Promise<TalentCalculatorData> {\n throwIfAborted(signal);\n const data = await loadPackageTalentData();\n throwIfAborted(signal);\n\n const hero = data.heroes.get(heroId);\n if (!hero) {\n throw new FellowshipDataError(\n \"hero-not-found\",\n `No hero with ID “${heroId}” exists in @crockery/fellowship-data.`,\n );\n }\n\n return {\n manifest: data.manifest,\n hero,\n talents: data.talentsByHero.get(heroId) ?? [],\n };\n },\n\n resolveAssetUrl(asset: ImageAssetRef): string {\n return options.assetBaseUrl\n ? getAssetUrl(asset, { baseUrl: options.assetBaseUrl })\n : getAssetUrl(asset);\n },\n});\n","import { findImage } from \"@crockery/fellowship-data\";\nimport type { Talent } from \"@crockery/fellowship-data\";\n\nimport type {\n FellowshipTalentDataSource,\n TalentCalculatorData,\n} from \"../data/talent-data-source.js\";\nimport type {\n TalentCalculatorRenderModel,\n TalentCalculatorTalentModel,\n TalentCalculatorWarning,\n} from \"./types.js\";\n\nconst romanNumerals = [\"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\"] as const;\n\nexport const talentRowLabel = (row: number): string => romanNumerals[row - 1] ?? String(row);\n\nconst warning = (\n code: TalentCalculatorWarning[\"code\"],\n talent: Talent,\n message: string,\n): TalentCalculatorWarning => ({ code, message, talentId: talent.id });\n\nexport const createTalentCalculatorRenderModel = (\n data: TalentCalculatorData,\n source: FellowshipTalentDataSource,\n): TalentCalculatorRenderModel => {\n const warnings: TalentCalculatorWarning[] = [];\n const occupied = new Set<string>();\n const models: TalentCalculatorTalentModel[] = [];\n\n for (const talent of data.talents) {\n if (talent.row === null && talent.column === null) {\n continue;\n }\n if (\n !Number.isInteger(talent.row) ||\n !Number.isInteger(talent.column) ||\n talent.row === null ||\n talent.column === null ||\n talent.row < 1 ||\n talent.row > 6 ||\n talent.column < 1 ||\n talent.column > 3\n ) {\n warnings.push(\n warning(\n \"invalid-position\",\n talent,\n `Talent “${talent.id}” has an invalid row or column and was skipped.`,\n ),\n );\n continue;\n }\n\n const cell = `${talent.row}:${talent.column}`;\n if (occupied.has(cell)) {\n warnings.push(\n warning(\n \"duplicate-position\",\n talent,\n `Talent “${talent.id}” duplicates row ${talent.row}, column ${talent.column} and was skipped.`,\n ),\n );\n continue;\n }\n occupied.add(cell);\n\n const pointCost =\n Number.isInteger(talent.pointCost) && (talent.pointCost ?? 0) > 0 ? talent.pointCost : null;\n if (pointCost === null) {\n warnings.push(\n warning(\"invalid-cost\", talent, `Talent “${talent.id}” has an invalid point cost.`),\n );\n }\n if (!talent.name) {\n warnings.push(warning(\"missing-name\", talent, `Talent “${talent.id}” has no display name.`));\n }\n if (!talent.description) {\n warnings.push(\n warning(\"missing-description\", talent, `Talent “${talent.id}” has no description.`),\n );\n }\n\n const image =\n findImage(talent.images, { type: \"talent\", size: \"200\" }) ??\n findImage(talent.images, { type: \"talent\", size: \"full\" });\n let imageUrl: string | null = null;\n if (!image) {\n warnings.push(warning(\"missing-image\", talent, `Talent “${talent.id}” has no icon.`));\n } else {\n try {\n imageUrl = source.resolveAssetUrl(image);\n } catch (error: unknown) {\n warnings.push(\n warning(\n \"asset-url-error\",\n talent,\n `The icon URL for talent “${talent.id}” could not be resolved: ${String(error)}.`,\n ),\n );\n }\n }\n\n models.push({\n talent,\n row: talent.row,\n column: talent.column,\n pointCost,\n imageUrl,\n });\n }\n\n models.sort((a, b) => a.row - b.row || a.column - b.column);\n const talentById = new Map(models.map((model) => [model.talent.id, model]));\n const rows = [...new Set(models.map(({ row }) => row))].map((row) => ({\n row,\n label: talentRowLabel(row),\n talents: models.filter((talent) => talent.row === row),\n }));\n\n return { hero: data.hero, rows, talentById, warnings };\n};\n","import type { TalentCalculatorRenderModel, TalentCalculatorSelectionState } from \"./types.js\";\n\nexport const MAX_TALENT_POINTS = 14;\n\nexport const normalizeTalentPoints = (value: number): number =>\n Math.min(\n MAX_TALENT_POINTS,\n Math.max(0, Math.round(Number.isFinite(value) ? value : MAX_TALENT_POINTS)),\n );\n\nexport const normalizeSelectedTalentIds = (\n model: TalentCalculatorRenderModel,\n selectedTalentIds: readonly string[],\n): {\n readonly selectedTalentIds: readonly string[];\n readonly removedTalentIds: readonly string[];\n} => {\n const requested = new Set(selectedTalentIds);\n const selected = [...model.talentById.values()]\n .filter(({ talent }) => requested.has(talent.id))\n .map(({ talent }) => talent.id);\n const selectedSet = new Set(selected);\n const removed = [...new Set(selectedTalentIds)].filter((id) => !selectedSet.has(id));\n return { selectedTalentIds: selected, removedTalentIds: removed };\n};\n\nexport const createTalentCalculatorSelectionState = (\n model: TalentCalculatorRenderModel,\n selectedTalentIds: readonly string[],\n talentPoints: number,\n): TalentCalculatorSelectionState => {\n const selected = new Set(selectedTalentIds);\n const spentPoints = [...model.talentById.values()].reduce(\n (total, talent) =>\n selected.has(talent.talent.id) && talent.pointCost !== null\n ? total + talent.pointCost\n : total,\n 0,\n );\n const remainingPoints = talentPoints - spentPoints;\n return {\n selectedTalentIds: [...model.talentById.values()]\n .filter(({ talent }) => selected.has(talent.id))\n .map(({ talent }) => talent.id),\n spentPoints,\n remainingPoints,\n overBudget: remainingPoints < 0,\n };\n};\n\nexport const canSelectTalent = (\n pointCost: number | null,\n state: TalentCalculatorSelectionState,\n): boolean => pointCost !== null && !state.overBudget && pointCost <= state.remainingPoints;\n","import { css } from \"lit\";\n\nexport const talentCalculatorStyles = css`\n :host {\n --fellowship-talent-background: var(--_fellowship-field-canvas);\n --fellowship-talent-surface: var(--_fellowship-field-surface);\n --fellowship-talent-card-background: var(--_fellowship-field-surface);\n --fellowship-talent-card-selected-background: color-mix(\n in srgb,\n var(--_fellowship-field-surface-raised) 88%,\n var(--_fellowship-field-accent)\n );\n --fellowship-talent-foreground: var(--_fellowship-field-ink);\n --fellowship-talent-muted: var(--_fellowship-field-muted);\n --fellowship-talent-disabled: var(--_fellowship-field-tertiary);\n --fellowship-talent-border: var(--_fellowship-field-border);\n --fellowship-talent-selection: var(--_fellowship-field-accent);\n --fellowship-talent-focus: var(--_fellowship-field-focus);\n --fellowship-talent-points: var(--_fellowship-field-brass);\n --fellowship-talent-danger: var(--_fellowship-field-ember);\n --fellowship-talent-control-surface: var(--_fellowship-field-inset);\n --fellowship-talent-icon-size: 56px;\n --fellowship-talent-font-family: var(--_fellowship-field-font-family);\n --fellowship-talent-tactical-font-family: var(--_fellowship-field-mono-font-family);\n --fellowship-talent-radius: var(--_fellowship-field-radius);\n\n display: block;\n min-width: 0;\n container-type: inline-size;\n color: var(--fellowship-talent-foreground);\n font-family: var(--fellowship-talent-font-family);\n -webkit-font-smoothing: antialiased;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n button {\n font: inherit;\n }\n\n .container {\n min-width: 0;\n padding: 16px;\n border-radius: calc(var(--fellowship-talent-radius) + 2px);\n background: var(--fellowship-talent-background);\n }\n\n .header {\n min-height: 72px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 24px;\n padding: 8px 8px 24px;\n }\n\n .heading {\n margin: 0;\n color: var(--fellowship-talent-muted);\n font-size: 12px;\n font-weight: 600;\n line-height: 16px;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n }\n\n .points-cluster,\n .point-controls {\n display: flex;\n align-items: center;\n }\n\n .points-cluster {\n gap: 16px;\n }\n\n .points {\n display: flex;\n align-items: baseline;\n gap: 4px;\n color: var(--fellowship-talent-foreground);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 32px;\n font-weight: 600;\n line-height: 40px;\n letter-spacing: -0.04em;\n font-variant-numeric: tabular-nums;\n }\n\n .points-spent {\n color: var(--fellowship-talent-points);\n }\n\n .points-separator,\n .points-total {\n color: var(--_fellowship-field-tertiary);\n }\n\n .points-separator {\n font-size: 18px;\n font-weight: 500;\n }\n\n .points[data-invalid=\"true\"],\n .points[data-invalid=\"true\"] .points-spent {\n color: var(--fellowship-talent-danger);\n }\n\n .point-controls {\n gap: 4px;\n padding: 2px;\n border-radius: calc(var(--fellowship-talent-radius) + 2px);\n background: var(--fellowship-talent-control-surface);\n box-shadow: inset 0 0 0 1px var(--fellowship-talent-border);\n }\n\n .point-control {\n width: 40px;\n height: 40px;\n display: inline-grid;\n place-items: center;\n padding: 0;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: var(--fellowship-talent-muted);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 18px;\n font-weight: 500;\n line-height: 1;\n cursor: pointer;\n transition:\n background-color 120ms cubic-bezier(0.23, 1, 0.32, 1),\n color 120ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 120ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n .point-control:active:not(:disabled) {\n transform: scale(0.97);\n }\n\n .point-control:focus-visible,\n .clear-all:focus-visible,\n .talent:focus-visible,\n .retry-button:focus-visible {\n outline: 2px solid var(--fellowship-talent-focus);\n outline-offset: 2px;\n }\n\n .point-control:disabled {\n color: var(--fellowship-talent-disabled);\n cursor: not-allowed;\n opacity: 0.48;\n }\n\n .clear-all {\n min-height: 44px;\n padding: 0 8px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: var(--fellowship-talent-muted);\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n letter-spacing: 0.04em;\n white-space: nowrap;\n cursor: pointer;\n transition:\n background-color 120ms cubic-bezier(0.23, 1, 0.32, 1),\n color 120ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 120ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n .clear-all:active:not(:disabled) {\n transform: scale(0.97);\n }\n\n .clear-all:disabled {\n color: var(--fellowship-talent-disabled);\n cursor: not-allowed;\n opacity: 0.42;\n }\n\n .grid {\n display: grid;\n gap: 16px;\n padding: 8px 0 0;\n }\n\n .row {\n min-width: 0;\n min-inline-size: 0;\n width: 100%;\n display: grid;\n grid-template-columns: 48px minmax(0, 1fr);\n align-items: stretch;\n gap: 8px;\n margin: 0;\n padding: 0;\n border: 0;\n }\n\n .row-label {\n position: relative;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 16px;\n color: var(--fellowship-talent-disabled);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 11px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n letter-spacing: 0.08em;\n }\n\n .row-label::before {\n position: absolute;\n inset: 0 0 0 auto;\n width: 1px;\n background: var(--fellowship-talent-border);\n content: \"\";\n }\n\n .row-label::after {\n position: absolute;\n top: 23px;\n right: -3px;\n width: 7px;\n height: 1px;\n background: var(--fellowship-talent-selection);\n content: \"\";\n }\n\n .row-talents {\n min-width: 0;\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 8px;\n }\n\n fellowship-tooltip {\n display: block;\n min-width: 0;\n }\n\n .talent {\n position: relative;\n width: 100%;\n min-width: 0;\n min-height: 88px;\n display: grid;\n grid-template-columns: var(--fellowship-talent-icon-size) minmax(0, 1fr);\n align-items: center;\n gap: 16px;\n padding: 16px 36px 16px 16px;\n overflow: hidden;\n border: 1px solid transparent;\n border-radius: var(--fellowship-talent-radius);\n background: var(--fellowship-talent-card-background);\n color: var(--fellowship-talent-foreground);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fellowship-talent-border) 64%, transparent);\n text-align: left;\n cursor: pointer;\n transition:\n background-color 140ms cubic-bezier(0.23, 1, 0.32, 1),\n box-shadow 140ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 120ms cubic-bezier(0.23, 1, 0.32, 1),\n opacity 140ms linear;\n }\n\n .talent::before {\n position: absolute;\n top: 16px;\n left: 0;\n width: 2px;\n height: 24px;\n background: transparent;\n content: \"\";\n }\n\n .talent:active:not([aria-disabled=\"true\"]) {\n transform: scale(0.99);\n }\n\n .talent[aria-pressed=\"true\"] {\n background: var(--fellowship-talent-card-selected-background);\n box-shadow: inset 0 0 0 1px\n color-mix(in srgb, var(--fellowship-talent-selection) 32%, transparent);\n }\n\n .talent[aria-pressed=\"true\"]::before {\n background: var(--fellowship-talent-selection);\n }\n\n .talent[aria-disabled=\"true\"] {\n color: var(--fellowship-talent-disabled);\n cursor: not-allowed;\n opacity: 0.46;\n }\n\n .talent-icon,\n .talent-icon-fallback {\n width: var(--fellowship-talent-icon-size);\n height: var(--fellowship-talent-icon-size);\n border-radius: calc(var(--fellowship-talent-radius) - 2px);\n box-shadow: inset 0 0 0 1px var(--fellowship-talent-border);\n }\n\n .talent-icon {\n display: block;\n object-fit: cover;\n }\n\n .talent-icon-fallback {\n display: grid;\n place-items: center;\n background: var(--fellowship-talent-control-surface);\n color: var(--fellowship-talent-muted);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 13px;\n font-weight: 600;\n letter-spacing: 0.08em;\n }\n\n .talent[aria-pressed=\"true\"] .talent-icon,\n .talent[aria-pressed=\"true\"] .talent-icon-fallback {\n box-shadow:\n inset 0 0 0 1px var(--fellowship-talent-border),\n 0 0 0 1px color-mix(in srgb, var(--fellowship-talent-selection) 60%, transparent);\n }\n\n .talent-name {\n min-width: 0;\n overflow: hidden;\n color: currentColor;\n font-size: 16px;\n font-weight: 600;\n line-height: 24px;\n text-overflow: ellipsis;\n text-wrap: balance;\n white-space: nowrap;\n }\n\n .talent-cost {\n position: absolute;\n top: 12px;\n right: 12px;\n color: var(--fellowship-talent-muted);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 11px;\n font-weight: 600;\n line-height: 16px;\n font-variant-numeric: tabular-nums;\n }\n\n .talent[aria-pressed=\"true\"] .talent-cost {\n color: var(--fellowship-talent-focus);\n }\n\n .status {\n min-height: 256px;\n display: grid;\n place-items: center;\n padding: 32px;\n color: var(--fellowship-talent-muted);\n text-align: center;\n }\n\n .status-card {\n max-width: 480px;\n }\n\n .status-mark {\n display: flex;\n width: 40px;\n height: 24px;\n align-items: center;\n justify-content: space-between;\n margin: 0 auto 16px;\n color: var(--fellowship-talent-points);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 16px;\n }\n\n .status-mark::before,\n .status-mark::after {\n width: 8px;\n height: 1px;\n background: var(--fellowship-talent-border);\n content: \"\";\n }\n\n .status-title {\n margin: 0;\n color: var(--fellowship-talent-foreground);\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n text-wrap: balance;\n }\n\n .status-copy {\n margin: 8px 0 0;\n font-size: 14px;\n line-height: 24px;\n text-wrap: pretty;\n }\n\n .retry-button {\n min-height: 44px;\n margin-top: 24px;\n padding: 0 16px;\n border: 1px solid var(--fellowship-talent-border);\n border-radius: 4px;\n background: var(--fellowship-talent-control-surface);\n color: var(--fellowship-talent-foreground);\n font-weight: 600;\n cursor: pointer;\n }\n\n .visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n @media (hover: hover) {\n .point-control:hover:not(:disabled) {\n background: var(--fellowship-talent-surface);\n color: var(--fellowship-talent-foreground);\n }\n\n .clear-all:hover:not(:disabled) {\n background: color-mix(in srgb, var(--fellowship-talent-danger) 8%, transparent);\n color: var(--fellowship-talent-danger);\n }\n\n .talent:hover:not([aria-disabled=\"true\"]) {\n background: var(--_fellowship-field-surface-raised);\n box-shadow: inset 0 0 0 1px var(--fellowship-talent-border);\n }\n }\n\n @container (max-width: 719px) {\n .container {\n padding: 16px 12px;\n }\n\n .header {\n min-height: 80px;\n grid-template-columns: 1fr;\n display: grid;\n gap: 8px;\n padding: 4px 4px 20px;\n }\n\n .heading {\n font-size: 11px;\n }\n\n .points-cluster {\n width: 100%;\n flex-wrap: wrap;\n justify-content: flex-start;\n gap: 8px;\n }\n\n .points {\n font-size: 28px;\n line-height: 36px;\n }\n\n .grid {\n gap: 24px;\n }\n\n .row {\n grid-template-columns: 1fr;\n gap: 8px;\n }\n\n .row-label {\n min-height: 24px;\n align-items: center;\n justify-content: flex-start;\n padding: 0 0 0 16px;\n text-align: left;\n }\n\n .row-label::before {\n inset: 12px auto auto 0;\n width: 8px;\n height: 1px;\n }\n\n .row-label::after {\n top: 9px;\n right: auto;\n left: 0;\n width: 1px;\n height: 7px;\n }\n\n .row-talents {\n grid-template-columns: 1fr;\n gap: 8px;\n }\n\n .talent {\n --fellowship-talent-icon-size: 52px;\n\n min-height: 80px;\n padding-block: 12px;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .point-control,\n .clear-all,\n .talent {\n transition:\n background-color 100ms linear,\n box-shadow 100ms linear,\n color 100ms linear,\n opacity 100ms linear;\n }\n\n .point-control:active:not(:disabled),\n .clear-all:active:not(:disabled),\n .talent:active:not([aria-disabled=\"true\"]) {\n transform: none;\n }\n }\n`;\n","import { html, LitElement, nothing, type PropertyValues, type TemplateResult } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\n\nimport { FellowshipDataError } from \"../data/data-source.js\";\nimport { createPackageFellowshipTalentDataSource } from \"../data/package-talent-data-source.js\";\nimport type { FellowshipTalentDataSource } from \"../data/talent-data-source.js\";\nimport { fieldManualStyles } from \"../styles/field-manual.styles.js\";\nimport { installFieldManualFonts } from \"../styles/field-manual-fonts.js\";\nimport { stripFellowshipRichText } from \"../tooltip/rich-text.js\";\nimport { defineFellowshipTooltip } from \"../tooltip/tooltip.js\";\nimport { createTalentCalculatorRenderModel } from \"./render-model.js\";\nimport {\n canSelectTalent,\n createTalentCalculatorSelectionState,\n MAX_TALENT_POINTS,\n normalizeSelectedTalentIds,\n normalizeTalentPoints,\n} from \"./selection-state.js\";\nimport { talentCalculatorStyles } from \"./talent-calculator.styles.js\";\nimport type {\n FellowshipTalentsErrorDetail,\n FellowshipTalentsReadyDetail,\n FellowshipTalentsWarningDetail,\n LitTalentRenderer,\n TalentCalculatorRenderModel,\n TalentCalculatorSelectionState,\n TalentCalculatorTalentModel,\n TalentCalculatorTalentRenderContext,\n TalentCalculatorWarning,\n TalentPointsChangeDetail,\n TalentPointsChangeSource,\n TalentSelectionChangeDetail,\n TalentSelectionChangeSource,\n} from \"./types.js\";\n\ntype ComponentStatus = \"empty\" | \"loading\" | \"ready\" | \"error\";\n\ninterface TalentPresentation {\n readonly context: TalentCalculatorTalentRenderContext;\n readonly name: string;\n readonly descriptionId: string;\n readonly affordability: string;\n readonly description: string;\n}\n\nconst eventOptions = { bubbles: true, composed: true } as const;\n\nconst stringArrayChanged = (\n value: readonly string[],\n previous: readonly string[] | undefined,\n): boolean =>\n value.length !== previous?.length || value.some((item, index) => item !== previous[index]);\n\nexport const talentSlotName = (talentId: string): string => `talent:${talentId}`;\n\nconst fallbackSelectionState = (talentPoints: number): TalentCalculatorSelectionState => ({\n selectedTalentIds: [],\n spentPoints: 0,\n remainingPoints: talentPoints,\n overBudget: false,\n});\n\nexport { MAX_TALENT_POINTS };\n\n/**\n * @fires fellowship-talents-ready - Fired after talent data is loaded and rendered.\n * @fires fellowship-talents-error - Fired when talent data cannot be loaded.\n * @fires fellowship-talents-warning - Fired for recoverable data and renderer issues.\n * @fires fellowship-talent-selection-change - Fired when selected talents change.\n * @fires fellowship-talent-points-change - Fired when the point budget changes.\n */\n@customElement(\"fellowship-talent-calculator\")\nexport class FellowshipTalentCalculatorElement extends LitElement {\n static override styles = [fieldManualStyles, talentCalculatorStyles];\n\n @property({ type: String, attribute: \"hero-id\" }) heroId = \"\";\n @property({ type: Number, attribute: \"talent-points\" }) talentPoints = MAX_TALENT_POINTS;\n @property({ attribute: false, hasChanged: stringArrayChanged })\n selectedTalentIds: readonly string[] = [];\n @property({ type: String, attribute: \"asset-base-url\" }) assetBaseUrl: string | null = null;\n @property({ attribute: false }) dataSource: FellowshipTalentDataSource | null = null;\n @property({ attribute: false }) renderTalent: LitTalentRenderer | null = null;\n @property({ type: Boolean, attribute: \"show-point-controls\" }) showPointControls = true;\n @property({ type: Boolean, attribute: \"show-tooltips\" }) showTooltips = true;\n\n @state() private _status: ComponentStatus = \"empty\";\n @state() private _error: FellowshipTalentsErrorDetail | null = null;\n @state() private _model: TalentCalculatorRenderModel | null = null;\n @state() private _failedImages = new Set<string>();\n @state() private _announcement = \"\";\n @state() private _renderSelectionState = fallbackSelectionState(MAX_TALENT_POINTS);\n @state() private _talentPresentation = new Map<string, TalentPresentation>();\n @state() private _talentRenderContent = new Map<string, unknown>();\n\n #abortController: AbortController | null = null;\n #loadToken = 0;\n #lastOverBudgetSignature: string | null = null;\n #skipNextPropertyPointEvent = false;\n #rendererFailures = new Set<string>();\n #pendingRendererWarnings: TalentCalculatorWarning[] = [];\n\n override connectedCallback(): void {\n super.connectedCallback();\n installFieldManualFonts(this.ownerDocument);\n }\n\n override disconnectedCallback(): void {\n this.#abortController?.abort();\n super.disconnectedCallback();\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n super.updated(changed);\n this.#flushRendererWarnings();\n\n if (changed.has(\"heroId\") || changed.has(\"dataSource\") || changed.has(\"assetBaseUrl\")) {\n queueMicrotask(() => {\n if (this.isConnected) {\n void this.#loadHeroTalents();\n }\n });\n return;\n }\n\n if (changed.has(\"talentPoints\")) {\n const normalized = normalizeTalentPoints(this.talentPoints);\n if (normalized !== this.talentPoints) {\n this.#skipNextPropertyPointEvent = true;\n this.talentPoints = normalized;\n this.#emitPointsChange(\"normalization\");\n return;\n }\n if (this.#skipNextPropertyPointEvent) {\n this.#skipNextPropertyPointEvent = false;\n } else if (changed.get(\"talentPoints\") !== undefined) {\n this.#emitPointsChange(\"property\");\n }\n }\n\n if (changed.has(\"selectedTalentIds\") && this._model) {\n this.#normalizeSelections(true);\n }\n\n this.#reportOverBudget();\n }\n\n protected override willUpdate(changed: PropertyValues): void {\n super.willUpdate(changed);\n\n const selectionChanged =\n changed.has(\"_model\") || changed.has(\"selectedTalentIds\") || changed.has(\"talentPoints\");\n if (selectionChanged) {\n this._renderSelectionState = this._model\n ? createTalentCalculatorSelectionState(\n this._model,\n this.selectedTalentIds,\n this.talentPoints,\n )\n : fallbackSelectionState(this.talentPoints);\n }\n\n if (changed.has(\"renderTalent\")) {\n this.#rendererFailures.clear();\n }\n if (selectionChanged || changed.has(\"renderTalent\")) {\n this.#prepareTalentRenderContent(this._renderSelectionState);\n }\n }\n\n get renderModel(): TalentCalculatorRenderModel | null {\n return this._model;\n }\n\n get selectionState(): TalentCalculatorSelectionState {\n return this._model\n ? createTalentCalculatorSelectionState(this._model, this.selectedTalentIds, this.talentPoints)\n : fallbackSelectionState(this.talentPoints);\n }\n\n reset(): void {\n if (this.selectedTalentIds.length === 0) {\n return;\n }\n this.selectedTalentIds = [];\n this.#emitSelectionChange(\"reset\");\n }\n\n selectTalent(talentId: string): boolean {\n const model = this._model?.talentById.get(talentId);\n const state = this.selectionState;\n if (\n !model ||\n state.selectedTalentIds.includes(talentId) ||\n !canSelectTalent(model.pointCost, state)\n ) {\n return false;\n }\n this.selectedTalentIds = [...state.selectedTalentIds, talentId];\n this.#emitSelectionChange(\"api\");\n return true;\n }\n\n deselectTalent(talentId: string): boolean {\n if (!this.selectionState.selectedTalentIds.includes(talentId)) {\n return false;\n }\n this.selectedTalentIds = this.selectionState.selectedTalentIds.filter((id) => id !== talentId);\n this.#emitSelectionChange(\"api\");\n return true;\n }\n\n retry(): void {\n void this.#loadHeroTalents();\n }\n\n override render(): TemplateResult {\n const state = this._renderSelectionState;\n const label = this._model?.hero.name ?? this._model?.hero.id ?? \"Fellowship hero\";\n return html`\n <section\n class=\"container\"\n part=\"container\"\n aria-label=${`${label} talent calculator`}\n aria-invalid=${String(state.overBudget)}\n >\n ${this.#renderHeader(state)}\n ${\n this._status === \"ready\" && this._model\n ? this.#renderGrid(this._model)\n : this.#renderStatus()\n }\n <p class=\"visually-hidden\" role=\"status\" aria-live=\"polite\">${this._announcement}</p>\n </section>\n `;\n }\n\n #renderHeader(state: TalentCalculatorSelectionState): TemplateResult {\n return html`\n <header class=\"header\" part=\"header\">\n <h2 class=\"heading\" part=\"heading\">Talent points</h2>\n <div class=\"points-cluster\">\n <div\n class=\"points\"\n part=\"points\"\n data-invalid=${String(state.overBudget)}\n aria-label=${`${state.spentPoints} of ${this.talentPoints} talent points spent`}\n aria-live=\"polite\"\n >\n <span class=\"points-spent\" part=\"points-spent\">${state.spentPoints}</span>\n <span class=\"points-separator\" aria-hidden=\"true\">/</span>\n <span class=\"points-total\" part=\"points-total\">${this.talentPoints}</span>\n </div>\n ${\n this.showPointControls\n ? html`\n <div\n class=\"point-controls\"\n part=\"point-controls\"\n role=\"group\"\n aria-label=\"Talent points\"\n >\n <button\n class=\"point-control\"\n part=\"point-control\"\n type=\"button\"\n aria-label=\"Decrease available talent points\"\n title=\"Decrease available talent points\"\n ?disabled=${this.talentPoints <= state.spentPoints}\n @click=${() => this.#setTalentPoints(this.talentPoints - 1, \"decrement\")}\n >\n −\n </button>\n <button\n class=\"point-control\"\n part=\"point-control\"\n type=\"button\"\n aria-label=\"Increase available talent points\"\n title=\"Increase available talent points\"\n ?disabled=${this.talentPoints >= MAX_TALENT_POINTS}\n @click=${() => this.#setTalentPoints(this.talentPoints + 1, \"increment\")}\n >\n +\n </button>\n </div>\n `\n : nothing\n }\n <button\n class=\"clear-all\"\n part=\"clear-all\"\n type=\"button\"\n ?disabled=${state.selectedTalentIds.length === 0}\n @click=${this.reset}\n >\n Clear All\n </button>\n </div>\n </header>\n `;\n }\n\n #renderGrid(model: TalentCalculatorRenderModel): TemplateResult {\n if (model.rows.length === 0) {\n return html`\n <div class=\"status\" part=\"empty-state\" role=\"status\">\n <div class=\"status-card\">\n <div class=\"status-mark\" aria-hidden=\"true\">◇</div>\n <p class=\"status-title\">No active talents</p>\n <p class=\"status-copy\">\n ${model.hero.name ?? model.hero.id} has no positioned talents.\n </p>\n </div>\n </div>\n `;\n }\n\n return html`\n <div class=\"grid\" part=\"grid\" role=\"group\" aria-label=\"Talent tiers\">\n ${repeat(\n model.rows,\n (row) => row.row,\n (row) => html`\n <fieldset class=\"row\" part=\"row\">\n <legend class=\"visually-hidden\">Talent tier ${row.label}</legend>\n <span class=\"row-label\" part=\"row-label\" aria-hidden=\"true\">${row.label}</span>\n <div class=\"row-talents\">\n ${repeat(\n row.talents,\n (talent) => talent.talent.id,\n (talent) => this.#renderTalent(talent),\n )}\n </div>\n </fieldset>\n `,\n )}\n </div>\n `;\n }\n\n #renderTalent(talent: TalentCalculatorTalentModel): TemplateResult | typeof nothing {\n const presentation = this._talentPresentation.get(talent.talent.id);\n if (!presentation) {\n return nothing;\n }\n const { context, name, descriptionId, affordability, description } = presentation;\n const { selected, disabled } = context;\n const card = html`\n <button\n class=\"talent\"\n part=\"talent\"\n type=\"button\"\n aria-label=${`${name}, ${affordability}`}\n aria-describedby=${descriptionId}\n aria-pressed=${String(selected)}\n aria-disabled=${String(disabled)}\n @click=${() => this.#handleTalentActivation(talent, selected, disabled)}\n >\n ${this.#renderTalentContent(context)}\n <span class=\"talent-cost\" part=\"talent-cost\">${talent.pointCost ?? \"–\"}</span>\n </button>\n `;\n\n return html`\n ${\n this.showTooltips && (talent.talent.description || disabled)\n ? html`\n <fellowship-tooltip\n .heading=${name}\n .text=${talent.talent.description ?? affordability}\n >\n ${card}\n </fellowship-tooltip>\n `\n : card\n }\n <span class=\"visually-hidden\" id=${descriptionId}>${description}</span>\n `;\n }\n\n #prepareTalentRenderContent(state: TalentCalculatorSelectionState): void {\n const renderer = this.renderTalent;\n const model = this._model;\n const content = new Map<string, unknown>();\n const presentations = new Map<string, TalentPresentation>();\n\n if (model) {\n for (const talent of model.talentById.values()) {\n const selected = state.selectedTalentIds.includes(talent.talent.id);\n const affordable = canSelectTalent(talent.pointCost, state);\n const disabled = !selected && !affordable;\n const name = talent.talent.name ?? talent.talent.id;\n const affordability =\n talent.pointCost === null\n ? \"This talent has no valid point cost.\"\n : disabled\n ? `Requires ${talent.pointCost} talent points; ${Math.max(0, state.remainingPoints)} remaining.`\n : `${talent.pointCost} talent ${talent.pointCost === 1 ? \"point\" : \"points\"}.`;\n const context: TalentCalculatorTalentRenderContext = {\n talent,\n hero: model.hero,\n selected,\n affordable,\n disabled,\n spentPoints: state.spentPoints,\n remainingPoints: state.remainingPoints,\n talentPoints: this.talentPoints,\n };\n presentations.set(talent.talent.id, {\n context,\n name,\n descriptionId: `talent-description-${talent.row}-${talent.column}`,\n affordability,\n description: [stripFellowshipRichText(talent.talent.description ?? \"\"), affordability]\n .filter(Boolean)\n .join(\" \"),\n });\n if (!renderer) {\n continue;\n }\n try {\n content.set(talent.talent.id, renderer(context));\n } catch (error: unknown) {\n if (!this.#rendererFailures.has(talent.talent.id)) {\n this.#rendererFailures.add(talent.talent.id);\n this.#pendingRendererWarnings.push({\n code: \"renderer-error\",\n message: `The talent renderer failed for “${talent.talent.id}”: ${String(error)}.`,\n talentId: talent.talent.id,\n });\n }\n }\n }\n }\n\n this._talentPresentation = presentations;\n this._talentRenderContent = content;\n }\n\n #renderTalentContent(context: TalentCalculatorTalentRenderContext): unknown {\n const talent = context.talent;\n const name = talent.talent.name ?? talent.talent.id;\n const content =\n this.renderTalent && this._talentRenderContent.has(talent.talent.id)\n ? this._talentRenderContent.get(talent.talent.id)\n : html`\n <slot name=${talentSlotName(talent.talent.id)}>\n <span class=\"talent-name\" part=\"talent-name\" title=${name}>${name}</span>\n </slot>\n `;\n\n return html`\n ${this.#renderDefaultTalentIcon(talent, name)} ${content}\n `;\n }\n\n #renderDefaultTalentIcon(talent: TalentCalculatorTalentModel, name: string): TemplateResult {\n if (talent.imageUrl && !this._failedImages.has(talent.talent.id)) {\n return html`\n <img\n class=\"talent-icon\"\n part=\"talent-icon\"\n src=${talent.imageUrl}\n alt=\"\"\n draggable=\"false\"\n @error=${() => this.#handleImageError(talent)}\n />\n `;\n }\n const rune = name\n .split(/\\s+/)\n .map((part) => part[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n return html`<span class=\"talent-icon-fallback\" part=\"talent-icon-fallback\">${rune}</span>`;\n }\n\n #renderStatus(): TemplateResult {\n const loading = this._status === \"loading\";\n const empty = this._status === \"empty\";\n const title = loading\n ? \"Reading talent codex\"\n : empty\n ? \"Choose a hero\"\n : \"Talents unavailable\";\n const copy = loading\n ? \"Loading the hero and their available talents.\"\n : empty\n ? \"Set the hero-id attribute or heroId property to build a talent loadout.\"\n : (this._error?.message ?? \"An unexpected talent data error occurred.\");\n return html`\n <div\n class=\"status\"\n part=${this._status === \"error\" ? \"error-state\" : \"status\"}\n role=${this._status === \"error\" ? \"alert\" : \"status\"}\n >\n <div class=\"status-card\">\n <div class=\"status-mark\" aria-hidden=\"true\">${loading ? \"…\" : \"✦\"}</div>\n <p class=\"status-title\">${title}</p>\n <p class=\"status-copy\">${copy}</p>\n ${\n this._status === \"error\"\n ? html`<button class=\"retry-button\" type=\"button\" @click=${this.retry}>Retry</button>`\n : nothing\n }\n </div>\n </div>\n `;\n }\n\n async #loadHeroTalents(): Promise<void> {\n const heroId = this.heroId.trim();\n const token = ++this.#loadToken;\n this.#abortController?.abort();\n this.#abortController = new AbortController();\n this._model = null;\n this._error = null;\n this._failedImages = new Set();\n this.#rendererFailures.clear();\n this.#lastOverBudgetSignature = null;\n\n if (!heroId) {\n this._status = \"empty\";\n return;\n }\n\n this._status = \"loading\";\n const source =\n this.dataSource ??\n createPackageFellowshipTalentDataSource(\n this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {},\n );\n\n try {\n const data = await source.loadHeroTalents(heroId, this.#abortController.signal);\n if (token !== this.#loadToken || this.#abortController.signal.aborted) {\n return;\n }\n if (data.manifest.schemaVersion !== 1) {\n throw new FellowshipDataError(\n \"unsupported-schema\",\n `Unsupported fellowship-data schema version: ${String(data.manifest.schemaVersion)}.`,\n );\n }\n const model = createTalentCalculatorRenderModel(data, source);\n this._model = model;\n this._status = \"ready\";\n const normalized = this.#normalizeSelections(false);\n await this.updateComplete;\n const selection = this.selectionState;\n this.dispatchEvent(\n new CustomEvent<FellowshipTalentsReadyDetail>(\"fellowship-talents-ready\", {\n ...eventOptions,\n detail: { data, model, selection },\n }),\n );\n if (model.warnings.length > 0) {\n this.#reportWarnings(model.warnings);\n }\n if (normalized) {\n this.#emitSelectionChange(\"normalization\");\n }\n this.#reportOverBudget();\n } catch (error: unknown) {\n if (token !== this.#loadToken || this.#abortController.signal.aborted) {\n return;\n }\n this.#setError(error);\n }\n }\n\n #normalizeSelections(emit: boolean): boolean {\n if (!this._model) {\n return false;\n }\n const normalized = normalizeSelectedTalentIds(this._model, this.selectedTalentIds);\n const changed =\n normalized.selectedTalentIds.length !== this.selectedTalentIds.length ||\n normalized.selectedTalentIds.some((id, index) => id !== this.selectedTalentIds[index]);\n if (!changed) {\n return false;\n }\n this.selectedTalentIds = normalized.selectedTalentIds;\n if (normalized.removedTalentIds.length > 0) {\n this.#reportWarnings([\n {\n code: \"invalid-selection\",\n message: `Removed unavailable talent selections: ${normalized.removedTalentIds.join(\", \")}.`,\n talentId: null,\n },\n ]);\n }\n if (emit) {\n this.#emitSelectionChange(\"normalization\");\n }\n return true;\n }\n\n #handleTalentActivation(\n talent: TalentCalculatorTalentModel,\n selected: boolean,\n disabled: boolean,\n ): void {\n if (selected) {\n this.selectedTalentIds = this.selectionState.selectedTalentIds.filter(\n (id) => id !== talent.talent.id,\n );\n this.#emitSelectionChange(\"user\");\n return;\n }\n if (disabled) {\n this._announcement = this.selectionState.overBudget\n ? \"The current build exceeds the available talent points. Deselect a talent first.\"\n : `Not enough points for ${talent.talent.name ?? talent.talent.id}.`;\n return;\n }\n this.selectedTalentIds = [...this.selectionState.selectedTalentIds, talent.talent.id];\n this.#emitSelectionChange(\"user\");\n }\n\n #setTalentPoints(value: number, source: TalentPointsChangeSource): void {\n const normalized = normalizeTalentPoints(value);\n const state = this.selectionState;\n if (normalized === this.talentPoints || normalized < state.spentPoints) {\n return;\n }\n this.talentPoints = normalized;\n this.#skipNextPropertyPointEvent = true;\n this.#emitPointsChange(source);\n }\n\n #emitSelectionChange(source: TalentSelectionChangeSource): void {\n const state = this._model\n ? createTalentCalculatorSelectionState(this._model, this.selectedTalentIds, this.talentPoints)\n : fallbackSelectionState(this.talentPoints);\n this.dispatchEvent(\n new CustomEvent<TalentSelectionChangeDetail>(\"fellowship-talent-selection-change\", {\n ...eventOptions,\n detail: { ...state, source },\n }),\n );\n }\n\n #emitPointsChange(source: TalentPointsChangeSource): void {\n const state = this.selectionState;\n this.dispatchEvent(\n new CustomEvent<TalentPointsChangeDetail>(\"fellowship-talent-points-change\", {\n ...eventOptions,\n detail: { ...state, talentPoints: this.talentPoints, source },\n }),\n );\n }\n\n #reportOverBudget(): void {\n const state = this.selectionState;\n if (!state.overBudget) {\n this.#lastOverBudgetSignature = null;\n this.removeAttribute(\"aria-invalid\");\n return;\n }\n this.setAttribute(\"aria-invalid\", \"true\");\n const signature = `${this.talentPoints}:${state.selectedTalentIds.join(\",\")}`;\n if (signature === this.#lastOverBudgetSignature) {\n return;\n }\n this.#lastOverBudgetSignature = signature;\n this.#reportWarnings([\n {\n code: \"over-budget\",\n message: `The selected talents cost ${state.spentPoints} points, exceeding the ${this.talentPoints}-point budget.`,\n talentId: null,\n },\n ]);\n }\n\n #handleImageError(talent: TalentCalculatorTalentModel): void {\n if (this._failedImages.has(talent.talent.id)) {\n return;\n }\n this._failedImages = new Set(this._failedImages).add(talent.talent.id);\n this.#reportWarnings([\n {\n code: \"asset-image-error\",\n message: `The icon for talent “${talent.talent.id}” could not be loaded.`,\n talentId: talent.talent.id,\n },\n ]);\n }\n\n #reportWarnings(warnings: readonly TalentCalculatorWarning[]): void {\n this.dispatchEvent(\n new CustomEvent<FellowshipTalentsWarningDetail>(\"fellowship-talents-warning\", {\n ...eventOptions,\n detail: { warnings, model: this._model },\n }),\n );\n }\n\n #flushRendererWarnings(): void {\n if (this.#pendingRendererWarnings.length === 0) {\n return;\n }\n const warnings = this.#pendingRendererWarnings;\n this.#pendingRendererWarnings = [];\n this.#reportWarnings(warnings);\n }\n\n #setError(error: unknown): void {\n const detail: FellowshipTalentsErrorDetail =\n typeof error === \"object\" && error !== null && \"code\" in error && \"message\" in error\n ? {\n code: String(error.code),\n message: String(error.message),\n ...(error instanceof FellowshipDataError && error.cause !== undefined\n ? { cause: error.cause }\n : {}),\n }\n : { code: \"unknown\", message: error instanceof Error ? error.message : String(error) };\n this._error = detail;\n this._status = \"error\";\n this.dispatchEvent(\n new CustomEvent<FellowshipTalentsErrorDetail>(\"fellowship-talents-error\", {\n ...eventOptions,\n detail,\n }),\n );\n }\n}\n\nexport const defineFellowshipTalentCalculator = (): void => {\n defineFellowshipTooltip();\n if (!customElements.get(\"fellowship-talent-calculator\")) {\n customElements.define(\"fellowship-talent-calculator\", FellowshipTalentCalculatorElement);\n }\n};\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fellowship-talent-calculator\": FellowshipTalentCalculatorElement;\n }\n}\n"],"mappings":";;;;;;;;AAqBA,IAAI,GAEE,KAAkB,MAA+B;CACrD,IAAI,GAAQ,SACV,MAAM,IAAI,EACR,WACA,6CACA,EAAO,MACT;AAEJ,GAEM,WACJ,MAA6B,QAAQ,IAAI;CACvC,OAAO;CACP,OAAO;CACP,OAAO;AACT,CAAC,CAAC,CACC,MAAM,CAAC,GAAY,GAAc,OAAoB;CACpD,IAAM,IAAW,EAAe;CAChC,IAAK,EAAuC,kBAAkB,GAC5D,MAAM,IAAI,EACR,sBACA,+CAA+C,OAAO,EAAS,aAAa,EAAE,EAChF;CAGF,IAAM,oBAAuB,IAAI,IAAsB;CACvD,KAAK,IAAM,KAAU,EAAa,SAAS;EACzC,IAAM,IAAU,EAAqB,IAAI,EAAO,QAAQ,KAAK,CAAC;EAE9D,AADA,EAAQ,KAAK,CAAM,GACnB,EAAqB,IAAI,EAAO,UAAU,CAAO;CACnD;CAEA,OAAO;EACL;EACA,QAAQ,IAAI,IAAI,EAAW,QAAQ,KAAK,MAAS,CAAC,EAAK,IAAI,CAAI,CAAC,CAAC;EACjE,eAAe,IAAI,IACjB,CAAC,GAAG,CAAoB,CAAC,CAAC,KAAK,CAAC,GAAQ,OAAa,CAAC,GAAQ,CAAO,CAAU,CACjF;CACF;AACF,CAAC,CAAC,CACD,OAAO,MAAmB;CAKzB,MAJA,IAA2B,KAAA,GACvB,aAAiB,IACb,IAEF,IAAI,EACR,oBACA,+DACA,CACF;AACF,CAAC,GAEI,IAGI,KACX,IAAoD,CAAC,OACrB;CAChC,MAAM,gBAAgB,GAAgB,GAAqD;EACzF,EAAe,CAAM;EACrB,IAAM,IAAO,MAAM,EAAsB;EACzC,EAAe,CAAM;EAErB,IAAM,IAAO,EAAK,OAAO,IAAI,CAAM;EACnC,IAAI,CAAC,GACH,MAAM,IAAI,EACR,kBACA,oBAAoB,EAAO,uCAC7B;EAGF,OAAO;GACL,UAAU,EAAK;GACf;GACA,SAAS,EAAK,cAAc,IAAI,CAAM,KAAK,CAAC;EAC9C;CACF;CAEA,gBAAgB,GAA8B;EAC5C,OAAO,EAAQ,eACX,EAAY,GAAO,EAAE,SAAS,EAAQ,aAAa,CAAC,IACpD,EAAY,CAAK;CACvB;AACF,IC7FM,IAAgB;CAAC;CAAK;CAAM;CAAO;CAAM;CAAK;AAAI,GAE3C,KAAkB,MAAwB,EAAc,IAAM,MAAM,OAAO,CAAG,GAErF,KACJ,GACA,GACA,OAC6B;CAAE;CAAM;CAAS,UAAU,EAAO;AAAG,IAEvD,KACX,GACA,MACgC;CAChC,IAAM,IAAsC,CAAC,GACvC,oBAAW,IAAI,IAAY,GAC3B,IAAwC,CAAC;CAE/C,KAAK,IAAM,KAAU,EAAK,SAAS;EACjC,IAAI,EAAO,QAAQ,QAAQ,EAAO,WAAW,MAC3C;EAEF,IACE,CAAC,OAAO,UAAU,EAAO,GAAG,KAC5B,CAAC,OAAO,UAAU,EAAO,MAAM,KAC/B,EAAO,QAAQ,QACf,EAAO,WAAW,QAClB,EAAO,MAAM,KACb,EAAO,MAAM,KACb,EAAO,SAAS,KAChB,EAAO,SAAS,GAChB;GACA,EAAS,KACP,EACE,oBACA,GACA,WAAW,EAAO,GAAG,gDACvB,CACF;GACA;EACF;EAEA,IAAM,IAAO,GAAG,EAAO,IAAI,GAAG,EAAO;EACrC,IAAI,EAAS,IAAI,CAAI,GAAG;GACtB,EAAS,KACP,EACE,sBACA,GACA,WAAW,EAAO,GAAG,mBAAmB,EAAO,IAAI,WAAW,EAAO,OAAO,kBAC9E,CACF;GACA;EACF;EACA,EAAS,IAAI,CAAI;EAEjB,IAAM,IACJ,OAAO,UAAU,EAAO,SAAS,MAAM,EAAO,aAAa,KAAK,IAAI,EAAO,YAAY;EASzF,AARI,MAAc,QAChB,EAAS,KACP,EAAQ,gBAAgB,GAAQ,WAAW,EAAO,GAAG,6BAA6B,CACpF,GAEG,EAAO,QACV,EAAS,KAAK,EAAQ,gBAAgB,GAAQ,WAAW,EAAO,GAAG,uBAAuB,CAAC,GAExF,EAAO,eACV,EAAS,KACP,EAAQ,uBAAuB,GAAQ,WAAW,EAAO,GAAG,sBAAsB,CACpF;EAGF,IAAM,IACJ,EAAU,EAAO,QAAQ;GAAE,MAAM;GAAU,MAAM;EAAM,CAAC,KACxD,EAAU,EAAO,QAAQ;GAAE,MAAM;GAAU,MAAM;EAAO,CAAC,GACvD,IAA0B;EAC9B,IAAI,CAAC,GACH,EAAS,KAAK,EAAQ,iBAAiB,GAAQ,WAAW,EAAO,GAAG,eAAe,CAAC;OAEpF,IAAI;GACF,IAAW,EAAO,gBAAgB,CAAK;EACzC,SAAS,GAAgB;GACvB,EAAS,KACP,EACE,mBACA,GACA,4BAA4B,EAAO,GAAG,2BAA2B,OAAO,CAAK,EAAE,EACjF,CACF;EACF;EAGF,EAAO,KAAK;GACV;GACA,KAAK,EAAO;GACZ,QAAQ,EAAO;GACf;GACA;EACF,CAAC;CACH;CAEA,EAAO,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM;CAC1D,IAAM,IAAa,IAAI,IAAI,EAAO,KAAK,MAAU,CAAC,EAAM,OAAO,IAAI,CAAK,CAAC,CAAC,GACpE,IAAO,CAAC,GAAG,IAAI,IAAI,EAAO,KAAK,EAAE,aAAU,CAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAS;EACpE;EACA,OAAO,EAAe,CAAG;EACzB,SAAS,EAAO,QAAQ,MAAW,EAAO,QAAQ,CAAG;CACvD,EAAE;CAEF,OAAO;EAAE,MAAM,EAAK;EAAM;EAAM;EAAY;CAAS;AACvD,GCxHa,IAAoB,IAEpB,KAAyB,MACpC,KAAK,IAAA,IAEH,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,SAAS,CAAK,IAAI,IAAA,EAAyB,CAAC,CAC5E,GAEW,KACX,GACA,MAIG;CACH,IAAM,IAAY,IAAI,IAAI,CAAiB,GACrC,IAAW,CAAC,GAAG,EAAM,WAAW,OAAO,CAAC,CAAC,CAC5C,QAAQ,EAAE,gBAAa,EAAU,IAAI,EAAO,EAAE,CAAC,CAAC,CAChD,KAAK,EAAE,gBAAa,EAAO,EAAE,GAC1B,IAAc,IAAI,IAAI,CAAQ;CAEpC,OAAO;EAAE,mBAAmB;EAAU,kBADtB,CAAC,GAAG,IAAI,IAAI,CAAiB,CAAC,CAAC,CAAC,QAAQ,MAAO,CAAC,EAAY,IAAI,CAAE,CAC1B;CAAQ;AAClE,GAEa,KACX,GACA,GACA,MACmC;CACnC,IAAM,IAAW,IAAI,IAAI,CAAiB,GACpC,IAAc,CAAC,GAAG,EAAM,WAAW,OAAO,CAAC,CAAC,CAAC,QAChD,GAAO,MACN,EAAS,IAAI,EAAO,OAAO,EAAE,KAAK,EAAO,cAAc,OACnD,IAAQ,EAAO,YACf,GACN,CACF,GACM,IAAkB,IAAe;CACvC,OAAO;EACL,mBAAmB,CAAC,GAAG,EAAM,WAAW,OAAO,CAAC,CAAC,CAC9C,QAAQ,EAAE,gBAAa,EAAS,IAAI,EAAO,EAAE,CAAC,CAAC,CAC/C,KAAK,EAAE,gBAAa,EAAO,EAAE;EAChC;EACA;EACA,YAAY,IAAkB;CAChC;AACF,GAEa,KACX,GACA,MACY,MAAc,QAAQ,CAAC,EAAM,cAAc,KAAa,EAAM,iBCnD/D,IAAyB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GC4CnC,IAAe;CAAE,SAAS;CAAM,UAAU;AAAK,GAE/C,KACJ,GACA,MAEA,EAAM,WAAW,GAAU,UAAU,EAAM,MAAM,GAAM,MAAU,MAAS,EAAS,EAAM,GAE9E,KAAkB,MAA6B,UAAU,KAEhE,KAA0B,OAA0D;CACxF,mBAAmB,CAAC;CACpB,aAAa;CACb,iBAAiB;CACjB,YAAY;AACd,IAYO,IAAA,cAAgD,EAAW;;6BAGL,qDAGpB,CAAC,uBAC+C,wBACP,0BACP,+BACU,wBACX,mBAE5B,uBACmB,oBACD,2CAC7B,IAAI,IAAY,wBAChB,iCACQ,EAAA,EAAwC,8CAC1C,IAAI,IAAgC,+CACnC,IAAI,IAAqB,aAEtB,gBAC9B,aAC6B,gBACZ,8BACV,IAAI,IAAY,aACkB,CAAC;;;gBA1B9B,CAAC,GAAmB,CAAsB;;CAqBnE;CACA;CACA;CACA;CACA;CACA;CAEA,oBAAmC;EAEjC,AADA,MAAM,kBAAkB,GACxB,EAAwB,KAAK,aAAa;CAC5C;CAEA,uBAAsC;EAEpC,AADA,KAAKA,IAAkB,MAAM,GAC7B,MAAM,qBAAqB;CAC7B;CAEA,QAA2B,GAAqC;EAI9D,IAHA,MAAM,QAAQ,CAAO,GACrB,KAAKC,GAAuB,GAExB,EAAQ,IAAI,QAAQ,KAAK,EAAQ,IAAI,YAAY,KAAK,EAAQ,IAAI,cAAc,GAAG;GACrF,qBAAqB;IACnB,AAAI,KAAK,eACP,KAAUC,GAAiB;GAE/B,CAAC;GACD;EACF;EAEA,IAAI,EAAQ,IAAI,cAAc,GAAG;GAC/B,IAAM,IAAa,EAAsB,KAAK,YAAY;GAC1D,IAAI,MAAe,KAAK,cAAc;IAGpC,AAFA,KAAKC,KAA8B,IACnC,KAAK,eAAe,GACpB,KAAKC,GAAkB,eAAe;IACtC;GACF;GACA,AAAI,KAAKD,KACP,KAAKA,KAA8B,KAC1B,EAAQ,IAAI,cAAc,MAAM,KAAA,KACzC,KAAKC,GAAkB,UAAU;EAErC;EAMA,AAJI,EAAQ,IAAI,mBAAmB,KAAK,KAAK,UAC3C,KAAKC,GAAqB,EAAI,GAGhC,KAAKC,GAAkB;CACzB;CAEA,WAA8B,GAA+B;EAC3D,MAAM,WAAW,CAAO;EAExB,IAAM,IACJ,EAAQ,IAAI,QAAQ,KAAK,EAAQ,IAAI,mBAAmB,KAAK,EAAQ,IAAI,cAAc;EAczF,AAbI,MACF,KAAK,wBAAwB,KAAK,SAC9B,EACE,KAAK,QACL,KAAK,mBACL,KAAK,YACP,IACA,EAAuB,KAAK,YAAY,IAG1C,EAAQ,IAAI,cAAc,KAC5B,KAAKC,GAAkB,MAAM,IAE3B,KAAoB,EAAQ,IAAI,cAAc,MAChD,KAAKC,GAA4B,KAAK,qBAAqB;CAE/D;CAEA,IAAI,cAAkD;EACpD,OAAO,KAAK;CACd;CAEA,IAAI,iBAAiD;EACnD,OAAO,KAAK,SACR,EAAqC,KAAK,QAAQ,KAAK,mBAAmB,KAAK,YAAY,IAC3F,EAAuB,KAAK,YAAY;CAC9C;CAEA,QAAc;EACR,KAAK,kBAAkB,WAAW,MAGtC,KAAK,oBAAoB,CAAC,GAC1B,KAAKC,GAAqB,OAAO;CACnC;CAEA,aAAa,GAA2B;EACtC,IAAM,IAAQ,KAAK,QAAQ,WAAW,IAAI,CAAQ,GAC5C,IAAQ,KAAK;EAUnB,OARE,CAAC,KACD,EAAM,kBAAkB,SAAS,CAAQ,KACzC,CAAC,EAAgB,EAAM,WAAW,CAAK,IAEhC,MAET,KAAK,oBAAoB,CAAC,GAAG,EAAM,mBAAmB,CAAQ,GAC9D,KAAKA,GAAqB,KAAK,GACxB;CACT;CAEA,eAAe,GAA2B;EAMxC,OALK,KAAK,eAAe,kBAAkB,SAAS,CAAQ,KAG5D,KAAK,oBAAoB,KAAK,eAAe,kBAAkB,QAAQ,MAAO,MAAO,CAAQ,GAC7F,KAAKA,GAAqB,KAAK,GACxB,MAJE;CAKX;CAEA,QAAc;EACZ,KAAUP,GAAiB;CAC7B;CAEA,SAAkC;EAChC,IAAM,IAAQ,KAAK;EAEnB,OAAO,CAAI;;;;qBAIM,GALH,KAAK,QAAQ,KAAK,QAAQ,KAAK,QAAQ,KAAK,MAAM,kBAKtC,oBAAoB;uBAC3B,OAAO,EAAM,UAAU,EAAE;;UAEtC,KAAKQ,GAAc,CAAK,EAAE;UAE1B,KAAK,YAAY,WAAW,KAAK,SAC7B,KAAKC,GAAY,KAAK,MAAM,IAC5B,KAAKC,GAAc,EACxB;sEAC6D,KAAK,cAAc;;;CAGvF;CAEA,GAAc,GAAuD;EACnE,OAAO,CAAI;;;;;;;2BAOY,OAAO,EAAM,UAAU,EAAE;yBAC3B,GAAG,EAAM,YAAY,MAAM,KAAK,aAAa,sBAAsB;;;6DAG/B,EAAM,YAAY;;6DAElB,KAAK,aAAa;;YAGnE,KAAK,oBACD,CAAI;;;;;;;;;;;;;kCAac,KAAK,gBAAgB,EAAM,YAAY;qCACpC,KAAKC,GAAiB,KAAK,eAAe,GAAG,WAAW,EAAE;;;;;;;;;;kCAU7D,KAAK,gBAAA,GAAkC;qCACpC,KAAKA,GAAiB,KAAK,eAAe,GAAG,WAAW,EAAE;;;;;oBAM/E,EACL;;;;;wBAKa,EAAM,kBAAkB,WAAW,EAAE;qBACxC,KAAK,MAAM;;;;;;;CAO9B;CAEA,GAAY,GAAoD;EAe9D,OAdI,EAAM,KAAK,WAAW,IACjB,CAAI;;;;;;gBAMD,EAAM,KAAK,QAAQ,EAAM,KAAK,GAAG;;;;UAOtC,CAAI;;UAEL,EACA,EAAM,OACL,MAAQ,EAAI,MACZ,MAAQ,CAAI;;4DAEqC,EAAI,MAAM;4EACM,EAAI,MAAM;;kBAEpE,EACA,EAAI,UACH,MAAW,EAAO,OAAO,KACzB,MAAW,KAAKC,GAAc,CAAM,CACvC,EAAE;;;WAIV,EAAE;;;CAGR;CAEA,GAAc,GAAsE;EAClF,IAAM,IAAe,KAAK,oBAAoB,IAAI,EAAO,OAAO,EAAE;EAClE,IAAI,CAAC,GACH,OAAO;EAET,IAAM,EAAE,YAAS,SAAM,kBAAe,kBAAe,mBAAgB,GAC/D,EAAE,aAAU,gBAAa,GACzB,IAAO,CAAI;;;;;qBAKA,GAAG,EAAK,IAAI,IAAgB;2BACtB,EAAc;uBAClB,OAAO,CAAQ,EAAE;wBAChB,OAAO,CAAQ,EAAE;uBAClB,KAAKC,GAAwB,GAAQ,GAAU,CAAQ,EAAE;;UAEtE,KAAKC,GAAqB,CAAO,EAAE;uDACU,EAAO,aAAa,IAAI;;;EAI3E,OAAO,CAAI;QAEP,KAAK,iBAAiB,EAAO,OAAO,eAAe,KAC/C,CAAI;;2BAEW,EAAK;wBACR,EAAO,OAAO,eAAe,EAAc;;kBAEjD,EAAK;;gBAGX,EACL;yCACkC,EAAc,GAAG,EAAY;;CAEpE;CAEA,GAA4B,GAA6C;EACvE,IAAM,IAAW,KAAK,cAChB,IAAQ,KAAK,QACb,oBAAU,IAAI,IAAqB,GACnC,oBAAgB,IAAI,IAAgC;EAE1D,IAAI,GACF,KAAK,IAAM,KAAU,EAAM,WAAW,OAAO,GAAG;GAC9C,IAAM,IAAW,EAAM,kBAAkB,SAAS,EAAO,OAAO,EAAE,GAC5D,IAAa,EAAgB,EAAO,WAAW,CAAK,GACpD,IAAW,CAAC,KAAY,CAAC,GACzB,IAAO,EAAO,OAAO,QAAQ,EAAO,OAAO,IAC3C,IACJ,EAAO,cAAc,OACjB,yCACA,IACE,YAAY,EAAO,UAAU,kBAAkB,KAAK,IAAI,GAAG,EAAM,eAAe,EAAE,eAClF,GAAG,EAAO,UAAU,UAAU,EAAO,cAAc,IAAI,UAAU,SAAS,IAC5E,IAA+C;IACnD;IACA,MAAM,EAAM;IACZ;IACA;IACA;IACA,aAAa,EAAM;IACnB,iBAAiB,EAAM;IACvB,cAAc,KAAK;GACrB;GACA,MAAc,IAAI,EAAO,OAAO,IAAI;IAClC;IACA;IACA,eAAe,sBAAsB,EAAO,IAAI,GAAG,EAAO;IAC1D;IACA,aAAa,CAAC,EAAwB,EAAO,OAAO,eAAe,EAAE,GAAG,CAAa,CAAC,CACnF,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;GACb,CAAC,GACI,GAGL,IAAI;IACF,EAAQ,IAAI,EAAO,OAAO,IAAI,EAAS,CAAO,CAAC;GACjD,SAAS,GAAgB;IACvB,AAAK,KAAKT,GAAkB,IAAI,EAAO,OAAO,EAAE,MAC9C,KAAKA,GAAkB,IAAI,EAAO,OAAO,EAAE,GAC3C,KAAKU,GAAyB,KAAK;KACjC,MAAM;KACN,SAAS,mCAAmC,EAAO,OAAO,GAAG,KAAK,OAAO,CAAK,EAAE;KAChF,UAAU,EAAO,OAAO;IAC1B,CAAC;GAEL;EACF;EAIF,AADA,KAAK,sBAAsB,GAC3B,KAAK,uBAAuB;CAC9B;CAEA,GAAqB,GAAuD;EAC1E,IAAM,IAAS,EAAQ,QACjB,IAAO,EAAO,OAAO,QAAQ,EAAO,OAAO,IAC3C,IACJ,KAAK,gBAAgB,KAAK,qBAAqB,IAAI,EAAO,OAAO,EAAE,IAC/D,KAAK,qBAAqB,IAAI,EAAO,OAAO,EAAE,IAC9C,CAAI;yBACW,EAAe,EAAO,OAAO,EAAE,EAAE;mEACS,EAAK,GAAG,EAAK;;;EAI5E,OAAO,CAAI;QACP,KAAKC,GAAyB,GAAQ,CAAI,EAAE,GAAG,EAAQ;;CAE7D;CAEA,GAAyB,GAAqC,GAA8B;EAmB1F,OAlBI,EAAO,YAAY,CAAC,KAAK,cAAc,IAAI,EAAO,OAAO,EAAE,IACtD,CAAI;;;;gBAID,EAAO,SAAS;;;yBAGP,KAAKC,GAAkB,CAAM,EAAE;;UAU7C,CAAI,kEANE,EACV,MAAM,KAAK,CAAC,CACZ,KAAK,MAAS,EAAK,EAAE,CAAC,CACtB,KAAK,EAAE,CAAC,CACR,MAAM,GAAG,CAAC,CAAC,CACX,YAC0E,EAAK;CACpF;CAEA,KAAgC;EAC9B,IAAM,IAAU,KAAK,YAAY,WAC3B,IAAQ,KAAK,YAAY,SACzB,IAAQ,IACV,yBACA,IACE,kBACA,uBACA,IAAO,IACT,kDACA,IACE,4EACC,KAAK,QAAQ,WAAW;EAC/B,OAAO,CAAI;;;eAGA,KAAK,YAAY,UAAU,gBAAgB,SAAS;eACpD,KAAK,YAAY,UAAU,UAAU,SAAS;;;wDAGL,IAAU,MAAM,IAAI;oCACxC,EAAM;mCACP,EAAK;YAE5B,KAAK,YAAY,UACb,CAAI,qDAAqD,KAAK,MAAM,mBACpE,EACL;;;;CAIT;CAEA,MAAMjB,KAAkC;EACtC,IAAM,IAAS,KAAK,OAAO,KAAK,GAC1B,IAAQ,EAAE,KAAKkB;EASrB,IARA,KAAKpB,IAAkB,MAAM,GAC7B,KAAKA,KAAmB,IAAI,gBAAgB,GAC5C,KAAK,SAAS,MACd,KAAK,SAAS,MACd,KAAK,gCAAgB,IAAI,IAAI,GAC7B,KAAKO,GAAkB,MAAM,GAC7B,KAAKc,KAA2B,MAE5B,CAAC,GAAQ;GACX,KAAK,UAAU;GACf;EACF;EAEA,KAAK,UAAU;EACf,IAAM,IACJ,KAAK,cACL,EACE,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC,CAC7D;EAEF,IAAI;GACF,IAAM,IAAO,MAAM,EAAO,gBAAgB,GAAQ,KAAKrB,GAAiB,MAAM;GAC9E,IAAI,MAAU,KAAKoB,MAAc,KAAKpB,GAAiB,OAAO,SAC5D;GAEF,IAAI,EAAK,SAAS,kBAAkB,GAClC,MAAM,IAAI,EACR,sBACA,+CAA+C,OAAO,EAAK,SAAS,aAAa,EAAE,EACrF;GAEF,IAAM,IAAQ,EAAkC,GAAM,CAAM;GAE5D,AADA,KAAK,SAAS,GACd,KAAK,UAAU;GACf,IAAM,IAAa,KAAKK,GAAqB,EAAK;GAClD,MAAM,KAAK;GACX,IAAM,IAAY,KAAK;GAavB,AAZA,KAAK,cACH,IAAI,YAA0C,4BAA4B;IACxE,GAAG;IACH,QAAQ;KAAE;KAAM;KAAO;IAAU;GACnC,CAAC,CACH,GACI,EAAM,SAAS,SAAS,KAC1B,KAAKiB,GAAgB,EAAM,QAAQ,GAEjC,KACF,KAAKb,GAAqB,eAAe,GAE3C,KAAKH,GAAkB;EACzB,SAAS,GAAgB;GACvB,IAAI,MAAU,KAAKc,MAAc,KAAKpB,GAAiB,OAAO,SAC5D;GAEF,KAAKuB,GAAU,CAAK;EACtB;CACF;CAEA,GAAqB,GAAwB;EAC3C,IAAI,CAAC,KAAK,QACR,OAAO;EAET,IAAM,IAAa,EAA2B,KAAK,QAAQ,KAAK,iBAAiB;EAoBjF,OAlBE,EAAW,kBAAkB,WAAW,KAAK,kBAAkB,UAC/D,EAAW,kBAAkB,MAAM,GAAI,MAAU,MAAO,KAAK,kBAAkB,EAAM,KAIvF,KAAK,oBAAoB,EAAW,mBAChC,EAAW,iBAAiB,SAAS,KACvC,KAAKD,GAAgB,CACnB;GACE,MAAM;GACN,SAAS,0CAA0C,EAAW,iBAAiB,KAAK,IAAI,EAAE;GAC1F,UAAU;EACZ,CACF,CAAC,GAEC,KACF,KAAKb,GAAqB,eAAe,GAEpC,MAfE;CAgBX;CAEA,GACE,GACA,GACA,GACM;EACN,IAAI,GAAU;GAIZ,AAHA,KAAK,oBAAoB,KAAK,eAAe,kBAAkB,QAC5D,MAAO,MAAO,EAAO,OAAO,EAC/B,GACA,KAAKA,GAAqB,MAAM;GAChC;EACF;EACA,IAAI,GAAU;GACZ,KAAK,gBAAgB,KAAK,eAAe,aACrC,oFACA,yBAAyB,EAAO,OAAO,QAAQ,EAAO,OAAO,GAAG;GACpE;EACF;EAEA,AADA,KAAK,oBAAoB,CAAC,GAAG,KAAK,eAAe,mBAAmB,EAAO,OAAO,EAAE,GACpF,KAAKA,GAAqB,MAAM;CAClC;CAEA,GAAiB,GAAe,GAAwC;EACtE,IAAM,IAAa,EAAsB,CAAK,GACxC,IAAQ,KAAK;EACf,MAAe,KAAK,gBAAgB,IAAa,EAAM,gBAG3D,KAAK,eAAe,GACpB,KAAKN,KAA8B,IACnC,KAAKC,GAAkB,CAAM;CAC/B;CAEA,GAAqB,GAA2C;EAC9D,IAAM,IAAQ,KAAK,SACf,EAAqC,KAAK,QAAQ,KAAK,mBAAmB,KAAK,YAAY,IAC3F,EAAuB,KAAK,YAAY;EAC5C,KAAK,cACH,IAAI,YAAyC,sCAAsC;GACjF,GAAG;GACH,QAAQ;IAAE,GAAG;IAAO;GAAO;EAC7B,CAAC,CACH;CACF;CAEA,GAAkB,GAAwC;EACxD,IAAM,IAAQ,KAAK;EACnB,KAAK,cACH,IAAI,YAAsC,mCAAmC;GAC3E,GAAG;GACH,QAAQ;IAAE,GAAG;IAAO,cAAc,KAAK;IAAc;GAAO;EAC9D,CAAC,CACH;CACF;CAEA,KAA0B;EACxB,IAAM,IAAQ,KAAK;EACnB,IAAI,CAAC,EAAM,YAAY;GAErB,AADA,KAAKiB,KAA2B,MAChC,KAAK,gBAAgB,cAAc;GACnC;EACF;EACA,KAAK,aAAa,gBAAgB,MAAM;EACxC,IAAM,IAAY,GAAG,KAAK,aAAa,GAAG,EAAM,kBAAkB,KAAK,GAAG;EACtE,MAAc,KAAKA,OAGvB,KAAKA,KAA2B,GAChC,KAAKC,GAAgB,CACnB;GACE,MAAM;GACN,SAAS,6BAA6B,EAAM,YAAY,yBAAyB,KAAK,aAAa;GACnG,UAAU;EACZ,CACF,CAAC;CACH;CAEA,GAAkB,GAA2C;EACvD,KAAK,cAAc,IAAI,EAAO,OAAO,EAAE,MAG3C,KAAK,gBAAgB,IAAI,IAAI,KAAK,aAAa,CAAC,CAAC,IAAI,EAAO,OAAO,EAAE,GACrE,KAAKA,GAAgB,CACnB;GACE,MAAM;GACN,SAAS,wBAAwB,EAAO,OAAO,GAAG;GAClD,UAAU,EAAO,OAAO;EAC1B,CACF,CAAC;CACH;CAEA,GAAgB,GAAoD;EAClE,KAAK,cACH,IAAI,YAA4C,8BAA8B;GAC5E,GAAG;GACH,QAAQ;IAAE;IAAU,OAAO,KAAK;GAAO;EACzC,CAAC,CACH;CACF;CAEA,KAA+B;EAC7B,IAAI,KAAKL,GAAyB,WAAW,GAC3C;EAEF,IAAM,IAAW,KAAKA;EAEtB,AADA,KAAKA,KAA2B,CAAC,GACjC,KAAKK,GAAgB,CAAQ;CAC/B;CAEA,GAAU,GAAsB;EAC9B,IAAM,IACJ,OAAO,KAAU,YAAY,KAAkB,UAAU,KAAS,aAAa,IAC3E;GACE,MAAM,OAAO,EAAM,IAAI;GACvB,SAAS,OAAO,EAAM,OAAO;GAC7B,GAAI,aAAiB,KAAuB,EAAM,UAAU,KAAA,IACxD,EAAE,OAAO,EAAM,MAAM,IACrB,CAAC;EACP,IACA;GAAE,MAAM;GAAW,SAAS,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK;EAAE;EAGzF,AAFA,KAAK,SAAS,GACd,KAAK,UAAU,SACf,KAAK,cACH,IAAI,YAA0C,4BAA4B;GACxE,GAAG;GACH;EACF,CAAC,CACH;CACF;AACF;GA5oBG,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAU,CAAC,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,MAC/C,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAgB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MACrD,EAAS;CAAE,WAAW;CAAO,YAAY;AAAmB,CAAC,CAAA,GAAA,EAAA,WAAA,qBAAA,KAAA,CAAA,MAE7D,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAiB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MACtD,EAAS,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,MAC7B,EAAS,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MAC7B,EAAS;CAAE,MAAM;CAAS,WAAW;AAAsB,CAAC,CAAA,GAAA,EAAA,WAAA,qBAAA,KAAA,CAAA,MAC5D,EAAS;CAAE,MAAM;CAAS,WAAW;AAAgB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MAEtD,EAAM,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,iBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,iBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,yBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,uBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,wBAAA,KAAA,CAAA,UArBR,EAAc,8BAA8B,CAAA,GAAA,CAAA;AAkpB7C,IAAa,UAA+C;CAE1D,AADA,EAAwB,GACnB,eAAe,IAAI,8BAA8B,KACpD,eAAe,OAAO,gCAAgC,CAAiC;AAE3F"}
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { n as e, r as t, t as n } from "./decorate-B_ZTN9li.js";
|
|
2
|
+
import { LitElement as r, css as i, html as a, nothing as o } from "lit";
|
|
3
|
+
import { customElement as s, property as c, query as l, state as u } from "lit/decorators.js";
|
|
4
|
+
import { repeat as d } from "lit/directives/repeat.js";
|
|
5
|
+
import { styleMap as f } from "lit/directives/style-map.js";
|
|
6
|
+
//#region src/tooltip/rich-text.ts
|
|
7
|
+
var p = {
|
|
8
|
+
bold: "strong",
|
|
9
|
+
"rt.bold": "strong",
|
|
10
|
+
"rt.absorb": "ability",
|
|
11
|
+
"rt.warning": "warning",
|
|
12
|
+
"rt.effect": "effect",
|
|
13
|
+
"rt.mana": "resource",
|
|
14
|
+
"rt.heal": "healing",
|
|
15
|
+
"rt.meikoability1": "meiko-primary",
|
|
16
|
+
"rt.meikoability2": "meiko-secondary"
|
|
17
|
+
}, m = (e, t, n) => {
|
|
18
|
+
let r = t.replace(/\r\n?/g, "\n").split(/\n{2,}/);
|
|
19
|
+
r.forEach((t, i) => {
|
|
20
|
+
let a = e.at(-1);
|
|
21
|
+
if (t.length > 0) {
|
|
22
|
+
let e = a.at(-1);
|
|
23
|
+
e?.tone === n ? a[a.length - 1] = {
|
|
24
|
+
text: `${e.text}${t}`,
|
|
25
|
+
tone: n
|
|
26
|
+
} : a.push({
|
|
27
|
+
text: t,
|
|
28
|
+
tone: n
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
i < r.length - 1 && e.push([]);
|
|
32
|
+
});
|
|
33
|
+
}, h = (e) => {
|
|
34
|
+
let t = [[]], n = ["default"], r = e.split(/(<\/?[^>]+>|<\/>)/g);
|
|
35
|
+
for (let e of r) {
|
|
36
|
+
if (!e) continue;
|
|
37
|
+
if (e === "</>") {
|
|
38
|
+
n.length > 1 && n.pop();
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (e.match(/^<\/\s*([^>\s]+)\s*>$/)) {
|
|
42
|
+
n.length > 1 && n.pop();
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
let r = e.match(/^<\s*([^/][^>\s]*)\s*>$/);
|
|
46
|
+
if (r) {
|
|
47
|
+
let e = r[1]?.toLowerCase() ?? "";
|
|
48
|
+
n.push(p[e] ?? "default");
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
m(t, e, n.at(-1) ?? "default");
|
|
52
|
+
}
|
|
53
|
+
return t;
|
|
54
|
+
}, g = (e) => h(e).map((e) => e.map(({ text: e }) => e).join("")).join("\n\n").trim(), _ = (e, t, n) => Math.min(Math.max(e, t), Math.max(t, n)), v = (e, t, n) => {
|
|
55
|
+
let r = n.gap ?? 8, i = n.margin ?? 8, a = {
|
|
56
|
+
top: e.top - i,
|
|
57
|
+
bottom: n.viewportHeight - e.bottom - i,
|
|
58
|
+
right: n.viewportWidth - e.right - i,
|
|
59
|
+
left: e.left - i
|
|
60
|
+
}, o = {
|
|
61
|
+
top: t.height + r,
|
|
62
|
+
bottom: t.height + r,
|
|
63
|
+
right: t.width + r,
|
|
64
|
+
left: t.width + r
|
|
65
|
+
}, s = [
|
|
66
|
+
"top",
|
|
67
|
+
"bottom",
|
|
68
|
+
"right",
|
|
69
|
+
"left"
|
|
70
|
+
], c = n.placement ?? "auto", l = c === "auto" ? s.find((e) => a[e] >= o[e]) ?? [...s].sort((e, t) => a[t] - a[e])[0] : c, u = e.left + e.width / 2 - t.width / 2, d = e.top + e.height / 2 - t.height / 2, f = l === "left" ? e.left - t.width - r : l === "right" ? e.right + r : u, p = l === "top" ? e.top - t.height - r : l === "bottom" ? e.bottom + r : d;
|
|
71
|
+
return {
|
|
72
|
+
placement: l,
|
|
73
|
+
x: Math.round(_(f, i, n.viewportWidth - t.width - i)),
|
|
74
|
+
y: Math.round(_(p, i, n.viewportHeight - t.height - i))
|
|
75
|
+
};
|
|
76
|
+
}, y = i`
|
|
77
|
+
:host {
|
|
78
|
+
--fellowship-tooltip-void: var(--_fellowship-field-surface-raised);
|
|
79
|
+
--fellowship-tooltip-ink: var(--_fellowship-field-ink);
|
|
80
|
+
--fellowship-tooltip-muted-ink: var(--_fellowship-field-muted);
|
|
81
|
+
--fellowship-tooltip-border: var(--_fellowship-field-border-strong);
|
|
82
|
+
--fellowship-tooltip-focus: var(--_fellowship-field-focus);
|
|
83
|
+
--fellowship-tooltip-ability: #76c5d2;
|
|
84
|
+
--fellowship-tooltip-warning: #d4a761;
|
|
85
|
+
--fellowship-tooltip-effect: #afa1ca;
|
|
86
|
+
--fellowship-tooltip-resource: #7cb2c2;
|
|
87
|
+
--fellowship-tooltip-healing: #82b997;
|
|
88
|
+
--fellowship-tooltip-meiko-primary: #c892a0;
|
|
89
|
+
--fellowship-tooltip-meiko-secondary: #8dbab7;
|
|
90
|
+
--fellowship-tooltip-font-family: var(--_fellowship-field-font-family);
|
|
91
|
+
--fellowship-tooltip-radius: var(--_fellowship-field-radius);
|
|
92
|
+
--fellowship-tooltip-max-width: 360px;
|
|
93
|
+
|
|
94
|
+
display: inline-block;
|
|
95
|
+
min-width: 0;
|
|
96
|
+
color: var(--fellowship-tooltip-ink);
|
|
97
|
+
font-family: var(--fellowship-tooltip-font-family);
|
|
98
|
+
-webkit-font-smoothing: antialiased;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
:host([hidden]) {
|
|
102
|
+
display: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.trigger {
|
|
106
|
+
display: contents;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.surface {
|
|
110
|
+
position: fixed;
|
|
111
|
+
z-index: 2147483647;
|
|
112
|
+
box-sizing: border-box;
|
|
113
|
+
display: none;
|
|
114
|
+
overflow: visible;
|
|
115
|
+
width: max-content;
|
|
116
|
+
max-width: min(var(--fellowship-tooltip-max-width), calc(100vw - 16px));
|
|
117
|
+
margin: 0;
|
|
118
|
+
padding: 12px 14px 14px;
|
|
119
|
+
border: 1px solid var(--fellowship-tooltip-border);
|
|
120
|
+
border-radius: var(--fellowship-tooltip-radius);
|
|
121
|
+
background: var(--fellowship-tooltip-void);
|
|
122
|
+
color: var(--fellowship-tooltip-ink);
|
|
123
|
+
box-shadow: 0 8px 24px rgb(0 0 0 / 34%);
|
|
124
|
+
opacity: 0;
|
|
125
|
+
transform: translateY(4px) scale(0.98);
|
|
126
|
+
transform-origin: bottom center;
|
|
127
|
+
transition:
|
|
128
|
+
opacity 140ms cubic-bezier(0.23, 1, 0.32, 1),
|
|
129
|
+
transform 140ms cubic-bezier(0.23, 1, 0.32, 1);
|
|
130
|
+
pointer-events: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.surface[data-open="true"] {
|
|
134
|
+
display: block;
|
|
135
|
+
opacity: 1;
|
|
136
|
+
transform: translateY(0) scale(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.surface[data-placement="bottom"] {
|
|
140
|
+
transform-origin: top center;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.surface[data-placement="left"] {
|
|
144
|
+
transform-origin: center right;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.surface[data-placement="right"] {
|
|
148
|
+
transform-origin: center left;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.heading {
|
|
152
|
+
position: relative;
|
|
153
|
+
margin: 0 0 6px;
|
|
154
|
+
padding-left: 12px;
|
|
155
|
+
color: var(--fellowship-tooltip-ink);
|
|
156
|
+
font-size: 14px;
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
line-height: 20px;
|
|
159
|
+
text-wrap: balance;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.heading::before {
|
|
163
|
+
position: absolute;
|
|
164
|
+
inset: 4px auto 4px 0;
|
|
165
|
+
width: 2px;
|
|
166
|
+
background: var(--_fellowship-field-accent);
|
|
167
|
+
content: "";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.content {
|
|
171
|
+
color: var(--fellowship-tooltip-muted-ink);
|
|
172
|
+
font-size: 13px;
|
|
173
|
+
font-weight: 400;
|
|
174
|
+
line-height: 20px;
|
|
175
|
+
text-wrap: pretty;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.paragraph {
|
|
179
|
+
margin: 0;
|
|
180
|
+
white-space: pre-line;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.paragraph + .paragraph {
|
|
184
|
+
margin-top: 8px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
[data-tone="strong"] {
|
|
188
|
+
color: var(--fellowship-tooltip-ink);
|
|
189
|
+
font-weight: 600;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
[data-tone="ability"] {
|
|
193
|
+
color: var(--fellowship-tooltip-ability);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
[data-tone="warning"] {
|
|
197
|
+
color: var(--fellowship-tooltip-warning);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
[data-tone="effect"] {
|
|
201
|
+
color: var(--fellowship-tooltip-effect);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
[data-tone="resource"] {
|
|
205
|
+
color: var(--fellowship-tooltip-resource);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
[data-tone="healing"] {
|
|
209
|
+
color: var(--fellowship-tooltip-healing);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
[data-tone="meiko-primary"] {
|
|
213
|
+
color: var(--fellowship-tooltip-meiko-primary);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
[data-tone="meiko-secondary"] {
|
|
217
|
+
color: var(--fellowship-tooltip-meiko-secondary);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.arrow {
|
|
221
|
+
position: absolute;
|
|
222
|
+
width: 8px;
|
|
223
|
+
height: 8px;
|
|
224
|
+
border: solid var(--fellowship-tooltip-border);
|
|
225
|
+
background: var(--fellowship-tooltip-void);
|
|
226
|
+
transform: rotate(45deg);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.surface[data-placement="top"] .arrow {
|
|
230
|
+
bottom: -5px;
|
|
231
|
+
left: calc(50% - 4px);
|
|
232
|
+
border-width: 0 1px 1px 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.surface[data-placement="bottom"] .arrow {
|
|
236
|
+
top: -5px;
|
|
237
|
+
left: calc(50% - 4px);
|
|
238
|
+
border-width: 1px 0 0 1px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.surface[data-placement="left"] .arrow {
|
|
242
|
+
top: calc(50% - 4px);
|
|
243
|
+
right: -5px;
|
|
244
|
+
border-width: 1px 1px 0 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.surface[data-placement="right"] .arrow {
|
|
248
|
+
top: calc(50% - 4px);
|
|
249
|
+
left: -5px;
|
|
250
|
+
border-width: 0 0 1px 1px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@media (prefers-reduced-motion: reduce) {
|
|
254
|
+
.surface {
|
|
255
|
+
transition: opacity 100ms linear;
|
|
256
|
+
transform: none;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
`, b = {
|
|
260
|
+
bubbles: !0,
|
|
261
|
+
composed: !0
|
|
262
|
+
}, x = class extends r {
|
|
263
|
+
constructor(...e) {
|
|
264
|
+
super(...e), this.heading = null, this.text = null, this.placement = "auto", this.openDelay = 250, this.disabled = !1, this._open = !1, this._position = {
|
|
265
|
+
x: 0,
|
|
266
|
+
y: 0
|
|
267
|
+
}, this._resolvedPlacement = "top", this._paragraphs = [], this.#e = null, this.#t = null, this.#n = null, this.#r = !1, this.#i = null, this.#o = () => {
|
|
268
|
+
if (!this._open || !this.#t || !this._surface) return;
|
|
269
|
+
let e = v(this.#t.getBoundingClientRect(), this._surface.getBoundingClientRect(), {
|
|
270
|
+
placement: this.placement,
|
|
271
|
+
viewportWidth: window.innerWidth,
|
|
272
|
+
viewportHeight: window.innerHeight
|
|
273
|
+
});
|
|
274
|
+
e.x === this._position.x && e.y === this._position.y && e.placement === this._resolvedPlacement || (this._position = {
|
|
275
|
+
x: e.x,
|
|
276
|
+
y: e.y
|
|
277
|
+
}, this._resolvedPlacement = e.placement);
|
|
278
|
+
}, this.#l = () => {
|
|
279
|
+
this.disabled || this._open || (this.#_(), this.#e = window.setTimeout(() => {
|
|
280
|
+
this.#e = null, this.#a(!0, "pointer");
|
|
281
|
+
}, Math.max(0, this.openDelay)));
|
|
282
|
+
}, this.#u = () => {
|
|
283
|
+
this.#_(), this._open && this.#a(!1, "pointer");
|
|
284
|
+
}, this.#d = () => {
|
|
285
|
+
this.#_(), !this.disabled && !this._open && this.#a(!0, "focus");
|
|
286
|
+
}, this.#f = (e) => {
|
|
287
|
+
!this.contains(e.relatedTarget) && this._open && this.#a(!1, "focus");
|
|
288
|
+
}, this.#p = (e) => {
|
|
289
|
+
e.key === "Escape" && this._open && (e.stopPropagation(), this.#a(!1, "escape"));
|
|
290
|
+
}, this.#m = (e) => {
|
|
291
|
+
let t = e.currentTarget.assignedElements({ flatten: !0 })[0];
|
|
292
|
+
this.#g(), this.#t = t instanceof HTMLElement ? t : null, this.#h();
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
static {
|
|
296
|
+
this.styles = [t, y];
|
|
297
|
+
}
|
|
298
|
+
#e;
|
|
299
|
+
#t;
|
|
300
|
+
#n;
|
|
301
|
+
#r;
|
|
302
|
+
#i;
|
|
303
|
+
connectedCallback() {
|
|
304
|
+
super.connectedCallback(), e(this.ownerDocument), this.#i ??= new ResizeObserver(() => this.#o());
|
|
305
|
+
}
|
|
306
|
+
disconnectedCallback() {
|
|
307
|
+
this.#_(), this.#c(), this.#g(), super.disconnectedCallback();
|
|
308
|
+
}
|
|
309
|
+
updated(e) {
|
|
310
|
+
super.updated(e), (e.has("text") || e.has("heading")) && this.#h(), e.has("disabled") && this.disabled && this._open && this.#a(!1, "disabled"), e.has("placement") && this._open && this.#o();
|
|
311
|
+
}
|
|
312
|
+
willUpdate(e) {
|
|
313
|
+
super.willUpdate(e), e.has("text") && (this._paragraphs = h(this.text ?? ""));
|
|
314
|
+
}
|
|
315
|
+
show() {
|
|
316
|
+
this.disabled || this._open || this.#a(!0, "programmatic");
|
|
317
|
+
}
|
|
318
|
+
hide() {
|
|
319
|
+
this.#_(), this._open && this.#a(!1, "programmatic");
|
|
320
|
+
}
|
|
321
|
+
render() {
|
|
322
|
+
let e = {
|
|
323
|
+
left: `${this._position.x}px`,
|
|
324
|
+
top: `${this._position.y}px`
|
|
325
|
+
};
|
|
326
|
+
return a`
|
|
327
|
+
<span
|
|
328
|
+
class="trigger"
|
|
329
|
+
part="trigger"
|
|
330
|
+
@pointerenter=${this.#l}
|
|
331
|
+
@pointerleave=${this.#u}
|
|
332
|
+
@focusin=${this.#d}
|
|
333
|
+
@focusout=${this.#f}
|
|
334
|
+
@keydown=${this.#p}
|
|
335
|
+
>
|
|
336
|
+
<slot @slotchange=${this.#m}></slot>
|
|
337
|
+
</span>
|
|
338
|
+
<div
|
|
339
|
+
class="surface"
|
|
340
|
+
part="surface"
|
|
341
|
+
role="tooltip"
|
|
342
|
+
popover="manual"
|
|
343
|
+
data-open=${String(this._open)}
|
|
344
|
+
data-placement=${this._resolvedPlacement}
|
|
345
|
+
style=${f(e)}
|
|
346
|
+
>
|
|
347
|
+
<span class="arrow" part="arrow" aria-hidden="true"></span>
|
|
348
|
+
<slot name="content">
|
|
349
|
+
${this.heading ? a`<p class="heading" part="heading">${this.heading}</p>` : o}
|
|
350
|
+
<div class="content" part="content">
|
|
351
|
+
${d(this._paragraphs, (e, t) => t, (e, t) => a`
|
|
352
|
+
<p class="paragraph" part="paragraph">
|
|
353
|
+
${d(e, (e, n) => `${t}:${n}:${e.tone}:${e.text}`, (e) => a`<span data-tone=${e.tone}>${e.text}</span>`)}
|
|
354
|
+
</p>
|
|
355
|
+
`)}
|
|
356
|
+
</div>
|
|
357
|
+
</slot>
|
|
358
|
+
</div>
|
|
359
|
+
`;
|
|
360
|
+
}
|
|
361
|
+
#a(e, t) {
|
|
362
|
+
e !== this._open && (this._open = e, this.updateComplete.then(() => {
|
|
363
|
+
let t = this._surface;
|
|
364
|
+
t && (e ? ("showPopover" in t && !t.matches(":popover-open") && t.showPopover(), this.#s(), this.#o()) : ("hidePopover" in t && t.matches(":popover-open") && t.hidePopover(), this.#c()));
|
|
365
|
+
}), this.dispatchEvent(new CustomEvent("fellowship-tooltip-toggle", {
|
|
366
|
+
...b,
|
|
367
|
+
detail: {
|
|
368
|
+
open: e,
|
|
369
|
+
reason: t
|
|
370
|
+
}
|
|
371
|
+
})));
|
|
372
|
+
}
|
|
373
|
+
#o;
|
|
374
|
+
#s() {
|
|
375
|
+
window.addEventListener("resize", this.#o), window.addEventListener("scroll", this.#o, !0), this.#t && this.#i?.observe(this.#t), this._surface && this.#i?.observe(this._surface);
|
|
376
|
+
}
|
|
377
|
+
#c() {
|
|
378
|
+
window.removeEventListener("resize", this.#o), window.removeEventListener("scroll", this.#o, !0), this.#i?.disconnect();
|
|
379
|
+
}
|
|
380
|
+
#l;
|
|
381
|
+
#u;
|
|
382
|
+
#d;
|
|
383
|
+
#f;
|
|
384
|
+
#p;
|
|
385
|
+
#m;
|
|
386
|
+
#h() {
|
|
387
|
+
if (!this.#t) return;
|
|
388
|
+
this.#r ||= (this.#n = this.#t.getAttribute("aria-description"), !0);
|
|
389
|
+
let e = [this.heading, g(this.text ?? "")].filter(Boolean).join(". ");
|
|
390
|
+
e ? this.#t.setAttribute("aria-description", e) : this.#n === null && this.#t.removeAttribute("aria-description");
|
|
391
|
+
}
|
|
392
|
+
#g() {
|
|
393
|
+
this.#t && (this.#n === null ? this.#t.removeAttribute("aria-description") : this.#t.setAttribute("aria-description", this.#n), this.#t = null, this.#n = null, this.#r = !1);
|
|
394
|
+
}
|
|
395
|
+
#_() {
|
|
396
|
+
this.#e !== null && (window.clearTimeout(this.#e), this.#e = null);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
n([c({ type: String })], x.prototype, "heading", void 0), n([c({ type: String })], x.prototype, "text", void 0), n([c({ type: String })], x.prototype, "placement", void 0), n([c({
|
|
400
|
+
type: Number,
|
|
401
|
+
attribute: "open-delay"
|
|
402
|
+
})], x.prototype, "openDelay", void 0), n([c({
|
|
403
|
+
type: Boolean,
|
|
404
|
+
reflect: !0
|
|
405
|
+
})], x.prototype, "disabled", void 0), n([u()], x.prototype, "_open", void 0), n([u()], x.prototype, "_position", void 0), n([u()], x.prototype, "_resolvedPlacement", void 0), n([u()], x.prototype, "_paragraphs", void 0), n([l(".surface")], x.prototype, "_surface", void 0), x = n([s("fellowship-tooltip")], x);
|
|
406
|
+
var S = () => {
|
|
407
|
+
customElements.get("fellowship-tooltip") || customElements.define("fellowship-tooltip", x);
|
|
408
|
+
};
|
|
409
|
+
//#endregion
|
|
410
|
+
export { g as a, h as i, S as n, v as r, x as t };
|
|
411
|
+
|
|
412
|
+
//# sourceMappingURL=tooltip-DoQVSnOn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip-DoQVSnOn.js","names":["#trigger","#clearTimer","#timer","#setOpen","#restoreTriggerDescription","#syncTriggerDescription","#resizeObserver","#positionSurface","#stopPositionTracking","#handlePointerEnter","#handlePointerLeave","#handleFocusIn","#handleFocusOut","#handleKeydown","#handleTriggerSlotChange","#startPositionTracking","#hasStoredAriaDescription","#previousAriaDescription"],"sources":["../../src/tooltip/rich-text.ts","../../src/tooltip/positioning.ts","../../src/tooltip/tooltip.styles.ts","../../src/tooltip/tooltip.ts"],"sourcesContent":["import type {\n FellowshipRichTextDocument,\n FellowshipRichTextSegment,\n FellowshipTextTone,\n} from \"./types.js\";\n\nconst toneByTag: Readonly<Record<string, FellowshipTextTone>> = {\n bold: \"strong\",\n \"rt.bold\": \"strong\",\n \"rt.absorb\": \"ability\",\n \"rt.warning\": \"warning\",\n \"rt.effect\": \"effect\",\n \"rt.mana\": \"resource\",\n \"rt.heal\": \"healing\",\n \"rt.meikoability1\": \"meiko-primary\",\n \"rt.meikoability2\": \"meiko-secondary\",\n};\n\nconst appendText = (\n paragraphs: FellowshipRichTextSegment[][],\n text: string,\n tone: FellowshipTextTone,\n): void => {\n const normalized = text.replace(/\\r\\n?/g, \"\\n\");\n const parts = normalized.split(/\\n{2,}/);\n parts.forEach((part, index) => {\n const paragraph = paragraphs.at(-1)!;\n if (part.length > 0) {\n const previous = paragraph.at(-1);\n if (previous?.tone === tone) {\n paragraph[paragraph.length - 1] = { text: `${previous.text}${part}`, tone };\n } else {\n paragraph.push({ text: part, tone });\n }\n }\n if (index < parts.length - 1) {\n paragraphs.push([]);\n }\n });\n};\n\nexport const resolveFellowshipRichText = (value: string): FellowshipRichTextDocument => {\n const paragraphs: FellowshipRichTextSegment[][] = [[]];\n const tones: FellowshipTextTone[] = [\"default\"];\n const tokens = value.split(/(<\\/?[^>]+>|<\\/>)/g);\n\n for (const token of tokens) {\n if (!token) {\n continue;\n }\n if (token === \"</>\") {\n if (tones.length > 1) {\n tones.pop();\n }\n continue;\n }\n const closing = token.match(/^<\\/\\s*([^>\\s]+)\\s*>$/);\n if (closing) {\n if (tones.length > 1) {\n tones.pop();\n }\n continue;\n }\n const opening = token.match(/^<\\s*([^/][^>\\s]*)\\s*>$/);\n if (opening) {\n const tag = opening[1]?.toLowerCase() ?? \"\";\n tones.push(toneByTag[tag] ?? \"default\");\n continue;\n }\n appendText(paragraphs, token, tones.at(-1) ?? \"default\");\n }\n\n return paragraphs;\n};\n\nexport const stripFellowshipRichText = (value: string): string =>\n resolveFellowshipRichText(value)\n .map((paragraph) => paragraph.map(({ text }) => text).join(\"\"))\n .join(\"\\n\\n\")\n .trim();\n","import type { FellowshipTooltipPlacement, ResolvedTooltipPlacement } from \"./types.js\";\n\nexport interface TooltipRect {\n readonly top: number;\n readonly right: number;\n readonly bottom: number;\n readonly left: number;\n readonly width: number;\n readonly height: number;\n}\n\nexport interface TooltipPosition {\n readonly x: number;\n readonly y: number;\n readonly placement: ResolvedTooltipPlacement;\n}\n\nexport interface TooltipPositionOptions {\n readonly placement?: FellowshipTooltipPlacement;\n readonly viewportWidth: number;\n readonly viewportHeight: number;\n readonly gap?: number;\n readonly margin?: number;\n}\n\nconst clamp = (value: number, minimum: number, maximum: number): number =>\n Math.min(Math.max(value, minimum), Math.max(minimum, maximum));\n\nexport const calculateTooltipPosition = (\n trigger: TooltipRect,\n tooltip: Pick<TooltipRect, \"width\" | \"height\">,\n options: TooltipPositionOptions,\n): TooltipPosition => {\n const gap = options.gap ?? 8;\n const margin = options.margin ?? 8;\n const available: Record<ResolvedTooltipPlacement, number> = {\n top: trigger.top - margin,\n bottom: options.viewportHeight - trigger.bottom - margin,\n right: options.viewportWidth - trigger.right - margin,\n left: trigger.left - margin,\n };\n const required: Record<ResolvedTooltipPlacement, number> = {\n top: tooltip.height + gap,\n bottom: tooltip.height + gap,\n right: tooltip.width + gap,\n left: tooltip.width + gap,\n };\n const order: readonly ResolvedTooltipPlacement[] = [\"top\", \"bottom\", \"right\", \"left\"];\n const preferred = options.placement ?? \"auto\";\n const placement =\n preferred === \"auto\"\n ? (order.find((candidate) => available[candidate] >= required[candidate]) ??\n [...order].sort((a, b) => available[b] - available[a])[0]!)\n : preferred;\n\n const centeredX = trigger.left + trigger.width / 2 - tooltip.width / 2;\n const centeredY = trigger.top + trigger.height / 2 - tooltip.height / 2;\n const x =\n placement === \"left\"\n ? trigger.left - tooltip.width - gap\n : placement === \"right\"\n ? trigger.right + gap\n : centeredX;\n const y =\n placement === \"top\"\n ? trigger.top - tooltip.height - gap\n : placement === \"bottom\"\n ? trigger.bottom + gap\n : centeredY;\n\n return {\n placement,\n x: Math.round(clamp(x, margin, options.viewportWidth - tooltip.width - margin)),\n y: Math.round(clamp(y, margin, options.viewportHeight - tooltip.height - margin)),\n };\n};\n","import { css } from \"lit\";\n\nexport const tooltipStyles = css`\n :host {\n --fellowship-tooltip-void: var(--_fellowship-field-surface-raised);\n --fellowship-tooltip-ink: var(--_fellowship-field-ink);\n --fellowship-tooltip-muted-ink: var(--_fellowship-field-muted);\n --fellowship-tooltip-border: var(--_fellowship-field-border-strong);\n --fellowship-tooltip-focus: var(--_fellowship-field-focus);\n --fellowship-tooltip-ability: #76c5d2;\n --fellowship-tooltip-warning: #d4a761;\n --fellowship-tooltip-effect: #afa1ca;\n --fellowship-tooltip-resource: #7cb2c2;\n --fellowship-tooltip-healing: #82b997;\n --fellowship-tooltip-meiko-primary: #c892a0;\n --fellowship-tooltip-meiko-secondary: #8dbab7;\n --fellowship-tooltip-font-family: var(--_fellowship-field-font-family);\n --fellowship-tooltip-radius: var(--_fellowship-field-radius);\n --fellowship-tooltip-max-width: 360px;\n\n display: inline-block;\n min-width: 0;\n color: var(--fellowship-tooltip-ink);\n font-family: var(--fellowship-tooltip-font-family);\n -webkit-font-smoothing: antialiased;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n .trigger {\n display: contents;\n }\n\n .surface {\n position: fixed;\n z-index: 2147483647;\n box-sizing: border-box;\n display: none;\n overflow: visible;\n width: max-content;\n max-width: min(var(--fellowship-tooltip-max-width), calc(100vw - 16px));\n margin: 0;\n padding: 12px 14px 14px;\n border: 1px solid var(--fellowship-tooltip-border);\n border-radius: var(--fellowship-tooltip-radius);\n background: var(--fellowship-tooltip-void);\n color: var(--fellowship-tooltip-ink);\n box-shadow: 0 8px 24px rgb(0 0 0 / 34%);\n opacity: 0;\n transform: translateY(4px) scale(0.98);\n transform-origin: bottom center;\n transition:\n opacity 140ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 140ms cubic-bezier(0.23, 1, 0.32, 1);\n pointer-events: none;\n }\n\n .surface[data-open=\"true\"] {\n display: block;\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n\n .surface[data-placement=\"bottom\"] {\n transform-origin: top center;\n }\n\n .surface[data-placement=\"left\"] {\n transform-origin: center right;\n }\n\n .surface[data-placement=\"right\"] {\n transform-origin: center left;\n }\n\n .heading {\n position: relative;\n margin: 0 0 6px;\n padding-left: 12px;\n color: var(--fellowship-tooltip-ink);\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n text-wrap: balance;\n }\n\n .heading::before {\n position: absolute;\n inset: 4px auto 4px 0;\n width: 2px;\n background: var(--_fellowship-field-accent);\n content: \"\";\n }\n\n .content {\n color: var(--fellowship-tooltip-muted-ink);\n font-size: 13px;\n font-weight: 400;\n line-height: 20px;\n text-wrap: pretty;\n }\n\n .paragraph {\n margin: 0;\n white-space: pre-line;\n }\n\n .paragraph + .paragraph {\n margin-top: 8px;\n }\n\n [data-tone=\"strong\"] {\n color: var(--fellowship-tooltip-ink);\n font-weight: 600;\n }\n\n [data-tone=\"ability\"] {\n color: var(--fellowship-tooltip-ability);\n }\n\n [data-tone=\"warning\"] {\n color: var(--fellowship-tooltip-warning);\n }\n\n [data-tone=\"effect\"] {\n color: var(--fellowship-tooltip-effect);\n }\n\n [data-tone=\"resource\"] {\n color: var(--fellowship-tooltip-resource);\n }\n\n [data-tone=\"healing\"] {\n color: var(--fellowship-tooltip-healing);\n }\n\n [data-tone=\"meiko-primary\"] {\n color: var(--fellowship-tooltip-meiko-primary);\n }\n\n [data-tone=\"meiko-secondary\"] {\n color: var(--fellowship-tooltip-meiko-secondary);\n }\n\n .arrow {\n position: absolute;\n width: 8px;\n height: 8px;\n border: solid var(--fellowship-tooltip-border);\n background: var(--fellowship-tooltip-void);\n transform: rotate(45deg);\n }\n\n .surface[data-placement=\"top\"] .arrow {\n bottom: -5px;\n left: calc(50% - 4px);\n border-width: 0 1px 1px 0;\n }\n\n .surface[data-placement=\"bottom\"] .arrow {\n top: -5px;\n left: calc(50% - 4px);\n border-width: 1px 0 0 1px;\n }\n\n .surface[data-placement=\"left\"] .arrow {\n top: calc(50% - 4px);\n right: -5px;\n border-width: 1px 1px 0 0;\n }\n\n .surface[data-placement=\"right\"] .arrow {\n top: calc(50% - 4px);\n left: -5px;\n border-width: 0 0 1px 1px;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .surface {\n transition: opacity 100ms linear;\n transform: none;\n }\n }\n`;\n","import { html, LitElement, nothing, type PropertyValues, type TemplateResult } from \"lit\";\nimport { customElement, property, query, state } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\n\nimport { fieldManualStyles } from \"../styles/field-manual.styles.js\";\nimport { installFieldManualFonts } from \"../styles/field-manual-fonts.js\";\nimport { calculateTooltipPosition } from \"./positioning.js\";\nimport { resolveFellowshipRichText, stripFellowshipRichText } from \"./rich-text.js\";\nimport { tooltipStyles } from \"./tooltip.styles.js\";\nimport type {\n FellowshipTooltipPlacement,\n FellowshipRichTextDocument,\n FellowshipTooltipToggleDetail,\n ResolvedTooltipPlacement,\n} from \"./types.js\";\n\nconst eventOptions = { bubbles: true, composed: true } as const;\n\n/**\n * @fires fellowship-tooltip-toggle - Fired when the tooltip opens or closes.\n */\n@customElement(\"fellowship-tooltip\")\nexport class FellowshipTooltipElement extends LitElement {\n static override styles = [fieldManualStyles, tooltipStyles];\n\n @property({ type: String }) heading: string | null = null;\n @property({ type: String }) text: string | null = null;\n @property({ type: String }) placement: FellowshipTooltipPlacement = \"auto\";\n @property({ type: Number, attribute: \"open-delay\" }) openDelay = 250;\n @property({ type: Boolean, reflect: true }) disabled = false;\n\n @state() private _open = false;\n @state() private _position = { x: 0, y: 0 };\n @state() private _resolvedPlacement: ResolvedTooltipPlacement = \"top\";\n @state() private _paragraphs: FellowshipRichTextDocument = [];\n\n @query(\".surface\") private _surface?: HTMLElement;\n\n #timer: number | null = null;\n #trigger: HTMLElement | null = null;\n #previousAriaDescription: string | null = null;\n #hasStoredAriaDescription = false;\n #resizeObserver: ResizeObserver | null = null;\n\n override connectedCallback(): void {\n super.connectedCallback();\n installFieldManualFonts(this.ownerDocument);\n this.#resizeObserver ??= new ResizeObserver(() => this.#positionSurface());\n }\n\n override disconnectedCallback(): void {\n this.#clearTimer();\n this.#stopPositionTracking();\n this.#restoreTriggerDescription();\n super.disconnectedCallback();\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n super.updated(changed);\n if (changed.has(\"text\") || changed.has(\"heading\")) {\n this.#syncTriggerDescription();\n }\n if (changed.has(\"disabled\") && this.disabled && this._open) {\n this.#setOpen(false, \"disabled\");\n }\n if (changed.has(\"placement\") && this._open) {\n this.#positionSurface();\n }\n }\n\n protected override willUpdate(changed: PropertyValues<this>): void {\n super.willUpdate(changed);\n if (changed.has(\"text\")) {\n this._paragraphs = resolveFellowshipRichText(this.text ?? \"\");\n }\n }\n\n show(): void {\n if (this.disabled || this._open) {\n return;\n }\n this.#setOpen(true, \"programmatic\");\n }\n\n hide(): void {\n this.#clearTimer();\n if (this._open) {\n this.#setOpen(false, \"programmatic\");\n }\n }\n\n override render(): TemplateResult {\n const surfaceStyle = {\n left: `${this._position.x}px`,\n top: `${this._position.y}px`,\n };\n\n return html`\n <span\n class=\"trigger\"\n part=\"trigger\"\n @pointerenter=${this.#handlePointerEnter}\n @pointerleave=${this.#handlePointerLeave}\n @focusin=${this.#handleFocusIn}\n @focusout=${this.#handleFocusOut}\n @keydown=${this.#handleKeydown}\n >\n <slot @slotchange=${this.#handleTriggerSlotChange}></slot>\n </span>\n <div\n class=\"surface\"\n part=\"surface\"\n role=\"tooltip\"\n popover=\"manual\"\n data-open=${String(this._open)}\n data-placement=${this._resolvedPlacement}\n style=${styleMap(surfaceStyle)}\n >\n <span class=\"arrow\" part=\"arrow\" aria-hidden=\"true\"></span>\n <slot name=\"content\">\n ${this.heading ? html`<p class=\"heading\" part=\"heading\">${this.heading}</p>` : nothing}\n <div class=\"content\" part=\"content\">\n ${repeat(\n this._paragraphs,\n (_paragraph, paragraphIndex) => paragraphIndex,\n (paragraph, paragraphIndex) => html`\n <p class=\"paragraph\" part=\"paragraph\">\n ${repeat(\n paragraph,\n (segment, segmentIndex) =>\n `${paragraphIndex}:${segmentIndex}:${segment.tone}:${segment.text}`,\n (segment) => html`<span data-tone=${segment.tone}>${segment.text}</span>`,\n )}\n </p>\n `,\n )}\n </div>\n </slot>\n </div>\n `;\n }\n\n #setOpen(open: boolean, reason: FellowshipTooltipToggleDetail[\"reason\"]): void {\n if (open === this._open) {\n return;\n }\n this._open = open;\n void this.updateComplete.then(() => {\n const surface = this._surface;\n if (!surface) {\n return;\n }\n if (open) {\n if (\"showPopover\" in surface && !surface.matches(\":popover-open\")) {\n surface.showPopover();\n }\n this.#startPositionTracking();\n this.#positionSurface();\n } else {\n if (\"hidePopover\" in surface && surface.matches(\":popover-open\")) {\n surface.hidePopover();\n }\n this.#stopPositionTracking();\n }\n });\n this.dispatchEvent(\n new CustomEvent<FellowshipTooltipToggleDetail>(\"fellowship-tooltip-toggle\", {\n ...eventOptions,\n detail: { open, reason },\n }),\n );\n }\n\n #positionSurface = (): void => {\n if (!this._open || !this.#trigger || !this._surface) {\n return;\n }\n const trigger = this.#trigger.getBoundingClientRect();\n const surface = this._surface.getBoundingClientRect();\n const next = calculateTooltipPosition(trigger, surface, {\n placement: this.placement,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n });\n if (\n next.x === this._position.x &&\n next.y === this._position.y &&\n next.placement === this._resolvedPlacement\n ) {\n return;\n }\n this._position = { x: next.x, y: next.y };\n this._resolvedPlacement = next.placement;\n };\n\n #startPositionTracking(): void {\n window.addEventListener(\"resize\", this.#positionSurface);\n window.addEventListener(\"scroll\", this.#positionSurface, true);\n if (this.#trigger) {\n this.#resizeObserver?.observe(this.#trigger);\n }\n if (this._surface) {\n this.#resizeObserver?.observe(this._surface);\n }\n }\n\n #stopPositionTracking(): void {\n window.removeEventListener(\"resize\", this.#positionSurface);\n window.removeEventListener(\"scroll\", this.#positionSurface, true);\n this.#resizeObserver?.disconnect();\n }\n\n #handlePointerEnter = (): void => {\n if (this.disabled || this._open) {\n return;\n }\n this.#clearTimer();\n this.#timer = window.setTimeout(\n () => {\n this.#timer = null;\n this.#setOpen(true, \"pointer\");\n },\n Math.max(0, this.openDelay),\n );\n };\n\n #handlePointerLeave = (): void => {\n this.#clearTimer();\n if (this._open) {\n this.#setOpen(false, \"pointer\");\n }\n };\n\n #handleFocusIn = (): void => {\n this.#clearTimer();\n if (!this.disabled && !this._open) {\n this.#setOpen(true, \"focus\");\n }\n };\n\n #handleFocusOut = (event: FocusEvent): void => {\n if (!this.contains(event.relatedTarget as Node | null) && this._open) {\n this.#setOpen(false, \"focus\");\n }\n };\n\n #handleKeydown = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\" && this._open) {\n event.stopPropagation();\n this.#setOpen(false, \"escape\");\n }\n };\n\n #handleTriggerSlotChange = (event: Event): void => {\n const slot = event.currentTarget as HTMLSlotElement;\n const nextTrigger = slot.assignedElements({ flatten: true })[0];\n this.#restoreTriggerDescription();\n this.#trigger = nextTrigger instanceof HTMLElement ? nextTrigger : null;\n this.#syncTriggerDescription();\n };\n\n #syncTriggerDescription(): void {\n if (!this.#trigger) {\n return;\n }\n if (!this.#hasStoredAriaDescription) {\n this.#previousAriaDescription = this.#trigger.getAttribute(\"aria-description\");\n this.#hasStoredAriaDescription = true;\n }\n const description = [this.heading, stripFellowshipRichText(this.text ?? \"\")]\n .filter(Boolean)\n .join(\". \");\n if (description) {\n this.#trigger.setAttribute(\"aria-description\", description);\n } else if (this.#previousAriaDescription === null) {\n this.#trigger.removeAttribute(\"aria-description\");\n }\n }\n\n #restoreTriggerDescription(): void {\n if (!this.#trigger) {\n return;\n }\n if (this.#previousAriaDescription === null) {\n this.#trigger.removeAttribute(\"aria-description\");\n } else {\n this.#trigger.setAttribute(\"aria-description\", this.#previousAriaDescription);\n }\n this.#trigger = null;\n this.#previousAriaDescription = null;\n this.#hasStoredAriaDescription = false;\n }\n\n #clearTimer(): void {\n if (this.#timer !== null) {\n window.clearTimeout(this.#timer);\n this.#timer = null;\n }\n }\n}\n\nexport const defineFellowshipTooltip = (): void => {\n if (!customElements.get(\"fellowship-tooltip\")) {\n customElements.define(\"fellowship-tooltip\", FellowshipTooltipElement);\n }\n};\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fellowship-tooltip\": FellowshipTooltipElement;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Dungeon, Enemy, FellowshipDataManifest, ImageAssetRef } from "@crockery/fellowship-data";
|
|
2
|
+
export interface DungeonMapData {
|
|
3
|
+
readonly manifest: FellowshipDataManifest;
|
|
4
|
+
readonly dungeon: Dungeon;
|
|
5
|
+
readonly enemies: ReadonlyMap<string, Enemy>;
|
|
6
|
+
}
|
|
7
|
+
export interface FellowshipDataSource {
|
|
8
|
+
loadDungeon(dungeonId: string, signal?: AbortSignal): Promise<DungeonMapData>;
|
|
9
|
+
resolveAssetUrl(asset: ImageAssetRef): string;
|
|
10
|
+
}
|
|
11
|
+
export type FellowshipDataErrorCode = "aborted" | "dungeon-not-found" | "hero-not-found" | "unsupported-schema" | "data-load-failed";
|
|
12
|
+
export declare class FellowshipDataError extends Error {
|
|
13
|
+
readonly code: FellowshipDataErrorCode;
|
|
14
|
+
readonly cause?: unknown;
|
|
15
|
+
constructor(code: FellowshipDataErrorCode, message: string, cause?: unknown);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=data-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-source.d.ts","sourceRoot":"","sources":["../../src/data/data-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,KAAK,EACL,sBAAsB,EACtB,aAAa,EACd,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9E,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAAC;CAC/C;AAED,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,mBAAmB,GACnB,gBAAgB,GAChB,oBAAoB,GACpB,kBAAkB,CAAC;AAEvB,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,SAAkB,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEtB,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAM5E"}
|