@tungstenstudio/dartboard-input 1.0.0 → 1.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/README.md CHANGED
@@ -232,6 +232,69 @@ You can also define your own state classes. Use this specificity pattern to over
232
232
  }
233
233
  ```
234
234
 
235
+ ## Bed Labels
236
+
237
+ Overlay a short, centered text label on every scoring bed, and update or clear it at runtime — without re-rendering the board. The label text is **opaque**: whatever string your labeler returns is rendered verbatim and given no meaning by the library, so it works equally for scores, coaching cues, glyphs, hit counts, or anything else.
238
+
239
+ ### `board.labelBeds(labeler, options?): this`
240
+
241
+ Draws (or replaces) a label on every scoring bed. The `labeler` is called once per bed with a `BedLabelContext` — the full bed model (`segment`, `color`, `ring`) plus a convenience `score` (`segment × ring.multiplier`; single bull = 25, double bull = 50). Return a string to render, or `null` / `undefined` / `''` to skip that bed. Each call fully replaces the previous labels (no accumulation), so just call it again whenever your state changes. Chainable.
242
+
243
+ ```js
244
+ // "What's left" — overlay the score remaining after each bed, for a player on 70
245
+ board.labelBeds((bed) => String(70 - bed.score));
246
+ // S20 → "50", T20 → "10", D20 → "30", single bull → "45", double bull → "20"
247
+ ```
248
+
249
+ Because the returned string is opaque, the same API labels beds with anything:
250
+
251
+ ```js
252
+ // Mark good/bad zones — arbitrary text, no scoring involved
253
+ board.labelBeds((bed) => (bed.ring.multiplier === 3 ? 'GOOD' : 'BAD'));
254
+
255
+ // A glyph on one bed, nothing elsewhere
256
+ board.labelBeds((bed) => (bed.ring.abbr === 'T' && bed.segment === 20 ? '✓' : ''));
257
+ ```
258
+
259
+ ### `board.clearBedLabels(): this`
260
+
261
+ Removes all bed labels. (`destroy()` also removes them; `reset()` does **not** — it only clears highlight classes.) Chainable.
262
+
263
+ ### Notes
264
+
265
+ - **Coverage:** all 82 scoring beds — inner/outer single, double, triple, and both bull rings. The MISS ring is never labeled (it already renders the 1–20 numbers and always scores 0).
266
+ - **Placement:** labels are centered on each bed via the board's own geometry and scale with the board `size`. The double bull label sits at the center; the single bull label sits at the bottom of its ring, so the two never overlap.
267
+ - **Layering:** labels render above the beds but below dart markers (`addDart`), and never intercept pointer/keyboard events, highlights, or `throw`/`hover` events.
268
+
269
+ ### Styling
270
+
271
+ Each label is a `<text class="c-Dartboard-label">` (plus your optional `options.className`). Color is automatic: labels are light by default with a thin dark **halo** so they stay legible on the mid-tone red/green scoring beds, while the light (beige) single beds use dark text. All three are themeable:
272
+
273
+ ```css
274
+ .c-Dartboard {
275
+ --dartboard-label-on-dark: #fff; /* text on black / red / green beds */
276
+ --dartboard-label-on-light: #111; /* text on the beige single beds */
277
+ --dartboard-label-halo: rgba(0, 0, 0, 0.6); /* outline drawn behind every label */
278
+ }
279
+ ```
280
+
281
+ The scoring beds (double, triple, both bulls) are red/green even on their "light" segments, so their labels carry an extra `c-Dartboard-label--scoring` class that keeps the text light there.
282
+
283
+ The default font size scales with the board. Override it by targeting the text:
284
+
285
+ ```css
286
+ .c-Dartboard-label {
287
+ font-size: 18px;
288
+ font-weight: 700;
289
+ }
290
+ ```
291
+
292
+ Pass `options.className` to style one overlay independently:
293
+
294
+ ```js
295
+ board.labelBeds((bed) => String(70 - bed.score), { className: 'remaining' });
296
+ ```
297
+
235
298
  ## Mobile / Touch-Friendly Sizing
236
299
 
237
300
  The default ring proportions can be too narrow for comfortable tapping on small screens. The exported `MOBILE_OPTIONS` preset widens the scoring rings (double, triple, bull) for larger touch targets:
@@ -277,6 +340,9 @@ Override CSS custom properties on the `.c-Dartboard` element:
277
340
  --dartboard-good: #228b22;
278
341
  --dartboard-bad: #e32636;
279
342
  --dartboard-neutral: #4a6fa5;
343
+ --dartboard-label-on-dark: #fff;
344
+ --dartboard-label-on-light: #111;
345
+ --dartboard-label-halo: rgba(0, 0, 0, 0.6);
280
346
  }
281
347
  ```
282
348
 
@@ -295,6 +361,9 @@ Each bed element in the SVG has several classes you can use for fine-grained sty
295
361
  | `.is-highlighted` | Default highlight class (includes a pulsing animation) |
296
362
  | `.is-disabled` | Applied to the wrapper when the board is disabled |
297
363
  | `.c-Dartboard-missLabel` | Segment number labels in the miss ring |
364
+ | `.c-Dartboard-labels` | Group wrapping all bed labels (`pointer-events: none`) |
365
+ | `.c-Dartboard-label` | A single bed label from `labelBeds`; also carries `.isDark`/`.isLight` |
366
+ | `.c-Dartboard-label--scoring` | Label on a red/green scoring bed (double, triple, bull); keeps light text |
298
367
 
299
368
  To override fills for a specific ring, combine the ring group with bed classes:
300
369
 
@@ -338,6 +407,8 @@ All types are exported for TypeScript consumers:
338
407
  - `BedTarget` — explicit target (`{ segment, ring? }`) — omit `ring` to target all scoring beds
339
408
  - `BedInput` — union of `BedTarget | string | number` accepted by all targeting methods
340
409
  - `HighlightOptions` — options for `highlight`
410
+ - `BedLabelContext` — the labeler argument for `labelBeds` (`Bed & { score: number }`)
411
+ - `BedLabelOptions` — options for `labelBeds`
341
412
  - `Ring`, `Rings`, `Segment`, `Bed` — board model types
342
413
 
343
414
  ## Constants
package/dist/index.cjs CHANGED
@@ -162,6 +162,13 @@ var DART_OFFSETS = [
162
162
  [-0.25, 0.25],
163
163
  [0.25, -0.25]
164
164
  ];
165
+ var SCORING_COLOR_RINGS = /* @__PURE__ */ new Set([
166
+ "DOUBLE",
167
+ "TRIPLE",
168
+ "SINGLE_BULL",
169
+ "DOUBLE_BULL"
170
+ ]);
171
+ var LABEL_FONT_SCALE = 0.06;
165
172
  var Dartboard = class {
166
173
  constructor(container, options = {}, segments = DEFAULT_SEGMENTS, rings = DEFAULT_RINGS) {
167
174
  this.board = null;
@@ -169,6 +176,7 @@ var Dartboard = class {
169
176
  this.rendered = false;
170
177
  this._disabled = false;
171
178
  this.dartGroup = null;
179
+ this.labelGroup = null;
172
180
  this.dartCounts = /* @__PURE__ */ new Map();
173
181
  this.options = { ...DEFAULT_OPTIONS, ...options };
174
182
  this.segments = segments;
@@ -295,6 +303,7 @@ var Dartboard = class {
295
303
  this.sizes = null;
296
304
  this.rendered = false;
297
305
  this.dartGroup = null;
306
+ this.labelGroup = null;
298
307
  this.dartCounts.clear();
299
308
  }
300
309
  }
@@ -401,12 +410,49 @@ var Dartboard = class {
401
410
  }
402
411
  return this;
403
412
  }
413
+ labelBeds(labeler, options = {}) {
414
+ if (!this.board || !this.sizes) return this;
415
+ const extra = options.className;
416
+ const counterRotation = -this.board.rotation;
417
+ const group = this.ensureLabelGroup();
418
+ group.attr("font-size", this.board.radius * LABEL_FONT_SCALE);
419
+ group.selectAll("*").remove();
420
+ for (const ringKey of SCORING_RINGS) {
421
+ const ring = this.rings[ringKey];
422
+ if (!ring) continue;
423
+ const beds = isBull(ringKey) ? [{ segment: 25, color: ringKey === "DOUBLE_BULL" ? "Dark" : "Light", ring }] : addRingToSegments(ring, this.segments);
424
+ const scoring = SCORING_COLOR_RINGS.has(ringKey) ? " c-Dartboard-label--scoring" : "";
425
+ for (const bed of beds) {
426
+ const score = bed.segment * bed.ring.multiplier;
427
+ const text = labeler({ ...bed, score });
428
+ if (text == null || text === "") continue;
429
+ const [x, y] = this.labelPosition({ segment: bed.segment, ring: ringKey });
430
+ const className = `c-Dartboard-label is${bed.color}${scoring}${extra ? ` ${extra}` : ""}`;
431
+ group.append("text").attr("class", className).attr("data-bed", bedKey({ segment: bed.segment, ring: ringKey })).attr("x", x).attr("y", y).attr("dy", ".35em").attr("text-anchor", "middle").attr("transform", `rotate(${counterRotation}, ${x}, ${y})`).text(text);
432
+ }
433
+ }
434
+ this.dartGroup?.raise();
435
+ return this;
436
+ }
437
+ clearBedLabels() {
438
+ if (this.labelGroup) {
439
+ this.labelGroup.remove();
440
+ this.labelGroup = null;
441
+ }
442
+ return this;
443
+ }
404
444
  ensureDartGroup() {
405
445
  if (!this.dartGroup) {
406
446
  this.dartGroup = this.board.svg.append("g").classed("c-Dartboard-darts", true);
407
447
  }
408
448
  return this.dartGroup;
409
449
  }
450
+ ensureLabelGroup() {
451
+ if (!this.labelGroup) {
452
+ this.labelGroup = this.board.svg.append("g").classed("c-Dartboard-labels", true);
453
+ }
454
+ return this.labelGroup;
455
+ }
410
456
  ringRadii(ringKey) {
411
457
  const s = this.sizes;
412
458
  const doubleBullOuter = s.doubleBull;
@@ -451,6 +497,17 @@ var Dartboard = class {
451
497
  outerR
452
498
  };
453
499
  }
500
+ // Where a bed's label sits. Reuses bedGeometry for the 80 segment beds and
501
+ // the double bull (center). Single bull would also resolve to [0,0] and
502
+ // collide with the double bull label, so it's pushed to the bottom of its
503
+ // ring (6 o'clock) instead.
504
+ labelPosition(target) {
505
+ if (target.ring === "SINGLE_BULL") {
506
+ const [innerR, outerR] = this.ringRadii("SINGLE_BULL");
507
+ return [0, (innerR + outerR) / 2];
508
+ }
509
+ return this.bedGeometry(target).centroid;
510
+ }
454
511
  isValidTarget(target) {
455
512
  if (isBull(target.ring)) return target.segment === 25;
456
513
  return this.segments.some((s) => s.segment === target.segment);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/dartboard.ts","../src/constants.ts","../src/utils.ts"],"sourcesContent":["import './dartboard.css';\nexport { Dartboard } from './dartboard';\nexport type {\n Ring,\n Rings,\n Segment,\n Bed,\n DartboardOptions,\n ThrowDetail,\n HoverDetail,\n BedTarget,\n BedInput,\n HighlightOptions,\n} from './types';\nexport { DEFAULT_OPTIONS, MOBILE_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS } from './constants';\nexport { buildThrowDetail, asPercent, addRingToSegments, parseBed } from './utils';\n","import { select, type Selection } from 'd3-selection';\nimport { arc, pie, type PieArcDatum } from 'd3-shape';\nimport type {\n DartboardOptions,\n Rings,\n Ring,\n Segment,\n Bed,\n ThrowDetail,\n HoverDetail,\n BedTarget,\n BedInput,\n HighlightOptions,\n} from './types';\nimport { DEFAULT_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS } from './constants';\nimport { buildThrowDetail, asPercent, addRingToSegments, parseBed } from './utils';\n\ntype RingKey = keyof Rings;\n\n// D3's selection generics are deeply nested and don't compose well across\n// chained calls. Using `any` for internal state avoids 100+ lines of type\n// gymnastics while keeping the public API fully typed.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype D3Sel = Selection<any, any, any, any>;\n\ninterface BoardState {\n container: HTMLElement;\n wrapper: D3Sel;\n width: number;\n height: number;\n radius: number;\n rotation: number;\n segmentWidth: number;\n svg: D3Sel;\n pie: ReturnType<typeof pie<Bed>>;\n}\n\ninterface SizeMap {\n miss: number;\n double: number;\n outerSingle: number;\n triple: number;\n innerSingle: number;\n singleBull: number;\n doubleBull: number;\n}\n\nfunction buildAriaLabel(bed: Bed): string {\n const detail = buildThrowDetail(bed);\n return `${detail.bed}, scores ${detail.score}`;\n}\n\nfunction isBull(ringKey: RingKey): boolean {\n return ringKey === 'DOUBLE_BULL' || ringKey === 'SINGLE_BULL';\n}\n\nfunction bedKey(target: Required<BedTarget>): string {\n return `${target.ring}:${target.segment}`;\n}\n\n// Predefined offsets for multiple darts in the same bed.\n// [radialFraction, tangentialFraction] — scaled to ring thickness, oriented\n// along the bed's radial and arc directions so darts stay within the wedge.\nconst DART_OFFSETS: [number, number][] = [\n [0, 0],\n [-0.25, -0.25],\n [0.25, 0.25],\n [-0.25, 0.25],\n [0.25, -0.25],\n];\n\nexport class Dartboard {\n private board: BoardState | null = null;\n private sizes: SizeMap | null = null;\n private rendered = false;\n private _disabled = false;\n private dartGroup: D3Sel | null = null;\n private dartCounts = new Map<string, number>();\n private readonly options: DartboardOptions;\n readonly segments: Segment[];\n readonly rings: Rings;\n\n constructor(\n container: string | HTMLElement,\n options: Partial<DartboardOptions> = {},\n segments: Segment[] = DEFAULT_SEGMENTS,\n rings: Rings = DEFAULT_RINGS,\n ) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.segments = segments;\n this.rings = rings;\n\n const percentSum =\n this.options.missPercent +\n this.options.doublePercent +\n this.options.outerSinglePercent +\n this.options.triplePercent +\n this.options.innerSinglePercent +\n this.options.singleBullPercent +\n this.options.doubleBullPercent;\n\n if (percentSum !== 100) {\n throw new Error(\n `Dartboard: ring percentages must sum to 100 (got ${percentSum})`,\n );\n }\n\n const boardContainer =\n typeof container === 'string'\n ? document.querySelector<HTMLElement>(container)\n : container;\n\n if (!boardContainer) {\n throw new Error(`Dartboard: container not found`);\n }\n\n const padding = this.options.padding;\n const boardSize =\n this.options.size ||\n Math.min(boardContainer.offsetHeight, boardContainer.offsetWidth);\n const width = boardSize - padding * 2;\n const viewBoxSize = boardSize;\n const segmentWidth = 360 / segments.length;\n const radius = width / 2;\n const rotation = segmentWidth / -2;\n\n const wrapper = select(boardContainer)\n .append('div')\n .classed('c-Dartboard', true);\n\n const svg = wrapper\n .append('svg')\n .attr('viewBox', `0 0 ${viewBoxSize} ${viewBoxSize}`)\n .attr('role', 'group')\n .attr('aria-label', 'Dartboard')\n .append('g')\n .attr(\n 'transform',\n `translate(${viewBoxSize / 2}, ${viewBoxSize / 2}) rotate(${rotation})`,\n );\n\n const pieFn = pie<Bed>()\n .sort((a, b) => (a.position ?? 0) - (b.position ?? 0))\n .value(() => segmentWidth);\n\n this.board = {\n container: boardContainer,\n wrapper,\n width,\n height: width,\n radius,\n rotation,\n segmentWidth,\n svg,\n pie: pieFn,\n };\n\n this.sizes = {\n miss: radius * asPercent(this.options.missPercent),\n double: radius * asPercent(this.options.doublePercent),\n outerSingle: radius * asPercent(this.options.outerSinglePercent),\n triple: radius * asPercent(this.options.triplePercent),\n innerSingle: radius * asPercent(this.options.innerSinglePercent),\n singleBull: radius * asPercent(this.options.singleBullPercent),\n doubleBull: radius * asPercent(this.options.doubleBullPercent),\n };\n }\n\n render(): this {\n if (!this.board || !this.sizes) return this;\n if (this.rendered) return this;\n this.rendered = true;\n\n const { board, sizes } = this;\n\n let innerRadius = 0;\n let outerRadius = innerRadius + sizes.doubleBull;\n this.renderBeds(\n board,\n this.rings.DOUBLE_BULL,\n [{ segment: 25, color: 'Dark' }],\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.singleBull;\n this.renderBeds(\n board,\n this.rings.SINGLE_BULL,\n [{ segment: 25, color: 'Light' }],\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.innerSingle;\n this.renderBeds(\n board,\n this.rings.INNER_SINGLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.triple;\n this.renderBeds(\n board,\n this.rings.TRIPLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.outerSingle;\n this.renderBeds(\n board,\n this.rings.OUTER_SINGLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.double;\n this.renderBeds(\n board,\n this.rings.DOUBLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = board.radius - sizes.miss;\n outerRadius = board.radius;\n this.renderMissRing(\n board,\n this.rings.MISS,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n return this;\n }\n\n destroy(): void {\n if (this.board) {\n // Remove all D3 event listeners before removing from DOM\n this.board.wrapper.selectAll('.c-Dartboard-bed')\n .on('pointerup', null)\n .on('pointerenter', null)\n .on('pointerleave', null)\n .on('keydown', null);\n this.board.wrapper.remove();\n this.board = null;\n this.sizes = null;\n this.rendered = false;\n this.dartGroup = null;\n this.dartCounts.clear();\n }\n }\n\n get disabled(): boolean {\n return this._disabled;\n }\n\n disable(): this {\n this._disabled = true;\n if (this.board) {\n this.board.wrapper.classed('is-disabled', true);\n }\n return this;\n }\n\n enable(): this {\n this._disabled = false;\n if (this.board) {\n this.board.wrapper.classed('is-disabled', false);\n }\n return this;\n }\n\n throwAt(target: BedInput): void {\n if (!this.board || this._disabled) return;\n\n const targets = this.resolveTargets([target]);\n if (targets.length === 0) return;\n const first = targets[0];\n\n if (!this.isValidTarget(first)) {\n throw new Error(`Dartboard: invalid throw target — segment ${first.segment} does not exist`);\n }\n\n const ring = this.rings[first.ring];\n const bed: Bed = { segment: first.segment, color: 'Dark', ring };\n const detail = buildThrowDetail(bed);\n\n this.board.container.dispatchEvent(\n new CustomEvent<ThrowDetail>('throw', { detail }),\n );\n }\n\n highlight(targets: BedInput[], options: HighlightOptions = {}): void {\n if (!this.board) return;\n const className = options.className || 'is-highlighted';\n\n for (const target of this.resolveTargets(targets)) {\n const ring = this.rings[target.ring];\n const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;\n this.board.wrapper.selectAll(selector).classed(className, true);\n }\n }\n\n unhighlight(targets: BedInput[], options: HighlightOptions = {}): void {\n if (!this.board) return;\n const className = options.className || 'is-highlighted';\n\n for (const target of this.resolveTargets(targets)) {\n const ring = this.rings[target.ring];\n const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;\n this.board.wrapper.selectAll(selector).classed(className, false);\n }\n }\n\n reset(className = 'is-highlighted'): void {\n if (!this.board) return;\n this.board.wrapper.selectAll(`.${className}`).classed(className, false);\n }\n\n addDart(targets: BedInput[]): this {\n if (!this.board || !this.sizes) return this;\n\n const resolved = this.resolveTargets(targets);\n const group = this.ensureDartGroup();\n const dartRadius = this.sizes.doubleBull * 0.25;\n\n for (const target of resolved) {\n const key = bedKey(target);\n const count = this.dartCounts.get(key) ?? 0;\n this.dartCounts.set(key, count + 1);\n\n const { centroid, midAngle, innerR, outerR } = this.bedGeometry(target);\n let cx: number;\n let cy: number;\n\n if (isBull(target.ring)) {\n // Distribute bull darts evenly around the ring at its midpoint radius.\n const midR = (innerR + outerR) / 2;\n const angle = (count * 2 * Math.PI) / DART_OFFSETS.length;\n cx = midR * Math.sin(angle);\n cy = -midR * Math.cos(angle);\n } else {\n const [radialFrac, tangentialFrac] = DART_OFFSETS[count % DART_OFFSETS.length];\n const thickness = outerR - innerR;\n const midRadius = (innerR + outerR) / 2;\n const segWidthRad = (this.board.segmentWidth * Math.PI) / 180;\n // Arc half-width at the midpoint radius — limits tangential offsets\n // so darts stay within the segment's angular bounds.\n const arcHalfWidth = midRadius * Math.sin(segWidthRad / 2);\n\n // Radial unit (pointing outward from center)\n const radX = Math.sin(midAngle);\n const radY = -Math.cos(midAngle);\n // Tangential unit (perpendicular, along the arc)\n const tanX = Math.cos(midAngle);\n const tanY = Math.sin(midAngle);\n\n cx = centroid[0] + radialFrac * thickness * radX + tangentialFrac * arcHalfWidth * tanX;\n cy = centroid[1] + radialFrac * thickness * radY + tangentialFrac * arcHalfWidth * tanY;\n }\n\n group\n .append('circle')\n .classed('c-Dartboard-dart', true)\n .attr('cx', cx)\n .attr('cy', cy)\n .attr('r', dartRadius)\n .attr('data-bed', key);\n }\n\n return this;\n }\n\n removeDarts(targets?: BedInput[]): this {\n if (!this.dartGroup) return this;\n\n if (!targets) {\n this.dartGroup.selectAll('.c-Dartboard-dart').remove();\n this.dartCounts.clear();\n return this;\n }\n\n const resolved = this.resolveTargets(targets);\n for (const target of resolved) {\n const key = bedKey(target);\n this.dartGroup\n .selectAll(`.c-Dartboard-dart[data-bed=\"${key}\"]`)\n .remove();\n this.dartCounts.delete(key);\n }\n\n return this;\n }\n\n private ensureDartGroup(): D3Sel {\n if (!this.dartGroup) {\n this.dartGroup = this.board!.svg\n .append('g')\n .classed('c-Dartboard-darts', true);\n }\n return this.dartGroup;\n }\n\n private ringRadii(ringKey: RingKey): [number, number] {\n const s = this.sizes!;\n const doubleBullOuter = s.doubleBull;\n const singleBullOuter = doubleBullOuter + s.singleBull;\n const innerSingleOuter = singleBullOuter + s.innerSingle;\n const tripleOuter = innerSingleOuter + s.triple;\n const outerSingleOuter = tripleOuter + s.outerSingle;\n const doubleOuter = outerSingleOuter + s.double;\n\n switch (ringKey) {\n case 'DOUBLE_BULL': return [0, doubleBullOuter];\n case 'SINGLE_BULL': return [doubleBullOuter, singleBullOuter];\n case 'INNER_SINGLE': return [singleBullOuter, innerSingleOuter];\n case 'TRIPLE': return [innerSingleOuter, tripleOuter];\n case 'OUTER_SINGLE': return [tripleOuter, outerSingleOuter];\n case 'DOUBLE': return [outerSingleOuter, doubleOuter];\n case 'MISS': return [this.board!.radius - s.miss, this.board!.radius];\n }\n }\n\n private bedGeometry(target: Required<BedTarget>): {\n centroid: [number, number];\n midAngle: number;\n innerR: number;\n outerR: number;\n } {\n const [innerR, outerR] = this.ringRadii(target.ring);\n\n if (target.segment === 25) {\n return { centroid: [0, 0], midAngle: 0, innerR, outerR };\n }\n\n const seg = this.segments.find((s) => s.segment === target.segment);\n const segWidthRad = (this.board!.segmentWidth * Math.PI) / 180;\n const midAngle = ((seg?.position ?? 1) - 0.5) * segWidthRad;\n const midRadius = (innerR + outerR) / 2;\n\n return {\n centroid: [\n midRadius * Math.sin(midAngle),\n -midRadius * Math.cos(midAngle),\n ],\n midAngle,\n innerR,\n outerR,\n };\n }\n\n private isValidTarget(target: Required<BedTarget>): boolean {\n if (isBull(target.ring)) return target.segment === 25;\n return this.segments.some((s) => s.segment === target.segment);\n }\n\n private resolveTargets(inputs: BedInput[]): Required<BedTarget>[] {\n return inputs.flatMap((input): Required<BedTarget>[] => {\n if (typeof input === 'number') {\n return parseBed(String(input), this.rings, this.segments) as Required<BedTarget>[];\n }\n if (typeof input === 'string') {\n return parseBed(input, this.rings, this.segments) as Required<BedTarget>[];\n }\n if (!input.ring) {\n return parseBed(String(input.segment), this.rings, this.segments) as Required<BedTarget>[];\n }\n return [input as Required<BedTarget>];\n });\n }\n\n private dispatchThrow(bed: Bed): void {\n if (!this.board || this._disabled) return;\n const detail = buildThrowDetail(bed);\n this.board.container.dispatchEvent(\n new CustomEvent<ThrowDetail>('throw', { detail }),\n );\n }\n\n private dispatchHover(bed: Bed, hovering: boolean): void {\n if (!this.board || this._disabled) return;\n const throwDetail = buildThrowDetail(bed);\n const detail: HoverDetail = { ...throwDetail, hovering };\n this.board.container.dispatchEvent(\n new CustomEvent<HoverDetail>('hover', { detail }),\n );\n }\n\n private renderBeds(\n board: BoardState,\n ring: Ring,\n segments: Segment[],\n outerRadius: number,\n innerRadius: number,\n ): void {\n const className = `c-Dartboard-${ring.name}`;\n const arcFn = arc<PieArcDatum<Bed>>()\n .outerRadius(outerRadius)\n .innerRadius(innerRadius);\n\n const beds = board.svg\n .append('g')\n .classed(className, true)\n .selectAll<SVGGElement, PieArcDatum<Bed>>('arc')\n .data(board.pie(addRingToSegments(ring, segments)))\n .enter()\n .append('g')\n .attr(\n 'class',\n (d: PieArcDatum<Bed>) =>\n `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} ${className}--${d.data.segment} is${d.data.color}`,\n )\n .attr('role', 'button')\n .attr('tabindex', '0')\n .attr('aria-label', (d: PieArcDatum<Bed>) =>\n buildAriaLabel(d.data),\n )\n .on(\n 'pointerup',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n this.dispatchThrow(d.data);\n },\n )\n .on(\n 'pointerenter',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n select(_event.currentTarget as Element).classed('is-hovered', true);\n this.dispatchHover(d.data, true);\n },\n )\n .on(\n 'pointerleave',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n select(_event.currentTarget as Element).classed('is-hovered', false);\n this.dispatchHover(d.data, false);\n },\n )\n .on(\n 'keydown',\n (_event: KeyboardEvent, d: PieArcDatum<Bed>) => {\n if (_event.key === 'Enter' || _event.key === ' ') {\n _event.preventDefault();\n this.dispatchThrow(d.data);\n }\n },\n );\n\n beds.append('path').attr('d', arcFn as never);\n }\n\n private renderMissRing(\n board: BoardState,\n ring: Ring,\n segments: Segment[],\n outerRadius: number,\n innerRadius: number,\n ): void {\n const missArc = arc<PieArcDatum<Bed>>()\n .outerRadius(outerRadius)\n .innerRadius(innerRadius);\n\n const missBeds = board.svg\n .append('g')\n .classed('c-Dartboard-miss', true)\n .selectAll<SVGGElement, PieArcDatum<Bed>>('arc')\n .data(board.pie(addRingToSegments(ring, segments)))\n .enter()\n .append('g')\n .attr(\n 'class',\n (d: PieArcDatum<Bed>) =>\n `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} c-Dartboard-miss--${d.data.segment}`,\n )\n .attr('role', 'button')\n .attr('tabindex', '0')\n .attr('aria-label', (d: PieArcDatum<Bed>) =>\n buildAriaLabel(d.data),\n )\n .on(\n 'pointerup',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n this.dispatchThrow(d.data);\n },\n )\n .on(\n 'keydown',\n (_event: KeyboardEvent, d: PieArcDatum<Bed>) => {\n if (_event.key === 'Enter' || _event.key === ' ') {\n _event.preventDefault();\n this.dispatchThrow(d.data);\n }\n },\n );\n\n missBeds.append('path').attr('d', missArc as never);\n\n const determineRotation = (bedData: Bed) =>\n -board.rotation + board.segmentWidth * ((bedData.position ?? 0) - 1);\n\n const determineX = (d: PieArcDatum<Bed>) =>\n missArc.centroid(d)[0];\n\n const determineY = (d: PieArcDatum<Bed>) =>\n missArc.centroid(d)[1];\n\n missBeds\n .append('text')\n .classed('c-Dartboard-missLabel', true)\n .attr('x', (d: PieArcDatum<Bed>) => determineX(d))\n .attr('y', (d: PieArcDatum<Bed>) => determineY(d))\n .attr('dy', '.35em')\n .attr(\n 'transform',\n (d: PieArcDatum<Bed>) =>\n `rotate(${determineRotation(d.data)}, ${determineX(d)}, ${determineY(d)})`,\n )\n .attr('text-anchor', 'middle')\n .text((d: PieArcDatum<Bed>) => d.data.segment);\n }\n}\n","import type { DartboardOptions, Rings, Segment } from './types';\n\nexport const DEFAULT_OPTIONS: DartboardOptions = {\n size: null,\n padding: 2,\n missPercent: 10,\n doublePercent: 10,\n outerSinglePercent: 25,\n triplePercent: 10,\n innerSinglePercent: 30,\n singleBullPercent: 8,\n doubleBullPercent: 7,\n};\n\nexport const DEFAULT_RINGS: Rings = {\n MISS: { name: 'miss', abbr: 'M', multiplier: 0 },\n DOUBLE: { name: 'double', abbr: 'D', multiplier: 2 },\n OUTER_SINGLE: { name: 'outerSingle', abbr: 'S', multiplier: 1 },\n TRIPLE: { name: 'triple', abbr: 'T', multiplier: 3 },\n INNER_SINGLE: { name: 'innerSingle', abbr: 'S', multiplier: 1 },\n SINGLE_BULL: { name: 'singleBull', abbr: 'B', multiplier: 1 },\n DOUBLE_BULL: { name: 'doubleBull', abbr: 'DB', multiplier: 2 },\n};\n\nexport const MOBILE_OPTIONS: Partial<DartboardOptions> = {\n missPercent: 7,\n doublePercent: 14,\n outerSinglePercent: 21,\n triplePercent: 14,\n innerSinglePercent: 26,\n singleBullPercent: 9,\n doubleBullPercent: 9,\n};\n\nexport const DEFAULT_SEGMENTS: Segment[] = [\n { segment: 20, position: 1, color: 'Dark' },\n { segment: 1, position: 2, color: 'Light' },\n { segment: 18, position: 3, color: 'Dark' },\n { segment: 4, position: 4, color: 'Light' },\n { segment: 13, position: 5, color: 'Dark' },\n { segment: 6, position: 6, color: 'Light' },\n { segment: 10, position: 7, color: 'Dark' },\n { segment: 15, position: 8, color: 'Light' },\n { segment: 2, position: 9, color: 'Dark' },\n { segment: 17, position: 10, color: 'Light' },\n { segment: 3, position: 11, color: 'Dark' },\n { segment: 19, position: 12, color: 'Light' },\n { segment: 7, position: 13, color: 'Dark' },\n { segment: 16, position: 14, color: 'Light' },\n { segment: 8, position: 15, color: 'Dark' },\n { segment: 11, position: 16, color: 'Light' },\n { segment: 14, position: 17, color: 'Dark' },\n { segment: 9, position: 18, color: 'Light' },\n { segment: 12, position: 19, color: 'Dark' },\n { segment: 5, position: 20, color: 'Light' },\n];\n","import type { Ring, Rings, Segment, Bed, BedTarget, ThrowDetail } from './types';\n\nconst SCORING_RINGS: (keyof Rings)[] = [\n 'DOUBLE',\n 'TRIPLE',\n 'OUTER_SINGLE',\n 'INNER_SINGLE',\n 'SINGLE_BULL',\n 'DOUBLE_BULL',\n];\n\nexport function buildThrowDetail(bed: Bed): ThrowDetail {\n return {\n bed: bed.ring.abbr + bed.segment,\n segment: bed.segment,\n ring: bed.ring.name,\n score: bed.segment * bed.ring.multiplier,\n };\n}\n\nexport function asPercent(n: number): number {\n return parseFloat((n / 100).toFixed(2));\n}\n\nexport function addRingToSegments(ring: Ring, segments: Segment[]): Bed[] {\n return segments.map((segment) => ({ ...segment, ring }));\n}\n\nexport function parseBed(\n bed: string,\n rings: Rings,\n segments?: Segment[],\n): BedTarget[] {\n // 'miss' keyword — target all segments in the miss ring\n if (bed.toLowerCase() === 'miss') {\n if (!segments) {\n throw new Error(\n 'parseBed: segments are required to resolve the \"miss\" keyword',\n );\n }\n return segments.map((s) => ({ segment: s.segment, ring: 'MISS' as keyof Rings }));\n }\n\n // Number-only string — target all scoring rings on that segment\n const numOnly = bed.match(/^(\\d+)$/);\n if (numOnly) {\n const segment = parseInt(numOnly[1], 10);\n return SCORING_RINGS\n .filter((key) => key in rings)\n .map((ring) => ({ segment, ring }));\n }\n\n const match = bed.match(/^([A-Za-z]+)(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid bed format: \"${bed}\"`);\n }\n\n const [, abbr, numStr] = match.map((s, i) => i === 1 ? s.toUpperCase() : s);\n const segment = parseInt(numStr, 10);\n\n const targets: BedTarget[] = [];\n for (const [key, ring] of Object.entries(rings)) {\n if (ring.abbr === abbr) {\n targets.push({ segment, ring: key as keyof Rings });\n }\n }\n\n if (targets.length === 0) {\n throw new Error(`Unknown bed abbreviation: \"${abbr}\"`);\n }\n\n return targets;\n}\n"],"mappings":"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","names":["segment"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/dartboard.ts","../src/constants.ts","../src/utils.ts"],"sourcesContent":["import './dartboard.css';\nexport { Dartboard } from './dartboard';\nexport type {\n Ring,\n Rings,\n Segment,\n Bed,\n DartboardOptions,\n ThrowDetail,\n HoverDetail,\n BedTarget,\n BedInput,\n HighlightOptions,\n BedLabelContext,\n BedLabelOptions,\n} from './types';\nexport { DEFAULT_OPTIONS, MOBILE_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS } from './constants';\nexport { buildThrowDetail, asPercent, addRingToSegments, parseBed } from './utils';\n","import { select, type Selection } from 'd3-selection';\nimport { arc, pie, type PieArcDatum } from 'd3-shape';\nimport type {\n DartboardOptions,\n Rings,\n Ring,\n Segment,\n Bed,\n ThrowDetail,\n HoverDetail,\n BedTarget,\n BedInput,\n HighlightOptions,\n BedLabelContext,\n BedLabelOptions,\n} from './types';\nimport { DEFAULT_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS } from './constants';\nimport { buildThrowDetail, asPercent, addRingToSegments, parseBed, SCORING_RINGS } from './utils';\n\ntype RingKey = keyof Rings;\n\n// D3's selection generics are deeply nested and don't compose well across\n// chained calls. Using `any` for internal state avoids 100+ lines of type\n// gymnastics while keeping the public API fully typed.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype D3Sel = Selection<any, any, any, any>;\n\ninterface BoardState {\n container: HTMLElement;\n wrapper: D3Sel;\n width: number;\n height: number;\n radius: number;\n rotation: number;\n segmentWidth: number;\n svg: D3Sel;\n pie: ReturnType<typeof pie<Bed>>;\n}\n\ninterface SizeMap {\n miss: number;\n double: number;\n outerSingle: number;\n triple: number;\n innerSingle: number;\n singleBull: number;\n doubleBull: number;\n}\n\nfunction buildAriaLabel(bed: Bed): string {\n const detail = buildThrowDetail(bed);\n return `${detail.bed}, scores ${detail.score}`;\n}\n\nfunction isBull(ringKey: RingKey): boolean {\n return ringKey === 'DOUBLE_BULL' || ringKey === 'SINGLE_BULL';\n}\n\nfunction bedKey(target: Required<BedTarget>): string {\n return `${target.ring}:${target.segment}`;\n}\n\n// Predefined offsets for multiple darts in the same bed.\n// [radialFraction, tangentialFraction] — scaled to ring thickness, oriented\n// along the bed's radial and arc directions so darts stay within the wedge.\nconst DART_OFFSETS: [number, number][] = [\n [0, 0],\n [-0.25, -0.25],\n [0.25, 0.25],\n [-0.25, 0.25],\n [0.25, -0.25],\n];\n\n// Rings painted with the \"scoring\" colors (red/green) rather than the plain\n// dark/light bed colors. Labels on these keep light text (a \"light\" scoring bed\n// is still green, not beige), instead of the dark-on-light treatment. Distinct\n// from utils' SCORING_RINGS, the broader set of all label-eligible rings.\nconst SCORING_COLOR_RINGS: ReadonlySet<RingKey> = new Set<RingKey>([\n 'DOUBLE',\n 'TRIPLE',\n 'SINGLE_BULL',\n 'DOUBLE_BULL',\n]);\n\n// Default label font size as a fraction of the board radius. Set on the label\n// group so individual <text> can be overridden via the .c-Dartboard-label class.\nconst LABEL_FONT_SCALE = 0.06;\n\nexport class Dartboard {\n private board: BoardState | null = null;\n private sizes: SizeMap | null = null;\n private rendered = false;\n private _disabled = false;\n private dartGroup: D3Sel | null = null;\n private labelGroup: D3Sel | null = null;\n private dartCounts = new Map<string, number>();\n private readonly options: DartboardOptions;\n readonly segments: Segment[];\n readonly rings: Rings;\n\n constructor(\n container: string | HTMLElement,\n options: Partial<DartboardOptions> = {},\n segments: Segment[] = DEFAULT_SEGMENTS,\n rings: Rings = DEFAULT_RINGS,\n ) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.segments = segments;\n this.rings = rings;\n\n const percentSum =\n this.options.missPercent +\n this.options.doublePercent +\n this.options.outerSinglePercent +\n this.options.triplePercent +\n this.options.innerSinglePercent +\n this.options.singleBullPercent +\n this.options.doubleBullPercent;\n\n if (percentSum !== 100) {\n throw new Error(\n `Dartboard: ring percentages must sum to 100 (got ${percentSum})`,\n );\n }\n\n const boardContainer =\n typeof container === 'string'\n ? document.querySelector<HTMLElement>(container)\n : container;\n\n if (!boardContainer) {\n throw new Error(`Dartboard: container not found`);\n }\n\n const padding = this.options.padding;\n const boardSize =\n this.options.size ||\n Math.min(boardContainer.offsetHeight, boardContainer.offsetWidth);\n const width = boardSize - padding * 2;\n const viewBoxSize = boardSize;\n const segmentWidth = 360 / segments.length;\n const radius = width / 2;\n const rotation = segmentWidth / -2;\n\n const wrapper = select(boardContainer)\n .append('div')\n .classed('c-Dartboard', true);\n\n const svg = wrapper\n .append('svg')\n .attr('viewBox', `0 0 ${viewBoxSize} ${viewBoxSize}`)\n .attr('role', 'group')\n .attr('aria-label', 'Dartboard')\n .append('g')\n .attr(\n 'transform',\n `translate(${viewBoxSize / 2}, ${viewBoxSize / 2}) rotate(${rotation})`,\n );\n\n const pieFn = pie<Bed>()\n .sort((a, b) => (a.position ?? 0) - (b.position ?? 0))\n .value(() => segmentWidth);\n\n this.board = {\n container: boardContainer,\n wrapper,\n width,\n height: width,\n radius,\n rotation,\n segmentWidth,\n svg,\n pie: pieFn,\n };\n\n this.sizes = {\n miss: radius * asPercent(this.options.missPercent),\n double: radius * asPercent(this.options.doublePercent),\n outerSingle: radius * asPercent(this.options.outerSinglePercent),\n triple: radius * asPercent(this.options.triplePercent),\n innerSingle: radius * asPercent(this.options.innerSinglePercent),\n singleBull: radius * asPercent(this.options.singleBullPercent),\n doubleBull: radius * asPercent(this.options.doubleBullPercent),\n };\n }\n\n render(): this {\n if (!this.board || !this.sizes) return this;\n if (this.rendered) return this;\n this.rendered = true;\n\n const { board, sizes } = this;\n\n let innerRadius = 0;\n let outerRadius = innerRadius + sizes.doubleBull;\n this.renderBeds(\n board,\n this.rings.DOUBLE_BULL,\n [{ segment: 25, color: 'Dark' }],\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.singleBull;\n this.renderBeds(\n board,\n this.rings.SINGLE_BULL,\n [{ segment: 25, color: 'Light' }],\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.innerSingle;\n this.renderBeds(\n board,\n this.rings.INNER_SINGLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.triple;\n this.renderBeds(\n board,\n this.rings.TRIPLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.outerSingle;\n this.renderBeds(\n board,\n this.rings.OUTER_SINGLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.double;\n this.renderBeds(\n board,\n this.rings.DOUBLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = board.radius - sizes.miss;\n outerRadius = board.radius;\n this.renderMissRing(\n board,\n this.rings.MISS,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n return this;\n }\n\n destroy(): void {\n if (this.board) {\n // Remove all D3 event listeners before removing from DOM\n this.board.wrapper.selectAll('.c-Dartboard-bed')\n .on('pointerup', null)\n .on('pointerenter', null)\n .on('pointerleave', null)\n .on('keydown', null);\n this.board.wrapper.remove();\n this.board = null;\n this.sizes = null;\n this.rendered = false;\n this.dartGroup = null;\n this.labelGroup = null;\n this.dartCounts.clear();\n }\n }\n\n get disabled(): boolean {\n return this._disabled;\n }\n\n disable(): this {\n this._disabled = true;\n if (this.board) {\n this.board.wrapper.classed('is-disabled', true);\n }\n return this;\n }\n\n enable(): this {\n this._disabled = false;\n if (this.board) {\n this.board.wrapper.classed('is-disabled', false);\n }\n return this;\n }\n\n throwAt(target: BedInput): void {\n if (!this.board || this._disabled) return;\n\n const targets = this.resolveTargets([target]);\n if (targets.length === 0) return;\n const first = targets[0];\n\n if (!this.isValidTarget(first)) {\n throw new Error(`Dartboard: invalid throw target — segment ${first.segment} does not exist`);\n }\n\n const ring = this.rings[first.ring];\n const bed: Bed = { segment: first.segment, color: 'Dark', ring };\n const detail = buildThrowDetail(bed);\n\n this.board.container.dispatchEvent(\n new CustomEvent<ThrowDetail>('throw', { detail }),\n );\n }\n\n highlight(targets: BedInput[], options: HighlightOptions = {}): void {\n if (!this.board) return;\n const className = options.className || 'is-highlighted';\n\n for (const target of this.resolveTargets(targets)) {\n const ring = this.rings[target.ring];\n const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;\n this.board.wrapper.selectAll(selector).classed(className, true);\n }\n }\n\n unhighlight(targets: BedInput[], options: HighlightOptions = {}): void {\n if (!this.board) return;\n const className = options.className || 'is-highlighted';\n\n for (const target of this.resolveTargets(targets)) {\n const ring = this.rings[target.ring];\n const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;\n this.board.wrapper.selectAll(selector).classed(className, false);\n }\n }\n\n reset(className = 'is-highlighted'): void {\n if (!this.board) return;\n this.board.wrapper.selectAll(`.${className}`).classed(className, false);\n }\n\n addDart(targets: BedInput[]): this {\n if (!this.board || !this.sizes) return this;\n\n const resolved = this.resolveTargets(targets);\n const group = this.ensureDartGroup();\n const dartRadius = this.sizes.doubleBull * 0.25;\n\n for (const target of resolved) {\n const key = bedKey(target);\n const count = this.dartCounts.get(key) ?? 0;\n this.dartCounts.set(key, count + 1);\n\n const { centroid, midAngle, innerR, outerR } = this.bedGeometry(target);\n let cx: number;\n let cy: number;\n\n if (isBull(target.ring)) {\n // Distribute bull darts evenly around the ring at its midpoint radius.\n const midR = (innerR + outerR) / 2;\n const angle = (count * 2 * Math.PI) / DART_OFFSETS.length;\n cx = midR * Math.sin(angle);\n cy = -midR * Math.cos(angle);\n } else {\n const [radialFrac, tangentialFrac] = DART_OFFSETS[count % DART_OFFSETS.length];\n const thickness = outerR - innerR;\n const midRadius = (innerR + outerR) / 2;\n const segWidthRad = (this.board.segmentWidth * Math.PI) / 180;\n // Arc half-width at the midpoint radius — limits tangential offsets\n // so darts stay within the segment's angular bounds.\n const arcHalfWidth = midRadius * Math.sin(segWidthRad / 2);\n\n // Radial unit (pointing outward from center)\n const radX = Math.sin(midAngle);\n const radY = -Math.cos(midAngle);\n // Tangential unit (perpendicular, along the arc)\n const tanX = Math.cos(midAngle);\n const tanY = Math.sin(midAngle);\n\n cx = centroid[0] + radialFrac * thickness * radX + tangentialFrac * arcHalfWidth * tanX;\n cy = centroid[1] + radialFrac * thickness * radY + tangentialFrac * arcHalfWidth * tanY;\n }\n\n group\n .append('circle')\n .classed('c-Dartboard-dart', true)\n .attr('cx', cx)\n .attr('cy', cy)\n .attr('r', dartRadius)\n .attr('data-bed', key);\n }\n\n return this;\n }\n\n removeDarts(targets?: BedInput[]): this {\n if (!this.dartGroup) return this;\n\n if (!targets) {\n this.dartGroup.selectAll('.c-Dartboard-dart').remove();\n this.dartCounts.clear();\n return this;\n }\n\n const resolved = this.resolveTargets(targets);\n for (const target of resolved) {\n const key = bedKey(target);\n this.dartGroup\n .selectAll(`.c-Dartboard-dart[data-bed=\"${key}\"]`)\n .remove();\n this.dartCounts.delete(key);\n }\n\n return this;\n }\n\n labelBeds(\n labeler: (bed: BedLabelContext) => string | null | undefined,\n options: BedLabelOptions = {},\n ): this {\n if (!this.board || !this.sizes) return this;\n\n const extra = options.className;\n const counterRotation = -this.board.rotation;\n const group = this.ensureLabelGroup();\n\n // Default font size scales with the board; consumers can override per\n // <text> via the .c-Dartboard-label class since it's set on the group.\n group.attr('font-size', this.board.radius * LABEL_FONT_SCALE);\n\n // Full replace — wipe previous labels so repeated calls never accumulate.\n group.selectAll('*').remove();\n\n for (const ringKey of SCORING_RINGS) {\n const ring = this.rings[ringKey];\n if (!ring) continue;\n\n const beds: Bed[] = isBull(ringKey)\n ? [{ segment: 25, color: ringKey === 'DOUBLE_BULL' ? 'Dark' : 'Light', ring }]\n : addRingToSegments(ring, this.segments);\n\n // Per-ring (not per-bed): scoring-colored rings keep light text via CSS.\n const scoring = SCORING_COLOR_RINGS.has(ringKey) ? ' c-Dartboard-label--scoring' : '';\n\n for (const bed of beds) {\n // Same formula buildThrowDetail uses; avoids allocating a throwaway\n // ThrowDetail per bed on this (per-dart) re-label path.\n const score = bed.segment * bed.ring.multiplier;\n const text = labeler({ ...bed, score });\n if (text == null || text === '') continue;\n\n const [x, y] = this.labelPosition({ segment: bed.segment, ring: ringKey });\n const className = `c-Dartboard-label is${bed.color}${scoring}${extra ? ` ${extra}` : ''}`;\n\n group\n .append('text')\n .attr('class', className)\n .attr('data-bed', bedKey({ segment: bed.segment, ring: ringKey }))\n .attr('x', x)\n .attr('y', y)\n .attr('dy', '.35em')\n .attr('text-anchor', 'middle')\n // Counter-rotate the board's group rotation so labels read upright.\n .attr('transform', `rotate(${counterRotation}, ${x}, ${y})`)\n .text(text);\n }\n }\n\n // Keep dart markers above labels regardless of call order.\n this.dartGroup?.raise();\n\n return this;\n }\n\n clearBedLabels(): this {\n if (this.labelGroup) {\n this.labelGroup.remove();\n this.labelGroup = null;\n }\n return this;\n }\n\n private ensureDartGroup(): D3Sel {\n if (!this.dartGroup) {\n this.dartGroup = this.board!.svg\n .append('g')\n .classed('c-Dartboard-darts', true);\n }\n return this.dartGroup;\n }\n\n private ensureLabelGroup(): D3Sel {\n if (!this.labelGroup) {\n this.labelGroup = this.board!.svg\n .append('g')\n .classed('c-Dartboard-labels', true);\n }\n return this.labelGroup;\n }\n\n private ringRadii(ringKey: RingKey): [number, number] {\n const s = this.sizes!;\n const doubleBullOuter = s.doubleBull;\n const singleBullOuter = doubleBullOuter + s.singleBull;\n const innerSingleOuter = singleBullOuter + s.innerSingle;\n const tripleOuter = innerSingleOuter + s.triple;\n const outerSingleOuter = tripleOuter + s.outerSingle;\n const doubleOuter = outerSingleOuter + s.double;\n\n switch (ringKey) {\n case 'DOUBLE_BULL': return [0, doubleBullOuter];\n case 'SINGLE_BULL': return [doubleBullOuter, singleBullOuter];\n case 'INNER_SINGLE': return [singleBullOuter, innerSingleOuter];\n case 'TRIPLE': return [innerSingleOuter, tripleOuter];\n case 'OUTER_SINGLE': return [tripleOuter, outerSingleOuter];\n case 'DOUBLE': return [outerSingleOuter, doubleOuter];\n case 'MISS': return [this.board!.radius - s.miss, this.board!.radius];\n }\n }\n\n private bedGeometry(target: Required<BedTarget>): {\n centroid: [number, number];\n midAngle: number;\n innerR: number;\n outerR: number;\n } {\n const [innerR, outerR] = this.ringRadii(target.ring);\n\n if (target.segment === 25) {\n return { centroid: [0, 0], midAngle: 0, innerR, outerR };\n }\n\n const seg = this.segments.find((s) => s.segment === target.segment);\n const segWidthRad = (this.board!.segmentWidth * Math.PI) / 180;\n const midAngle = ((seg?.position ?? 1) - 0.5) * segWidthRad;\n const midRadius = (innerR + outerR) / 2;\n\n return {\n centroid: [\n midRadius * Math.sin(midAngle),\n -midRadius * Math.cos(midAngle),\n ],\n midAngle,\n innerR,\n outerR,\n };\n }\n\n // Where a bed's label sits. Reuses bedGeometry for the 80 segment beds and\n // the double bull (center). Single bull would also resolve to [0,0] and\n // collide with the double bull label, so it's pushed to the bottom of its\n // ring (6 o'clock) instead.\n private labelPosition(target: Required<BedTarget>): [number, number] {\n if (target.ring === 'SINGLE_BULL') {\n const [innerR, outerR] = this.ringRadii('SINGLE_BULL');\n return [0, (innerR + outerR) / 2];\n }\n return this.bedGeometry(target).centroid;\n }\n\n private isValidTarget(target: Required<BedTarget>): boolean {\n if (isBull(target.ring)) return target.segment === 25;\n return this.segments.some((s) => s.segment === target.segment);\n }\n\n private resolveTargets(inputs: BedInput[]): Required<BedTarget>[] {\n return inputs.flatMap((input): Required<BedTarget>[] => {\n if (typeof input === 'number') {\n return parseBed(String(input), this.rings, this.segments) as Required<BedTarget>[];\n }\n if (typeof input === 'string') {\n return parseBed(input, this.rings, this.segments) as Required<BedTarget>[];\n }\n if (!input.ring) {\n return parseBed(String(input.segment), this.rings, this.segments) as Required<BedTarget>[];\n }\n return [input as Required<BedTarget>];\n });\n }\n\n private dispatchThrow(bed: Bed): void {\n if (!this.board || this._disabled) return;\n const detail = buildThrowDetail(bed);\n this.board.container.dispatchEvent(\n new CustomEvent<ThrowDetail>('throw', { detail }),\n );\n }\n\n private dispatchHover(bed: Bed, hovering: boolean): void {\n if (!this.board || this._disabled) return;\n const throwDetail = buildThrowDetail(bed);\n const detail: HoverDetail = { ...throwDetail, hovering };\n this.board.container.dispatchEvent(\n new CustomEvent<HoverDetail>('hover', { detail }),\n );\n }\n\n private renderBeds(\n board: BoardState,\n ring: Ring,\n segments: Segment[],\n outerRadius: number,\n innerRadius: number,\n ): void {\n const className = `c-Dartboard-${ring.name}`;\n const arcFn = arc<PieArcDatum<Bed>>()\n .outerRadius(outerRadius)\n .innerRadius(innerRadius);\n\n const beds = board.svg\n .append('g')\n .classed(className, true)\n .selectAll<SVGGElement, PieArcDatum<Bed>>('arc')\n .data(board.pie(addRingToSegments(ring, segments)))\n .enter()\n .append('g')\n .attr(\n 'class',\n (d: PieArcDatum<Bed>) =>\n `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} ${className}--${d.data.segment} is${d.data.color}`,\n )\n .attr('role', 'button')\n .attr('tabindex', '0')\n .attr('aria-label', (d: PieArcDatum<Bed>) =>\n buildAriaLabel(d.data),\n )\n .on(\n 'pointerup',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n this.dispatchThrow(d.data);\n },\n )\n .on(\n 'pointerenter',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n select(_event.currentTarget as Element).classed('is-hovered', true);\n this.dispatchHover(d.data, true);\n },\n )\n .on(\n 'pointerleave',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n select(_event.currentTarget as Element).classed('is-hovered', false);\n this.dispatchHover(d.data, false);\n },\n )\n .on(\n 'keydown',\n (_event: KeyboardEvent, d: PieArcDatum<Bed>) => {\n if (_event.key === 'Enter' || _event.key === ' ') {\n _event.preventDefault();\n this.dispatchThrow(d.data);\n }\n },\n );\n\n beds.append('path').attr('d', arcFn as never);\n }\n\n private renderMissRing(\n board: BoardState,\n ring: Ring,\n segments: Segment[],\n outerRadius: number,\n innerRadius: number,\n ): void {\n const missArc = arc<PieArcDatum<Bed>>()\n .outerRadius(outerRadius)\n .innerRadius(innerRadius);\n\n const missBeds = board.svg\n .append('g')\n .classed('c-Dartboard-miss', true)\n .selectAll<SVGGElement, PieArcDatum<Bed>>('arc')\n .data(board.pie(addRingToSegments(ring, segments)))\n .enter()\n .append('g')\n .attr(\n 'class',\n (d: PieArcDatum<Bed>) =>\n `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} c-Dartboard-miss--${d.data.segment}`,\n )\n .attr('role', 'button')\n .attr('tabindex', '0')\n .attr('aria-label', (d: PieArcDatum<Bed>) =>\n buildAriaLabel(d.data),\n )\n .on(\n 'pointerup',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n this.dispatchThrow(d.data);\n },\n )\n .on(\n 'keydown',\n (_event: KeyboardEvent, d: PieArcDatum<Bed>) => {\n if (_event.key === 'Enter' || _event.key === ' ') {\n _event.preventDefault();\n this.dispatchThrow(d.data);\n }\n },\n );\n\n missBeds.append('path').attr('d', missArc as never);\n\n const determineRotation = (bedData: Bed) =>\n -board.rotation + board.segmentWidth * ((bedData.position ?? 0) - 1);\n\n const determineX = (d: PieArcDatum<Bed>) =>\n missArc.centroid(d)[0];\n\n const determineY = (d: PieArcDatum<Bed>) =>\n missArc.centroid(d)[1];\n\n missBeds\n .append('text')\n .classed('c-Dartboard-missLabel', true)\n .attr('x', (d: PieArcDatum<Bed>) => determineX(d))\n .attr('y', (d: PieArcDatum<Bed>) => determineY(d))\n .attr('dy', '.35em')\n .attr(\n 'transform',\n (d: PieArcDatum<Bed>) =>\n `rotate(${determineRotation(d.data)}, ${determineX(d)}, ${determineY(d)})`,\n )\n .attr('text-anchor', 'middle')\n .text((d: PieArcDatum<Bed>) => d.data.segment);\n }\n}\n","import type { DartboardOptions, Rings, Segment } from './types';\n\nexport const DEFAULT_OPTIONS: DartboardOptions = {\n size: null,\n padding: 2,\n missPercent: 10,\n doublePercent: 10,\n outerSinglePercent: 25,\n triplePercent: 10,\n innerSinglePercent: 30,\n singleBullPercent: 8,\n doubleBullPercent: 7,\n};\n\nexport const DEFAULT_RINGS: Rings = {\n MISS: { name: 'miss', abbr: 'M', multiplier: 0 },\n DOUBLE: { name: 'double', abbr: 'D', multiplier: 2 },\n OUTER_SINGLE: { name: 'outerSingle', abbr: 'S', multiplier: 1 },\n TRIPLE: { name: 'triple', abbr: 'T', multiplier: 3 },\n INNER_SINGLE: { name: 'innerSingle', abbr: 'S', multiplier: 1 },\n SINGLE_BULL: { name: 'singleBull', abbr: 'B', multiplier: 1 },\n DOUBLE_BULL: { name: 'doubleBull', abbr: 'DB', multiplier: 2 },\n};\n\nexport const MOBILE_OPTIONS: Partial<DartboardOptions> = {\n missPercent: 7,\n doublePercent: 14,\n outerSinglePercent: 21,\n triplePercent: 14,\n innerSinglePercent: 26,\n singleBullPercent: 9,\n doubleBullPercent: 9,\n};\n\nexport const DEFAULT_SEGMENTS: Segment[] = [\n { segment: 20, position: 1, color: 'Dark' },\n { segment: 1, position: 2, color: 'Light' },\n { segment: 18, position: 3, color: 'Dark' },\n { segment: 4, position: 4, color: 'Light' },\n { segment: 13, position: 5, color: 'Dark' },\n { segment: 6, position: 6, color: 'Light' },\n { segment: 10, position: 7, color: 'Dark' },\n { segment: 15, position: 8, color: 'Light' },\n { segment: 2, position: 9, color: 'Dark' },\n { segment: 17, position: 10, color: 'Light' },\n { segment: 3, position: 11, color: 'Dark' },\n { segment: 19, position: 12, color: 'Light' },\n { segment: 7, position: 13, color: 'Dark' },\n { segment: 16, position: 14, color: 'Light' },\n { segment: 8, position: 15, color: 'Dark' },\n { segment: 11, position: 16, color: 'Light' },\n { segment: 14, position: 17, color: 'Dark' },\n { segment: 9, position: 18, color: 'Light' },\n { segment: 12, position: 19, color: 'Dark' },\n { segment: 5, position: 20, color: 'Light' },\n];\n","import type { Ring, Rings, Segment, Bed, BedTarget, ThrowDetail } from './types';\n\nexport const SCORING_RINGS: (keyof Rings)[] = [\n 'DOUBLE',\n 'TRIPLE',\n 'OUTER_SINGLE',\n 'INNER_SINGLE',\n 'SINGLE_BULL',\n 'DOUBLE_BULL',\n];\n\nexport function buildThrowDetail(bed: Bed): ThrowDetail {\n return {\n bed: bed.ring.abbr + bed.segment,\n segment: bed.segment,\n ring: bed.ring.name,\n score: bed.segment * bed.ring.multiplier,\n };\n}\n\nexport function asPercent(n: number): number {\n return parseFloat((n / 100).toFixed(2));\n}\n\nexport function addRingToSegments(ring: Ring, segments: Segment[]): Bed[] {\n return segments.map((segment) => ({ ...segment, ring }));\n}\n\nexport function parseBed(\n bed: string,\n rings: Rings,\n segments?: Segment[],\n): BedTarget[] {\n // 'miss' keyword — target all segments in the miss ring\n if (bed.toLowerCase() === 'miss') {\n if (!segments) {\n throw new Error(\n 'parseBed: segments are required to resolve the \"miss\" keyword',\n );\n }\n return segments.map((s) => ({ segment: s.segment, ring: 'MISS' as keyof Rings }));\n }\n\n // Number-only string — target all scoring rings on that segment\n const numOnly = bed.match(/^(\\d+)$/);\n if (numOnly) {\n const segment = parseInt(numOnly[1], 10);\n return SCORING_RINGS\n .filter((key) => key in rings)\n .map((ring) => ({ segment, ring }));\n }\n\n const match = bed.match(/^([A-Za-z]+)(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid bed format: \"${bed}\"`);\n }\n\n const [, abbr, numStr] = match.map((s, i) => i === 1 ? s.toUpperCase() : s);\n const segment = parseInt(numStr, 10);\n\n const targets: BedTarget[] = [];\n for (const [key, ring] of Object.entries(rings)) {\n if (ring.abbr === abbr) {\n targets.push({ segment, ring: key as keyof Rings });\n }\n }\n\n if (targets.length === 0) {\n throw new Error(`Unknown bed abbreviation: \"${abbr}\"`);\n }\n\n return targets;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,0BAAuC;AACvC,sBAA2C;;;ACCpC,IAAM,kBAAoC;AAAA,EAC/C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AAAA,EACb,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AACrB;AAEO,IAAM,gBAAuB;AAAA,EAClC,MAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,YAAY,EAAE;AAAA,EAC/C,QAAQ,EAAE,MAAM,UAAU,MAAM,KAAK,YAAY,EAAE;AAAA,EACnD,cAAc,EAAE,MAAM,eAAe,MAAM,KAAK,YAAY,EAAE;AAAA,EAC9D,QAAQ,EAAE,MAAM,UAAU,MAAM,KAAK,YAAY,EAAE;AAAA,EACnD,cAAc,EAAE,MAAM,eAAe,MAAM,KAAK,YAAY,EAAE;AAAA,EAC9D,aAAa,EAAE,MAAM,cAAc,MAAM,KAAK,YAAY,EAAE;AAAA,EAC5D,aAAa,EAAE,MAAM,cAAc,MAAM,MAAM,YAAY,EAAE;AAC/D;AAEO,IAAM,iBAA4C;AAAA,EACvD,aAAa;AAAA,EACb,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AACrB;AAEO,IAAM,mBAA8B;AAAA,EACzC,EAAE,SAAS,IAAI,UAAU,GAAG,OAAO,OAAO;AAAA,EAC1C,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,QAAQ;AAAA,EAC1C,EAAE,SAAS,IAAI,UAAU,GAAG,OAAO,OAAO;AAAA,EAC1C,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,QAAQ;AAAA,EAC1C,EAAE,SAAS,IAAI,UAAU,GAAG,OAAO,OAAO;AAAA,EAC1C,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,QAAQ;AAAA,EAC1C,EAAE,SAAS,IAAI,UAAU,GAAG,OAAO,OAAO;AAAA,EAC1C,EAAE,SAAS,IAAI,UAAU,GAAG,OAAO,QAAQ;AAAA,EAC3C,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,OAAO;AAAA,EACzC,EAAE,SAAS,IAAI,UAAU,IAAI,OAAO,QAAQ;AAAA,EAC5C,EAAE,SAAS,GAAG,UAAU,IAAI,OAAO,OAAO;AAAA,EAC1C,EAAE,SAAS,IAAI,UAAU,IAAI,OAAO,QAAQ;AAAA,EAC5C,EAAE,SAAS,GAAG,UAAU,IAAI,OAAO,OAAO;AAAA,EAC1C,EAAE,SAAS,IAAI,UAAU,IAAI,OAAO,QAAQ;AAAA,EAC5C,EAAE,SAAS,GAAG,UAAU,IAAI,OAAO,OAAO;AAAA,EAC1C,EAAE,SAAS,IAAI,UAAU,IAAI,OAAO,QAAQ;AAAA,EAC5C,EAAE,SAAS,IAAI,UAAU,IAAI,OAAO,OAAO;AAAA,EAC3C,EAAE,SAAS,GAAG,UAAU,IAAI,OAAO,QAAQ;AAAA,EAC3C,EAAE,SAAS,IAAI,UAAU,IAAI,OAAO,OAAO;AAAA,EAC3C,EAAE,SAAS,GAAG,UAAU,IAAI,OAAO,QAAQ;AAC7C;;;ACrDO,IAAM,gBAAiC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,iBAAiB,KAAuB;AACtD,SAAO;AAAA,IACL,KAAK,IAAI,KAAK,OAAO,IAAI;AAAA,IACzB,SAAS,IAAI;AAAA,IACb,MAAM,IAAI,KAAK;AAAA,IACf,OAAO,IAAI,UAAU,IAAI,KAAK;AAAA,EAChC;AACF;AAEO,SAAS,UAAU,GAAmB;AAC3C,SAAO,YAAY,IAAI,KAAK,QAAQ,CAAC,CAAC;AACxC;AAEO,SAAS,kBAAkB,MAAY,UAA4B;AACxE,SAAO,SAAS,IAAI,CAAC,aAAa,EAAE,GAAG,SAAS,KAAK,EAAE;AACzD;AAEO,SAAS,SACd,KACA,OACA,UACa;AAEb,MAAI,IAAI,YAAY,MAAM,QAAQ;AAChC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,MAAM,OAAsB,EAAE;AAAA,EAClF;AAGA,QAAM,UAAU,IAAI,MAAM,SAAS;AACnC,MAAI,SAAS;AACX,UAAMA,WAAU,SAAS,QAAQ,CAAC,GAAG,EAAE;AACvC,WAAO,cACJ,OAAO,CAAC,QAAQ,OAAO,KAAK,EAC5B,IAAI,CAAC,UAAU,EAAE,SAAAA,UAAS,KAAK,EAAE;AAAA,EACtC;AAEA,QAAM,QAAQ,IAAI,MAAM,oBAAoB;AAC5C,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,wBAAwB,GAAG,GAAG;AAAA,EAChD;AAEA,QAAM,CAAC,EAAE,MAAM,MAAM,IAAI,MAAM,IAAI,CAAC,GAAG,MAAM,MAAM,IAAI,EAAE,YAAY,IAAI,CAAC;AAC1E,QAAM,UAAU,SAAS,QAAQ,EAAE;AAEnC,QAAM,UAAuB,CAAC;AAC9B,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC/C,QAAI,KAAK,SAAS,MAAM;AACtB,cAAQ,KAAK,EAAE,SAAS,MAAM,IAAmB,CAAC;AAAA,IACpD;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI,MAAM,8BAA8B,IAAI,GAAG;AAAA,EACvD;AAEA,SAAO;AACT;;;AFvBA,SAAS,eAAe,KAAkB;AACxC,QAAM,SAAS,iBAAiB,GAAG;AACnC,SAAO,GAAG,OAAO,GAAG,YAAY,OAAO,KAAK;AAC9C;AAEA,SAAS,OAAO,SAA2B;AACzC,SAAO,YAAY,iBAAiB,YAAY;AAClD;AAEA,SAAS,OAAO,QAAqC;AACnD,SAAO,GAAG,OAAO,IAAI,IAAI,OAAO,OAAO;AACzC;AAKA,IAAM,eAAmC;AAAA,EACvC,CAAC,GAAG,CAAC;AAAA,EACL,CAAC,OAAO,KAAK;AAAA,EACb,CAAC,MAAM,IAAI;AAAA,EACX,CAAC,OAAO,IAAI;AAAA,EACZ,CAAC,MAAM,KAAK;AACd;AAMA,IAAM,sBAA4C,oBAAI,IAAa;AAAA,EACjE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAID,IAAM,mBAAmB;AAElB,IAAM,YAAN,MAAgB;AAAA,EAYrB,YACE,WACA,UAAqC,CAAC,GACtC,WAAsB,kBACtB,QAAe,eACf;AAhBF,SAAQ,QAA2B;AACnC,SAAQ,QAAwB;AAChC,SAAQ,WAAW;AACnB,SAAQ,YAAY;AACpB,SAAQ,YAA0B;AAClC,SAAQ,aAA2B;AACnC,SAAQ,aAAa,oBAAI,IAAoB;AAW3C,SAAK,UAAU,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AAChD,SAAK,WAAW;AAChB,SAAK,QAAQ;AAEb,UAAM,aACJ,KAAK,QAAQ,cACb,KAAK,QAAQ,gBACb,KAAK,QAAQ,qBACb,KAAK,QAAQ,gBACb,KAAK,QAAQ,qBACb,KAAK,QAAQ,oBACb,KAAK,QAAQ;AAEf,QAAI,eAAe,KAAK;AACtB,YAAM,IAAI;AAAA,QACR,oDAAoD,UAAU;AAAA,MAChE;AAAA,IACF;AAEA,UAAM,iBACJ,OAAO,cAAc,WACjB,SAAS,cAA2B,SAAS,IAC7C;AAEN,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AAEA,UAAM,UAAU,KAAK,QAAQ;AAC7B,UAAM,YACJ,KAAK,QAAQ,QACb,KAAK,IAAI,eAAe,cAAc,eAAe,WAAW;AAClE,UAAM,QAAQ,YAAY,UAAU;AACpC,UAAM,cAAc;AACpB,UAAM,eAAe,MAAM,SAAS;AACpC,UAAM,SAAS,QAAQ;AACvB,UAAM,WAAW,eAAe;AAEhC,UAAM,cAAU,4BAAO,cAAc,EAClC,OAAO,KAAK,EACZ,QAAQ,eAAe,IAAI;AAE9B,UAAM,MAAM,QACT,OAAO,KAAK,EACZ,KAAK,WAAW,OAAO,WAAW,IAAI,WAAW,EAAE,EACnD,KAAK,QAAQ,OAAO,EACpB,KAAK,cAAc,WAAW,EAC9B,OAAO,GAAG,EACV;AAAA,MACC;AAAA,MACA,aAAa,cAAc,CAAC,KAAK,cAAc,CAAC,YAAY,QAAQ;AAAA,IACtE;AAEF,UAAM,YAAQ,qBAAS,EACpB,KAAK,CAAC,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE,EACpD,MAAM,MAAM,YAAY;AAE3B,SAAK,QAAQ;AAAA,MACX,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK;AAAA,IACP;AAEA,SAAK,QAAQ;AAAA,MACX,MAAM,SAAS,UAAU,KAAK,QAAQ,WAAW;AAAA,MACjD,QAAQ,SAAS,UAAU,KAAK,QAAQ,aAAa;AAAA,MACrD,aAAa,SAAS,UAAU,KAAK,QAAQ,kBAAkB;AAAA,MAC/D,QAAQ,SAAS,UAAU,KAAK,QAAQ,aAAa;AAAA,MACrD,aAAa,SAAS,UAAU,KAAK,QAAQ,kBAAkB;AAAA,MAC/D,YAAY,SAAS,UAAU,KAAK,QAAQ,iBAAiB;AAAA,MAC7D,YAAY,SAAS,UAAU,KAAK,QAAQ,iBAAiB;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,SAAe;AACb,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,MAAO,QAAO;AACvC,QAAI,KAAK,SAAU,QAAO;AAC1B,SAAK,WAAW;AAEhB,UAAM,EAAE,OAAO,MAAM,IAAI;AAEzB,QAAI,cAAc;AAClB,QAAI,cAAc,cAAc,MAAM;AACtC,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM;AAAA,MACX,CAAC,EAAE,SAAS,IAAI,OAAO,OAAO,CAAC;AAAA,MAC/B;AAAA,MACA;AAAA,IACF;AAEA,kBAAc;AACd,kBAAc,cAAc,MAAM;AAClC,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM;AAAA,MACX,CAAC,EAAE,SAAS,IAAI,OAAO,QAAQ,CAAC;AAAA,MAChC;AAAA,MACA;AAAA,IACF;AAEA,kBAAc;AACd,kBAAc,cAAc,MAAM;AAClC,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM;AAAA,MACX,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAEA,kBAAc;AACd,kBAAc,cAAc,MAAM;AAClC,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM;AAAA,MACX,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAEA,kBAAc;AACd,kBAAc,cAAc,MAAM;AAClC,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM;AAAA,MACX,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAEA,kBAAc;AACd,kBAAc,cAAc,MAAM;AAClC,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM;AAAA,MACX,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAEA,kBAAc,MAAM,SAAS,MAAM;AACnC,kBAAc,MAAM;AACpB,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM;AAAA,MACX,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,OAAO;AAEd,WAAK,MAAM,QAAQ,UAAU,kBAAkB,EAC5C,GAAG,aAAa,IAAI,EACpB,GAAG,gBAAgB,IAAI,EACvB,GAAG,gBAAgB,IAAI,EACvB,GAAG,WAAW,IAAI;AACrB,WAAK,MAAM,QAAQ,OAAO;AAC1B,WAAK,QAAQ;AACb,WAAK,QAAQ;AACb,WAAK,WAAW;AAChB,WAAK,YAAY;AACjB,WAAK,aAAa;AAClB,WAAK,WAAW,MAAM;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,SAAK,YAAY;AACjB,QAAI,KAAK,OAAO;AACd,WAAK,MAAM,QAAQ,QAAQ,eAAe,IAAI;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,SAAe;AACb,SAAK,YAAY;AACjB,QAAI,KAAK,OAAO;AACd,WAAK,MAAM,QAAQ,QAAQ,eAAe,KAAK;AAAA,IACjD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,QAAwB;AAC9B,QAAI,CAAC,KAAK,SAAS,KAAK,UAAW;AAEnC,UAAM,UAAU,KAAK,eAAe,CAAC,MAAM,CAAC;AAC5C,QAAI,QAAQ,WAAW,EAAG;AAC1B,UAAM,QAAQ,QAAQ,CAAC;AAEvB,QAAI,CAAC,KAAK,cAAc,KAAK,GAAG;AAC9B,YAAM,IAAI,MAAM,kDAA6C,MAAM,OAAO,iBAAiB;AAAA,IAC7F;AAEA,UAAM,OAAO,KAAK,MAAM,MAAM,IAAI;AAClC,UAAM,MAAW,EAAE,SAAS,MAAM,SAAS,OAAO,QAAQ,KAAK;AAC/D,UAAM,SAAS,iBAAiB,GAAG;AAEnC,SAAK,MAAM,UAAU;AAAA,MACnB,IAAI,YAAyB,SAAS,EAAE,OAAO,CAAC;AAAA,IAClD;AAAA,EACF;AAAA,EAEA,UAAU,SAAqB,UAA4B,CAAC,GAAS;AACnE,QAAI,CAAC,KAAK,MAAO;AACjB,UAAM,YAAY,QAAQ,aAAa;AAEvC,eAAW,UAAU,KAAK,eAAe,OAAO,GAAG;AACjD,YAAM,OAAO,KAAK,MAAM,OAAO,IAAI;AACnC,YAAM,WAAW,gBAAgB,KAAK,IAAI,sBAAsB,OAAO,OAAO;AAC9E,WAAK,MAAM,QAAQ,UAAU,QAAQ,EAAE,QAAQ,WAAW,IAAI;AAAA,IAChE;AAAA,EACF;AAAA,EAEA,YAAY,SAAqB,UAA4B,CAAC,GAAS;AACrE,QAAI,CAAC,KAAK,MAAO;AACjB,UAAM,YAAY,QAAQ,aAAa;AAEvC,eAAW,UAAU,KAAK,eAAe,OAAO,GAAG;AACjD,YAAM,OAAO,KAAK,MAAM,OAAO,IAAI;AACnC,YAAM,WAAW,gBAAgB,KAAK,IAAI,sBAAsB,OAAO,OAAO;AAC9E,WAAK,MAAM,QAAQ,UAAU,QAAQ,EAAE,QAAQ,WAAW,KAAK;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,kBAAwB;AACxC,QAAI,CAAC,KAAK,MAAO;AACjB,SAAK,MAAM,QAAQ,UAAU,IAAI,SAAS,EAAE,EAAE,QAAQ,WAAW,KAAK;AAAA,EACxE;AAAA,EAEA,QAAQ,SAA2B;AACjC,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,MAAO,QAAO;AAEvC,UAAM,WAAW,KAAK,eAAe,OAAO;AAC5C,UAAM,QAAQ,KAAK,gBAAgB;AACnC,UAAM,aAAa,KAAK,MAAM,aAAa;AAE3C,eAAW,UAAU,UAAU;AAC7B,YAAM,MAAM,OAAO,MAAM;AACzB,YAAM,QAAQ,KAAK,WAAW,IAAI,GAAG,KAAK;AAC1C,WAAK,WAAW,IAAI,KAAK,QAAQ,CAAC;AAElC,YAAM,EAAE,UAAU,UAAU,QAAQ,OAAO,IAAI,KAAK,YAAY,MAAM;AACtE,UAAI;AACJ,UAAI;AAEJ,UAAI,OAAO,OAAO,IAAI,GAAG;AAEvB,cAAM,QAAQ,SAAS,UAAU;AACjC,cAAM,QAAS,QAAQ,IAAI,KAAK,KAAM,aAAa;AACnD,aAAK,OAAO,KAAK,IAAI,KAAK;AAC1B,aAAK,CAAC,OAAO,KAAK,IAAI,KAAK;AAAA,MAC7B,OAAO;AACL,cAAM,CAAC,YAAY,cAAc,IAAI,aAAa,QAAQ,aAAa,MAAM;AAC7E,cAAM,YAAY,SAAS;AAC3B,cAAM,aAAa,SAAS,UAAU;AACtC,cAAM,cAAe,KAAK,MAAM,eAAe,KAAK,KAAM;AAG1D,cAAM,eAAe,YAAY,KAAK,IAAI,cAAc,CAAC;AAGzD,cAAM,OAAO,KAAK,IAAI,QAAQ;AAC9B,cAAM,OAAO,CAAC,KAAK,IAAI,QAAQ;AAE/B,cAAM,OAAO,KAAK,IAAI,QAAQ;AAC9B,cAAM,OAAO,KAAK,IAAI,QAAQ;AAE9B,aAAK,SAAS,CAAC,IAAI,aAAa,YAAY,OAAO,iBAAiB,eAAe;AACnF,aAAK,SAAS,CAAC,IAAI,aAAa,YAAY,OAAO,iBAAiB,eAAe;AAAA,MACrF;AAEA,YACG,OAAO,QAAQ,EACf,QAAQ,oBAAoB,IAAI,EAChC,KAAK,MAAM,EAAE,EACb,KAAK,MAAM,EAAE,EACb,KAAK,KAAK,UAAU,EACpB,KAAK,YAAY,GAAG;AAAA,IACzB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,SAA4B;AACtC,QAAI,CAAC,KAAK,UAAW,QAAO;AAE5B,QAAI,CAAC,SAAS;AACZ,WAAK,UAAU,UAAU,mBAAmB,EAAE,OAAO;AACrD,WAAK,WAAW,MAAM;AACtB,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,KAAK,eAAe,OAAO;AAC5C,eAAW,UAAU,UAAU;AAC7B,YAAM,MAAM,OAAO,MAAM;AACzB,WAAK,UACF,UAAU,+BAA+B,GAAG,IAAI,EAChD,OAAO;AACV,WAAK,WAAW,OAAO,GAAG;AAAA,IAC5B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,UACE,SACA,UAA2B,CAAC,GACtB;AACN,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,MAAO,QAAO;AAEvC,UAAM,QAAQ,QAAQ;AACtB,UAAM,kBAAkB,CAAC,KAAK,MAAM;AACpC,UAAM,QAAQ,KAAK,iBAAiB;AAIpC,UAAM,KAAK,aAAa,KAAK,MAAM,SAAS,gBAAgB;AAG5D,UAAM,UAAU,GAAG,EAAE,OAAO;AAE5B,eAAW,WAAW,eAAe;AACnC,YAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,UAAI,CAAC,KAAM;AAEX,YAAM,OAAc,OAAO,OAAO,IAC9B,CAAC,EAAE,SAAS,IAAI,OAAO,YAAY,gBAAgB,SAAS,SAAS,KAAK,CAAC,IAC3E,kBAAkB,MAAM,KAAK,QAAQ;AAGzC,YAAM,UAAU,oBAAoB,IAAI,OAAO,IAAI,gCAAgC;AAEnF,iBAAW,OAAO,MAAM;AAGtB,cAAM,QAAQ,IAAI,UAAU,IAAI,KAAK;AACrC,cAAM,OAAO,QAAQ,EAAE,GAAG,KAAK,MAAM,CAAC;AACtC,YAAI,QAAQ,QAAQ,SAAS,GAAI;AAEjC,cAAM,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,SAAS,IAAI,SAAS,MAAM,QAAQ,CAAC;AACzE,cAAM,YAAY,uBAAuB,IAAI,KAAK,GAAG,OAAO,GAAG,QAAQ,IAAI,KAAK,KAAK,EAAE;AAEvF,cACG,OAAO,MAAM,EACb,KAAK,SAAS,SAAS,EACvB,KAAK,YAAY,OAAO,EAAE,SAAS,IAAI,SAAS,MAAM,QAAQ,CAAC,CAAC,EAChE,KAAK,KAAK,CAAC,EACX,KAAK,KAAK,CAAC,EACX,KAAK,MAAM,OAAO,EAClB,KAAK,eAAe,QAAQ,EAE5B,KAAK,aAAa,UAAU,eAAe,KAAK,CAAC,KAAK,CAAC,GAAG,EAC1D,KAAK,IAAI;AAAA,MACd;AAAA,IACF;AAGA,SAAK,WAAW,MAAM;AAEtB,WAAO;AAAA,EACT;AAAA,EAEA,iBAAuB;AACrB,QAAI,KAAK,YAAY;AACnB,WAAK,WAAW,OAAO;AACvB,WAAK,aAAa;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAyB;AAC/B,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,KAAK,MAAO,IAC1B,OAAO,GAAG,EACV,QAAQ,qBAAqB,IAAI;AAAA,IACtC;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,mBAA0B;AAChC,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,aAAa,KAAK,MAAO,IAC3B,OAAO,GAAG,EACV,QAAQ,sBAAsB,IAAI;AAAA,IACvC;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,UAAU,SAAoC;AACpD,UAAM,IAAI,KAAK;AACf,UAAM,kBAAkB,EAAE;AAC1B,UAAM,kBAAkB,kBAAkB,EAAE;AAC5C,UAAM,mBAAmB,kBAAkB,EAAE;AAC7C,UAAM,cAAc,mBAAmB,EAAE;AACzC,UAAM,mBAAmB,cAAc,EAAE;AACzC,UAAM,cAAc,mBAAmB,EAAE;AAEzC,YAAQ,SAAS;AAAA,MACf,KAAK;AAAe,eAAO,CAAC,GAAG,eAAe;AAAA,MAC9C,KAAK;AAAe,eAAO,CAAC,iBAAiB,eAAe;AAAA,MAC5D,KAAK;AAAgB,eAAO,CAAC,iBAAiB,gBAAgB;AAAA,MAC9D,KAAK;AAAU,eAAO,CAAC,kBAAkB,WAAW;AAAA,MACpD,KAAK;AAAgB,eAAO,CAAC,aAAa,gBAAgB;AAAA,MAC1D,KAAK;AAAU,eAAO,CAAC,kBAAkB,WAAW;AAAA,MACpD,KAAK;AAAQ,eAAO,CAAC,KAAK,MAAO,SAAS,EAAE,MAAM,KAAK,MAAO,MAAM;AAAA,IACtE;AAAA,EACF;AAAA,EAEQ,YAAY,QAKlB;AACA,UAAM,CAAC,QAAQ,MAAM,IAAI,KAAK,UAAU,OAAO,IAAI;AAEnD,QAAI,OAAO,YAAY,IAAI;AACzB,aAAO,EAAE,UAAU,CAAC,GAAG,CAAC,GAAG,UAAU,GAAG,QAAQ,OAAO;AAAA,IACzD;AAEA,UAAM,MAAM,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,YAAY,OAAO,OAAO;AAClE,UAAM,cAAe,KAAK,MAAO,eAAe,KAAK,KAAM;AAC3D,UAAM,aAAa,KAAK,YAAY,KAAK,OAAO;AAChD,UAAM,aAAa,SAAS,UAAU;AAEtC,WAAO;AAAA,MACL,UAAU;AAAA,QACR,YAAY,KAAK,IAAI,QAAQ;AAAA,QAC7B,CAAC,YAAY,KAAK,IAAI,QAAQ;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAc,QAA+C;AACnE,QAAI,OAAO,SAAS,eAAe;AACjC,YAAM,CAAC,QAAQ,MAAM,IAAI,KAAK,UAAU,aAAa;AACrD,aAAO,CAAC,IAAI,SAAS,UAAU,CAAC;AAAA,IAClC;AACA,WAAO,KAAK,YAAY,MAAM,EAAE;AAAA,EAClC;AAAA,EAEQ,cAAc,QAAsC;AAC1D,QAAI,OAAO,OAAO,IAAI,EAAG,QAAO,OAAO,YAAY;AACnD,WAAO,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,YAAY,OAAO,OAAO;AAAA,EAC/D;AAAA,EAEQ,eAAe,QAA2C;AAChE,WAAO,OAAO,QAAQ,CAAC,UAAiC;AACtD,UAAI,OAAO,UAAU,UAAU;AAC7B,eAAO,SAAS,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,QAAQ;AAAA,MAC1D;AACA,UAAI,OAAO,UAAU,UAAU;AAC7B,eAAO,SAAS,OAAO,KAAK,OAAO,KAAK,QAAQ;AAAA,MAClD;AACA,UAAI,CAAC,MAAM,MAAM;AACf,eAAO,SAAS,OAAO,MAAM,OAAO,GAAG,KAAK,OAAO,KAAK,QAAQ;AAAA,MAClE;AACA,aAAO,CAAC,KAA4B;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEQ,cAAc,KAAgB;AACpC,QAAI,CAAC,KAAK,SAAS,KAAK,UAAW;AACnC,UAAM,SAAS,iBAAiB,GAAG;AACnC,SAAK,MAAM,UAAU;AAAA,MACnB,IAAI,YAAyB,SAAS,EAAE,OAAO,CAAC;AAAA,IAClD;AAAA,EACF;AAAA,EAEQ,cAAc,KAAU,UAAyB;AACvD,QAAI,CAAC,KAAK,SAAS,KAAK,UAAW;AACnC,UAAM,cAAc,iBAAiB,GAAG;AACxC,UAAM,SAAsB,EAAE,GAAG,aAAa,SAAS;AACvD,SAAK,MAAM,UAAU;AAAA,MACnB,IAAI,YAAyB,SAAS,EAAE,OAAO,CAAC;AAAA,IAClD;AAAA,EACF;AAAA,EAEQ,WACN,OACA,MACA,UACA,aACA,aACM;AACN,UAAM,YAAY,eAAe,KAAK,IAAI;AAC1C,UAAM,YAAQ,qBAAsB,EACjC,YAAY,WAAW,EACvB,YAAY,WAAW;AAE1B,UAAM,OAAO,MAAM,IAChB,OAAO,GAAG,EACV,QAAQ,WAAW,IAAI,EACvB,UAAyC,KAAK,EAC9C,KAAK,MAAM,IAAI,kBAAkB,MAAM,QAAQ,CAAC,CAAC,EACjD,MAAM,EACN,OAAO,GAAG,EACV;AAAA,MACC;AAAA,MACA,CAAC,MACC,oCAAoC,EAAE,KAAK,OAAO,IAAI,SAAS,KAAK,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,KAAK;AAAA,IACxG,EACC,KAAK,QAAQ,QAAQ,EACrB,KAAK,YAAY,GAAG,EACpB;AAAA,MAAK;AAAA,MAAc,CAAC,MACnB,eAAe,EAAE,IAAI;AAAA,IACvB,EACC;AAAA,MACC;AAAA,MACA,CAAC,QAAsB,MAAwB;AAC7C,aAAK,cAAc,EAAE,IAAI;AAAA,MAC3B;AAAA,IACF,EACC;AAAA,MACC;AAAA,MACA,CAAC,QAAsB,MAAwB;AAC7C,wCAAO,OAAO,aAAwB,EAAE,QAAQ,cAAc,IAAI;AAClE,aAAK,cAAc,EAAE,MAAM,IAAI;AAAA,MACjC;AAAA,IACF,EACC;AAAA,MACC;AAAA,MACA,CAAC,QAAsB,MAAwB;AAC7C,wCAAO,OAAO,aAAwB,EAAE,QAAQ,cAAc,KAAK;AACnE,aAAK,cAAc,EAAE,MAAM,KAAK;AAAA,MAClC;AAAA,IACF,EACC;AAAA,MACC;AAAA,MACA,CAAC,QAAuB,MAAwB;AAC9C,YAAI,OAAO,QAAQ,WAAW,OAAO,QAAQ,KAAK;AAChD,iBAAO,eAAe;AACtB,eAAK,cAAc,EAAE,IAAI;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAEF,SAAK,OAAO,MAAM,EAAE,KAAK,KAAK,KAAc;AAAA,EAC9C;AAAA,EAEQ,eACN,OACA,MACA,UACA,aACA,aACM;AACN,UAAM,cAAU,qBAAsB,EACnC,YAAY,WAAW,EACvB,YAAY,WAAW;AAE1B,UAAM,WAAW,MAAM,IACpB,OAAO,GAAG,EACV,QAAQ,oBAAoB,IAAI,EAChC,UAAyC,KAAK,EAC9C,KAAK,MAAM,IAAI,kBAAkB,MAAM,QAAQ,CAAC,CAAC,EACjD,MAAM,EACN,OAAO,GAAG,EACV;AAAA,MACC;AAAA,MACA,CAAC,MACC,oCAAoC,EAAE,KAAK,OAAO,sBAAsB,EAAE,KAAK,OAAO;AAAA,IAC1F,EACC,KAAK,QAAQ,QAAQ,EACrB,KAAK,YAAY,GAAG,EACpB;AAAA,MAAK;AAAA,MAAc,CAAC,MACnB,eAAe,EAAE,IAAI;AAAA,IACvB,EACC;AAAA,MACC;AAAA,MACA,CAAC,QAAsB,MAAwB;AAC7C,aAAK,cAAc,EAAE,IAAI;AAAA,MAC3B;AAAA,IACF,EACC;AAAA,MACC;AAAA,MACA,CAAC,QAAuB,MAAwB;AAC9C,YAAI,OAAO,QAAQ,WAAW,OAAO,QAAQ,KAAK;AAChD,iBAAO,eAAe;AACtB,eAAK,cAAc,EAAE,IAAI;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAEF,aAAS,OAAO,MAAM,EAAE,KAAK,KAAK,OAAgB;AAElD,UAAM,oBAAoB,CAAC,YACzB,CAAC,MAAM,WAAW,MAAM,iBAAiB,QAAQ,YAAY,KAAK;AAEpE,UAAM,aAAa,CAAC,MAClB,QAAQ,SAAS,CAAC,EAAE,CAAC;AAEvB,UAAM,aAAa,CAAC,MAClB,QAAQ,SAAS,CAAC,EAAE,CAAC;AAEvB,aACG,OAAO,MAAM,EACb,QAAQ,yBAAyB,IAAI,EACrC,KAAK,KAAK,CAAC,MAAwB,WAAW,CAAC,CAAC,EAChD,KAAK,KAAK,CAAC,MAAwB,WAAW,CAAC,CAAC,EAChD,KAAK,MAAM,OAAO,EAClB;AAAA,MACC;AAAA,MACA,CAAC,MACC,UAAU,kBAAkB,EAAE,IAAI,CAAC,KAAK,WAAW,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC;AAAA,IAC3E,EACC,KAAK,eAAe,QAAQ,EAC5B,KAAK,CAAC,MAAwB,EAAE,KAAK,OAAO;AAAA,EACjD;AACF;","names":["segment"]}
package/dist/index.css CHANGED
@@ -15,6 +15,9 @@
15
15
  --dartboard-neutral: #4a6fa5;
16
16
  --dartboard-dart-fill: #ff8c00;
17
17
  --dartboard-dart-stroke: #b35900;
18
+ --dartboard-label-on-dark: #fff;
19
+ --dartboard-label-on-light: #111;
20
+ --dartboard-label-halo: rgba(0, 0, 0, 0.6);
18
21
  stroke: var(--dartboard-stroke);
19
22
  stroke-width: var(--dartboard-stroke-width);
20
23
  touch-action: none;
@@ -101,6 +104,21 @@
101
104
  stroke-width: 1.5px;
102
105
  pointer-events: none;
103
106
  }
107
+ .c-Dartboard-labels {
108
+ pointer-events: none;
109
+ }
110
+ .c-Dartboard-label {
111
+ font-family: sans-serif;
112
+ fill: var(--dartboard-label-on-dark);
113
+ stroke: var(--dartboard-label-halo);
114
+ stroke-width: 2.5px;
115
+ stroke-linejoin: round;
116
+ paint-order: stroke;
117
+ }
118
+ .c-Dartboard-label.isLight:not(.c-Dartboard-label--scoring) {
119
+ fill: var(--dartboard-label-on-light);
120
+ stroke-width: 0;
121
+ }
104
122
  .c-Dartboard.is-disabled {
105
123
  pointer-events: none;
106
124
  opacity: 0.5;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dartboard.css"],"sourcesContent":[".c-Dartboard {\n --dartboard-dark: #000;\n --dartboard-light: #f5f5dc;\n --dartboard-scoring-dark: #e32636;\n --dartboard-scoring-light: #228b22;\n --dartboard-miss: #000;\n --dartboard-miss-label: #fff;\n --dartboard-stroke: silver;\n --dartboard-stroke-width: 2px;\n --dartboard-highlight-color: #ffd700;\n --dartboard-dark-hover: #444;\n --dartboard-good: #228b22;\n --dartboard-bad: #e32636;\n --dartboard-neutral: #4a6fa5;\n --dartboard-dart-fill: #ff8c00;\n --dartboard-dart-stroke: #b35900;\n\n stroke: var(--dartboard-stroke);\n stroke-width: var(--dartboard-stroke-width);\n touch-action: none;\n}\n\n.c-Dartboard svg {\n width: 100%;\n height: auto;\n display: block;\n}\n\n.c-Dartboard-miss {\n fill: var(--dartboard-miss);\n}\n\n.c-Dartboard-missLabel {\n font-family: sans-serif;\n fill: var(--dartboard-miss-label);\n stroke-width: 0;\n}\n\n.c-Dartboard-bed.isDark {\n fill: var(--dartboard-dark);\n}\n\n.c-Dartboard-bed.isLight {\n fill: var(--dartboard-light);\n}\n\n.c-Dartboard-double .c-Dartboard-bed.isDark,\n.c-Dartboard-triple .c-Dartboard-bed.isDark,\n.c-Dartboard-doubleBull .c-Dartboard-bed.isDark,\n.c-Dartboard-singleBull .c-Dartboard-bed.isDark {\n fill: var(--dartboard-scoring-dark);\n}\n\n.c-Dartboard-double .c-Dartboard-bed.isLight,\n.c-Dartboard-triple .c-Dartboard-bed.isLight,\n.c-Dartboard-doubleBull .c-Dartboard-bed.isLight,\n.c-Dartboard-singleBull .c-Dartboard-bed.isLight {\n fill: var(--dartboard-scoring-light);\n}\n\n.c-Dartboard-bed.is-hovered,\n.c-Dartboard-miss .c-Dartboard-bed:hover {\n fill-opacity: 0.6;\n cursor: pointer;\n}\n\n.c-Dartboard-innerSingle .c-Dartboard-bed.is-hovered.isDark,\n.c-Dartboard-outerSingle .c-Dartboard-bed.is-hovered.isDark {\n fill: var(--dartboard-dark-hover);\n fill-opacity: 1;\n}\n\n.c-Dartboard-bed.is-highlighted,\n.c-Dartboard .c-Dartboard-bed.is-highlighted.isDark,\n.c-Dartboard .c-Dartboard-bed.is-highlighted.isLight {\n fill: var(--dartboard-highlight-color);\n stroke: #fff;\n stroke-width: 3px;\n animation: dartboard-pulse 0.6s ease-in-out infinite alternate;\n}\n\n@keyframes dartboard-pulse {\n from {\n fill-opacity: 0.5;\n }\n to {\n fill-opacity: 1;\n }\n}\n\n.c-Dartboard .c-Dartboard-bed.is-good.isDark,\n.c-Dartboard .c-Dartboard-bed.is-good.isLight {\n fill: var(--dartboard-good);\n}\n\n.c-Dartboard .c-Dartboard-bed.is-bad.isDark,\n.c-Dartboard .c-Dartboard-bed.is-bad.isLight {\n fill: var(--dartboard-bad);\n}\n\n.c-Dartboard .c-Dartboard-bed.is-neutral.isDark,\n.c-Dartboard .c-Dartboard-bed.is-neutral.isLight {\n fill: var(--dartboard-neutral);\n}\n\n.c-Dartboard-bed {\n outline: none;\n}\n\n.c-Dartboard-bed:focus-visible {\n outline: 2px solid var(--dartboard-highlight-color);\n outline-offset: -2px;\n}\n\n.c-Dartboard-dart {\n fill: var(--dartboard-dart-fill);\n stroke: var(--dartboard-dart-stroke);\n stroke-width: 1.5px;\n pointer-events: none;\n}\n\n.c-Dartboard.is-disabled {\n pointer-events: none;\n opacity: 0.5;\n}\n"],"mappings":";AAAA,CAAC;AACC,oBAAkB;AAClB,qBAAmB;AACnB,4BAA0B;AAC1B,6BAA2B;AAC3B,oBAAkB;AAClB,0BAAwB;AACxB,sBAAoB;AACpB,4BAA0B;AAC1B,+BAA6B;AAC7B,0BAAwB;AACxB,oBAAkB;AAClB,mBAAiB;AACjB,uBAAqB;AACrB,yBAAuB;AACvB,2BAAyB;AAEzB,UAAQ,IAAI;AACZ,gBAAc,IAAI;AAClB,gBAAc;AAChB;AAEA,CAtBC,YAsBY;AACX,SAAO;AACP,UAAQ;AACR,WAAS;AACX;AAEA,CAAC;AACC,QAAM,IAAI;AACZ;AAEA,CAAC;AACC,eAAa;AACb,QAAM,IAAI;AACV,gBAAc;AAChB;AAEA,CAAC,eAAe,CAAC;AACf,QAAM,IAAI;AACZ;AAEA,CAJC,eAIe,CAAC;AACf,QAAM,IAAI;AACZ;AAEA,CAAC,mBAAmB,CARnB,eAQmC,CARnB;AASjB,CAAC,mBAAmB,CATnB,eASmC,CATnB;AAUjB,CAAC,uBAAuB,CAVvB,eAUuC,CAVvB;AAWjB,CAAC,uBAAuB,CAXvB,eAWuC,CAXvB;AAYf,QAAM,IAAI;AACZ;AAEA,CAPC,mBAOmB,CAfnB,eAemC,CAXnB;AAYjB,CAPC,mBAOmB,CAhBnB,eAgBmC,CAZnB;AAajB,CAPC,uBAOuB,CAjBvB,eAiBuC,CAbvB;AAcjB,CAPC,uBAOuB,CAlBvB,eAkBuC,CAdvB;AAef,QAAM,IAAI;AACZ;AAEA,CAtBC,eAsBe,CAAC;AACjB,CAjCC,iBAiCiB,CAvBjB,eAuBiC;AAChC,gBAAc;AACd,UAAQ;AACV;AAEA,CAAC,wBAAwB,CA5BxB,eA4BwC,CANxB,UAMmC,CA5BnC;AA6BjB,CAAC,wBAAwB,CA7BxB,eA6BwC,CAPxB,UAOmC,CA7BnC;AA8Bf,QAAM,IAAI;AACV,gBAAc;AAChB;AAEA,CAlCC,eAkCe,CAAC;AACjB,CAzEC,YAyEY,CAnCZ,eAmC4B,CADZ,cAC2B,CAnC3B;AAoCjB,CA1EC,YA0EY,CApCZ,eAoC4B,CAFZ,cAE2B,CAhC3B;AAiCf,QAAM,IAAI;AACV,UAAQ;AACR,gBAAc;AACd,aAAW,gBAAgB,KAAK,YAAY,SAAS;AACvD;AAEA,WAHa;AAIX;AACE,kBAAc;AAChB;AACA;AACE,kBAAc;AAChB;AACF;AAEA,CA1FC,YA0FY,CApDZ,eAoD4B,CAAC,OAAO,CApDpB;AAqDjB,CA3FC,YA2FY,CArDZ,eAqD4B,CADC,OACO,CAjDpB;AAkDf,QAAM,IAAI;AACZ;AAEA,CA/FC,YA+FY,CAzDZ,eAyD4B,CAAC,MAAM,CAzDnB;AA0DjB,CAhGC,YAgGY,CA1DZ,eA0D4B,CADC,MACM,CAtDnB;AAuDf,QAAM,IAAI;AACZ;AAEA,CApGC,YAoGY,CA9DZ,eA8D4B,CAAC,UAAU,CA9DvB;AA+DjB,CArGC,YAqGY,CA/DZ,eA+D4B,CADC,UACU,CA3DvB;AA4Df,QAAM,IAAI;AACZ;AAEA,CAnEC;AAoEC,WAAS;AACX;AAEA,CAvEC,eAuEe;AACd,WAAS,IAAI,MAAM,IAAI;AACvB,kBAAgB;AAClB;AAEA,CAAC;AACC,QAAM,IAAI;AACV,UAAQ,IAAI;AACZ,gBAAc;AACd,kBAAgB;AAClB;AAEA,CAzHC,WAyHW,CAAC;AACX,kBAAgB;AAChB,WAAS;AACX;","names":[]}
1
+ {"version":3,"sources":["../src/dartboard.css"],"sourcesContent":[".c-Dartboard {\n --dartboard-dark: #000;\n --dartboard-light: #f5f5dc;\n --dartboard-scoring-dark: #e32636;\n --dartboard-scoring-light: #228b22;\n --dartboard-miss: #000;\n --dartboard-miss-label: #fff;\n --dartboard-stroke: silver;\n --dartboard-stroke-width: 2px;\n --dartboard-highlight-color: #ffd700;\n --dartboard-dark-hover: #444;\n --dartboard-good: #228b22;\n --dartboard-bad: #e32636;\n --dartboard-neutral: #4a6fa5;\n --dartboard-dart-fill: #ff8c00;\n --dartboard-dart-stroke: #b35900;\n --dartboard-label-on-dark: #fff;\n --dartboard-label-on-light: #111;\n --dartboard-label-halo: rgba(0, 0, 0, 0.6);\n\n stroke: var(--dartboard-stroke);\n stroke-width: var(--dartboard-stroke-width);\n touch-action: none;\n}\n\n.c-Dartboard svg {\n width: 100%;\n height: auto;\n display: block;\n}\n\n.c-Dartboard-miss {\n fill: var(--dartboard-miss);\n}\n\n.c-Dartboard-missLabel {\n font-family: sans-serif;\n fill: var(--dartboard-miss-label);\n stroke-width: 0;\n}\n\n.c-Dartboard-bed.isDark {\n fill: var(--dartboard-dark);\n}\n\n.c-Dartboard-bed.isLight {\n fill: var(--dartboard-light);\n}\n\n.c-Dartboard-double .c-Dartboard-bed.isDark,\n.c-Dartboard-triple .c-Dartboard-bed.isDark,\n.c-Dartboard-doubleBull .c-Dartboard-bed.isDark,\n.c-Dartboard-singleBull .c-Dartboard-bed.isDark {\n fill: var(--dartboard-scoring-dark);\n}\n\n.c-Dartboard-double .c-Dartboard-bed.isLight,\n.c-Dartboard-triple .c-Dartboard-bed.isLight,\n.c-Dartboard-doubleBull .c-Dartboard-bed.isLight,\n.c-Dartboard-singleBull .c-Dartboard-bed.isLight {\n fill: var(--dartboard-scoring-light);\n}\n\n.c-Dartboard-bed.is-hovered,\n.c-Dartboard-miss .c-Dartboard-bed:hover {\n fill-opacity: 0.6;\n cursor: pointer;\n}\n\n.c-Dartboard-innerSingle .c-Dartboard-bed.is-hovered.isDark,\n.c-Dartboard-outerSingle .c-Dartboard-bed.is-hovered.isDark {\n fill: var(--dartboard-dark-hover);\n fill-opacity: 1;\n}\n\n.c-Dartboard-bed.is-highlighted,\n.c-Dartboard .c-Dartboard-bed.is-highlighted.isDark,\n.c-Dartboard .c-Dartboard-bed.is-highlighted.isLight {\n fill: var(--dartboard-highlight-color);\n stroke: #fff;\n stroke-width: 3px;\n animation: dartboard-pulse 0.6s ease-in-out infinite alternate;\n}\n\n@keyframes dartboard-pulse {\n from {\n fill-opacity: 0.5;\n }\n to {\n fill-opacity: 1;\n }\n}\n\n.c-Dartboard .c-Dartboard-bed.is-good.isDark,\n.c-Dartboard .c-Dartboard-bed.is-good.isLight {\n fill: var(--dartboard-good);\n}\n\n.c-Dartboard .c-Dartboard-bed.is-bad.isDark,\n.c-Dartboard .c-Dartboard-bed.is-bad.isLight {\n fill: var(--dartboard-bad);\n}\n\n.c-Dartboard .c-Dartboard-bed.is-neutral.isDark,\n.c-Dartboard .c-Dartboard-bed.is-neutral.isLight {\n fill: var(--dartboard-neutral);\n}\n\n.c-Dartboard-bed {\n outline: none;\n}\n\n.c-Dartboard-bed:focus-visible {\n outline: 2px solid var(--dartboard-highlight-color);\n outline-offset: -2px;\n}\n\n.c-Dartboard-dart {\n fill: var(--dartboard-dart-fill);\n stroke: var(--dartboard-dart-stroke);\n stroke-width: 1.5px;\n pointer-events: none;\n}\n\n.c-Dartboard-labels {\n pointer-events: none;\n}\n\n.c-Dartboard-label {\n font-family: sans-serif;\n /* Light text by default, with a thin dark halo so labels stay legible on the\n mid-tone red/green scoring beds. paint-order keeps the halo behind the glyph. */\n fill: var(--dartboard-label-on-dark);\n stroke: var(--dartboard-label-halo);\n stroke-width: 2.5px;\n stroke-linejoin: round;\n paint-order: stroke;\n}\n\n/* The plain (non-scoring) light beds — the beige singles — get dark text and no\n halo: dark-on-beige is already high-contrast, and a dark halo there only\n muddies the glyphs. The :not() makes this independent of rule order. */\n.c-Dartboard-label.isLight:not(.c-Dartboard-label--scoring) {\n fill: var(--dartboard-label-on-light);\n stroke-width: 0;\n}\n\n.c-Dartboard.is-disabled {\n pointer-events: none;\n opacity: 0.5;\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -52,6 +52,19 @@ type BedInput = BedTarget | string | number;
52
52
  interface HighlightOptions {
53
53
  className?: string;
54
54
  }
55
+ /**
56
+ * The descriptor passed to a `labelBeds` labeler for each bed. Carries the full
57
+ * bed model (segment, color, ring) plus a convenience `score`
58
+ * (`segment × ring.multiplier`; single bull = 25, double bull = 50) the labeler
59
+ * MAY use or ignore. The returned string is opaque — the library assigns it no
60
+ * meaning.
61
+ */
62
+ type BedLabelContext = Bed & {
63
+ score: number;
64
+ };
65
+ interface BedLabelOptions {
66
+ className?: string;
67
+ }
55
68
 
56
69
  declare class Dartboard {
57
70
  private board;
@@ -59,6 +72,7 @@ declare class Dartboard {
59
72
  private rendered;
60
73
  private _disabled;
61
74
  private dartGroup;
75
+ private labelGroup;
62
76
  private dartCounts;
63
77
  private readonly options;
64
78
  readonly segments: Segment[];
@@ -75,9 +89,13 @@ declare class Dartboard {
75
89
  reset(className?: string): void;
76
90
  addDart(targets: BedInput[]): this;
77
91
  removeDarts(targets?: BedInput[]): this;
92
+ labelBeds(labeler: (bed: BedLabelContext) => string | null | undefined, options?: BedLabelOptions): this;
93
+ clearBedLabels(): this;
78
94
  private ensureDartGroup;
95
+ private ensureLabelGroup;
79
96
  private ringRadii;
80
97
  private bedGeometry;
98
+ private labelPosition;
81
99
  private isValidTarget;
82
100
  private resolveTargets;
83
101
  private dispatchThrow;
@@ -96,4 +114,4 @@ declare function asPercent(n: number): number;
96
114
  declare function addRingToSegments(ring: Ring, segments: Segment[]): Bed[];
97
115
  declare function parseBed(bed: string, rings: Rings, segments?: Segment[]): BedTarget[];
98
116
 
99
- export { type Bed, type BedInput, type BedTarget, DEFAULT_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS, Dartboard, type DartboardOptions, type HighlightOptions, type HoverDetail, MOBILE_OPTIONS, type Ring, type Rings, type Segment, type ThrowDetail, addRingToSegments, asPercent, buildThrowDetail, parseBed };
117
+ export { type Bed, type BedInput, type BedLabelContext, type BedLabelOptions, type BedTarget, DEFAULT_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS, Dartboard, type DartboardOptions, type HighlightOptions, type HoverDetail, MOBILE_OPTIONS, type Ring, type Rings, type Segment, type ThrowDetail, addRingToSegments, asPercent, buildThrowDetail, parseBed };
package/dist/index.d.ts CHANGED
@@ -52,6 +52,19 @@ type BedInput = BedTarget | string | number;
52
52
  interface HighlightOptions {
53
53
  className?: string;
54
54
  }
55
+ /**
56
+ * The descriptor passed to a `labelBeds` labeler for each bed. Carries the full
57
+ * bed model (segment, color, ring) plus a convenience `score`
58
+ * (`segment × ring.multiplier`; single bull = 25, double bull = 50) the labeler
59
+ * MAY use or ignore. The returned string is opaque — the library assigns it no
60
+ * meaning.
61
+ */
62
+ type BedLabelContext = Bed & {
63
+ score: number;
64
+ };
65
+ interface BedLabelOptions {
66
+ className?: string;
67
+ }
55
68
 
56
69
  declare class Dartboard {
57
70
  private board;
@@ -59,6 +72,7 @@ declare class Dartboard {
59
72
  private rendered;
60
73
  private _disabled;
61
74
  private dartGroup;
75
+ private labelGroup;
62
76
  private dartCounts;
63
77
  private readonly options;
64
78
  readonly segments: Segment[];
@@ -75,9 +89,13 @@ declare class Dartboard {
75
89
  reset(className?: string): void;
76
90
  addDart(targets: BedInput[]): this;
77
91
  removeDarts(targets?: BedInput[]): this;
92
+ labelBeds(labeler: (bed: BedLabelContext) => string | null | undefined, options?: BedLabelOptions): this;
93
+ clearBedLabels(): this;
78
94
  private ensureDartGroup;
95
+ private ensureLabelGroup;
79
96
  private ringRadii;
80
97
  private bedGeometry;
98
+ private labelPosition;
81
99
  private isValidTarget;
82
100
  private resolveTargets;
83
101
  private dispatchThrow;
@@ -96,4 +114,4 @@ declare function asPercent(n: number): number;
96
114
  declare function addRingToSegments(ring: Ring, segments: Segment[]): Bed[];
97
115
  declare function parseBed(bed: string, rings: Rings, segments?: Segment[]): BedTarget[];
98
116
 
99
- export { type Bed, type BedInput, type BedTarget, DEFAULT_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS, Dartboard, type DartboardOptions, type HighlightOptions, type HoverDetail, MOBILE_OPTIONS, type Ring, type Rings, type Segment, type ThrowDetail, addRingToSegments, asPercent, buildThrowDetail, parseBed };
117
+ export { type Bed, type BedInput, type BedLabelContext, type BedLabelOptions, type BedTarget, DEFAULT_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS, Dartboard, type DartboardOptions, type HighlightOptions, type HoverDetail, MOBILE_OPTIONS, type Ring, type Rings, type Segment, type ThrowDetail, addRingToSegments, asPercent, buildThrowDetail, parseBed };
package/dist/index.js CHANGED
@@ -128,6 +128,13 @@ var DART_OFFSETS = [
128
128
  [-0.25, 0.25],
129
129
  [0.25, -0.25]
130
130
  ];
131
+ var SCORING_COLOR_RINGS = /* @__PURE__ */ new Set([
132
+ "DOUBLE",
133
+ "TRIPLE",
134
+ "SINGLE_BULL",
135
+ "DOUBLE_BULL"
136
+ ]);
137
+ var LABEL_FONT_SCALE = 0.06;
131
138
  var Dartboard = class {
132
139
  constructor(container, options = {}, segments = DEFAULT_SEGMENTS, rings = DEFAULT_RINGS) {
133
140
  this.board = null;
@@ -135,6 +142,7 @@ var Dartboard = class {
135
142
  this.rendered = false;
136
143
  this._disabled = false;
137
144
  this.dartGroup = null;
145
+ this.labelGroup = null;
138
146
  this.dartCounts = /* @__PURE__ */ new Map();
139
147
  this.options = { ...DEFAULT_OPTIONS, ...options };
140
148
  this.segments = segments;
@@ -261,6 +269,7 @@ var Dartboard = class {
261
269
  this.sizes = null;
262
270
  this.rendered = false;
263
271
  this.dartGroup = null;
272
+ this.labelGroup = null;
264
273
  this.dartCounts.clear();
265
274
  }
266
275
  }
@@ -367,12 +376,49 @@ var Dartboard = class {
367
376
  }
368
377
  return this;
369
378
  }
379
+ labelBeds(labeler, options = {}) {
380
+ if (!this.board || !this.sizes) return this;
381
+ const extra = options.className;
382
+ const counterRotation = -this.board.rotation;
383
+ const group = this.ensureLabelGroup();
384
+ group.attr("font-size", this.board.radius * LABEL_FONT_SCALE);
385
+ group.selectAll("*").remove();
386
+ for (const ringKey of SCORING_RINGS) {
387
+ const ring = this.rings[ringKey];
388
+ if (!ring) continue;
389
+ const beds = isBull(ringKey) ? [{ segment: 25, color: ringKey === "DOUBLE_BULL" ? "Dark" : "Light", ring }] : addRingToSegments(ring, this.segments);
390
+ const scoring = SCORING_COLOR_RINGS.has(ringKey) ? " c-Dartboard-label--scoring" : "";
391
+ for (const bed of beds) {
392
+ const score = bed.segment * bed.ring.multiplier;
393
+ const text = labeler({ ...bed, score });
394
+ if (text == null || text === "") continue;
395
+ const [x, y] = this.labelPosition({ segment: bed.segment, ring: ringKey });
396
+ const className = `c-Dartboard-label is${bed.color}${scoring}${extra ? ` ${extra}` : ""}`;
397
+ group.append("text").attr("class", className).attr("data-bed", bedKey({ segment: bed.segment, ring: ringKey })).attr("x", x).attr("y", y).attr("dy", ".35em").attr("text-anchor", "middle").attr("transform", `rotate(${counterRotation}, ${x}, ${y})`).text(text);
398
+ }
399
+ }
400
+ this.dartGroup?.raise();
401
+ return this;
402
+ }
403
+ clearBedLabels() {
404
+ if (this.labelGroup) {
405
+ this.labelGroup.remove();
406
+ this.labelGroup = null;
407
+ }
408
+ return this;
409
+ }
370
410
  ensureDartGroup() {
371
411
  if (!this.dartGroup) {
372
412
  this.dartGroup = this.board.svg.append("g").classed("c-Dartboard-darts", true);
373
413
  }
374
414
  return this.dartGroup;
375
415
  }
416
+ ensureLabelGroup() {
417
+ if (!this.labelGroup) {
418
+ this.labelGroup = this.board.svg.append("g").classed("c-Dartboard-labels", true);
419
+ }
420
+ return this.labelGroup;
421
+ }
376
422
  ringRadii(ringKey) {
377
423
  const s = this.sizes;
378
424
  const doubleBullOuter = s.doubleBull;
@@ -417,6 +463,17 @@ var Dartboard = class {
417
463
  outerR
418
464
  };
419
465
  }
466
+ // Where a bed's label sits. Reuses bedGeometry for the 80 segment beds and
467
+ // the double bull (center). Single bull would also resolve to [0,0] and
468
+ // collide with the double bull label, so it's pushed to the bottom of its
469
+ // ring (6 o'clock) instead.
470
+ labelPosition(target) {
471
+ if (target.ring === "SINGLE_BULL") {
472
+ const [innerR, outerR] = this.ringRadii("SINGLE_BULL");
473
+ return [0, (innerR + outerR) / 2];
474
+ }
475
+ return this.bedGeometry(target).centroid;
476
+ }
420
477
  isValidTarget(target) {
421
478
  if (isBull(target.ring)) return target.segment === 25;
422
479
  return this.segments.some((s) => s.segment === target.segment);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dartboard.ts","../src/constants.ts","../src/utils.ts"],"sourcesContent":["import { select, type Selection } from 'd3-selection';\nimport { arc, pie, type PieArcDatum } from 'd3-shape';\nimport type {\n DartboardOptions,\n Rings,\n Ring,\n Segment,\n Bed,\n ThrowDetail,\n HoverDetail,\n BedTarget,\n BedInput,\n HighlightOptions,\n} from './types';\nimport { DEFAULT_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS } from './constants';\nimport { buildThrowDetail, asPercent, addRingToSegments, parseBed } from './utils';\n\ntype RingKey = keyof Rings;\n\n// D3's selection generics are deeply nested and don't compose well across\n// chained calls. Using `any` for internal state avoids 100+ lines of type\n// gymnastics while keeping the public API fully typed.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype D3Sel = Selection<any, any, any, any>;\n\ninterface BoardState {\n container: HTMLElement;\n wrapper: D3Sel;\n width: number;\n height: number;\n radius: number;\n rotation: number;\n segmentWidth: number;\n svg: D3Sel;\n pie: ReturnType<typeof pie<Bed>>;\n}\n\ninterface SizeMap {\n miss: number;\n double: number;\n outerSingle: number;\n triple: number;\n innerSingle: number;\n singleBull: number;\n doubleBull: number;\n}\n\nfunction buildAriaLabel(bed: Bed): string {\n const detail = buildThrowDetail(bed);\n return `${detail.bed}, scores ${detail.score}`;\n}\n\nfunction isBull(ringKey: RingKey): boolean {\n return ringKey === 'DOUBLE_BULL' || ringKey === 'SINGLE_BULL';\n}\n\nfunction bedKey(target: Required<BedTarget>): string {\n return `${target.ring}:${target.segment}`;\n}\n\n// Predefined offsets for multiple darts in the same bed.\n// [radialFraction, tangentialFraction] — scaled to ring thickness, oriented\n// along the bed's radial and arc directions so darts stay within the wedge.\nconst DART_OFFSETS: [number, number][] = [\n [0, 0],\n [-0.25, -0.25],\n [0.25, 0.25],\n [-0.25, 0.25],\n [0.25, -0.25],\n];\n\nexport class Dartboard {\n private board: BoardState | null = null;\n private sizes: SizeMap | null = null;\n private rendered = false;\n private _disabled = false;\n private dartGroup: D3Sel | null = null;\n private dartCounts = new Map<string, number>();\n private readonly options: DartboardOptions;\n readonly segments: Segment[];\n readonly rings: Rings;\n\n constructor(\n container: string | HTMLElement,\n options: Partial<DartboardOptions> = {},\n segments: Segment[] = DEFAULT_SEGMENTS,\n rings: Rings = DEFAULT_RINGS,\n ) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.segments = segments;\n this.rings = rings;\n\n const percentSum =\n this.options.missPercent +\n this.options.doublePercent +\n this.options.outerSinglePercent +\n this.options.triplePercent +\n this.options.innerSinglePercent +\n this.options.singleBullPercent +\n this.options.doubleBullPercent;\n\n if (percentSum !== 100) {\n throw new Error(\n `Dartboard: ring percentages must sum to 100 (got ${percentSum})`,\n );\n }\n\n const boardContainer =\n typeof container === 'string'\n ? document.querySelector<HTMLElement>(container)\n : container;\n\n if (!boardContainer) {\n throw new Error(`Dartboard: container not found`);\n }\n\n const padding = this.options.padding;\n const boardSize =\n this.options.size ||\n Math.min(boardContainer.offsetHeight, boardContainer.offsetWidth);\n const width = boardSize - padding * 2;\n const viewBoxSize = boardSize;\n const segmentWidth = 360 / segments.length;\n const radius = width / 2;\n const rotation = segmentWidth / -2;\n\n const wrapper = select(boardContainer)\n .append('div')\n .classed('c-Dartboard', true);\n\n const svg = wrapper\n .append('svg')\n .attr('viewBox', `0 0 ${viewBoxSize} ${viewBoxSize}`)\n .attr('role', 'group')\n .attr('aria-label', 'Dartboard')\n .append('g')\n .attr(\n 'transform',\n `translate(${viewBoxSize / 2}, ${viewBoxSize / 2}) rotate(${rotation})`,\n );\n\n const pieFn = pie<Bed>()\n .sort((a, b) => (a.position ?? 0) - (b.position ?? 0))\n .value(() => segmentWidth);\n\n this.board = {\n container: boardContainer,\n wrapper,\n width,\n height: width,\n radius,\n rotation,\n segmentWidth,\n svg,\n pie: pieFn,\n };\n\n this.sizes = {\n miss: radius * asPercent(this.options.missPercent),\n double: radius * asPercent(this.options.doublePercent),\n outerSingle: radius * asPercent(this.options.outerSinglePercent),\n triple: radius * asPercent(this.options.triplePercent),\n innerSingle: radius * asPercent(this.options.innerSinglePercent),\n singleBull: radius * asPercent(this.options.singleBullPercent),\n doubleBull: radius * asPercent(this.options.doubleBullPercent),\n };\n }\n\n render(): this {\n if (!this.board || !this.sizes) return this;\n if (this.rendered) return this;\n this.rendered = true;\n\n const { board, sizes } = this;\n\n let innerRadius = 0;\n let outerRadius = innerRadius + sizes.doubleBull;\n this.renderBeds(\n board,\n this.rings.DOUBLE_BULL,\n [{ segment: 25, color: 'Dark' }],\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.singleBull;\n this.renderBeds(\n board,\n this.rings.SINGLE_BULL,\n [{ segment: 25, color: 'Light' }],\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.innerSingle;\n this.renderBeds(\n board,\n this.rings.INNER_SINGLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.triple;\n this.renderBeds(\n board,\n this.rings.TRIPLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.outerSingle;\n this.renderBeds(\n board,\n this.rings.OUTER_SINGLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.double;\n this.renderBeds(\n board,\n this.rings.DOUBLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = board.radius - sizes.miss;\n outerRadius = board.radius;\n this.renderMissRing(\n board,\n this.rings.MISS,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n return this;\n }\n\n destroy(): void {\n if (this.board) {\n // Remove all D3 event listeners before removing from DOM\n this.board.wrapper.selectAll('.c-Dartboard-bed')\n .on('pointerup', null)\n .on('pointerenter', null)\n .on('pointerleave', null)\n .on('keydown', null);\n this.board.wrapper.remove();\n this.board = null;\n this.sizes = null;\n this.rendered = false;\n this.dartGroup = null;\n this.dartCounts.clear();\n }\n }\n\n get disabled(): boolean {\n return this._disabled;\n }\n\n disable(): this {\n this._disabled = true;\n if (this.board) {\n this.board.wrapper.classed('is-disabled', true);\n }\n return this;\n }\n\n enable(): this {\n this._disabled = false;\n if (this.board) {\n this.board.wrapper.classed('is-disabled', false);\n }\n return this;\n }\n\n throwAt(target: BedInput): void {\n if (!this.board || this._disabled) return;\n\n const targets = this.resolveTargets([target]);\n if (targets.length === 0) return;\n const first = targets[0];\n\n if (!this.isValidTarget(first)) {\n throw new Error(`Dartboard: invalid throw target — segment ${first.segment} does not exist`);\n }\n\n const ring = this.rings[first.ring];\n const bed: Bed = { segment: first.segment, color: 'Dark', ring };\n const detail = buildThrowDetail(bed);\n\n this.board.container.dispatchEvent(\n new CustomEvent<ThrowDetail>('throw', { detail }),\n );\n }\n\n highlight(targets: BedInput[], options: HighlightOptions = {}): void {\n if (!this.board) return;\n const className = options.className || 'is-highlighted';\n\n for (const target of this.resolveTargets(targets)) {\n const ring = this.rings[target.ring];\n const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;\n this.board.wrapper.selectAll(selector).classed(className, true);\n }\n }\n\n unhighlight(targets: BedInput[], options: HighlightOptions = {}): void {\n if (!this.board) return;\n const className = options.className || 'is-highlighted';\n\n for (const target of this.resolveTargets(targets)) {\n const ring = this.rings[target.ring];\n const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;\n this.board.wrapper.selectAll(selector).classed(className, false);\n }\n }\n\n reset(className = 'is-highlighted'): void {\n if (!this.board) return;\n this.board.wrapper.selectAll(`.${className}`).classed(className, false);\n }\n\n addDart(targets: BedInput[]): this {\n if (!this.board || !this.sizes) return this;\n\n const resolved = this.resolveTargets(targets);\n const group = this.ensureDartGroup();\n const dartRadius = this.sizes.doubleBull * 0.25;\n\n for (const target of resolved) {\n const key = bedKey(target);\n const count = this.dartCounts.get(key) ?? 0;\n this.dartCounts.set(key, count + 1);\n\n const { centroid, midAngle, innerR, outerR } = this.bedGeometry(target);\n let cx: number;\n let cy: number;\n\n if (isBull(target.ring)) {\n // Distribute bull darts evenly around the ring at its midpoint radius.\n const midR = (innerR + outerR) / 2;\n const angle = (count * 2 * Math.PI) / DART_OFFSETS.length;\n cx = midR * Math.sin(angle);\n cy = -midR * Math.cos(angle);\n } else {\n const [radialFrac, tangentialFrac] = DART_OFFSETS[count % DART_OFFSETS.length];\n const thickness = outerR - innerR;\n const midRadius = (innerR + outerR) / 2;\n const segWidthRad = (this.board.segmentWidth * Math.PI) / 180;\n // Arc half-width at the midpoint radius — limits tangential offsets\n // so darts stay within the segment's angular bounds.\n const arcHalfWidth = midRadius * Math.sin(segWidthRad / 2);\n\n // Radial unit (pointing outward from center)\n const radX = Math.sin(midAngle);\n const radY = -Math.cos(midAngle);\n // Tangential unit (perpendicular, along the arc)\n const tanX = Math.cos(midAngle);\n const tanY = Math.sin(midAngle);\n\n cx = centroid[0] + radialFrac * thickness * radX + tangentialFrac * arcHalfWidth * tanX;\n cy = centroid[1] + radialFrac * thickness * radY + tangentialFrac * arcHalfWidth * tanY;\n }\n\n group\n .append('circle')\n .classed('c-Dartboard-dart', true)\n .attr('cx', cx)\n .attr('cy', cy)\n .attr('r', dartRadius)\n .attr('data-bed', key);\n }\n\n return this;\n }\n\n removeDarts(targets?: BedInput[]): this {\n if (!this.dartGroup) return this;\n\n if (!targets) {\n this.dartGroup.selectAll('.c-Dartboard-dart').remove();\n this.dartCounts.clear();\n return this;\n }\n\n const resolved = this.resolveTargets(targets);\n for (const target of resolved) {\n const key = bedKey(target);\n this.dartGroup\n .selectAll(`.c-Dartboard-dart[data-bed=\"${key}\"]`)\n .remove();\n this.dartCounts.delete(key);\n }\n\n return this;\n }\n\n private ensureDartGroup(): D3Sel {\n if (!this.dartGroup) {\n this.dartGroup = this.board!.svg\n .append('g')\n .classed('c-Dartboard-darts', true);\n }\n return this.dartGroup;\n }\n\n private ringRadii(ringKey: RingKey): [number, number] {\n const s = this.sizes!;\n const doubleBullOuter = s.doubleBull;\n const singleBullOuter = doubleBullOuter + s.singleBull;\n const innerSingleOuter = singleBullOuter + s.innerSingle;\n const tripleOuter = innerSingleOuter + s.triple;\n const outerSingleOuter = tripleOuter + s.outerSingle;\n const doubleOuter = outerSingleOuter + s.double;\n\n switch (ringKey) {\n case 'DOUBLE_BULL': return [0, doubleBullOuter];\n case 'SINGLE_BULL': return [doubleBullOuter, singleBullOuter];\n case 'INNER_SINGLE': return [singleBullOuter, innerSingleOuter];\n case 'TRIPLE': return [innerSingleOuter, tripleOuter];\n case 'OUTER_SINGLE': return [tripleOuter, outerSingleOuter];\n case 'DOUBLE': return [outerSingleOuter, doubleOuter];\n case 'MISS': return [this.board!.radius - s.miss, this.board!.radius];\n }\n }\n\n private bedGeometry(target: Required<BedTarget>): {\n centroid: [number, number];\n midAngle: number;\n innerR: number;\n outerR: number;\n } {\n const [innerR, outerR] = this.ringRadii(target.ring);\n\n if (target.segment === 25) {\n return { centroid: [0, 0], midAngle: 0, innerR, outerR };\n }\n\n const seg = this.segments.find((s) => s.segment === target.segment);\n const segWidthRad = (this.board!.segmentWidth * Math.PI) / 180;\n const midAngle = ((seg?.position ?? 1) - 0.5) * segWidthRad;\n const midRadius = (innerR + outerR) / 2;\n\n return {\n centroid: [\n midRadius * Math.sin(midAngle),\n -midRadius * Math.cos(midAngle),\n ],\n midAngle,\n innerR,\n outerR,\n };\n }\n\n private isValidTarget(target: Required<BedTarget>): boolean {\n if (isBull(target.ring)) return target.segment === 25;\n return this.segments.some((s) => s.segment === target.segment);\n }\n\n private resolveTargets(inputs: BedInput[]): Required<BedTarget>[] {\n return inputs.flatMap((input): Required<BedTarget>[] => {\n if (typeof input === 'number') {\n return parseBed(String(input), this.rings, this.segments) as Required<BedTarget>[];\n }\n if (typeof input === 'string') {\n return parseBed(input, this.rings, this.segments) as Required<BedTarget>[];\n }\n if (!input.ring) {\n return parseBed(String(input.segment), this.rings, this.segments) as Required<BedTarget>[];\n }\n return [input as Required<BedTarget>];\n });\n }\n\n private dispatchThrow(bed: Bed): void {\n if (!this.board || this._disabled) return;\n const detail = buildThrowDetail(bed);\n this.board.container.dispatchEvent(\n new CustomEvent<ThrowDetail>('throw', { detail }),\n );\n }\n\n private dispatchHover(bed: Bed, hovering: boolean): void {\n if (!this.board || this._disabled) return;\n const throwDetail = buildThrowDetail(bed);\n const detail: HoverDetail = { ...throwDetail, hovering };\n this.board.container.dispatchEvent(\n new CustomEvent<HoverDetail>('hover', { detail }),\n );\n }\n\n private renderBeds(\n board: BoardState,\n ring: Ring,\n segments: Segment[],\n outerRadius: number,\n innerRadius: number,\n ): void {\n const className = `c-Dartboard-${ring.name}`;\n const arcFn = arc<PieArcDatum<Bed>>()\n .outerRadius(outerRadius)\n .innerRadius(innerRadius);\n\n const beds = board.svg\n .append('g')\n .classed(className, true)\n .selectAll<SVGGElement, PieArcDatum<Bed>>('arc')\n .data(board.pie(addRingToSegments(ring, segments)))\n .enter()\n .append('g')\n .attr(\n 'class',\n (d: PieArcDatum<Bed>) =>\n `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} ${className}--${d.data.segment} is${d.data.color}`,\n )\n .attr('role', 'button')\n .attr('tabindex', '0')\n .attr('aria-label', (d: PieArcDatum<Bed>) =>\n buildAriaLabel(d.data),\n )\n .on(\n 'pointerup',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n this.dispatchThrow(d.data);\n },\n )\n .on(\n 'pointerenter',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n select(_event.currentTarget as Element).classed('is-hovered', true);\n this.dispatchHover(d.data, true);\n },\n )\n .on(\n 'pointerleave',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n select(_event.currentTarget as Element).classed('is-hovered', false);\n this.dispatchHover(d.data, false);\n },\n )\n .on(\n 'keydown',\n (_event: KeyboardEvent, d: PieArcDatum<Bed>) => {\n if (_event.key === 'Enter' || _event.key === ' ') {\n _event.preventDefault();\n this.dispatchThrow(d.data);\n }\n },\n );\n\n beds.append('path').attr('d', arcFn as never);\n }\n\n private renderMissRing(\n board: BoardState,\n ring: Ring,\n segments: Segment[],\n outerRadius: number,\n innerRadius: number,\n ): void {\n const missArc = arc<PieArcDatum<Bed>>()\n .outerRadius(outerRadius)\n .innerRadius(innerRadius);\n\n const missBeds = board.svg\n .append('g')\n .classed('c-Dartboard-miss', true)\n .selectAll<SVGGElement, PieArcDatum<Bed>>('arc')\n .data(board.pie(addRingToSegments(ring, segments)))\n .enter()\n .append('g')\n .attr(\n 'class',\n (d: PieArcDatum<Bed>) =>\n `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} c-Dartboard-miss--${d.data.segment}`,\n )\n .attr('role', 'button')\n .attr('tabindex', '0')\n .attr('aria-label', (d: PieArcDatum<Bed>) =>\n buildAriaLabel(d.data),\n )\n .on(\n 'pointerup',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n this.dispatchThrow(d.data);\n },\n )\n .on(\n 'keydown',\n (_event: KeyboardEvent, d: PieArcDatum<Bed>) => {\n if (_event.key === 'Enter' || _event.key === ' ') {\n _event.preventDefault();\n this.dispatchThrow(d.data);\n }\n },\n );\n\n missBeds.append('path').attr('d', missArc as never);\n\n const determineRotation = (bedData: Bed) =>\n -board.rotation + board.segmentWidth * ((bedData.position ?? 0) - 1);\n\n const determineX = (d: PieArcDatum<Bed>) =>\n missArc.centroid(d)[0];\n\n const determineY = (d: PieArcDatum<Bed>) =>\n missArc.centroid(d)[1];\n\n missBeds\n .append('text')\n .classed('c-Dartboard-missLabel', true)\n .attr('x', (d: PieArcDatum<Bed>) => determineX(d))\n .attr('y', (d: PieArcDatum<Bed>) => determineY(d))\n .attr('dy', '.35em')\n .attr(\n 'transform',\n (d: PieArcDatum<Bed>) =>\n `rotate(${determineRotation(d.data)}, ${determineX(d)}, ${determineY(d)})`,\n )\n .attr('text-anchor', 'middle')\n .text((d: PieArcDatum<Bed>) => d.data.segment);\n }\n}\n","import type { DartboardOptions, Rings, Segment } from './types';\n\nexport const DEFAULT_OPTIONS: DartboardOptions = {\n size: null,\n padding: 2,\n missPercent: 10,\n doublePercent: 10,\n outerSinglePercent: 25,\n triplePercent: 10,\n innerSinglePercent: 30,\n singleBullPercent: 8,\n doubleBullPercent: 7,\n};\n\nexport const DEFAULT_RINGS: Rings = {\n MISS: { name: 'miss', abbr: 'M', multiplier: 0 },\n DOUBLE: { name: 'double', abbr: 'D', multiplier: 2 },\n OUTER_SINGLE: { name: 'outerSingle', abbr: 'S', multiplier: 1 },\n TRIPLE: { name: 'triple', abbr: 'T', multiplier: 3 },\n INNER_SINGLE: { name: 'innerSingle', abbr: 'S', multiplier: 1 },\n SINGLE_BULL: { name: 'singleBull', abbr: 'B', multiplier: 1 },\n DOUBLE_BULL: { name: 'doubleBull', abbr: 'DB', multiplier: 2 },\n};\n\nexport const MOBILE_OPTIONS: Partial<DartboardOptions> = {\n missPercent: 7,\n doublePercent: 14,\n outerSinglePercent: 21,\n triplePercent: 14,\n innerSinglePercent: 26,\n singleBullPercent: 9,\n doubleBullPercent: 9,\n};\n\nexport const DEFAULT_SEGMENTS: Segment[] = [\n { segment: 20, position: 1, color: 'Dark' },\n { segment: 1, position: 2, color: 'Light' },\n { segment: 18, position: 3, color: 'Dark' },\n { segment: 4, position: 4, color: 'Light' },\n { segment: 13, position: 5, color: 'Dark' },\n { segment: 6, position: 6, color: 'Light' },\n { segment: 10, position: 7, color: 'Dark' },\n { segment: 15, position: 8, color: 'Light' },\n { segment: 2, position: 9, color: 'Dark' },\n { segment: 17, position: 10, color: 'Light' },\n { segment: 3, position: 11, color: 'Dark' },\n { segment: 19, position: 12, color: 'Light' },\n { segment: 7, position: 13, color: 'Dark' },\n { segment: 16, position: 14, color: 'Light' },\n { segment: 8, position: 15, color: 'Dark' },\n { segment: 11, position: 16, color: 'Light' },\n { segment: 14, position: 17, color: 'Dark' },\n { segment: 9, position: 18, color: 'Light' },\n { segment: 12, position: 19, color: 'Dark' },\n { segment: 5, position: 20, color: 'Light' },\n];\n","import type { Ring, Rings, Segment, Bed, BedTarget, ThrowDetail } from './types';\n\nconst SCORING_RINGS: (keyof Rings)[] = [\n 'DOUBLE',\n 'TRIPLE',\n 'OUTER_SINGLE',\n 'INNER_SINGLE',\n 'SINGLE_BULL',\n 'DOUBLE_BULL',\n];\n\nexport function buildThrowDetail(bed: Bed): ThrowDetail {\n return {\n bed: bed.ring.abbr + bed.segment,\n segment: bed.segment,\n ring: bed.ring.name,\n score: bed.segment * bed.ring.multiplier,\n };\n}\n\nexport function asPercent(n: number): number {\n return parseFloat((n / 100).toFixed(2));\n}\n\nexport function addRingToSegments(ring: Ring, segments: Segment[]): Bed[] {\n return segments.map((segment) => ({ ...segment, ring }));\n}\n\nexport function parseBed(\n bed: string,\n rings: Rings,\n segments?: Segment[],\n): BedTarget[] {\n // 'miss' keyword — target all segments in the miss ring\n if (bed.toLowerCase() === 'miss') {\n if (!segments) {\n throw new Error(\n 'parseBed: segments are required to resolve the \"miss\" keyword',\n );\n }\n return segments.map((s) => ({ segment: s.segment, ring: 'MISS' as keyof Rings }));\n }\n\n // Number-only string — target all scoring rings on that segment\n const numOnly = bed.match(/^(\\d+)$/);\n if (numOnly) {\n const segment = parseInt(numOnly[1], 10);\n return SCORING_RINGS\n .filter((key) => key in rings)\n .map((ring) => ({ segment, ring }));\n }\n\n const match = bed.match(/^([A-Za-z]+)(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid bed format: \"${bed}\"`);\n }\n\n const [, abbr, numStr] = match.map((s, i) => i === 1 ? s.toUpperCase() : s);\n const segment = parseInt(numStr, 10);\n\n const targets: BedTarget[] = [];\n for (const [key, ring] of Object.entries(rings)) {\n if (ring.abbr === abbr) {\n targets.push({ segment, ring: key as keyof Rings });\n }\n }\n\n if (targets.length === 0) {\n throw new Error(`Unknown bed abbreviation: \"${abbr}\"`);\n }\n\n return targets;\n}\n"],"mappings":"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","names":["segment"]}
1
+ {"version":3,"sources":["../src/dartboard.ts","../src/constants.ts","../src/utils.ts"],"sourcesContent":["import { select, type Selection } from 'd3-selection';\nimport { arc, pie, type PieArcDatum } from 'd3-shape';\nimport type {\n DartboardOptions,\n Rings,\n Ring,\n Segment,\n Bed,\n ThrowDetail,\n HoverDetail,\n BedTarget,\n BedInput,\n HighlightOptions,\n BedLabelContext,\n BedLabelOptions,\n} from './types';\nimport { DEFAULT_OPTIONS, DEFAULT_RINGS, DEFAULT_SEGMENTS } from './constants';\nimport { buildThrowDetail, asPercent, addRingToSegments, parseBed, SCORING_RINGS } from './utils';\n\ntype RingKey = keyof Rings;\n\n// D3's selection generics are deeply nested and don't compose well across\n// chained calls. Using `any` for internal state avoids 100+ lines of type\n// gymnastics while keeping the public API fully typed.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype D3Sel = Selection<any, any, any, any>;\n\ninterface BoardState {\n container: HTMLElement;\n wrapper: D3Sel;\n width: number;\n height: number;\n radius: number;\n rotation: number;\n segmentWidth: number;\n svg: D3Sel;\n pie: ReturnType<typeof pie<Bed>>;\n}\n\ninterface SizeMap {\n miss: number;\n double: number;\n outerSingle: number;\n triple: number;\n innerSingle: number;\n singleBull: number;\n doubleBull: number;\n}\n\nfunction buildAriaLabel(bed: Bed): string {\n const detail = buildThrowDetail(bed);\n return `${detail.bed}, scores ${detail.score}`;\n}\n\nfunction isBull(ringKey: RingKey): boolean {\n return ringKey === 'DOUBLE_BULL' || ringKey === 'SINGLE_BULL';\n}\n\nfunction bedKey(target: Required<BedTarget>): string {\n return `${target.ring}:${target.segment}`;\n}\n\n// Predefined offsets for multiple darts in the same bed.\n// [radialFraction, tangentialFraction] — scaled to ring thickness, oriented\n// along the bed's radial and arc directions so darts stay within the wedge.\nconst DART_OFFSETS: [number, number][] = [\n [0, 0],\n [-0.25, -0.25],\n [0.25, 0.25],\n [-0.25, 0.25],\n [0.25, -0.25],\n];\n\n// Rings painted with the \"scoring\" colors (red/green) rather than the plain\n// dark/light bed colors. Labels on these keep light text (a \"light\" scoring bed\n// is still green, not beige), instead of the dark-on-light treatment. Distinct\n// from utils' SCORING_RINGS, the broader set of all label-eligible rings.\nconst SCORING_COLOR_RINGS: ReadonlySet<RingKey> = new Set<RingKey>([\n 'DOUBLE',\n 'TRIPLE',\n 'SINGLE_BULL',\n 'DOUBLE_BULL',\n]);\n\n// Default label font size as a fraction of the board radius. Set on the label\n// group so individual <text> can be overridden via the .c-Dartboard-label class.\nconst LABEL_FONT_SCALE = 0.06;\n\nexport class Dartboard {\n private board: BoardState | null = null;\n private sizes: SizeMap | null = null;\n private rendered = false;\n private _disabled = false;\n private dartGroup: D3Sel | null = null;\n private labelGroup: D3Sel | null = null;\n private dartCounts = new Map<string, number>();\n private readonly options: DartboardOptions;\n readonly segments: Segment[];\n readonly rings: Rings;\n\n constructor(\n container: string | HTMLElement,\n options: Partial<DartboardOptions> = {},\n segments: Segment[] = DEFAULT_SEGMENTS,\n rings: Rings = DEFAULT_RINGS,\n ) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.segments = segments;\n this.rings = rings;\n\n const percentSum =\n this.options.missPercent +\n this.options.doublePercent +\n this.options.outerSinglePercent +\n this.options.triplePercent +\n this.options.innerSinglePercent +\n this.options.singleBullPercent +\n this.options.doubleBullPercent;\n\n if (percentSum !== 100) {\n throw new Error(\n `Dartboard: ring percentages must sum to 100 (got ${percentSum})`,\n );\n }\n\n const boardContainer =\n typeof container === 'string'\n ? document.querySelector<HTMLElement>(container)\n : container;\n\n if (!boardContainer) {\n throw new Error(`Dartboard: container not found`);\n }\n\n const padding = this.options.padding;\n const boardSize =\n this.options.size ||\n Math.min(boardContainer.offsetHeight, boardContainer.offsetWidth);\n const width = boardSize - padding * 2;\n const viewBoxSize = boardSize;\n const segmentWidth = 360 / segments.length;\n const radius = width / 2;\n const rotation = segmentWidth / -2;\n\n const wrapper = select(boardContainer)\n .append('div')\n .classed('c-Dartboard', true);\n\n const svg = wrapper\n .append('svg')\n .attr('viewBox', `0 0 ${viewBoxSize} ${viewBoxSize}`)\n .attr('role', 'group')\n .attr('aria-label', 'Dartboard')\n .append('g')\n .attr(\n 'transform',\n `translate(${viewBoxSize / 2}, ${viewBoxSize / 2}) rotate(${rotation})`,\n );\n\n const pieFn = pie<Bed>()\n .sort((a, b) => (a.position ?? 0) - (b.position ?? 0))\n .value(() => segmentWidth);\n\n this.board = {\n container: boardContainer,\n wrapper,\n width,\n height: width,\n radius,\n rotation,\n segmentWidth,\n svg,\n pie: pieFn,\n };\n\n this.sizes = {\n miss: radius * asPercent(this.options.missPercent),\n double: radius * asPercent(this.options.doublePercent),\n outerSingle: radius * asPercent(this.options.outerSinglePercent),\n triple: radius * asPercent(this.options.triplePercent),\n innerSingle: radius * asPercent(this.options.innerSinglePercent),\n singleBull: radius * asPercent(this.options.singleBullPercent),\n doubleBull: radius * asPercent(this.options.doubleBullPercent),\n };\n }\n\n render(): this {\n if (!this.board || !this.sizes) return this;\n if (this.rendered) return this;\n this.rendered = true;\n\n const { board, sizes } = this;\n\n let innerRadius = 0;\n let outerRadius = innerRadius + sizes.doubleBull;\n this.renderBeds(\n board,\n this.rings.DOUBLE_BULL,\n [{ segment: 25, color: 'Dark' }],\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.singleBull;\n this.renderBeds(\n board,\n this.rings.SINGLE_BULL,\n [{ segment: 25, color: 'Light' }],\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.innerSingle;\n this.renderBeds(\n board,\n this.rings.INNER_SINGLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.triple;\n this.renderBeds(\n board,\n this.rings.TRIPLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.outerSingle;\n this.renderBeds(\n board,\n this.rings.OUTER_SINGLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = outerRadius;\n outerRadius = innerRadius + sizes.double;\n this.renderBeds(\n board,\n this.rings.DOUBLE,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n innerRadius = board.radius - sizes.miss;\n outerRadius = board.radius;\n this.renderMissRing(\n board,\n this.rings.MISS,\n this.segments,\n outerRadius,\n innerRadius,\n );\n\n return this;\n }\n\n destroy(): void {\n if (this.board) {\n // Remove all D3 event listeners before removing from DOM\n this.board.wrapper.selectAll('.c-Dartboard-bed')\n .on('pointerup', null)\n .on('pointerenter', null)\n .on('pointerleave', null)\n .on('keydown', null);\n this.board.wrapper.remove();\n this.board = null;\n this.sizes = null;\n this.rendered = false;\n this.dartGroup = null;\n this.labelGroup = null;\n this.dartCounts.clear();\n }\n }\n\n get disabled(): boolean {\n return this._disabled;\n }\n\n disable(): this {\n this._disabled = true;\n if (this.board) {\n this.board.wrapper.classed('is-disabled', true);\n }\n return this;\n }\n\n enable(): this {\n this._disabled = false;\n if (this.board) {\n this.board.wrapper.classed('is-disabled', false);\n }\n return this;\n }\n\n throwAt(target: BedInput): void {\n if (!this.board || this._disabled) return;\n\n const targets = this.resolveTargets([target]);\n if (targets.length === 0) return;\n const first = targets[0];\n\n if (!this.isValidTarget(first)) {\n throw new Error(`Dartboard: invalid throw target — segment ${first.segment} does not exist`);\n }\n\n const ring = this.rings[first.ring];\n const bed: Bed = { segment: first.segment, color: 'Dark', ring };\n const detail = buildThrowDetail(bed);\n\n this.board.container.dispatchEvent(\n new CustomEvent<ThrowDetail>('throw', { detail }),\n );\n }\n\n highlight(targets: BedInput[], options: HighlightOptions = {}): void {\n if (!this.board) return;\n const className = options.className || 'is-highlighted';\n\n for (const target of this.resolveTargets(targets)) {\n const ring = this.rings[target.ring];\n const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;\n this.board.wrapper.selectAll(selector).classed(className, true);\n }\n }\n\n unhighlight(targets: BedInput[], options: HighlightOptions = {}): void {\n if (!this.board) return;\n const className = options.className || 'is-highlighted';\n\n for (const target of this.resolveTargets(targets)) {\n const ring = this.rings[target.ring];\n const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;\n this.board.wrapper.selectAll(selector).classed(className, false);\n }\n }\n\n reset(className = 'is-highlighted'): void {\n if (!this.board) return;\n this.board.wrapper.selectAll(`.${className}`).classed(className, false);\n }\n\n addDart(targets: BedInput[]): this {\n if (!this.board || !this.sizes) return this;\n\n const resolved = this.resolveTargets(targets);\n const group = this.ensureDartGroup();\n const dartRadius = this.sizes.doubleBull * 0.25;\n\n for (const target of resolved) {\n const key = bedKey(target);\n const count = this.dartCounts.get(key) ?? 0;\n this.dartCounts.set(key, count + 1);\n\n const { centroid, midAngle, innerR, outerR } = this.bedGeometry(target);\n let cx: number;\n let cy: number;\n\n if (isBull(target.ring)) {\n // Distribute bull darts evenly around the ring at its midpoint radius.\n const midR = (innerR + outerR) / 2;\n const angle = (count * 2 * Math.PI) / DART_OFFSETS.length;\n cx = midR * Math.sin(angle);\n cy = -midR * Math.cos(angle);\n } else {\n const [radialFrac, tangentialFrac] = DART_OFFSETS[count % DART_OFFSETS.length];\n const thickness = outerR - innerR;\n const midRadius = (innerR + outerR) / 2;\n const segWidthRad = (this.board.segmentWidth * Math.PI) / 180;\n // Arc half-width at the midpoint radius — limits tangential offsets\n // so darts stay within the segment's angular bounds.\n const arcHalfWidth = midRadius * Math.sin(segWidthRad / 2);\n\n // Radial unit (pointing outward from center)\n const radX = Math.sin(midAngle);\n const radY = -Math.cos(midAngle);\n // Tangential unit (perpendicular, along the arc)\n const tanX = Math.cos(midAngle);\n const tanY = Math.sin(midAngle);\n\n cx = centroid[0] + radialFrac * thickness * radX + tangentialFrac * arcHalfWidth * tanX;\n cy = centroid[1] + radialFrac * thickness * radY + tangentialFrac * arcHalfWidth * tanY;\n }\n\n group\n .append('circle')\n .classed('c-Dartboard-dart', true)\n .attr('cx', cx)\n .attr('cy', cy)\n .attr('r', dartRadius)\n .attr('data-bed', key);\n }\n\n return this;\n }\n\n removeDarts(targets?: BedInput[]): this {\n if (!this.dartGroup) return this;\n\n if (!targets) {\n this.dartGroup.selectAll('.c-Dartboard-dart').remove();\n this.dartCounts.clear();\n return this;\n }\n\n const resolved = this.resolveTargets(targets);\n for (const target of resolved) {\n const key = bedKey(target);\n this.dartGroup\n .selectAll(`.c-Dartboard-dart[data-bed=\"${key}\"]`)\n .remove();\n this.dartCounts.delete(key);\n }\n\n return this;\n }\n\n labelBeds(\n labeler: (bed: BedLabelContext) => string | null | undefined,\n options: BedLabelOptions = {},\n ): this {\n if (!this.board || !this.sizes) return this;\n\n const extra = options.className;\n const counterRotation = -this.board.rotation;\n const group = this.ensureLabelGroup();\n\n // Default font size scales with the board; consumers can override per\n // <text> via the .c-Dartboard-label class since it's set on the group.\n group.attr('font-size', this.board.radius * LABEL_FONT_SCALE);\n\n // Full replace — wipe previous labels so repeated calls never accumulate.\n group.selectAll('*').remove();\n\n for (const ringKey of SCORING_RINGS) {\n const ring = this.rings[ringKey];\n if (!ring) continue;\n\n const beds: Bed[] = isBull(ringKey)\n ? [{ segment: 25, color: ringKey === 'DOUBLE_BULL' ? 'Dark' : 'Light', ring }]\n : addRingToSegments(ring, this.segments);\n\n // Per-ring (not per-bed): scoring-colored rings keep light text via CSS.\n const scoring = SCORING_COLOR_RINGS.has(ringKey) ? ' c-Dartboard-label--scoring' : '';\n\n for (const bed of beds) {\n // Same formula buildThrowDetail uses; avoids allocating a throwaway\n // ThrowDetail per bed on this (per-dart) re-label path.\n const score = bed.segment * bed.ring.multiplier;\n const text = labeler({ ...bed, score });\n if (text == null || text === '') continue;\n\n const [x, y] = this.labelPosition({ segment: bed.segment, ring: ringKey });\n const className = `c-Dartboard-label is${bed.color}${scoring}${extra ? ` ${extra}` : ''}`;\n\n group\n .append('text')\n .attr('class', className)\n .attr('data-bed', bedKey({ segment: bed.segment, ring: ringKey }))\n .attr('x', x)\n .attr('y', y)\n .attr('dy', '.35em')\n .attr('text-anchor', 'middle')\n // Counter-rotate the board's group rotation so labels read upright.\n .attr('transform', `rotate(${counterRotation}, ${x}, ${y})`)\n .text(text);\n }\n }\n\n // Keep dart markers above labels regardless of call order.\n this.dartGroup?.raise();\n\n return this;\n }\n\n clearBedLabels(): this {\n if (this.labelGroup) {\n this.labelGroup.remove();\n this.labelGroup = null;\n }\n return this;\n }\n\n private ensureDartGroup(): D3Sel {\n if (!this.dartGroup) {\n this.dartGroup = this.board!.svg\n .append('g')\n .classed('c-Dartboard-darts', true);\n }\n return this.dartGroup;\n }\n\n private ensureLabelGroup(): D3Sel {\n if (!this.labelGroup) {\n this.labelGroup = this.board!.svg\n .append('g')\n .classed('c-Dartboard-labels', true);\n }\n return this.labelGroup;\n }\n\n private ringRadii(ringKey: RingKey): [number, number] {\n const s = this.sizes!;\n const doubleBullOuter = s.doubleBull;\n const singleBullOuter = doubleBullOuter + s.singleBull;\n const innerSingleOuter = singleBullOuter + s.innerSingle;\n const tripleOuter = innerSingleOuter + s.triple;\n const outerSingleOuter = tripleOuter + s.outerSingle;\n const doubleOuter = outerSingleOuter + s.double;\n\n switch (ringKey) {\n case 'DOUBLE_BULL': return [0, doubleBullOuter];\n case 'SINGLE_BULL': return [doubleBullOuter, singleBullOuter];\n case 'INNER_SINGLE': return [singleBullOuter, innerSingleOuter];\n case 'TRIPLE': return [innerSingleOuter, tripleOuter];\n case 'OUTER_SINGLE': return [tripleOuter, outerSingleOuter];\n case 'DOUBLE': return [outerSingleOuter, doubleOuter];\n case 'MISS': return [this.board!.radius - s.miss, this.board!.radius];\n }\n }\n\n private bedGeometry(target: Required<BedTarget>): {\n centroid: [number, number];\n midAngle: number;\n innerR: number;\n outerR: number;\n } {\n const [innerR, outerR] = this.ringRadii(target.ring);\n\n if (target.segment === 25) {\n return { centroid: [0, 0], midAngle: 0, innerR, outerR };\n }\n\n const seg = this.segments.find((s) => s.segment === target.segment);\n const segWidthRad = (this.board!.segmentWidth * Math.PI) / 180;\n const midAngle = ((seg?.position ?? 1) - 0.5) * segWidthRad;\n const midRadius = (innerR + outerR) / 2;\n\n return {\n centroid: [\n midRadius * Math.sin(midAngle),\n -midRadius * Math.cos(midAngle),\n ],\n midAngle,\n innerR,\n outerR,\n };\n }\n\n // Where a bed's label sits. Reuses bedGeometry for the 80 segment beds and\n // the double bull (center). Single bull would also resolve to [0,0] and\n // collide with the double bull label, so it's pushed to the bottom of its\n // ring (6 o'clock) instead.\n private labelPosition(target: Required<BedTarget>): [number, number] {\n if (target.ring === 'SINGLE_BULL') {\n const [innerR, outerR] = this.ringRadii('SINGLE_BULL');\n return [0, (innerR + outerR) / 2];\n }\n return this.bedGeometry(target).centroid;\n }\n\n private isValidTarget(target: Required<BedTarget>): boolean {\n if (isBull(target.ring)) return target.segment === 25;\n return this.segments.some((s) => s.segment === target.segment);\n }\n\n private resolveTargets(inputs: BedInput[]): Required<BedTarget>[] {\n return inputs.flatMap((input): Required<BedTarget>[] => {\n if (typeof input === 'number') {\n return parseBed(String(input), this.rings, this.segments) as Required<BedTarget>[];\n }\n if (typeof input === 'string') {\n return parseBed(input, this.rings, this.segments) as Required<BedTarget>[];\n }\n if (!input.ring) {\n return parseBed(String(input.segment), this.rings, this.segments) as Required<BedTarget>[];\n }\n return [input as Required<BedTarget>];\n });\n }\n\n private dispatchThrow(bed: Bed): void {\n if (!this.board || this._disabled) return;\n const detail = buildThrowDetail(bed);\n this.board.container.dispatchEvent(\n new CustomEvent<ThrowDetail>('throw', { detail }),\n );\n }\n\n private dispatchHover(bed: Bed, hovering: boolean): void {\n if (!this.board || this._disabled) return;\n const throwDetail = buildThrowDetail(bed);\n const detail: HoverDetail = { ...throwDetail, hovering };\n this.board.container.dispatchEvent(\n new CustomEvent<HoverDetail>('hover', { detail }),\n );\n }\n\n private renderBeds(\n board: BoardState,\n ring: Ring,\n segments: Segment[],\n outerRadius: number,\n innerRadius: number,\n ): void {\n const className = `c-Dartboard-${ring.name}`;\n const arcFn = arc<PieArcDatum<Bed>>()\n .outerRadius(outerRadius)\n .innerRadius(innerRadius);\n\n const beds = board.svg\n .append('g')\n .classed(className, true)\n .selectAll<SVGGElement, PieArcDatum<Bed>>('arc')\n .data(board.pie(addRingToSegments(ring, segments)))\n .enter()\n .append('g')\n .attr(\n 'class',\n (d: PieArcDatum<Bed>) =>\n `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} ${className}--${d.data.segment} is${d.data.color}`,\n )\n .attr('role', 'button')\n .attr('tabindex', '0')\n .attr('aria-label', (d: PieArcDatum<Bed>) =>\n buildAriaLabel(d.data),\n )\n .on(\n 'pointerup',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n this.dispatchThrow(d.data);\n },\n )\n .on(\n 'pointerenter',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n select(_event.currentTarget as Element).classed('is-hovered', true);\n this.dispatchHover(d.data, true);\n },\n )\n .on(\n 'pointerleave',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n select(_event.currentTarget as Element).classed('is-hovered', false);\n this.dispatchHover(d.data, false);\n },\n )\n .on(\n 'keydown',\n (_event: KeyboardEvent, d: PieArcDatum<Bed>) => {\n if (_event.key === 'Enter' || _event.key === ' ') {\n _event.preventDefault();\n this.dispatchThrow(d.data);\n }\n },\n );\n\n beds.append('path').attr('d', arcFn as never);\n }\n\n private renderMissRing(\n board: BoardState,\n ring: Ring,\n segments: Segment[],\n outerRadius: number,\n innerRadius: number,\n ): void {\n const missArc = arc<PieArcDatum<Bed>>()\n .outerRadius(outerRadius)\n .innerRadius(innerRadius);\n\n const missBeds = board.svg\n .append('g')\n .classed('c-Dartboard-miss', true)\n .selectAll<SVGGElement, PieArcDatum<Bed>>('arc')\n .data(board.pie(addRingToSegments(ring, segments)))\n .enter()\n .append('g')\n .attr(\n 'class',\n (d: PieArcDatum<Bed>) =>\n `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} c-Dartboard-miss--${d.data.segment}`,\n )\n .attr('role', 'button')\n .attr('tabindex', '0')\n .attr('aria-label', (d: PieArcDatum<Bed>) =>\n buildAriaLabel(d.data),\n )\n .on(\n 'pointerup',\n (_event: PointerEvent, d: PieArcDatum<Bed>) => {\n this.dispatchThrow(d.data);\n },\n )\n .on(\n 'keydown',\n (_event: KeyboardEvent, d: PieArcDatum<Bed>) => {\n if (_event.key === 'Enter' || _event.key === ' ') {\n _event.preventDefault();\n this.dispatchThrow(d.data);\n }\n },\n );\n\n missBeds.append('path').attr('d', missArc as never);\n\n const determineRotation = (bedData: Bed) =>\n -board.rotation + board.segmentWidth * ((bedData.position ?? 0) - 1);\n\n const determineX = (d: PieArcDatum<Bed>) =>\n missArc.centroid(d)[0];\n\n const determineY = (d: PieArcDatum<Bed>) =>\n missArc.centroid(d)[1];\n\n missBeds\n .append('text')\n .classed('c-Dartboard-missLabel', true)\n .attr('x', (d: PieArcDatum<Bed>) => determineX(d))\n .attr('y', (d: PieArcDatum<Bed>) => determineY(d))\n .attr('dy', '.35em')\n .attr(\n 'transform',\n (d: PieArcDatum<Bed>) =>\n `rotate(${determineRotation(d.data)}, ${determineX(d)}, ${determineY(d)})`,\n )\n .attr('text-anchor', 'middle')\n .text((d: PieArcDatum<Bed>) => d.data.segment);\n }\n}\n","import type { DartboardOptions, Rings, Segment } from './types';\n\nexport const DEFAULT_OPTIONS: DartboardOptions = {\n size: null,\n padding: 2,\n missPercent: 10,\n doublePercent: 10,\n outerSinglePercent: 25,\n triplePercent: 10,\n innerSinglePercent: 30,\n singleBullPercent: 8,\n doubleBullPercent: 7,\n};\n\nexport const DEFAULT_RINGS: Rings = {\n MISS: { name: 'miss', abbr: 'M', multiplier: 0 },\n DOUBLE: { name: 'double', abbr: 'D', multiplier: 2 },\n OUTER_SINGLE: { name: 'outerSingle', abbr: 'S', multiplier: 1 },\n TRIPLE: { name: 'triple', abbr: 'T', multiplier: 3 },\n INNER_SINGLE: { name: 'innerSingle', abbr: 'S', multiplier: 1 },\n SINGLE_BULL: { name: 'singleBull', abbr: 'B', multiplier: 1 },\n DOUBLE_BULL: { name: 'doubleBull', abbr: 'DB', multiplier: 2 },\n};\n\nexport const MOBILE_OPTIONS: Partial<DartboardOptions> = {\n missPercent: 7,\n doublePercent: 14,\n outerSinglePercent: 21,\n triplePercent: 14,\n innerSinglePercent: 26,\n singleBullPercent: 9,\n doubleBullPercent: 9,\n};\n\nexport const DEFAULT_SEGMENTS: Segment[] = [\n { segment: 20, position: 1, color: 'Dark' },\n { segment: 1, position: 2, color: 'Light' },\n { segment: 18, position: 3, color: 'Dark' },\n { segment: 4, position: 4, color: 'Light' },\n { segment: 13, position: 5, color: 'Dark' },\n { segment: 6, position: 6, color: 'Light' },\n { segment: 10, position: 7, color: 'Dark' },\n { segment: 15, position: 8, color: 'Light' },\n { segment: 2, position: 9, color: 'Dark' },\n { segment: 17, position: 10, color: 'Light' },\n { segment: 3, position: 11, color: 'Dark' },\n { segment: 19, position: 12, color: 'Light' },\n { segment: 7, position: 13, color: 'Dark' },\n { segment: 16, position: 14, color: 'Light' },\n { segment: 8, position: 15, color: 'Dark' },\n { segment: 11, position: 16, color: 'Light' },\n { segment: 14, position: 17, color: 'Dark' },\n { segment: 9, position: 18, color: 'Light' },\n { segment: 12, position: 19, color: 'Dark' },\n { segment: 5, position: 20, color: 'Light' },\n];\n","import type { Ring, Rings, Segment, Bed, BedTarget, ThrowDetail } from './types';\n\nexport const SCORING_RINGS: (keyof Rings)[] = [\n 'DOUBLE',\n 'TRIPLE',\n 'OUTER_SINGLE',\n 'INNER_SINGLE',\n 'SINGLE_BULL',\n 'DOUBLE_BULL',\n];\n\nexport function buildThrowDetail(bed: Bed): ThrowDetail {\n return {\n bed: bed.ring.abbr + bed.segment,\n segment: bed.segment,\n ring: bed.ring.name,\n score: bed.segment * bed.ring.multiplier,\n };\n}\n\nexport function asPercent(n: number): number {\n return parseFloat((n / 100).toFixed(2));\n}\n\nexport function addRingToSegments(ring: Ring, segments: Segment[]): Bed[] {\n return segments.map((segment) => ({ ...segment, ring }));\n}\n\nexport function parseBed(\n bed: string,\n rings: Rings,\n segments?: Segment[],\n): BedTarget[] {\n // 'miss' keyword — target all segments in the miss ring\n if (bed.toLowerCase() === 'miss') {\n if (!segments) {\n throw new Error(\n 'parseBed: segments are required to resolve the \"miss\" keyword',\n );\n }\n return segments.map((s) => ({ segment: s.segment, ring: 'MISS' as keyof Rings }));\n }\n\n // Number-only string — target all scoring rings on that segment\n const numOnly = bed.match(/^(\\d+)$/);\n if (numOnly) {\n const segment = parseInt(numOnly[1], 10);\n return SCORING_RINGS\n .filter((key) => key in rings)\n .map((ring) => ({ segment, ring }));\n }\n\n const match = bed.match(/^([A-Za-z]+)(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid bed format: \"${bed}\"`);\n }\n\n const [, abbr, numStr] = match.map((s, i) => i === 1 ? s.toUpperCase() : s);\n const segment = parseInt(numStr, 10);\n\n const targets: BedTarget[] = [];\n for (const [key, ring] of Object.entries(rings)) {\n if (ring.abbr === abbr) {\n targets.push({ segment, ring: key as keyof Rings });\n }\n }\n\n if (targets.length === 0) {\n throw new Error(`Unknown bed abbreviation: \"${abbr}\"`);\n }\n\n return targets;\n}\n"],"mappings":"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","names":["segment"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tungstenstudio/dartboard-input",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Interactive SVG dartboard input component built with D3",
5
5
  "type": "module",
6
6
  "sideEffects": [