@tungstenstudio/dartboard-input 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +294 -0
- package/dist/index.cjs +450 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +91 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +415 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
interface Ring {
|
|
2
|
+
name: string;
|
|
3
|
+
abbr: string;
|
|
4
|
+
multiplier: number;
|
|
5
|
+
}
|
|
6
|
+
interface Rings {
|
|
7
|
+
MISS: Ring;
|
|
8
|
+
DOUBLE: Ring;
|
|
9
|
+
OUTER_SINGLE: Ring;
|
|
10
|
+
TRIPLE: Ring;
|
|
11
|
+
INNER_SINGLE: Ring;
|
|
12
|
+
SINGLE_BULL: Ring;
|
|
13
|
+
DOUBLE_BULL: Ring;
|
|
14
|
+
}
|
|
15
|
+
interface Segment {
|
|
16
|
+
segment: number;
|
|
17
|
+
position?: number;
|
|
18
|
+
color: 'Dark' | 'Light';
|
|
19
|
+
}
|
|
20
|
+
interface Bed extends Segment {
|
|
21
|
+
ring: Ring;
|
|
22
|
+
}
|
|
23
|
+
interface DartboardOptions {
|
|
24
|
+
size: number | null;
|
|
25
|
+
missPercent: number;
|
|
26
|
+
doublePercent: number;
|
|
27
|
+
outerSinglePercent: number;
|
|
28
|
+
triplePercent: number;
|
|
29
|
+
innerSinglePercent: number;
|
|
30
|
+
singleBullPercent: number;
|
|
31
|
+
doubleBullPercent: number;
|
|
32
|
+
}
|
|
33
|
+
interface ThrowDetail {
|
|
34
|
+
bed: string;
|
|
35
|
+
ring: string;
|
|
36
|
+
score: number;
|
|
37
|
+
}
|
|
38
|
+
interface HoverDetail {
|
|
39
|
+
bed: string;
|
|
40
|
+
ring: string;
|
|
41
|
+
score: number;
|
|
42
|
+
hovering: boolean;
|
|
43
|
+
}
|
|
44
|
+
interface BedTarget {
|
|
45
|
+
segment: number;
|
|
46
|
+
ring?: keyof Rings;
|
|
47
|
+
}
|
|
48
|
+
type BedInput = BedTarget | string | number;
|
|
49
|
+
interface HighlightOptions {
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare class Dartboard {
|
|
54
|
+
private board;
|
|
55
|
+
private sizes;
|
|
56
|
+
private rendered;
|
|
57
|
+
private _disabled;
|
|
58
|
+
private readonly options;
|
|
59
|
+
readonly segments: Segment[];
|
|
60
|
+
readonly rings: Rings;
|
|
61
|
+
constructor(container: string | HTMLElement, options?: Partial<DartboardOptions>, segments?: Segment[], rings?: Rings);
|
|
62
|
+
render(): this;
|
|
63
|
+
destroy(): void;
|
|
64
|
+
get disabled(): boolean;
|
|
65
|
+
disable(): this;
|
|
66
|
+
enable(): this;
|
|
67
|
+
throwAt(target: BedInput): void;
|
|
68
|
+
highlight(targets: BedInput[], options?: HighlightOptions): void;
|
|
69
|
+
unhighlight(targets: BedInput[], options?: HighlightOptions): void;
|
|
70
|
+
reset(className?: string): void;
|
|
71
|
+
private isValidTarget;
|
|
72
|
+
private resolveTargets;
|
|
73
|
+
private dispatchThrow;
|
|
74
|
+
private dispatchHover;
|
|
75
|
+
private renderBeds;
|
|
76
|
+
private renderMissRing;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
declare const DEFAULT_OPTIONS: DartboardOptions;
|
|
80
|
+
declare const DEFAULT_RINGS: Rings;
|
|
81
|
+
declare const MOBILE_OPTIONS: Partial<DartboardOptions>;
|
|
82
|
+
declare const DEFAULT_SEGMENTS: Segment[];
|
|
83
|
+
|
|
84
|
+
declare function buildThrowDetail(bed: Bed): ThrowDetail;
|
|
85
|
+
declare function asPercent(n: number): number;
|
|
86
|
+
declare function addRingToSegments(ring: Ring, segments: Segment[]): Bed[];
|
|
87
|
+
declare function parseBed(bed: string, rings: Rings, segments?: Segment[]): BedTarget[];
|
|
88
|
+
|
|
89
|
+
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 };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
interface Ring {
|
|
2
|
+
name: string;
|
|
3
|
+
abbr: string;
|
|
4
|
+
multiplier: number;
|
|
5
|
+
}
|
|
6
|
+
interface Rings {
|
|
7
|
+
MISS: Ring;
|
|
8
|
+
DOUBLE: Ring;
|
|
9
|
+
OUTER_SINGLE: Ring;
|
|
10
|
+
TRIPLE: Ring;
|
|
11
|
+
INNER_SINGLE: Ring;
|
|
12
|
+
SINGLE_BULL: Ring;
|
|
13
|
+
DOUBLE_BULL: Ring;
|
|
14
|
+
}
|
|
15
|
+
interface Segment {
|
|
16
|
+
segment: number;
|
|
17
|
+
position?: number;
|
|
18
|
+
color: 'Dark' | 'Light';
|
|
19
|
+
}
|
|
20
|
+
interface Bed extends Segment {
|
|
21
|
+
ring: Ring;
|
|
22
|
+
}
|
|
23
|
+
interface DartboardOptions {
|
|
24
|
+
size: number | null;
|
|
25
|
+
missPercent: number;
|
|
26
|
+
doublePercent: number;
|
|
27
|
+
outerSinglePercent: number;
|
|
28
|
+
triplePercent: number;
|
|
29
|
+
innerSinglePercent: number;
|
|
30
|
+
singleBullPercent: number;
|
|
31
|
+
doubleBullPercent: number;
|
|
32
|
+
}
|
|
33
|
+
interface ThrowDetail {
|
|
34
|
+
bed: string;
|
|
35
|
+
ring: string;
|
|
36
|
+
score: number;
|
|
37
|
+
}
|
|
38
|
+
interface HoverDetail {
|
|
39
|
+
bed: string;
|
|
40
|
+
ring: string;
|
|
41
|
+
score: number;
|
|
42
|
+
hovering: boolean;
|
|
43
|
+
}
|
|
44
|
+
interface BedTarget {
|
|
45
|
+
segment: number;
|
|
46
|
+
ring?: keyof Rings;
|
|
47
|
+
}
|
|
48
|
+
type BedInput = BedTarget | string | number;
|
|
49
|
+
interface HighlightOptions {
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare class Dartboard {
|
|
54
|
+
private board;
|
|
55
|
+
private sizes;
|
|
56
|
+
private rendered;
|
|
57
|
+
private _disabled;
|
|
58
|
+
private readonly options;
|
|
59
|
+
readonly segments: Segment[];
|
|
60
|
+
readonly rings: Rings;
|
|
61
|
+
constructor(container: string | HTMLElement, options?: Partial<DartboardOptions>, segments?: Segment[], rings?: Rings);
|
|
62
|
+
render(): this;
|
|
63
|
+
destroy(): void;
|
|
64
|
+
get disabled(): boolean;
|
|
65
|
+
disable(): this;
|
|
66
|
+
enable(): this;
|
|
67
|
+
throwAt(target: BedInput): void;
|
|
68
|
+
highlight(targets: BedInput[], options?: HighlightOptions): void;
|
|
69
|
+
unhighlight(targets: BedInput[], options?: HighlightOptions): void;
|
|
70
|
+
reset(className?: string): void;
|
|
71
|
+
private isValidTarget;
|
|
72
|
+
private resolveTargets;
|
|
73
|
+
private dispatchThrow;
|
|
74
|
+
private dispatchHover;
|
|
75
|
+
private renderBeds;
|
|
76
|
+
private renderMissRing;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
declare const DEFAULT_OPTIONS: DartboardOptions;
|
|
80
|
+
declare const DEFAULT_RINGS: Rings;
|
|
81
|
+
declare const MOBILE_OPTIONS: Partial<DartboardOptions>;
|
|
82
|
+
declare const DEFAULT_SEGMENTS: Segment[];
|
|
83
|
+
|
|
84
|
+
declare function buildThrowDetail(bed: Bed): ThrowDetail;
|
|
85
|
+
declare function asPercent(n: number): number;
|
|
86
|
+
declare function addRingToSegments(ring: Ring, segments: Segment[]): Bed[];
|
|
87
|
+
declare function parseBed(bed: string, rings: Rings, segments?: Segment[]): BedTarget[];
|
|
88
|
+
|
|
89
|
+
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 };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
// src/dartboard.ts
|
|
2
|
+
import { select } from "d3-selection";
|
|
3
|
+
import { arc, pie } from "d3-shape";
|
|
4
|
+
|
|
5
|
+
// src/constants.ts
|
|
6
|
+
var DEFAULT_OPTIONS = {
|
|
7
|
+
size: null,
|
|
8
|
+
missPercent: 10,
|
|
9
|
+
doublePercent: 10,
|
|
10
|
+
outerSinglePercent: 25,
|
|
11
|
+
triplePercent: 10,
|
|
12
|
+
innerSinglePercent: 30,
|
|
13
|
+
singleBullPercent: 8,
|
|
14
|
+
doubleBullPercent: 7
|
|
15
|
+
};
|
|
16
|
+
var DEFAULT_RINGS = {
|
|
17
|
+
MISS: { name: "miss", abbr: "M", multiplier: 0 },
|
|
18
|
+
DOUBLE: { name: "double", abbr: "D", multiplier: 2 },
|
|
19
|
+
OUTER_SINGLE: { name: "outerSingle", abbr: "S", multiplier: 1 },
|
|
20
|
+
TRIPLE: { name: "triple", abbr: "T", multiplier: 3 },
|
|
21
|
+
INNER_SINGLE: { name: "innerSingle", abbr: "S", multiplier: 1 },
|
|
22
|
+
SINGLE_BULL: { name: "singleBull", abbr: "B", multiplier: 1 },
|
|
23
|
+
DOUBLE_BULL: { name: "doubleBull", abbr: "DB", multiplier: 2 }
|
|
24
|
+
};
|
|
25
|
+
var MOBILE_OPTIONS = {
|
|
26
|
+
missPercent: 7,
|
|
27
|
+
doublePercent: 14,
|
|
28
|
+
outerSinglePercent: 21,
|
|
29
|
+
triplePercent: 14,
|
|
30
|
+
innerSinglePercent: 26,
|
|
31
|
+
singleBullPercent: 9,
|
|
32
|
+
doubleBullPercent: 9
|
|
33
|
+
};
|
|
34
|
+
var DEFAULT_SEGMENTS = [
|
|
35
|
+
{ segment: 20, position: 1, color: "Dark" },
|
|
36
|
+
{ segment: 1, position: 2, color: "Light" },
|
|
37
|
+
{ segment: 18, position: 3, color: "Dark" },
|
|
38
|
+
{ segment: 4, position: 4, color: "Light" },
|
|
39
|
+
{ segment: 13, position: 5, color: "Dark" },
|
|
40
|
+
{ segment: 6, position: 6, color: "Light" },
|
|
41
|
+
{ segment: 10, position: 7, color: "Dark" },
|
|
42
|
+
{ segment: 15, position: 8, color: "Light" },
|
|
43
|
+
{ segment: 2, position: 9, color: "Dark" },
|
|
44
|
+
{ segment: 17, position: 10, color: "Light" },
|
|
45
|
+
{ segment: 3, position: 11, color: "Dark" },
|
|
46
|
+
{ segment: 19, position: 12, color: "Light" },
|
|
47
|
+
{ segment: 7, position: 13, color: "Dark" },
|
|
48
|
+
{ segment: 16, position: 14, color: "Light" },
|
|
49
|
+
{ segment: 8, position: 15, color: "Dark" },
|
|
50
|
+
{ segment: 11, position: 16, color: "Light" },
|
|
51
|
+
{ segment: 14, position: 17, color: "Dark" },
|
|
52
|
+
{ segment: 9, position: 18, color: "Light" },
|
|
53
|
+
{ segment: 12, position: 19, color: "Dark" },
|
|
54
|
+
{ segment: 5, position: 20, color: "Light" }
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
// src/utils.ts
|
|
58
|
+
var SCORING_RINGS = [
|
|
59
|
+
"DOUBLE",
|
|
60
|
+
"TRIPLE",
|
|
61
|
+
"OUTER_SINGLE",
|
|
62
|
+
"INNER_SINGLE",
|
|
63
|
+
"SINGLE_BULL",
|
|
64
|
+
"DOUBLE_BULL"
|
|
65
|
+
];
|
|
66
|
+
function buildThrowDetail(bed) {
|
|
67
|
+
return {
|
|
68
|
+
bed: bed.ring.abbr + bed.segment,
|
|
69
|
+
ring: bed.ring.name,
|
|
70
|
+
score: bed.segment * bed.ring.multiplier
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function asPercent(n) {
|
|
74
|
+
return parseFloat((n / 100).toFixed(2));
|
|
75
|
+
}
|
|
76
|
+
function addRingToSegments(ring, segments) {
|
|
77
|
+
return segments.map((segment) => ({ ...segment, ring }));
|
|
78
|
+
}
|
|
79
|
+
function parseBed(bed, rings, segments) {
|
|
80
|
+
if (bed.toLowerCase() === "miss") {
|
|
81
|
+
if (!segments) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
'parseBed: segments are required to resolve the "miss" keyword'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return segments.map((s) => ({ segment: s.segment, ring: "MISS" }));
|
|
87
|
+
}
|
|
88
|
+
const numOnly = bed.match(/^(\d+)$/);
|
|
89
|
+
if (numOnly) {
|
|
90
|
+
const segment2 = parseInt(numOnly[1], 10);
|
|
91
|
+
return SCORING_RINGS.filter((key) => key in rings).map((ring) => ({ segment: segment2, ring }));
|
|
92
|
+
}
|
|
93
|
+
const match = bed.match(/^([A-Za-z]+)(\d+)$/);
|
|
94
|
+
if (!match) {
|
|
95
|
+
throw new Error(`Invalid bed format: "${bed}"`);
|
|
96
|
+
}
|
|
97
|
+
const [, abbr, numStr] = match.map((s, i) => i === 1 ? s.toUpperCase() : s);
|
|
98
|
+
const segment = parseInt(numStr, 10);
|
|
99
|
+
const targets = [];
|
|
100
|
+
for (const [key, ring] of Object.entries(rings)) {
|
|
101
|
+
if (ring.abbr === abbr) {
|
|
102
|
+
targets.push({ segment, ring: key });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (targets.length === 0) {
|
|
106
|
+
throw new Error(`Unknown bed abbreviation: "${abbr}"`);
|
|
107
|
+
}
|
|
108
|
+
return targets;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/dartboard.ts
|
|
112
|
+
function buildAriaLabel(bed) {
|
|
113
|
+
const detail = buildThrowDetail(bed);
|
|
114
|
+
return `${detail.bed}, scores ${detail.score}`;
|
|
115
|
+
}
|
|
116
|
+
var Dartboard = class {
|
|
117
|
+
constructor(container, options = {}, segments = DEFAULT_SEGMENTS, rings = DEFAULT_RINGS) {
|
|
118
|
+
this.board = null;
|
|
119
|
+
this.sizes = null;
|
|
120
|
+
this.rendered = false;
|
|
121
|
+
this._disabled = false;
|
|
122
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
123
|
+
this.segments = segments;
|
|
124
|
+
this.rings = rings;
|
|
125
|
+
const percentSum = this.options.missPercent + this.options.doublePercent + this.options.outerSinglePercent + this.options.triplePercent + this.options.innerSinglePercent + this.options.singleBullPercent + this.options.doubleBullPercent;
|
|
126
|
+
if (percentSum !== 100) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Dartboard: ring percentages must sum to 100 (got ${percentSum})`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const boardContainer = typeof container === "string" ? document.querySelector(container) : container;
|
|
132
|
+
if (!boardContainer) {
|
|
133
|
+
throw new Error(`Dartboard: container not found`);
|
|
134
|
+
}
|
|
135
|
+
const width = this.options.size || Math.min(boardContainer.offsetHeight, boardContainer.offsetWidth);
|
|
136
|
+
const segmentWidth = 360 / segments.length;
|
|
137
|
+
const radius = width / 2;
|
|
138
|
+
const rotation = segmentWidth / -2;
|
|
139
|
+
const wrapper = select(boardContainer).append("div").classed("c-Dartboard", true);
|
|
140
|
+
const svg = wrapper.append("svg").attr("viewBox", `0 0 ${width} ${width}`).attr("role", "group").attr("aria-label", "Dartboard").append("g").attr(
|
|
141
|
+
"transform",
|
|
142
|
+
`translate(${radius}, ${radius}) rotate(${rotation})`
|
|
143
|
+
);
|
|
144
|
+
const pieFn = pie().sort((a, b) => (a.position ?? 0) - (b.position ?? 0)).value(() => segmentWidth);
|
|
145
|
+
this.board = {
|
|
146
|
+
container: boardContainer,
|
|
147
|
+
wrapper,
|
|
148
|
+
width,
|
|
149
|
+
height: width,
|
|
150
|
+
radius,
|
|
151
|
+
rotation,
|
|
152
|
+
segmentWidth,
|
|
153
|
+
svg,
|
|
154
|
+
pie: pieFn
|
|
155
|
+
};
|
|
156
|
+
this.sizes = {
|
|
157
|
+
miss: radius * asPercent(this.options.missPercent),
|
|
158
|
+
double: radius * asPercent(this.options.doublePercent),
|
|
159
|
+
outerSingle: radius * asPercent(this.options.outerSinglePercent),
|
|
160
|
+
triple: radius * asPercent(this.options.triplePercent),
|
|
161
|
+
innerSingle: radius * asPercent(this.options.innerSinglePercent),
|
|
162
|
+
singleBull: radius * asPercent(this.options.singleBullPercent),
|
|
163
|
+
doubleBull: radius * asPercent(this.options.doubleBullPercent)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
render() {
|
|
167
|
+
if (!this.board || !this.sizes) return this;
|
|
168
|
+
if (this.rendered) return this;
|
|
169
|
+
this.rendered = true;
|
|
170
|
+
const { board, sizes } = this;
|
|
171
|
+
let innerRadius = 0;
|
|
172
|
+
let outerRadius = innerRadius + sizes.doubleBull;
|
|
173
|
+
this.renderBeds(
|
|
174
|
+
board,
|
|
175
|
+
this.rings.DOUBLE_BULL,
|
|
176
|
+
[{ segment: 25, color: "Dark" }],
|
|
177
|
+
outerRadius,
|
|
178
|
+
innerRadius
|
|
179
|
+
);
|
|
180
|
+
innerRadius = outerRadius;
|
|
181
|
+
outerRadius = innerRadius + sizes.singleBull;
|
|
182
|
+
this.renderBeds(
|
|
183
|
+
board,
|
|
184
|
+
this.rings.SINGLE_BULL,
|
|
185
|
+
[{ segment: 25, color: "Light" }],
|
|
186
|
+
outerRadius,
|
|
187
|
+
innerRadius
|
|
188
|
+
);
|
|
189
|
+
innerRadius = outerRadius;
|
|
190
|
+
outerRadius = innerRadius + sizes.innerSingle;
|
|
191
|
+
this.renderBeds(
|
|
192
|
+
board,
|
|
193
|
+
this.rings.INNER_SINGLE,
|
|
194
|
+
this.segments,
|
|
195
|
+
outerRadius,
|
|
196
|
+
innerRadius
|
|
197
|
+
);
|
|
198
|
+
innerRadius = outerRadius;
|
|
199
|
+
outerRadius = innerRadius + sizes.triple;
|
|
200
|
+
this.renderBeds(
|
|
201
|
+
board,
|
|
202
|
+
this.rings.TRIPLE,
|
|
203
|
+
this.segments,
|
|
204
|
+
outerRadius,
|
|
205
|
+
innerRadius
|
|
206
|
+
);
|
|
207
|
+
innerRadius = outerRadius;
|
|
208
|
+
outerRadius = innerRadius + sizes.outerSingle;
|
|
209
|
+
this.renderBeds(
|
|
210
|
+
board,
|
|
211
|
+
this.rings.OUTER_SINGLE,
|
|
212
|
+
this.segments,
|
|
213
|
+
outerRadius,
|
|
214
|
+
innerRadius
|
|
215
|
+
);
|
|
216
|
+
innerRadius = outerRadius;
|
|
217
|
+
outerRadius = innerRadius + sizes.double;
|
|
218
|
+
this.renderBeds(
|
|
219
|
+
board,
|
|
220
|
+
this.rings.DOUBLE,
|
|
221
|
+
this.segments,
|
|
222
|
+
outerRadius,
|
|
223
|
+
innerRadius
|
|
224
|
+
);
|
|
225
|
+
innerRadius = board.radius - sizes.miss;
|
|
226
|
+
outerRadius = board.radius;
|
|
227
|
+
this.renderMissRing(
|
|
228
|
+
board,
|
|
229
|
+
this.rings.MISS,
|
|
230
|
+
this.segments,
|
|
231
|
+
outerRadius,
|
|
232
|
+
innerRadius
|
|
233
|
+
);
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
destroy() {
|
|
237
|
+
if (this.board) {
|
|
238
|
+
this.board.wrapper.selectAll(".c-Dartboard-bed").on("pointerup", null).on("pointerenter", null).on("pointerleave", null).on("keydown", null);
|
|
239
|
+
this.board.wrapper.remove();
|
|
240
|
+
this.board = null;
|
|
241
|
+
this.sizes = null;
|
|
242
|
+
this.rendered = false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
get disabled() {
|
|
246
|
+
return this._disabled;
|
|
247
|
+
}
|
|
248
|
+
disable() {
|
|
249
|
+
this._disabled = true;
|
|
250
|
+
if (this.board) {
|
|
251
|
+
this.board.wrapper.classed("is-disabled", true);
|
|
252
|
+
}
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
enable() {
|
|
256
|
+
this._disabled = false;
|
|
257
|
+
if (this.board) {
|
|
258
|
+
this.board.wrapper.classed("is-disabled", false);
|
|
259
|
+
}
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
262
|
+
throwAt(target) {
|
|
263
|
+
if (!this.board || this._disabled) return;
|
|
264
|
+
const targets = this.resolveTargets([target]);
|
|
265
|
+
if (targets.length === 0) return;
|
|
266
|
+
const first = targets[0];
|
|
267
|
+
if (!this.isValidTarget(first)) {
|
|
268
|
+
throw new Error(`Dartboard: invalid throw target \u2014 segment ${first.segment} does not exist`);
|
|
269
|
+
}
|
|
270
|
+
const ring = this.rings[first.ring];
|
|
271
|
+
const bed = { segment: first.segment, color: "Dark", ring };
|
|
272
|
+
const detail = buildThrowDetail(bed);
|
|
273
|
+
this.board.container.dispatchEvent(
|
|
274
|
+
new CustomEvent("throw", { detail })
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
highlight(targets, options = {}) {
|
|
278
|
+
if (!this.board) return;
|
|
279
|
+
const className = options.className || "is-highlighted";
|
|
280
|
+
for (const target of this.resolveTargets(targets)) {
|
|
281
|
+
const ring = this.rings[target.ring];
|
|
282
|
+
const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;
|
|
283
|
+
this.board.wrapper.selectAll(selector).classed(className, true);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
unhighlight(targets, options = {}) {
|
|
287
|
+
if (!this.board) return;
|
|
288
|
+
const className = options.className || "is-highlighted";
|
|
289
|
+
for (const target of this.resolveTargets(targets)) {
|
|
290
|
+
const ring = this.rings[target.ring];
|
|
291
|
+
const selector = `.c-Dartboard-${ring.name} .c-Dartboard-bed--${target.segment}`;
|
|
292
|
+
this.board.wrapper.selectAll(selector).classed(className, false);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
reset(className = "is-highlighted") {
|
|
296
|
+
if (!this.board) return;
|
|
297
|
+
this.board.wrapper.selectAll(`.${className}`).classed(className, false);
|
|
298
|
+
}
|
|
299
|
+
isValidTarget(target) {
|
|
300
|
+
const isBull = target.ring === "DOUBLE_BULL" || target.ring === "SINGLE_BULL";
|
|
301
|
+
if (isBull) {
|
|
302
|
+
return target.segment === 25;
|
|
303
|
+
}
|
|
304
|
+
return this.segments.some((s) => s.segment === target.segment);
|
|
305
|
+
}
|
|
306
|
+
resolveTargets(inputs) {
|
|
307
|
+
return inputs.flatMap((input) => {
|
|
308
|
+
if (typeof input === "number") {
|
|
309
|
+
return parseBed(String(input), this.rings, this.segments);
|
|
310
|
+
}
|
|
311
|
+
if (typeof input === "string") {
|
|
312
|
+
return parseBed(input, this.rings, this.segments);
|
|
313
|
+
}
|
|
314
|
+
if (!input.ring) {
|
|
315
|
+
return parseBed(String(input.segment), this.rings, this.segments);
|
|
316
|
+
}
|
|
317
|
+
return [input];
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
dispatchThrow(bed) {
|
|
321
|
+
if (!this.board || this._disabled) return;
|
|
322
|
+
const detail = buildThrowDetail(bed);
|
|
323
|
+
this.board.container.dispatchEvent(
|
|
324
|
+
new CustomEvent("throw", { detail })
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
dispatchHover(bed, hovering) {
|
|
328
|
+
if (!this.board || this._disabled) return;
|
|
329
|
+
const throwDetail = buildThrowDetail(bed);
|
|
330
|
+
const detail = { ...throwDetail, hovering };
|
|
331
|
+
this.board.container.dispatchEvent(
|
|
332
|
+
new CustomEvent("hover", { detail })
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
renderBeds(board, ring, segments, outerRadius, innerRadius) {
|
|
336
|
+
const className = `c-Dartboard-${ring.name}`;
|
|
337
|
+
const arcFn = arc().outerRadius(outerRadius).innerRadius(innerRadius);
|
|
338
|
+
const beds = board.svg.append("g").classed(className, true).selectAll("arc").data(board.pie(addRingToSegments(ring, segments))).enter().append("g").attr(
|
|
339
|
+
"class",
|
|
340
|
+
(d) => `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} ${className}--${d.data.segment} is${d.data.color}`
|
|
341
|
+
).attr("role", "button").attr("tabindex", "0").attr(
|
|
342
|
+
"aria-label",
|
|
343
|
+
(d) => buildAriaLabel(d.data)
|
|
344
|
+
).on(
|
|
345
|
+
"pointerup",
|
|
346
|
+
(_event, d) => {
|
|
347
|
+
this.dispatchThrow(d.data);
|
|
348
|
+
}
|
|
349
|
+
).on(
|
|
350
|
+
"pointerenter",
|
|
351
|
+
(_event, d) => {
|
|
352
|
+
select(_event.currentTarget).classed("is-hovered", true);
|
|
353
|
+
this.dispatchHover(d.data, true);
|
|
354
|
+
}
|
|
355
|
+
).on(
|
|
356
|
+
"pointerleave",
|
|
357
|
+
(_event, d) => {
|
|
358
|
+
select(_event.currentTarget).classed("is-hovered", false);
|
|
359
|
+
this.dispatchHover(d.data, false);
|
|
360
|
+
}
|
|
361
|
+
).on(
|
|
362
|
+
"keydown",
|
|
363
|
+
(_event, d) => {
|
|
364
|
+
if (_event.key === "Enter" || _event.key === " ") {
|
|
365
|
+
_event.preventDefault();
|
|
366
|
+
this.dispatchThrow(d.data);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
beds.append("path").attr("d", arcFn);
|
|
371
|
+
}
|
|
372
|
+
renderMissRing(board, ring, segments, outerRadius, innerRadius) {
|
|
373
|
+
const missArc = arc().outerRadius(outerRadius).innerRadius(innerRadius);
|
|
374
|
+
const missBeds = board.svg.append("g").classed("c-Dartboard-miss", true).selectAll("arc").data(board.pie(addRingToSegments(ring, segments))).enter().append("g").attr(
|
|
375
|
+
"class",
|
|
376
|
+
(d) => `c-Dartboard-bed c-Dartboard-bed--${d.data.segment} c-Dartboard-miss--${d.data.segment}`
|
|
377
|
+
).attr("role", "button").attr("tabindex", "0").attr(
|
|
378
|
+
"aria-label",
|
|
379
|
+
(d) => buildAriaLabel(d.data)
|
|
380
|
+
).on(
|
|
381
|
+
"pointerup",
|
|
382
|
+
(_event, d) => {
|
|
383
|
+
this.dispatchThrow(d.data);
|
|
384
|
+
}
|
|
385
|
+
).on(
|
|
386
|
+
"keydown",
|
|
387
|
+
(_event, d) => {
|
|
388
|
+
if (_event.key === "Enter" || _event.key === " ") {
|
|
389
|
+
_event.preventDefault();
|
|
390
|
+
this.dispatchThrow(d.data);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
missBeds.append("path").attr("d", missArc);
|
|
395
|
+
const determineRotation = (bedData) => -board.rotation + board.segmentWidth * ((bedData.position ?? 0) - 1);
|
|
396
|
+
const determineX = (d) => missArc.centroid(d)[0];
|
|
397
|
+
const determineY = (d) => missArc.centroid(d)[1];
|
|
398
|
+
missBeds.append("text").classed("c-Dartboard-missLabel", true).attr("x", (d) => determineX(d)).attr("y", (d) => determineY(d)).attr("dy", ".35em").attr(
|
|
399
|
+
"transform",
|
|
400
|
+
(d) => `rotate(${determineRotation(d.data)}, ${determineX(d)}, ${determineY(d)})`
|
|
401
|
+
).attr("text-anchor", "middle").text((d) => d.data.segment);
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
export {
|
|
405
|
+
DEFAULT_OPTIONS,
|
|
406
|
+
DEFAULT_RINGS,
|
|
407
|
+
DEFAULT_SEGMENTS,
|
|
408
|
+
Dartboard,
|
|
409
|
+
MOBILE_OPTIONS,
|
|
410
|
+
addRingToSegments,
|
|
411
|
+
asPercent,
|
|
412
|
+
buildThrowDetail,
|
|
413
|
+
parseBed
|
|
414
|
+
};
|
|
415
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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\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 @typescript-eslint/no-explicit-any */\ninterface BoardState {\n container: HTMLElement;\n wrapper: Selection<any, any, any, any>;\n width: number;\n height: number;\n radius: number;\n rotation: number;\n segmentWidth: number;\n svg: Selection<any, any, any, any>;\n pie: ReturnType<typeof pie<Bed>>;\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\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\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 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 width =\n this.options.size ||\n Math.min(boardContainer.offsetHeight, boardContainer.offsetWidth);\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 ${width} ${width}`)\n .attr('role', 'group')\n .attr('aria-label', 'Dartboard')\n .append('g')\n .attr(\n 'transform',\n `translate(${radius}, ${radius}) 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 }\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 private isValidTarget(target: Required<BedTarget>): boolean {\n const isBull = target.ring === 'DOUBLE_BULL' || target.ring === 'SINGLE_BULL';\n if (isBull) {\n return target.segment === 25;\n }\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 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 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"]}
|