@tungstenstudio/dartboard-input 1.0.0-rc.6 → 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 +71 -0
- package/dist/index.cjs +57 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +18 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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);
|
package/dist/index.cjs.map
CHANGED
|
@@ -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":"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","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;
|
package/dist/index.css.map
CHANGED
|
@@ -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;
|
|
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"]}
|