@salloomd/teeth-selector 0.0.3 → 0.0.4

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.
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import type { TeethPreviewProps } from "../lib/types";
2
+ import type { TeethPreviewProps } from "@lib/types";
3
3
  declare const TeethPreview: React.ForwardRefExoticComponent<TeethPreviewProps & React.RefAttributes<SVGSVGElement>>;
4
4
  export { TeethPreview };
5
5
  //# sourceMappingURL=teeth-preview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"teeth-preview.d.ts","sourceRoot":"","sources":["../../src/components/teeth-preview.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAMtD,QAAA,MAAM,YAAY,yFAiHjB,CAAC;AAIF,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"teeth-preview.d.ts","sourceRoot":"","sources":["../../src/components/teeth-preview.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAMpD,QAAA,MAAM,YAAY,yFAiHjB,CAAC;AAIF,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
- import { type Tooth } from "../lib/teeth";
3
- import type { TeethSelectorProps, TeethSelectorState } from "../lib/types";
2
+ import { type Tooth } from "@lib/teeth";
3
+ import type { TeethSelectorProps, TeethSelectorState } from "@lib/types";
4
4
  type SelectionAction = {
5
5
  type: "toggle-tooth";
6
6
  toothId: number;
@@ -1 +1 @@
1
- {"version":3,"file":"teeth-selector.d.ts","sourceRoot":"","sources":["../../src/components/teeth-selector.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAIL,KAAK,KAAK,EACX,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAM3E,KAAK,eAAe,GAChB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC;AA2G/C,UAAU,yBAAyB;IACjC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC1C,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC;CAC/D;AAKD,wBAAgB,uBAAuB,8BAQtC;AAMD,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAYD,QAAA,MAAM,aAAa,2FAwHlB,CAAC;AAQF,UAAU,eAAe;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,CACZ,KAAK,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,EACpE,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,KAC9B,KAAK,CAAC,SAAS,CAAC;IACrB,YAAY,CAAC,EAAE,CACb,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE;YACR,EAAE,EAAE,MAAM,CAAC;YACX,EAAE,EAAE,MAAM,CAAC;YACX,EAAE,EAAE,MAAM,CAAC;YACX,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;KACH,EACD,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,KAC9B,KAAK,CAAC,SAAS,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,QAAA,MAAM,UAAU,uFA4Mf,CAAC;AAIF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"teeth-selector.d.ts","sourceRoot":"","sources":["../../src/components/teeth-selector.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAIL,KAAK,KAAK,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMzE,KAAK,eAAe,GAChB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC;AA2G/C,UAAU,yBAAyB;IACjC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC1C,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC;CAC/D;AAKD,wBAAgB,uBAAuB,8BAQtC;AAMD,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAYD,QAAA,MAAM,aAAa,2FAwHlB,CAAC;AAQF,UAAU,eAAe;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,CACZ,KAAK,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,EACpE,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,KAC9B,KAAK,CAAC,SAAS,CAAC;IACrB,YAAY,CAAC,EAAE,CACb,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE;YACR,EAAE,EAAE,MAAM,CAAC;YACX,EAAE,EAAE,MAAM,CAAC;YACX,EAAE,EAAE,MAAM,CAAC;YACX,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;KACH,EACD,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,KAC9B,KAAK,CAAC,SAAS,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,QAAA,MAAM,UAAU,uFA4Mf,CAAC;AAIF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { TeethSelector, TeethChart, useTeethSelectorContext, } from "./components/teeth-selector";
2
- export { TeethPreview } from "./components/teeth-preview";
3
- export { teethData, adjacentTeethPairs, getTeethInRange, parseTeethSelection, formatTeethSelection, sameSide, } from "./lib/teeth";
4
- export type { Tooth } from "./lib/teeth";
5
- export type { TeethSelectorProps, TeethSelectorState, TeethSelectorRenderProps, TeethChartProps, TeethPreviewProps, ToothRenderProps, BridgeRenderProps, } from "./lib/types";
1
+ export { TeethSelector, TeethChart, useTeethSelectorContext, } from "@components/teeth-selector";
2
+ export { TeethPreview } from "@components/teeth-preview";
3
+ export { teethData, adjacentTeethPairs, getTeethInRange, parseTeethSelection, formatTeethSelection, sameSide, } from "@lib/teeth";
4
+ export type { Tooth } from "@lib/teeth";
5
+ export type { TeethSelectorProps, TeethSelectorState, TeethSelectorRenderProps, TeethChartProps, TeethPreviewProps, ToothRenderProps, BridgeRenderProps, } from "@lib/types";
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,UAAU,EACV,uBAAuB,GACxB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,QAAQ,GACT,MAAM,aAAa,CAAC;AAGrB,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,UAAU,EACV,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,QAAQ,GACT,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
package/dist/lib.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { teethData, adjacentTeethPairs, getTeethInRange, parseTeethSelection, formatTeethSelection, sameSide, } from "@lib/teeth";
2
+ export type { Tooth } from "@lib/teeth";
3
+ //# sourceMappingURL=lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,QAAQ,GACT,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
package/dist/lib.js ADDED
@@ -0,0 +1,391 @@
1
+ // src/lib/teeth.ts
2
+ var teethData = [
3
+ {
4
+ order: 0,
5
+ id: 18,
6
+ name: "Upper Right Third Molar",
7
+ position: "upper",
8
+ side: "right",
9
+ d: "m30 344c-1.8-0.6-3.4-1.6-4.7-3-1.5-1.6-2.7-3.7-3.6-6.5-0.63-2.1-0.94-4.2-0.94-6.1a16 16 0 0 1 0.73-4.7c0.87-2.8 2.3-4.7 3.4-6.2 0.78-1 1.3-1.8 1.4-2.4 0.026-1.2-0.17-2.8-0.37-4.5-0.4-3.4-0.86-7.3-0.044-9.8l5e-3 -0.015 3e-3 -0.015c0.51-2.5 2.5-4.7 6-6.4 2.8-1.4 5.8-2.1 7.1-2.4 0.21-0.049 0.37-0.088 0.48-0.12 4.7-1.1 10-0.73 14-0.23 4.9 0.62 9.3 1.7 12 2.4l0.1 0.034c7.1 2.3 14 7.5 18 14 4.3 7.1 5.5 15 3.3 23-1.1 3.7-2.7 7-4.8 9.8s-4.7 5-7.6 6.6c-5.6 3-12 3.3-19 1-2.4-0.76-4.8-1.8-7.2-3.2l-0.021-0.012-0.023-7e-3c-2.5-0.82-5.4-0.73-8.4-0.63-3.1 0.1-6.3 0.21-9.1-0.72z"
10
+ },
11
+ {
12
+ order: 1,
13
+ id: 17,
14
+ name: "Upper Right Second Molar",
15
+ position: "upper",
16
+ side: "right",
17
+ d: "m39 279c-3-0.96-6.3-2.5-8.5-6.2-1.5-2.9-2.2-10-1.8-21l6e-3 -0.19c0.026-1.2-0.17-2.8-0.37-4.5-0.4-3.4-0.86-7.3-0.045-9.8 0.4-1.1 1.9-2.4 4.2-3.7 1.8-1 3.9-1.9 4.6-2.1l6e-3 -1e-3 6e-3 -2e-3c6.5-2.1 24 2.2 29 4l0.13 0.043c3.6 1.2 6.7 2.7 9.3 4.7s4.7 4.3 6.2 6.9c1.5 2.7 2.4 5.8 2.6 9.1 0.22 3.4-0.26 7.2-1.4 11-1.9 6.6-5.5 11-11 14-3 1.6-6.6 2.4-11 2.5-3.7 0.027-7.8-0.66-12-2l-0.24-0.078c-2-0.63-3.9-1.1-5.7-1.6-1.7-0.43-3.4-0.83-4.9-1.3z"
18
+ },
19
+ {
20
+ order: 2,
21
+ id: 16,
22
+ name: "Upper Right First Molar",
23
+ position: "upper",
24
+ side: "right",
25
+ d: "m72 229c-0.72 0-1.5-0.018-2.2-0.053-8.3-0.39-16-2.8-21-6.7-3-2.1-5.3-4.7-6.9-7.5-1.7-3.1-2.6-6.6-2.6-10 0-2.7 0.77-5.6 2-7.3 0.41-0.58 1-1.2 1.7-1.9 1.2-1.2 2.6-2.7 3.4-4.4 0.41-0.98 0.45-2.4 0.5-4 0.072-2.5 0.16-5.6 1.8-8.1l4e-3 -7e-3 4e-3 -6e-3c1.4-2.4 3.6-4.1 6.6-5.1 2.4-0.79 5.4-1.1 9.5-1.1 4.7 0.79 9 2.2 13 4.3 3.9 2 7.3 4.6 10 7.5 2.8 3 4.9 6.4 6.3 10a27 27 0 0 1 1.6 12c-1.3 16-8.7 23-23 23z"
26
+ },
27
+ {
28
+ order: 3,
29
+ id: 15,
30
+ name: "Upper Right Second Premolar",
31
+ position: "upper",
32
+ side: "right",
33
+ d: "m73 168a39 39 0 0 1-0.2-0.1c-3.9-1.9-7.1-4.1-9.5-6.5-2.3-2.3-3.8-4.7-4.6-7.2a13 13 0 0 1-0.52-5.8c0.24-1.9 0.89-3.8 1.9-5.7 2.2-4.1 6.2-6.9 11-8 2.6-0.56 5.3-0.64 8.1-0.23 3 0.42 6 1.4 9 2.8 0.71 0.34 1.4 0.72 2.1 1.1 3.2 1.8 6.1 3.9 8.5 6.2 2.4 2.2 4.3 4.5 5.7 6.8s2.2 4.6 2.5 6.8c0.29 2.2-0.014 4.3-0.9 6.2-0.62 1.3-1.6 2.5-2.9 3.6-1.3 1-2.9 1.9-4.8 2.5-3.5 1.2-7.6 1.7-12 1.3-4.7-0.38-9.4-1.7-14-3.8z"
34
+ },
35
+ {
36
+ order: 4,
37
+ id: 14,
38
+ name: "Upper Right First Premolar",
39
+ position: "upper",
40
+ side: "right",
41
+ d: "m118 135a6.3 6.3 0 0 1-0.3 0.28c-2.4 2.1-6.6 2.8-12 2-5.4-0.85-11-3.3-16-6.8-5.7-4.1-9.3-9.3-9.7-14-0.28-3.4 0.92-6.6 3.5-9.2a16 16 0 0 1 1.6-1.4c8.7-6.6 22-4.9 29 3.7a26 26 0 0 1 4.2 7 27 27 0 0 1 1.8 7.4c0.47 4.9-0.57 9.1-2.8 11z"
42
+ },
43
+ {
44
+ order: 5,
45
+ id: 13,
46
+ name: "Upper Right Canine",
47
+ position: "upper",
48
+ side: "right",
49
+ d: "m144 104c-0.09 0.084-0.19 0.17-0.29 0.24-1.2 0.9-2.9 1.5-5.1 1.8-2.2 0.33-4.9 0.3-7.7-0.086-6.4-0.88-13-3.5-19-7.5-2.2-1.6-4-3.4-5.2-5.4a13 13 0 0 1-1.8-5.8c-0.18-2.6 0.36-5.4 1.6-8 1.1-2.3 2.6-4.5 4.3-6.1 0.36-0.34 0.73-0.65 1.1-0.92 1.4-1.1 4.4-2.7 7.8-4 2.8-1.1 6.8-2.2 10-1.8h8e-3c2.5 0.26 4.4 1.4 5.8 3.6 1.3 2 2.1 4.4 2.9 6.8 0.19 0.58 0.36 1.1 0.55 1.7 0.52 1.5 1.1 3.1 1.7 4.7 1.6 4.3 3.2 8.8 3.9 12 0.83 4.1 0.48 6.9-1.1 8.3z"
50
+ },
51
+ {
52
+ order: 6,
53
+ id: 12,
54
+ name: "Upper Right Lateral Incisor",
55
+ position: "upper",
56
+ side: "right",
57
+ d: "m168 82c-2.9 1.2-6.4 0.91-11-1-3.7-1.7-7.9-4.5-13-8.8-0.54-0.44-1.1-0.86-1.6-1.3-2-1.6-3.8-3-5-4.4-1.3-1.5-1.9-2.7-1.7-4 0.17-1.6 1.4-3.3 3.9-5.3 2.6-2.1 6.8-4.5 13-7.4a105 105 0 0 1 5.9-2.8c6.4-2.7 10-3.3 13-2 2.5 1.6 2.7 5.7 3 11 0.073 1.5 0.15 3 0.28 4.6 0.61 7.4 0.52 12-0.3 15-0.86 3.2-2.6 5.2-5.8 6.6l-0.04 0.017z"
58
+ },
59
+ {
60
+ order: 7,
61
+ id: 11,
62
+ name: "Upper Right Central Incisor",
63
+ position: "upper",
64
+ side: "right",
65
+ d: "m205 74c-10 1.1-18-9.3-23-18l-0.3-0.5c-1.4-2.4-2.7-4.6-3.5-6.7-0.85-2.3-0.88-4.2-0.11-5.9 0.9-2 2.9-3.6 6.2-5.1 3.7-1.6 8.9-3 16-4.2a82 82 0 0 1 5-0.69c4.3-0.46 7.9-0.39 11 0.2 2.4 0.52 4.2 1.5 5.4 2.8 1.3 1.5 1.9 3.6 1.7 6.3-0.14 2.4-0.79 5-1.4 7.4-0.23 0.89-0.45 1.7-0.63 2.5l-0.29 1.3c-0.98 4.3-2.1 9.3-4.2 13-2.4 4.5-5.8 6.9-10 7.6-0.21 0.03-0.42 0.057-0.62 0.079z"
66
+ },
67
+ {
68
+ order: 8,
69
+ id: 21,
70
+ name: "Upper Left Central Incisor",
71
+ position: "upper",
72
+ side: "left",
73
+ d: "m246 32h1e-3c1.5 0.16 3.2 0.4 5 0.69 7 1.2 12 2.5 16 4.2 3.3 1.5 5.3 3.1 6.2 5.1 0.77 1.7 0.73 3.5-0.12 5.9-0.77 2.1-2.1 4.4-3.5 6.7l-0.3 0.5c-5.4 9.2-13 20-23 18a15 15 0 0 1-0.62-0.079c-4.5-0.66-7.9-3.2-10-7.6-2.1-3.9-3.2-8.9-4.2-13l-0.29-1.3c-0.18-0.81-0.4-1.7-0.63-2.5-0.63-2.5-1.3-5-1.4-7.4-0.15-2.7 0.41-4.8 1.7-6.3 1.2-1.3 3-2.3 5.4-2.8 2.7-0.58 6.3-0.65 11-0.19z"
74
+ },
75
+ {
76
+ order: 9,
77
+ id: 22,
78
+ name: "Upper Left Lateral Incisor",
79
+ position: "upper",
80
+ side: "left",
81
+ d: "m292 47h1e-3c1.8 0.75 3.8 1.7 5.9 2.8 5.8 2.9 9.9 5.4 13 7.4 2.5 1.9 3.7 3.7 3.9 5.3 0.14 1.3-0.41 2.5-1.7 4-1.2 1.4-3 2.8-5 4.4-0.51 0.41-1 0.83-1.6 1.3-5.3 4.3-9.5 7.1-13 8.8-4.3 1.9-7.8 2.3-11 1l-0.04-0.018c-3.2-1.4-4.9-3.3-5.8-6.6-0.82-3.1-0.91-7.3-0.3-15 0.13-1.6 0.21-3.1 0.28-4.6 0.26-5.3 0.47-9.4 3-11 2.2-1.4 6.2-0.77 13 2z"
82
+ },
83
+ {
84
+ order: 10,
85
+ id: 23,
86
+ name: "Upper Left Canine",
87
+ position: "upper",
88
+ side: "left",
89
+ d: "m339 73c1.7 1.6 3.2 3.7 4.3 6.1 1.2 2.7 1.8 5.5 1.6 8a13 13 0 0 1-1.8 5.8c-1.2 2-2.9 3.8-5.2 5.4-5.6 3.9-12 6.6-19 7.5-2.8 0.38-5.4 0.41-7.7 0.086-2.2-0.32-3.9-0.96-5.1-1.8a4 4 0 0 1-0.29-0.24c-1.6-1.4-1.9-4.2-1.1-8.3 0.75-3.7 2.4-8.2 3.9-12a301 301 0 0 0 1.7-4.7c0.18-0.54 0.36-1.1 0.55-1.7 0.77-2.4 1.6-4.8 2.9-6.8 1.5-2.2 3.4-3.4 5.8-3.6h5e-3l4e-3 -1e-3c3.3-0.45 7.3 0.71 10 1.8 3.4 1.3 6.4 3 7.8 4 0.36 0.27 0.73 0.58 1.1 0.92z"
90
+ },
91
+ {
92
+ order: 11,
93
+ id: 24,
94
+ name: "Upper Left First Premolar",
95
+ position: "upper",
96
+ side: "left",
97
+ d: "m367 107h1e-3c2.6 2.6 3.8 5.7 3.5 9.2-0.42 5-3.9 10-9.7 14-5 3.6-11 6-16 6.8-5.1 0.81-9.4 0.076-12-2a6.3 6.3 0 0 1-0.3-0.28c-2.2-2.2-3.3-6.5-2.8-11a27 27 0 0 1 1.8-7.4 26 26 0 0 1 4.2-7c7.4-8.7 21-10 29-3.7a16 16 0 0 1 1.6 1.4z"
98
+ },
99
+ {
100
+ order: 12,
101
+ id: 25,
102
+ name: "Upper Left Second Premolar",
103
+ position: "upper",
104
+ side: "left",
105
+ d: "m362 137 2e-3 -1e-3c3-1.5 6-2.4 9-2.8 2.8-0.4 5.6-0.32 8.1 0.24 5 1.1 9 3.9 11 8 1 1.9 1.7 3.8 1.9 5.7a13 13 0 0 1-0.52 5.8c-0.8 2.5-2.4 5-4.6 7.2-2.4 2.4-5.6 4.6-9.5 6.5l-0.2 0.1c-4.3 2.1-9.1 3.4-14 3.8-4.3 0.35-8.5-0.11-12-1.3-1.9-0.65-3.5-1.5-4.8-2.5s-2.3-2.2-2.9-3.6c-0.89-1.9-1.2-4-0.9-6.2 0.28-2.2 1.1-4.5 2.5-6.8s3.3-4.6 5.7-6.8 5.3-4.3 8.5-6.2a37 37 0 0 1 2.1-1.1z"
106
+ },
107
+ {
108
+ order: 13,
109
+ id: 26,
110
+ name: "Upper Left First Molar",
111
+ position: "upper",
112
+ side: "left",
113
+ d: "m378 229c-15 0-22-7.3-23-23-0.33-4.1 0.2-8 1.6-12 1.3-3.7 3.4-7 6.3-10 2.8-3 6.2-5.5 10-7.5 4-2.1 8.4-3.5 13-4.3 4.1 1e-3 7.1 0.36 9.5 1.1 3 0.98 5.2 2.6 6.6 5.1l4e-3 6e-3 4e-3 7e-3c1.6 2.4 1.7 5.5 1.8 8.1 0.046 1.6 0.087 3 0.5 4 0.76 1.8 2.1 3.2 3.4 4.4 0.65 0.66 1.3 1.3 1.7 1.9 1.2 1.7 2 4.5 2 7.3 0 3.8-0.87 7.2-2.6 10-1.6 2.9-3.9 5.4-6.9 7.5-5.5 3.9-13 6.3-21 6.7-0.76 0.035-1.5 0.053-2.2 0.053z"
114
+ },
115
+ {
116
+ order: 14,
117
+ id: 27,
118
+ name: "Upper Left Second Molar",
119
+ position: "upper",
120
+ side: "left",
121
+ d: "m386 236c5.5-1.8 23-6.1 29-4l6e-3 2e-3 6e-3 1e-3c0.74 0.2 2.7 1 4.6 2.1 2.4 1.3 3.8 2.6 4.2 3.7 0.81 2.4 0.36 6.3-0.045 9.8-0.2 1.7-0.4 3.4-0.37 4.5l7e-3 0.2c0.36 11-0.3 18-1.8 21-2.2 3.8-5.6 5.3-8.5 6.2-1.5 0.5-3.2 0.9-4.9 1.3-1.9 0.48-3.9 0.97-6 1.6-4.2 1.4-8.3 2.1-12 2-4-0.028-7.5-0.86-11-2.5-5-2.7-8.6-7.4-11-14-1.2-4-1.7-7.8-1.4-11 0.22-3.3 1.1-6.4 2.6-9.1 1.5-2.6 3.5-5 6.2-6.9 2.6-1.9 5.7-3.5 9.3-4.7l0.13-0.043z"
122
+ },
123
+ {
124
+ order: 15,
125
+ id: 28,
126
+ name: "Upper Left Third Molar",
127
+ position: "upper",
128
+ side: "left",
129
+ d: "m384 294c2.3-0.74 6.7-1.8 12-2.4 4-0.5 9.7-0.87 14 0.23 0.11 0.03 0.28 0.068 0.48 0.12 1.3 0.3 4.3 1 7.1 2.4 3.5 1.7 5.5 3.9 6 6.4l3e-3 0.015 5e-3 0.015c0.82 2.4 0.36 6.3-0.044 9.8-0.2 1.7-0.4 3.4-0.37 4.5 0.021 0.63 0.58 1.4 1.4 2.4 1.1 1.4 2.6 3.4 3.4 6.2 0.48 1.5 0.72 3.1 0.73 4.7 8e-3 1.9-0.31 4-0.94 6.1-0.91 2.8-2.1 4.9-3.6 6.5-1.3 1.4-2.8 2.4-4.7 3-2.8 0.93-6 0.82-9.1 0.72-3-0.1-5.9-0.2-8.4 0.63l-0.023 7e-3 -0.021 0.011c-2.4 1.3-4.8 2.4-7.2 3.2-7 2.3-14 1.9-19-1-2.9-1.5-5.5-3.8-7.6-6.6s-3.8-6.1-4.8-9.8c-2.2-7.6-1.1-16 3.3-23 4-6.7 10-12 18-14l0.1-0.034z"
130
+ },
131
+ {
132
+ order: 16,
133
+ id: 38,
134
+ name: "Lower Left Third Molar",
135
+ position: "lower",
136
+ side: "left",
137
+ d: "m420 406c1.8 0.6 3.4 1.6 4.7 3 1.5 1.6 2.7 3.7 3.6 6.5 0.63 2.1 0.94 4.2 0.94 6.1a16 16 0 0 1-0.73 4.7c-0.87 2.8-2.3 4.7-3.4 6.2-0.78 1-1.3 1.8-1.4 2.4-0.026 1.2 0.17 2.8 0.37 4.5 0.4 3.4 0.86 7.3 0.044 9.8l-5e-3 0.015-3e-3 0.015c-0.51 2.5-2.5 4.7-6 6.4-2.8 1.4-5.8 2.1-7.1 2.4-0.21 0.049-0.37 0.088-0.48 0.12-4.7 1.1-10 0.73-14 0.23-4.9-0.62-9.3-1.7-12-2.4l-0.1-0.034c-7.1-2.3-14-7.5-18-14-4.3-7.1-5.5-15-3.3-23 1.1-3.7 2.7-7 4.8-9.8s4.7-5 7.6-6.6c5.6-3 12-3.3 19-1 2.4 0.76 4.8 1.8 7.2 3.2l0.021 0.012 0.023 7e-3c2.5 0.82 5.4 0.73 8.4 0.63 3.1-0.1 6.3-0.21 9.1 0.72z"
138
+ },
139
+ {
140
+ order: 17,
141
+ id: 37,
142
+ name: "Lower Left Second Molar",
143
+ position: "lower",
144
+ side: "left",
145
+ d: "m411 471c3 0.96 6.3 2.5 8.5 6.2 1.5 2.9 2.2 10 1.8 21l-6e-3 0.19c-0.026 1.2 0.17 2.8 0.37 4.5 0.4 3.4 0.86 7.3 0.045 9.8-0.4 1.1-1.9 2.4-4.2 3.7-1.8 1-3.9 1.9-4.6 2.1l-6e-3 1e-3 -6e-3 2e-3c-6.5 2.1-24-2.2-29-4a32 32 0 0 1-0.13-0.043c-3.6-1.2-6.7-2.7-9.3-4.7s-4.7-4.3-6.2-6.9c-1.5-2.7-2.4-5.8-2.6-9.1-0.22-3.4 0.26-7.2 1.4-11 1.9-6.6 5.5-11 11-14 3-1.6 6.6-2.4 11-2.5 3.7-0.027 7.8 0.66 12 2l0.24 0.078c2 0.63 3.9 1.1 5.7 1.6 1.7 0.43 3.4 0.83 4.9 1.3z"
146
+ },
147
+ {
148
+ order: 18,
149
+ id: 36,
150
+ name: "Lower Left First Molar",
151
+ position: "lower",
152
+ side: "left",
153
+ d: "m386 578c-4.7-0.79-9-2.2-13-4.3-3.9-2-7.3-4.6-10-7.5-2.8-3-4.9-6.4-6.3-10a27 27 0 0 1-1.6-12c1.3-16 8.7-23 23-23 0.72 0 1.5 0.018 2.2 0.053 8.3 0.39 16 2.8 21 6.7 3 2.1 5.3 4.7 6.9 7.5 1.7 3.1 2.6 6.6 2.6 10 0 2.7-0.77 5.6-2 7.3-0.41 0.58-1 1.2-1.7 1.9-1.2 1.2-2.6 2.7-3.4 4.4-0.41 0.98-0.45 2.4-0.5 4-0.072 2.5-0.16 5.6-1.8 8.1l-8e-3 0.012c-1.4 2.4-3.6 4.1-6.6 5.1-2.4 0.79-5.4 1.1-9.5 1.1z"
154
+ },
155
+ {
156
+ order: 19,
157
+ id: 35,
158
+ name: "Lower Left Second Premolar",
159
+ position: "lower",
160
+ side: "left",
161
+ d: "m377 582 0.2 0.1c3.9 1.9 7.1 4.1 9.5 6.5 2.3 2.3 3.8 4.7 4.6 7.2 0.59 1.9 0.77 3.8 0.52 5.8-0.24 1.9-0.89 3.8-1.9 5.7-2.2 4.1-6.2 6.9-11 8-2.6 0.56-5.3 0.64-8.1 0.23-3-0.42-6-1.4-9-2.8a37 37 0 0 1-2.1-1.1c-3.2-1.8-6.1-3.9-8.5-6.2-2.4-2.2-4.3-4.5-5.7-6.8s-2.2-4.6-2.5-6.8c-0.29-2.2 0.014-4.3 0.9-6.2 0.62-1.3 1.6-2.5 2.9-3.6 1.3-1 2.9-1.9 4.8-2.5 3.5-1.2 7.6-1.7 12-1.3 4.7 0.38 9.4 1.7 14 3.8z"
162
+ },
163
+ {
164
+ order: 20,
165
+ id: 34,
166
+ name: "Lower Left First Premolar",
167
+ position: "lower",
168
+ side: "left",
169
+ d: "m332 615a6.3 6.3 0 0 1 0.3-0.28c2.4-2.1 6.6-2.8 12-2 5.4 0.85 11 3.3 16 6.8 5.7 4.1 9.3 9.3 9.7 14 0.28 3.4-0.92 6.6-3.5 9.2-0.49 0.49-1 0.96-1.6 1.4-8.7 6.6-22 4.9-29-3.7a26 26 0 0 1-4.2-7 27 27 0 0 1-1.8-7.4c-0.47-4.9 0.57-9.1 2.8-11z"
170
+ },
171
+ {
172
+ order: 21,
173
+ id: 33,
174
+ name: "Lower Left Canine",
175
+ position: "lower",
176
+ side: "left",
177
+ d: "m307 647c0.09-0.084 0.19-0.17 0.29-0.24 1.2-0.9 2.9-1.5 5.1-1.8 2.2-0.33 4.9-0.3 7.7 0.086 6.4 0.88 13 3.5 19 7.5 2.2 1.6 4 3.4 5.2 5.4a13 13 0 0 1 1.8 5.8c0.18 2.6-0.36 5.4-1.6 8-1.1 2.3-2.6 4.5-4.3 6.1-0.36 0.34-0.73 0.65-1.1 0.92-1.4 1.1-4.4 2.7-7.8 4-2.8 1.1-6.8 2.2-10 1.8h-8e-3c-2.5-0.26-4.4-1.4-5.8-3.6-1.3-2-2.1-4.4-2.9-6.8-0.19-0.58-0.36-1.1-0.55-1.7a301 301 0 0 0-1.7-4.7c-1.6-4.3-3.2-8.8-3.9-12-0.83-4.1-0.48-6.9 1.1-8.3z"
178
+ },
179
+ {
180
+ order: 22,
181
+ id: 32,
182
+ name: "Lower Left Lateral Incisor",
183
+ position: "lower",
184
+ side: "left",
185
+ d: "m283 669c2.9-1.2 6.4-0.91 11 1 3.7 1.7 7.9 4.5 13 8.8 0.54 0.44 1.1 0.86 1.6 1.3 2 1.6 3.8 3 5 4.4 1.3 1.5 1.9 2.7 1.7 4-0.17 1.6-1.4 3.3-3.9 5.3-2.6 2.1-6.8 4.5-13 7.4a105 105 0 0 1-5.9 2.8c-6.4 2.7-10 3.3-13 2-2.5-1.6-2.7-5.7-3-11-0.073-1.5-0.15-3-0.28-4.6-0.61-7.4-0.52-12 0.3-15 0.86-3.2 2.6-5.2 5.8-6.6l0.04-0.017z"
186
+ },
187
+ {
188
+ order: 23,
189
+ id: 31,
190
+ name: "Lower Left Central Incisor",
191
+ position: "lower",
192
+ side: "left",
193
+ d: "m245 677c10-1.1 18 9.3 23 18l0.3 0.5c1.4 2.4 2.7 4.6 3.5 6.7 0.85 2.3 0.88 4.2 0.11 5.9-0.9 2-2.9 3.6-6.2 5.1-3.7 1.6-8.9 3-16 4.2a82 82 0 0 1-5 0.69c-4.3 0.46-7.9 0.39-11-0.2-2.4-0.52-4.2-1.5-5.4-2.8-1.3-1.5-1.9-3.6-1.7-6.3 0.14-2.4 0.79-5 1.4-7.4a96 96 0 0 0 0.92-3.8c0.98-4.3 2.1-9.3 4.2-13 2.4-4.5 5.8-6.9 10-7.6 0.21-0.03 0.42-0.057 0.62-0.079z"
194
+ },
195
+ {
196
+ order: 24,
197
+ id: 41,
198
+ name: "Lower Right Central Incisor",
199
+ position: "lower",
200
+ side: "right",
201
+ d: "m205 718h-1e-3a82 82 0 0 1-5-0.69c-7-1.2-12-2.5-16-4.2-3.3-1.5-5.3-3.1-6.2-5.1-0.77-1.7-0.73-3.5 0.12-5.9 0.77-2.1 2.1-4.4 3.5-6.7l0.3-0.5c5.4-9.2 13-20 23-18 0.21 0.022 0.42 0.048 0.62 0.079 4.5 0.66 7.9 3.2 10 7.6 2.1 3.9 3.2 8.9 4.2 13l0.29 1.3c0.18 0.81 0.4 1.7 0.63 2.5 0.63 2.5 1.3 5 1.4 7.4 0.15 2.7-0.41 4.8-1.7 6.3-1.2 1.3-3 2.3-5.4 2.8-2.7 0.58-6.3 0.65-11 0.19z"
202
+ },
203
+ {
204
+ order: 25,
205
+ id: 42,
206
+ name: "Lower Right Lateral Incisor",
207
+ position: "lower",
208
+ side: "right",
209
+ d: "m158 704h-1e-3a105 105 0 0 1-5.9-2.8c-5.8-2.9-9.9-5.4-13-7.4-2.5-1.9-3.7-3.7-3.9-5.3-0.14-1.3 0.41-2.5 1.7-4 1.2-1.4 3-2.8 5-4.4 0.51-0.41 1-0.83 1.6-1.3 5.3-4.3 9.5-7.1 13-8.8 4.3-1.9 7.8-2.3 11-1l0.04 0.018c3.2 1.4 4.9 3.3 5.8 6.6 0.82 3.1 0.91 7.3 0.3 15-0.13 1.6-0.21 3.1-0.28 4.6-0.26 5.3-0.47 9.4-3 11-2.2 1.4-6.2 0.77-13-2z"
210
+ },
211
+ {
212
+ order: 26,
213
+ id: 43,
214
+ name: "Lower Right Canine",
215
+ position: "lower",
216
+ side: "right",
217
+ d: "m111 677c-1.7-1.6-3.2-3.7-4.3-6.1-1.2-2.7-1.8-5.5-1.6-8a13 13 0 0 1 1.8-5.8c1.2-2 2.9-3.8 5.2-5.4 5.6-3.9 12-6.6 19-7.5 2.8-0.38 5.4-0.41 7.7-0.086 2.2 0.32 3.9 0.96 5.1 1.8 0.1 0.076 0.2 0.16 0.29 0.24 1.6 1.4 1.9 4.2 1.1 8.3-0.75 3.7-2.4 8.2-3.9 12a300 300 0 0 0-1.7 4.7c-0.18 0.54-0.36 1.1-0.55 1.7-0.77 2.4-1.6 4.8-2.9 6.8-1.5 2.2-3.4 3.4-5.8 3.6h-5e-3l-4e-3 1e-3c-3.3 0.45-7.3-0.71-10-1.8-3.4-1.3-6.4-3-7.8-4a13 13 0 0 1-1.1-0.92z"
218
+ },
219
+ {
220
+ order: 27,
221
+ id: 44,
222
+ name: "Lower Right First Premolar",
223
+ position: "lower",
224
+ side: "right",
225
+ d: "m83 643h-1e-3c-2.6-2.6-3.8-5.7-3.5-9.2 0.42-5 3.9-10 9.7-14 5-3.6 11-6 16-6.8 5.1-0.81 9.4-0.076 12 2 0.1 0.089 0.2 0.18 0.3 0.28 2.2 2.2 3.3 6.5 2.8 11a27 27 0 0 1-1.8 7.4 26 26 0 0 1-4.2 7c-7.4 8.7-21 10-29 3.7a16 16 0 0 1-1.6-1.4z"
226
+ },
227
+ {
228
+ order: 28,
229
+ id: 45,
230
+ name: "Lower Right Second Premolar",
231
+ position: "lower",
232
+ side: "right",
233
+ d: "m88 613-2e-3 1e-3c-3 1.5-6 2.4-9 2.8-2.8 0.4-5.6 0.32-8.1-0.24-5-1.1-9-3.9-11-8-1-1.9-1.7-3.8-1.9-5.7a13 13 0 0 1 0.52-5.8c0.8-2.5 2.4-5 4.6-7.2 2.4-2.4 5.6-4.6 9.5-6.5l0.2-0.1c4.3-2.1 9.1-3.4 14-3.8 4.3-0.35 8.5 0.11 12 1.3 1.9 0.65 3.5 1.5 4.8 2.5s2.3 2.2 2.9 3.6c0.89 1.9 1.2 4 0.9 6.2-0.28 2.2-1.1 4.5-2.5 6.8s-3.3 4.6-5.7 6.8-5.3 4.3-8.5 6.2c-0.7 0.4-1.4 0.78-2.1 1.1z"
234
+ },
235
+ {
236
+ order: 29,
237
+ id: 46,
238
+ name: "Lower Right First Molar",
239
+ position: "lower",
240
+ side: "right",
241
+ d: "m65 578c-4.1-1e-3 -7.1-0.36-9.5-1.1-3-0.98-5.2-2.6-6.6-5.1l-4e-3 -7e-3 -4e-3 -6e-3c-1.6-2.4-1.7-5.5-1.8-8.1-0.047-1.6-0.087-3-0.5-4-0.76-1.8-2.1-3.2-3.4-4.4-0.65-0.66-1.3-1.3-1.7-1.9-1.2-1.7-2-4.5-2-7.3 0-3.8 0.87-7.2 2.6-10 1.6-2.9 3.9-5.4 6.9-7.5 5.5-3.9 13-6.3 21-6.7 0.76-0.035 1.5-0.053 2.2-0.053 15 0 22 7.3 23 23 0.34 4.1-0.2 8-1.6 12-1.3 3.7-3.5 7-6.3 10s-6.2 5.5-10 7.5c-4 2.1-8.4 3.5-13 4.3z"
242
+ },
243
+ {
244
+ order: 30,
245
+ id: 47,
246
+ name: "Lower Right Second Molar",
247
+ position: "lower",
248
+ side: "right",
249
+ d: "m66 515c-5.5 1.8-23 6.1-29 4l-7e-3 -2e-3 -6e-3 -1e-3c-0.74-0.2-2.7-1-4.6-2.1-2.4-1.3-3.8-2.6-4.2-3.7-0.81-2.4-0.36-6.3 0.045-9.8 0.2-1.7 0.4-3.4 0.37-4.5l-7e-3 -0.2c-0.36-11 0.3-18 1.8-21 2.2-3.8 5.6-5.3 8.5-6.2 1.5-0.5 3.2-0.9 4.9-1.3 1.9-0.48 3.9-0.97 6-1.6 4.2-1.4 8.3-2.1 12-2 4 0.028 7.5 0.86 11 2.5 5 2.7 8.6 7.4 11 14 1.2 4 1.7 7.8 1.4 11-0.22 3.3-1.1 6.4-2.6 9.1-1.5 2.6-3.5 5-6.2 6.9-2.6 1.9-5.7 3.5-9.3 4.7l-0.13 0.043z"
250
+ },
251
+ {
252
+ order: 31,
253
+ id: 48,
254
+ name: "Lower Right Third Molar",
255
+ position: "lower",
256
+ side: "right",
257
+ d: "m66 456c-2.3 0.74-6.7 1.8-12 2.4-4 0.5-9.7 0.87-14-0.23a21 21 0 0 0-0.48-0.12c-1.3-0.3-4.3-1-7.1-2.4-3.5-1.7-5.5-3.9-6-6.4l-3e-3 -0.015-5e-3 -0.015c-0.82-2.4-0.36-6.3 0.044-9.8 0.2-1.7 0.4-3.4 0.37-4.5-0.021-0.63-0.58-1.4-1.4-2.4-1.1-1.4-2.6-3.4-3.4-6.2a16 16 0 0 1-0.73-4.7c-8e-3 -1.9 0.31-4 0.94-6.1 0.91-2.8 2.1-4.9 3.6-6.5 1.3-1.4 2.8-2.4 4.7-3 2.8-0.93 6-0.82 9.1-0.72 3 0.1 5.9 0.2 8.4-0.63l0.023-7e-3 0.021-0.011c2.4-1.3 4.8-2.4 7.2-3.2 7-2.3 14-1.9 19 1 2.9 1.5 5.5 3.8 7.6 6.6s3.8 6.1 4.8 9.8c2.2 7.6 1.1 16-3.3 23-4 6.7-10 12-18 14l-0.1 0.034z"
258
+ }
259
+ ];
260
+ var adjacentTeethPairs = [
261
+ [17, 18],
262
+ [16, 17],
263
+ [15, 16],
264
+ [14, 15],
265
+ [13, 14],
266
+ [12, 13],
267
+ [11, 12],
268
+ [11, 21],
269
+ [21, 22],
270
+ [22, 23],
271
+ [23, 24],
272
+ [24, 25],
273
+ [25, 26],
274
+ [26, 27],
275
+ [27, 28],
276
+ [37, 38],
277
+ [36, 37],
278
+ [35, 36],
279
+ [34, 35],
280
+ [33, 34],
281
+ [32, 33],
282
+ [31, 32],
283
+ [31, 41],
284
+ [41, 42],
285
+ [42, 43],
286
+ [43, 44],
287
+ [44, 45],
288
+ [45, 46],
289
+ [46, 47],
290
+ [47, 48]
291
+ ];
292
+ function getTeethInRange(startId, endId) {
293
+ const startTooth = teethData.find((t) => t.id === startId);
294
+ const endTooth = teethData.find((t) => t.id === endId);
295
+ if (!startTooth || !endTooth)
296
+ return [];
297
+ const minOrder = Math.min(startTooth.order, endTooth.order);
298
+ const maxOrder = Math.max(startTooth.order, endTooth.order);
299
+ return teethData.filter((t) => t.order >= minOrder && t.order <= maxOrder).map((t) => t.id);
300
+ }
301
+ function sameSide(tooth1, tooth2) {
302
+ const [a, b] = [tooth1.toString(), tooth2.toString()];
303
+ return a[0] === b[0];
304
+ }
305
+ function parseTeethSelection(input) {
306
+ const selectedTeeth = [];
307
+ const bridges = [];
308
+ const regex = /\[(\d+)\s*,\s*(\d+)\]|(\d+)/g;
309
+ let match;
310
+ while ((match = regex.exec(input)) !== null) {
311
+ if (match[1] && match[2]) {
312
+ let start = Number(match[1]);
313
+ let end = Number(match[2]);
314
+ [start, end] = [Math.min(start, end), Math.max(start, end)];
315
+ const range = [];
316
+ if (sameSide(start, end)) {
317
+ for (let i = start;i <= end; i++) {
318
+ range.push(i);
319
+ }
320
+ } else {
321
+ const crossA = Number(`${start.toString()[0]}1`);
322
+ const crossB = Number(`${end.toString()[0]}1`);
323
+ for (let i = start;i >= crossA; i--) {
324
+ range.push(i);
325
+ }
326
+ for (let i = crossB;i <= end; i++) {
327
+ range.push(i);
328
+ }
329
+ }
330
+ selectedTeeth.push(...range);
331
+ for (let i = 0;i < range.length - 1; i++) {
332
+ bridges.push([range[i], range[i + 1]]);
333
+ }
334
+ } else if (match[3]) {
335
+ selectedTeeth.push(Number(match[3]));
336
+ }
337
+ }
338
+ return { selectedTeeth, bridges };
339
+ }
340
+ function formatTeethSelection(selectedTeeth, bridges) {
341
+ if (!selectedTeeth || selectedTeeth.length === 0) {
342
+ return "";
343
+ }
344
+ const bridgeMap = new Map;
345
+ bridges.forEach(([tooth1, tooth2]) => {
346
+ if (!bridgeMap.has(tooth1))
347
+ bridgeMap.set(tooth1, []);
348
+ if (!bridgeMap.has(tooth2))
349
+ bridgeMap.set(tooth2, []);
350
+ bridgeMap.get(tooth1).push(tooth2);
351
+ bridgeMap.get(tooth2).push(tooth1);
352
+ });
353
+ const result = [];
354
+ let currentGroup = [];
355
+ selectedTeeth.forEach((tooth, index) => {
356
+ if (index === 0) {
357
+ currentGroup.push(tooth);
358
+ return;
359
+ }
360
+ const prevTooth = selectedTeeth[index - 1];
361
+ const hasBridge = bridgeMap.has(prevTooth) && bridgeMap.get(prevTooth).includes(tooth) && Math.abs(selectedTeeth.indexOf(tooth) - selectedTeeth.indexOf(prevTooth)) === 1;
362
+ if (hasBridge) {
363
+ currentGroup.push(tooth);
364
+ } else {
365
+ if (currentGroup.length > 0) {
366
+ if (currentGroup.length === 1) {
367
+ result.push(`${currentGroup[0]}`);
368
+ } else {
369
+ result.push(`[${currentGroup[0]}, ${currentGroup[currentGroup.length - 1]}]`);
370
+ }
371
+ }
372
+ currentGroup = [tooth];
373
+ }
374
+ });
375
+ if (currentGroup.length > 0) {
376
+ if (currentGroup.length === 1) {
377
+ result.push(`${currentGroup[0]}`);
378
+ } else {
379
+ result.push(`[${currentGroup[0]}, ${currentGroup[currentGroup.length - 1]}]`);
380
+ }
381
+ }
382
+ return result.join(", ");
383
+ }
384
+ export {
385
+ teethData,
386
+ sameSide,
387
+ parseTeethSelection,
388
+ getTeethInRange,
389
+ formatTeethSelection,
390
+ adjacentTeethPairs
391
+ };
package/dist/ui.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { TeethSelector, TeethChart, useTeethSelectorContext, } from "@components/teeth-selector";
2
+ export { TeethPreview } from "@components/teeth-preview";
3
+ export type { TeethSelectorProps, TeethSelectorState, TeethSelectorRenderProps, TeethChartProps, TeethPreviewProps, ToothRenderProps, BridgeRenderProps, } from "@lib/types";
4
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,UAAU,EACV,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
package/dist/ui.js ADDED
@@ -0,0 +1,797 @@
1
+ // src/components/teeth-selector.tsx
2
+ import * as React from "react";
3
+
4
+ // src/lib/teeth.ts
5
+ var teethData = [
6
+ {
7
+ order: 0,
8
+ id: 18,
9
+ name: "Upper Right Third Molar",
10
+ position: "upper",
11
+ side: "right",
12
+ d: "m30 344c-1.8-0.6-3.4-1.6-4.7-3-1.5-1.6-2.7-3.7-3.6-6.5-0.63-2.1-0.94-4.2-0.94-6.1a16 16 0 0 1 0.73-4.7c0.87-2.8 2.3-4.7 3.4-6.2 0.78-1 1.3-1.8 1.4-2.4 0.026-1.2-0.17-2.8-0.37-4.5-0.4-3.4-0.86-7.3-0.044-9.8l5e-3 -0.015 3e-3 -0.015c0.51-2.5 2.5-4.7 6-6.4 2.8-1.4 5.8-2.1 7.1-2.4 0.21-0.049 0.37-0.088 0.48-0.12 4.7-1.1 10-0.73 14-0.23 4.9 0.62 9.3 1.7 12 2.4l0.1 0.034c7.1 2.3 14 7.5 18 14 4.3 7.1 5.5 15 3.3 23-1.1 3.7-2.7 7-4.8 9.8s-4.7 5-7.6 6.6c-5.6 3-12 3.3-19 1-2.4-0.76-4.8-1.8-7.2-3.2l-0.021-0.012-0.023-7e-3c-2.5-0.82-5.4-0.73-8.4-0.63-3.1 0.1-6.3 0.21-9.1-0.72z"
13
+ },
14
+ {
15
+ order: 1,
16
+ id: 17,
17
+ name: "Upper Right Second Molar",
18
+ position: "upper",
19
+ side: "right",
20
+ d: "m39 279c-3-0.96-6.3-2.5-8.5-6.2-1.5-2.9-2.2-10-1.8-21l6e-3 -0.19c0.026-1.2-0.17-2.8-0.37-4.5-0.4-3.4-0.86-7.3-0.045-9.8 0.4-1.1 1.9-2.4 4.2-3.7 1.8-1 3.9-1.9 4.6-2.1l6e-3 -1e-3 6e-3 -2e-3c6.5-2.1 24 2.2 29 4l0.13 0.043c3.6 1.2 6.7 2.7 9.3 4.7s4.7 4.3 6.2 6.9c1.5 2.7 2.4 5.8 2.6 9.1 0.22 3.4-0.26 7.2-1.4 11-1.9 6.6-5.5 11-11 14-3 1.6-6.6 2.4-11 2.5-3.7 0.027-7.8-0.66-12-2l-0.24-0.078c-2-0.63-3.9-1.1-5.7-1.6-1.7-0.43-3.4-0.83-4.9-1.3z"
21
+ },
22
+ {
23
+ order: 2,
24
+ id: 16,
25
+ name: "Upper Right First Molar",
26
+ position: "upper",
27
+ side: "right",
28
+ d: "m72 229c-0.72 0-1.5-0.018-2.2-0.053-8.3-0.39-16-2.8-21-6.7-3-2.1-5.3-4.7-6.9-7.5-1.7-3.1-2.6-6.6-2.6-10 0-2.7 0.77-5.6 2-7.3 0.41-0.58 1-1.2 1.7-1.9 1.2-1.2 2.6-2.7 3.4-4.4 0.41-0.98 0.45-2.4 0.5-4 0.072-2.5 0.16-5.6 1.8-8.1l4e-3 -7e-3 4e-3 -6e-3c1.4-2.4 3.6-4.1 6.6-5.1 2.4-0.79 5.4-1.1 9.5-1.1 4.7 0.79 9 2.2 13 4.3 3.9 2 7.3 4.6 10 7.5 2.8 3 4.9 6.4 6.3 10a27 27 0 0 1 1.6 12c-1.3 16-8.7 23-23 23z"
29
+ },
30
+ {
31
+ order: 3,
32
+ id: 15,
33
+ name: "Upper Right Second Premolar",
34
+ position: "upper",
35
+ side: "right",
36
+ d: "m73 168a39 39 0 0 1-0.2-0.1c-3.9-1.9-7.1-4.1-9.5-6.5-2.3-2.3-3.8-4.7-4.6-7.2a13 13 0 0 1-0.52-5.8c0.24-1.9 0.89-3.8 1.9-5.7 2.2-4.1 6.2-6.9 11-8 2.6-0.56 5.3-0.64 8.1-0.23 3 0.42 6 1.4 9 2.8 0.71 0.34 1.4 0.72 2.1 1.1 3.2 1.8 6.1 3.9 8.5 6.2 2.4 2.2 4.3 4.5 5.7 6.8s2.2 4.6 2.5 6.8c0.29 2.2-0.014 4.3-0.9 6.2-0.62 1.3-1.6 2.5-2.9 3.6-1.3 1-2.9 1.9-4.8 2.5-3.5 1.2-7.6 1.7-12 1.3-4.7-0.38-9.4-1.7-14-3.8z"
37
+ },
38
+ {
39
+ order: 4,
40
+ id: 14,
41
+ name: "Upper Right First Premolar",
42
+ position: "upper",
43
+ side: "right",
44
+ d: "m118 135a6.3 6.3 0 0 1-0.3 0.28c-2.4 2.1-6.6 2.8-12 2-5.4-0.85-11-3.3-16-6.8-5.7-4.1-9.3-9.3-9.7-14-0.28-3.4 0.92-6.6 3.5-9.2a16 16 0 0 1 1.6-1.4c8.7-6.6 22-4.9 29 3.7a26 26 0 0 1 4.2 7 27 27 0 0 1 1.8 7.4c0.47 4.9-0.57 9.1-2.8 11z"
45
+ },
46
+ {
47
+ order: 5,
48
+ id: 13,
49
+ name: "Upper Right Canine",
50
+ position: "upper",
51
+ side: "right",
52
+ d: "m144 104c-0.09 0.084-0.19 0.17-0.29 0.24-1.2 0.9-2.9 1.5-5.1 1.8-2.2 0.33-4.9 0.3-7.7-0.086-6.4-0.88-13-3.5-19-7.5-2.2-1.6-4-3.4-5.2-5.4a13 13 0 0 1-1.8-5.8c-0.18-2.6 0.36-5.4 1.6-8 1.1-2.3 2.6-4.5 4.3-6.1 0.36-0.34 0.73-0.65 1.1-0.92 1.4-1.1 4.4-2.7 7.8-4 2.8-1.1 6.8-2.2 10-1.8h8e-3c2.5 0.26 4.4 1.4 5.8 3.6 1.3 2 2.1 4.4 2.9 6.8 0.19 0.58 0.36 1.1 0.55 1.7 0.52 1.5 1.1 3.1 1.7 4.7 1.6 4.3 3.2 8.8 3.9 12 0.83 4.1 0.48 6.9-1.1 8.3z"
53
+ },
54
+ {
55
+ order: 6,
56
+ id: 12,
57
+ name: "Upper Right Lateral Incisor",
58
+ position: "upper",
59
+ side: "right",
60
+ d: "m168 82c-2.9 1.2-6.4 0.91-11-1-3.7-1.7-7.9-4.5-13-8.8-0.54-0.44-1.1-0.86-1.6-1.3-2-1.6-3.8-3-5-4.4-1.3-1.5-1.9-2.7-1.7-4 0.17-1.6 1.4-3.3 3.9-5.3 2.6-2.1 6.8-4.5 13-7.4a105 105 0 0 1 5.9-2.8c6.4-2.7 10-3.3 13-2 2.5 1.6 2.7 5.7 3 11 0.073 1.5 0.15 3 0.28 4.6 0.61 7.4 0.52 12-0.3 15-0.86 3.2-2.6 5.2-5.8 6.6l-0.04 0.017z"
61
+ },
62
+ {
63
+ order: 7,
64
+ id: 11,
65
+ name: "Upper Right Central Incisor",
66
+ position: "upper",
67
+ side: "right",
68
+ d: "m205 74c-10 1.1-18-9.3-23-18l-0.3-0.5c-1.4-2.4-2.7-4.6-3.5-6.7-0.85-2.3-0.88-4.2-0.11-5.9 0.9-2 2.9-3.6 6.2-5.1 3.7-1.6 8.9-3 16-4.2a82 82 0 0 1 5-0.69c4.3-0.46 7.9-0.39 11 0.2 2.4 0.52 4.2 1.5 5.4 2.8 1.3 1.5 1.9 3.6 1.7 6.3-0.14 2.4-0.79 5-1.4 7.4-0.23 0.89-0.45 1.7-0.63 2.5l-0.29 1.3c-0.98 4.3-2.1 9.3-4.2 13-2.4 4.5-5.8 6.9-10 7.6-0.21 0.03-0.42 0.057-0.62 0.079z"
69
+ },
70
+ {
71
+ order: 8,
72
+ id: 21,
73
+ name: "Upper Left Central Incisor",
74
+ position: "upper",
75
+ side: "left",
76
+ d: "m246 32h1e-3c1.5 0.16 3.2 0.4 5 0.69 7 1.2 12 2.5 16 4.2 3.3 1.5 5.3 3.1 6.2 5.1 0.77 1.7 0.73 3.5-0.12 5.9-0.77 2.1-2.1 4.4-3.5 6.7l-0.3 0.5c-5.4 9.2-13 20-23 18a15 15 0 0 1-0.62-0.079c-4.5-0.66-7.9-3.2-10-7.6-2.1-3.9-3.2-8.9-4.2-13l-0.29-1.3c-0.18-0.81-0.4-1.7-0.63-2.5-0.63-2.5-1.3-5-1.4-7.4-0.15-2.7 0.41-4.8 1.7-6.3 1.2-1.3 3-2.3 5.4-2.8 2.7-0.58 6.3-0.65 11-0.19z"
77
+ },
78
+ {
79
+ order: 9,
80
+ id: 22,
81
+ name: "Upper Left Lateral Incisor",
82
+ position: "upper",
83
+ side: "left",
84
+ d: "m292 47h1e-3c1.8 0.75 3.8 1.7 5.9 2.8 5.8 2.9 9.9 5.4 13 7.4 2.5 1.9 3.7 3.7 3.9 5.3 0.14 1.3-0.41 2.5-1.7 4-1.2 1.4-3 2.8-5 4.4-0.51 0.41-1 0.83-1.6 1.3-5.3 4.3-9.5 7.1-13 8.8-4.3 1.9-7.8 2.3-11 1l-0.04-0.018c-3.2-1.4-4.9-3.3-5.8-6.6-0.82-3.1-0.91-7.3-0.3-15 0.13-1.6 0.21-3.1 0.28-4.6 0.26-5.3 0.47-9.4 3-11 2.2-1.4 6.2-0.77 13 2z"
85
+ },
86
+ {
87
+ order: 10,
88
+ id: 23,
89
+ name: "Upper Left Canine",
90
+ position: "upper",
91
+ side: "left",
92
+ d: "m339 73c1.7 1.6 3.2 3.7 4.3 6.1 1.2 2.7 1.8 5.5 1.6 8a13 13 0 0 1-1.8 5.8c-1.2 2-2.9 3.8-5.2 5.4-5.6 3.9-12 6.6-19 7.5-2.8 0.38-5.4 0.41-7.7 0.086-2.2-0.32-3.9-0.96-5.1-1.8a4 4 0 0 1-0.29-0.24c-1.6-1.4-1.9-4.2-1.1-8.3 0.75-3.7 2.4-8.2 3.9-12a301 301 0 0 0 1.7-4.7c0.18-0.54 0.36-1.1 0.55-1.7 0.77-2.4 1.6-4.8 2.9-6.8 1.5-2.2 3.4-3.4 5.8-3.6h5e-3l4e-3 -1e-3c3.3-0.45 7.3 0.71 10 1.8 3.4 1.3 6.4 3 7.8 4 0.36 0.27 0.73 0.58 1.1 0.92z"
93
+ },
94
+ {
95
+ order: 11,
96
+ id: 24,
97
+ name: "Upper Left First Premolar",
98
+ position: "upper",
99
+ side: "left",
100
+ d: "m367 107h1e-3c2.6 2.6 3.8 5.7 3.5 9.2-0.42 5-3.9 10-9.7 14-5 3.6-11 6-16 6.8-5.1 0.81-9.4 0.076-12-2a6.3 6.3 0 0 1-0.3-0.28c-2.2-2.2-3.3-6.5-2.8-11a27 27 0 0 1 1.8-7.4 26 26 0 0 1 4.2-7c7.4-8.7 21-10 29-3.7a16 16 0 0 1 1.6 1.4z"
101
+ },
102
+ {
103
+ order: 12,
104
+ id: 25,
105
+ name: "Upper Left Second Premolar",
106
+ position: "upper",
107
+ side: "left",
108
+ d: "m362 137 2e-3 -1e-3c3-1.5 6-2.4 9-2.8 2.8-0.4 5.6-0.32 8.1 0.24 5 1.1 9 3.9 11 8 1 1.9 1.7 3.8 1.9 5.7a13 13 0 0 1-0.52 5.8c-0.8 2.5-2.4 5-4.6 7.2-2.4 2.4-5.6 4.6-9.5 6.5l-0.2 0.1c-4.3 2.1-9.1 3.4-14 3.8-4.3 0.35-8.5-0.11-12-1.3-1.9-0.65-3.5-1.5-4.8-2.5s-2.3-2.2-2.9-3.6c-0.89-1.9-1.2-4-0.9-6.2 0.28-2.2 1.1-4.5 2.5-6.8s3.3-4.6 5.7-6.8 5.3-4.3 8.5-6.2a37 37 0 0 1 2.1-1.1z"
109
+ },
110
+ {
111
+ order: 13,
112
+ id: 26,
113
+ name: "Upper Left First Molar",
114
+ position: "upper",
115
+ side: "left",
116
+ d: "m378 229c-15 0-22-7.3-23-23-0.33-4.1 0.2-8 1.6-12 1.3-3.7 3.4-7 6.3-10 2.8-3 6.2-5.5 10-7.5 4-2.1 8.4-3.5 13-4.3 4.1 1e-3 7.1 0.36 9.5 1.1 3 0.98 5.2 2.6 6.6 5.1l4e-3 6e-3 4e-3 7e-3c1.6 2.4 1.7 5.5 1.8 8.1 0.046 1.6 0.087 3 0.5 4 0.76 1.8 2.1 3.2 3.4 4.4 0.65 0.66 1.3 1.3 1.7 1.9 1.2 1.7 2 4.5 2 7.3 0 3.8-0.87 7.2-2.6 10-1.6 2.9-3.9 5.4-6.9 7.5-5.5 3.9-13 6.3-21 6.7-0.76 0.035-1.5 0.053-2.2 0.053z"
117
+ },
118
+ {
119
+ order: 14,
120
+ id: 27,
121
+ name: "Upper Left Second Molar",
122
+ position: "upper",
123
+ side: "left",
124
+ d: "m386 236c5.5-1.8 23-6.1 29-4l6e-3 2e-3 6e-3 1e-3c0.74 0.2 2.7 1 4.6 2.1 2.4 1.3 3.8 2.6 4.2 3.7 0.81 2.4 0.36 6.3-0.045 9.8-0.2 1.7-0.4 3.4-0.37 4.5l7e-3 0.2c0.36 11-0.3 18-1.8 21-2.2 3.8-5.6 5.3-8.5 6.2-1.5 0.5-3.2 0.9-4.9 1.3-1.9 0.48-3.9 0.97-6 1.6-4.2 1.4-8.3 2.1-12 2-4-0.028-7.5-0.86-11-2.5-5-2.7-8.6-7.4-11-14-1.2-4-1.7-7.8-1.4-11 0.22-3.3 1.1-6.4 2.6-9.1 1.5-2.6 3.5-5 6.2-6.9 2.6-1.9 5.7-3.5 9.3-4.7l0.13-0.043z"
125
+ },
126
+ {
127
+ order: 15,
128
+ id: 28,
129
+ name: "Upper Left Third Molar",
130
+ position: "upper",
131
+ side: "left",
132
+ d: "m384 294c2.3-0.74 6.7-1.8 12-2.4 4-0.5 9.7-0.87 14 0.23 0.11 0.03 0.28 0.068 0.48 0.12 1.3 0.3 4.3 1 7.1 2.4 3.5 1.7 5.5 3.9 6 6.4l3e-3 0.015 5e-3 0.015c0.82 2.4 0.36 6.3-0.044 9.8-0.2 1.7-0.4 3.4-0.37 4.5 0.021 0.63 0.58 1.4 1.4 2.4 1.1 1.4 2.6 3.4 3.4 6.2 0.48 1.5 0.72 3.1 0.73 4.7 8e-3 1.9-0.31 4-0.94 6.1-0.91 2.8-2.1 4.9-3.6 6.5-1.3 1.4-2.8 2.4-4.7 3-2.8 0.93-6 0.82-9.1 0.72-3-0.1-5.9-0.2-8.4 0.63l-0.023 7e-3 -0.021 0.011c-2.4 1.3-4.8 2.4-7.2 3.2-7 2.3-14 1.9-19-1-2.9-1.5-5.5-3.8-7.6-6.6s-3.8-6.1-4.8-9.8c-2.2-7.6-1.1-16 3.3-23 4-6.7 10-12 18-14l0.1-0.034z"
133
+ },
134
+ {
135
+ order: 16,
136
+ id: 38,
137
+ name: "Lower Left Third Molar",
138
+ position: "lower",
139
+ side: "left",
140
+ d: "m420 406c1.8 0.6 3.4 1.6 4.7 3 1.5 1.6 2.7 3.7 3.6 6.5 0.63 2.1 0.94 4.2 0.94 6.1a16 16 0 0 1-0.73 4.7c-0.87 2.8-2.3 4.7-3.4 6.2-0.78 1-1.3 1.8-1.4 2.4-0.026 1.2 0.17 2.8 0.37 4.5 0.4 3.4 0.86 7.3 0.044 9.8l-5e-3 0.015-3e-3 0.015c-0.51 2.5-2.5 4.7-6 6.4-2.8 1.4-5.8 2.1-7.1 2.4-0.21 0.049-0.37 0.088-0.48 0.12-4.7 1.1-10 0.73-14 0.23-4.9-0.62-9.3-1.7-12-2.4l-0.1-0.034c-7.1-2.3-14-7.5-18-14-4.3-7.1-5.5-15-3.3-23 1.1-3.7 2.7-7 4.8-9.8s4.7-5 7.6-6.6c5.6-3 12-3.3 19-1 2.4 0.76 4.8 1.8 7.2 3.2l0.021 0.012 0.023 7e-3c2.5 0.82 5.4 0.73 8.4 0.63 3.1-0.1 6.3-0.21 9.1 0.72z"
141
+ },
142
+ {
143
+ order: 17,
144
+ id: 37,
145
+ name: "Lower Left Second Molar",
146
+ position: "lower",
147
+ side: "left",
148
+ d: "m411 471c3 0.96 6.3 2.5 8.5 6.2 1.5 2.9 2.2 10 1.8 21l-6e-3 0.19c-0.026 1.2 0.17 2.8 0.37 4.5 0.4 3.4 0.86 7.3 0.045 9.8-0.4 1.1-1.9 2.4-4.2 3.7-1.8 1-3.9 1.9-4.6 2.1l-6e-3 1e-3 -6e-3 2e-3c-6.5 2.1-24-2.2-29-4a32 32 0 0 1-0.13-0.043c-3.6-1.2-6.7-2.7-9.3-4.7s-4.7-4.3-6.2-6.9c-1.5-2.7-2.4-5.8-2.6-9.1-0.22-3.4 0.26-7.2 1.4-11 1.9-6.6 5.5-11 11-14 3-1.6 6.6-2.4 11-2.5 3.7-0.027 7.8 0.66 12 2l0.24 0.078c2 0.63 3.9 1.1 5.7 1.6 1.7 0.43 3.4 0.83 4.9 1.3z"
149
+ },
150
+ {
151
+ order: 18,
152
+ id: 36,
153
+ name: "Lower Left First Molar",
154
+ position: "lower",
155
+ side: "left",
156
+ d: "m386 578c-4.7-0.79-9-2.2-13-4.3-3.9-2-7.3-4.6-10-7.5-2.8-3-4.9-6.4-6.3-10a27 27 0 0 1-1.6-12c1.3-16 8.7-23 23-23 0.72 0 1.5 0.018 2.2 0.053 8.3 0.39 16 2.8 21 6.7 3 2.1 5.3 4.7 6.9 7.5 1.7 3.1 2.6 6.6 2.6 10 0 2.7-0.77 5.6-2 7.3-0.41 0.58-1 1.2-1.7 1.9-1.2 1.2-2.6 2.7-3.4 4.4-0.41 0.98-0.45 2.4-0.5 4-0.072 2.5-0.16 5.6-1.8 8.1l-8e-3 0.012c-1.4 2.4-3.6 4.1-6.6 5.1-2.4 0.79-5.4 1.1-9.5 1.1z"
157
+ },
158
+ {
159
+ order: 19,
160
+ id: 35,
161
+ name: "Lower Left Second Premolar",
162
+ position: "lower",
163
+ side: "left",
164
+ d: "m377 582 0.2 0.1c3.9 1.9 7.1 4.1 9.5 6.5 2.3 2.3 3.8 4.7 4.6 7.2 0.59 1.9 0.77 3.8 0.52 5.8-0.24 1.9-0.89 3.8-1.9 5.7-2.2 4.1-6.2 6.9-11 8-2.6 0.56-5.3 0.64-8.1 0.23-3-0.42-6-1.4-9-2.8a37 37 0 0 1-2.1-1.1c-3.2-1.8-6.1-3.9-8.5-6.2-2.4-2.2-4.3-4.5-5.7-6.8s-2.2-4.6-2.5-6.8c-0.29-2.2 0.014-4.3 0.9-6.2 0.62-1.3 1.6-2.5 2.9-3.6 1.3-1 2.9-1.9 4.8-2.5 3.5-1.2 7.6-1.7 12-1.3 4.7 0.38 9.4 1.7 14 3.8z"
165
+ },
166
+ {
167
+ order: 20,
168
+ id: 34,
169
+ name: "Lower Left First Premolar",
170
+ position: "lower",
171
+ side: "left",
172
+ d: "m332 615a6.3 6.3 0 0 1 0.3-0.28c2.4-2.1 6.6-2.8 12-2 5.4 0.85 11 3.3 16 6.8 5.7 4.1 9.3 9.3 9.7 14 0.28 3.4-0.92 6.6-3.5 9.2-0.49 0.49-1 0.96-1.6 1.4-8.7 6.6-22 4.9-29-3.7a26 26 0 0 1-4.2-7 27 27 0 0 1-1.8-7.4c-0.47-4.9 0.57-9.1 2.8-11z"
173
+ },
174
+ {
175
+ order: 21,
176
+ id: 33,
177
+ name: "Lower Left Canine",
178
+ position: "lower",
179
+ side: "left",
180
+ d: "m307 647c0.09-0.084 0.19-0.17 0.29-0.24 1.2-0.9 2.9-1.5 5.1-1.8 2.2-0.33 4.9-0.3 7.7 0.086 6.4 0.88 13 3.5 19 7.5 2.2 1.6 4 3.4 5.2 5.4a13 13 0 0 1 1.8 5.8c0.18 2.6-0.36 5.4-1.6 8-1.1 2.3-2.6 4.5-4.3 6.1-0.36 0.34-0.73 0.65-1.1 0.92-1.4 1.1-4.4 2.7-7.8 4-2.8 1.1-6.8 2.2-10 1.8h-8e-3c-2.5-0.26-4.4-1.4-5.8-3.6-1.3-2-2.1-4.4-2.9-6.8-0.19-0.58-0.36-1.1-0.55-1.7a301 301 0 0 0-1.7-4.7c-1.6-4.3-3.2-8.8-3.9-12-0.83-4.1-0.48-6.9 1.1-8.3z"
181
+ },
182
+ {
183
+ order: 22,
184
+ id: 32,
185
+ name: "Lower Left Lateral Incisor",
186
+ position: "lower",
187
+ side: "left",
188
+ d: "m283 669c2.9-1.2 6.4-0.91 11 1 3.7 1.7 7.9 4.5 13 8.8 0.54 0.44 1.1 0.86 1.6 1.3 2 1.6 3.8 3 5 4.4 1.3 1.5 1.9 2.7 1.7 4-0.17 1.6-1.4 3.3-3.9 5.3-2.6 2.1-6.8 4.5-13 7.4a105 105 0 0 1-5.9 2.8c-6.4 2.7-10 3.3-13 2-2.5-1.6-2.7-5.7-3-11-0.073-1.5-0.15-3-0.28-4.6-0.61-7.4-0.52-12 0.3-15 0.86-3.2 2.6-5.2 5.8-6.6l0.04-0.017z"
189
+ },
190
+ {
191
+ order: 23,
192
+ id: 31,
193
+ name: "Lower Left Central Incisor",
194
+ position: "lower",
195
+ side: "left",
196
+ d: "m245 677c10-1.1 18 9.3 23 18l0.3 0.5c1.4 2.4 2.7 4.6 3.5 6.7 0.85 2.3 0.88 4.2 0.11 5.9-0.9 2-2.9 3.6-6.2 5.1-3.7 1.6-8.9 3-16 4.2a82 82 0 0 1-5 0.69c-4.3 0.46-7.9 0.39-11-0.2-2.4-0.52-4.2-1.5-5.4-2.8-1.3-1.5-1.9-3.6-1.7-6.3 0.14-2.4 0.79-5 1.4-7.4a96 96 0 0 0 0.92-3.8c0.98-4.3 2.1-9.3 4.2-13 2.4-4.5 5.8-6.9 10-7.6 0.21-0.03 0.42-0.057 0.62-0.079z"
197
+ },
198
+ {
199
+ order: 24,
200
+ id: 41,
201
+ name: "Lower Right Central Incisor",
202
+ position: "lower",
203
+ side: "right",
204
+ d: "m205 718h-1e-3a82 82 0 0 1-5-0.69c-7-1.2-12-2.5-16-4.2-3.3-1.5-5.3-3.1-6.2-5.1-0.77-1.7-0.73-3.5 0.12-5.9 0.77-2.1 2.1-4.4 3.5-6.7l0.3-0.5c5.4-9.2 13-20 23-18 0.21 0.022 0.42 0.048 0.62 0.079 4.5 0.66 7.9 3.2 10 7.6 2.1 3.9 3.2 8.9 4.2 13l0.29 1.3c0.18 0.81 0.4 1.7 0.63 2.5 0.63 2.5 1.3 5 1.4 7.4 0.15 2.7-0.41 4.8-1.7 6.3-1.2 1.3-3 2.3-5.4 2.8-2.7 0.58-6.3 0.65-11 0.19z"
205
+ },
206
+ {
207
+ order: 25,
208
+ id: 42,
209
+ name: "Lower Right Lateral Incisor",
210
+ position: "lower",
211
+ side: "right",
212
+ d: "m158 704h-1e-3a105 105 0 0 1-5.9-2.8c-5.8-2.9-9.9-5.4-13-7.4-2.5-1.9-3.7-3.7-3.9-5.3-0.14-1.3 0.41-2.5 1.7-4 1.2-1.4 3-2.8 5-4.4 0.51-0.41 1-0.83 1.6-1.3 5.3-4.3 9.5-7.1 13-8.8 4.3-1.9 7.8-2.3 11-1l0.04 0.018c3.2 1.4 4.9 3.3 5.8 6.6 0.82 3.1 0.91 7.3 0.3 15-0.13 1.6-0.21 3.1-0.28 4.6-0.26 5.3-0.47 9.4-3 11-2.2 1.4-6.2 0.77-13-2z"
213
+ },
214
+ {
215
+ order: 26,
216
+ id: 43,
217
+ name: "Lower Right Canine",
218
+ position: "lower",
219
+ side: "right",
220
+ d: "m111 677c-1.7-1.6-3.2-3.7-4.3-6.1-1.2-2.7-1.8-5.5-1.6-8a13 13 0 0 1 1.8-5.8c1.2-2 2.9-3.8 5.2-5.4 5.6-3.9 12-6.6 19-7.5 2.8-0.38 5.4-0.41 7.7-0.086 2.2 0.32 3.9 0.96 5.1 1.8 0.1 0.076 0.2 0.16 0.29 0.24 1.6 1.4 1.9 4.2 1.1 8.3-0.75 3.7-2.4 8.2-3.9 12a300 300 0 0 0-1.7 4.7c-0.18 0.54-0.36 1.1-0.55 1.7-0.77 2.4-1.6 4.8-2.9 6.8-1.5 2.2-3.4 3.4-5.8 3.6h-5e-3l-4e-3 1e-3c-3.3 0.45-7.3-0.71-10-1.8-3.4-1.3-6.4-3-7.8-4a13 13 0 0 1-1.1-0.92z"
221
+ },
222
+ {
223
+ order: 27,
224
+ id: 44,
225
+ name: "Lower Right First Premolar",
226
+ position: "lower",
227
+ side: "right",
228
+ d: "m83 643h-1e-3c-2.6-2.6-3.8-5.7-3.5-9.2 0.42-5 3.9-10 9.7-14 5-3.6 11-6 16-6.8 5.1-0.81 9.4-0.076 12 2 0.1 0.089 0.2 0.18 0.3 0.28 2.2 2.2 3.3 6.5 2.8 11a27 27 0 0 1-1.8 7.4 26 26 0 0 1-4.2 7c-7.4 8.7-21 10-29 3.7a16 16 0 0 1-1.6-1.4z"
229
+ },
230
+ {
231
+ order: 28,
232
+ id: 45,
233
+ name: "Lower Right Second Premolar",
234
+ position: "lower",
235
+ side: "right",
236
+ d: "m88 613-2e-3 1e-3c-3 1.5-6 2.4-9 2.8-2.8 0.4-5.6 0.32-8.1-0.24-5-1.1-9-3.9-11-8-1-1.9-1.7-3.8-1.9-5.7a13 13 0 0 1 0.52-5.8c0.8-2.5 2.4-5 4.6-7.2 2.4-2.4 5.6-4.6 9.5-6.5l0.2-0.1c4.3-2.1 9.1-3.4 14-3.8 4.3-0.35 8.5 0.11 12 1.3 1.9 0.65 3.5 1.5 4.8 2.5s2.3 2.2 2.9 3.6c0.89 1.9 1.2 4 0.9 6.2-0.28 2.2-1.1 4.5-2.5 6.8s-3.3 4.6-5.7 6.8-5.3 4.3-8.5 6.2c-0.7 0.4-1.4 0.78-2.1 1.1z"
237
+ },
238
+ {
239
+ order: 29,
240
+ id: 46,
241
+ name: "Lower Right First Molar",
242
+ position: "lower",
243
+ side: "right",
244
+ d: "m65 578c-4.1-1e-3 -7.1-0.36-9.5-1.1-3-0.98-5.2-2.6-6.6-5.1l-4e-3 -7e-3 -4e-3 -6e-3c-1.6-2.4-1.7-5.5-1.8-8.1-0.047-1.6-0.087-3-0.5-4-0.76-1.8-2.1-3.2-3.4-4.4-0.65-0.66-1.3-1.3-1.7-1.9-1.2-1.7-2-4.5-2-7.3 0-3.8 0.87-7.2 2.6-10 1.6-2.9 3.9-5.4 6.9-7.5 5.5-3.9 13-6.3 21-6.7 0.76-0.035 1.5-0.053 2.2-0.053 15 0 22 7.3 23 23 0.34 4.1-0.2 8-1.6 12-1.3 3.7-3.5 7-6.3 10s-6.2 5.5-10 7.5c-4 2.1-8.4 3.5-13 4.3z"
245
+ },
246
+ {
247
+ order: 30,
248
+ id: 47,
249
+ name: "Lower Right Second Molar",
250
+ position: "lower",
251
+ side: "right",
252
+ d: "m66 515c-5.5 1.8-23 6.1-29 4l-7e-3 -2e-3 -6e-3 -1e-3c-0.74-0.2-2.7-1-4.6-2.1-2.4-1.3-3.8-2.6-4.2-3.7-0.81-2.4-0.36-6.3 0.045-9.8 0.2-1.7 0.4-3.4 0.37-4.5l-7e-3 -0.2c-0.36-11 0.3-18 1.8-21 2.2-3.8 5.6-5.3 8.5-6.2 1.5-0.5 3.2-0.9 4.9-1.3 1.9-0.48 3.9-0.97 6-1.6 4.2-1.4 8.3-2.1 12-2 4 0.028 7.5 0.86 11 2.5 5 2.7 8.6 7.4 11 14 1.2 4 1.7 7.8 1.4 11-0.22 3.3-1.1 6.4-2.6 9.1-1.5 2.6-3.5 5-6.2 6.9-2.6 1.9-5.7 3.5-9.3 4.7l-0.13 0.043z"
253
+ },
254
+ {
255
+ order: 31,
256
+ id: 48,
257
+ name: "Lower Right Third Molar",
258
+ position: "lower",
259
+ side: "right",
260
+ d: "m66 456c-2.3 0.74-6.7 1.8-12 2.4-4 0.5-9.7 0.87-14-0.23a21 21 0 0 0-0.48-0.12c-1.3-0.3-4.3-1-7.1-2.4-3.5-1.7-5.5-3.9-6-6.4l-3e-3 -0.015-5e-3 -0.015c-0.82-2.4-0.36-6.3 0.044-9.8 0.2-1.7 0.4-3.4 0.37-4.5-0.021-0.63-0.58-1.4-1.4-2.4-1.1-1.4-2.6-3.4-3.4-6.2a16 16 0 0 1-0.73-4.7c-8e-3 -1.9 0.31-4 0.94-6.1 0.91-2.8 2.1-4.9 3.6-6.5 1.3-1.4 2.8-2.4 4.7-3 2.8-0.93 6-0.82 9.1-0.72 3 0.1 5.9 0.2 8.4-0.63l0.023-7e-3 0.021-0.011c2.4-1.3 4.8-2.4 7.2-3.2 7-2.3 14-1.9 19 1 2.9 1.5 5.5 3.8 7.6 6.6s3.8 6.1 4.8 9.8c2.2 7.6 1.1 16-3.3 23-4 6.7-10 12-18 14l-0.1 0.034z"
261
+ }
262
+ ];
263
+ var adjacentTeethPairs = [
264
+ [17, 18],
265
+ [16, 17],
266
+ [15, 16],
267
+ [14, 15],
268
+ [13, 14],
269
+ [12, 13],
270
+ [11, 12],
271
+ [11, 21],
272
+ [21, 22],
273
+ [22, 23],
274
+ [23, 24],
275
+ [24, 25],
276
+ [25, 26],
277
+ [26, 27],
278
+ [27, 28],
279
+ [37, 38],
280
+ [36, 37],
281
+ [35, 36],
282
+ [34, 35],
283
+ [33, 34],
284
+ [32, 33],
285
+ [31, 32],
286
+ [31, 41],
287
+ [41, 42],
288
+ [42, 43],
289
+ [43, 44],
290
+ [44, 45],
291
+ [45, 46],
292
+ [46, 47],
293
+ [47, 48]
294
+ ];
295
+ function getTeethInRange(startId, endId) {
296
+ const startTooth = teethData.find((t) => t.id === startId);
297
+ const endTooth = teethData.find((t) => t.id === endId);
298
+ if (!startTooth || !endTooth)
299
+ return [];
300
+ const minOrder = Math.min(startTooth.order, endTooth.order);
301
+ const maxOrder = Math.max(startTooth.order, endTooth.order);
302
+ return teethData.filter((t) => t.order >= minOrder && t.order <= maxOrder).map((t) => t.id);
303
+ }
304
+ function sameSide(tooth1, tooth2) {
305
+ const [a, b] = [tooth1.toString(), tooth2.toString()];
306
+ return a[0] === b[0];
307
+ }
308
+ function parseTeethSelection(input) {
309
+ const selectedTeeth = [];
310
+ const bridges = [];
311
+ const regex = /\[(\d+)\s*,\s*(\d+)\]|(\d+)/g;
312
+ let match;
313
+ while ((match = regex.exec(input)) !== null) {
314
+ if (match[1] && match[2]) {
315
+ let start = Number(match[1]);
316
+ let end = Number(match[2]);
317
+ [start, end] = [Math.min(start, end), Math.max(start, end)];
318
+ const range = [];
319
+ if (sameSide(start, end)) {
320
+ for (let i = start;i <= end; i++) {
321
+ range.push(i);
322
+ }
323
+ } else {
324
+ const crossA = Number(`${start.toString()[0]}1`);
325
+ const crossB = Number(`${end.toString()[0]}1`);
326
+ for (let i = start;i >= crossA; i--) {
327
+ range.push(i);
328
+ }
329
+ for (let i = crossB;i <= end; i++) {
330
+ range.push(i);
331
+ }
332
+ }
333
+ selectedTeeth.push(...range);
334
+ for (let i = 0;i < range.length - 1; i++) {
335
+ bridges.push([range[i], range[i + 1]]);
336
+ }
337
+ } else if (match[3]) {
338
+ selectedTeeth.push(Number(match[3]));
339
+ }
340
+ }
341
+ return { selectedTeeth, bridges };
342
+ }
343
+ function formatTeethSelection(selectedTeeth, bridges) {
344
+ if (!selectedTeeth || selectedTeeth.length === 0) {
345
+ return "";
346
+ }
347
+ const bridgeMap = new Map;
348
+ bridges.forEach(([tooth1, tooth2]) => {
349
+ if (!bridgeMap.has(tooth1))
350
+ bridgeMap.set(tooth1, []);
351
+ if (!bridgeMap.has(tooth2))
352
+ bridgeMap.set(tooth2, []);
353
+ bridgeMap.get(tooth1).push(tooth2);
354
+ bridgeMap.get(tooth2).push(tooth1);
355
+ });
356
+ const result = [];
357
+ let currentGroup = [];
358
+ selectedTeeth.forEach((tooth, index) => {
359
+ if (index === 0) {
360
+ currentGroup.push(tooth);
361
+ return;
362
+ }
363
+ const prevTooth = selectedTeeth[index - 1];
364
+ const hasBridge = bridgeMap.has(prevTooth) && bridgeMap.get(prevTooth).includes(tooth) && Math.abs(selectedTeeth.indexOf(tooth) - selectedTeeth.indexOf(prevTooth)) === 1;
365
+ if (hasBridge) {
366
+ currentGroup.push(tooth);
367
+ } else {
368
+ if (currentGroup.length > 0) {
369
+ if (currentGroup.length === 1) {
370
+ result.push(`${currentGroup[0]}`);
371
+ } else {
372
+ result.push(`[${currentGroup[0]}, ${currentGroup[currentGroup.length - 1]}]`);
373
+ }
374
+ }
375
+ currentGroup = [tooth];
376
+ }
377
+ });
378
+ if (currentGroup.length > 0) {
379
+ if (currentGroup.length === 1) {
380
+ result.push(`${currentGroup[0]}`);
381
+ } else {
382
+ result.push(`[${currentGroup[0]}, ${currentGroup[currentGroup.length - 1]}]`);
383
+ }
384
+ }
385
+ return result.join(", ");
386
+ }
387
+
388
+ // src/components/teeth-selector.tsx
389
+ import { jsxDEV } from "react/jsx-dev-runtime";
390
+ "use client";
391
+ var initialState = {
392
+ selectedTeeth: [],
393
+ bridges: []
394
+ };
395
+ function selectionReducer(state, action) {
396
+ switch (action.type) {
397
+ case "toggle-tooth": {
398
+ const exists = state.selectedTeeth.includes(action.toothId);
399
+ const selectedTeeth = exists ? state.selectedTeeth.filter((id) => id !== action.toothId) : [...state.selectedTeeth, action.toothId];
400
+ const bridges = exists ? state.bridges.filter(([a, b]) => a !== action.toothId && b !== action.toothId) : state.bridges;
401
+ return { selectedTeeth, bridges };
402
+ }
403
+ case "select-range": {
404
+ const selectedTeeth = Array.from(new Set([...state.selectedTeeth, ...action.teethIds]));
405
+ const consecutivePairs = [];
406
+ for (let i = 0;i < action.teethIds.length - 1; i++) {
407
+ consecutivePairs.push([action.teethIds[i], action.teethIds[i + 1]]);
408
+ }
409
+ const existing = new Set(state.bridges.map((b) => `${Math.min(...b)}-${Math.max(...b)}`));
410
+ const newBridges = consecutivePairs.filter(([a, b]) => adjacentTeethPairs.some(([x, y]) => x === a && y === b || x === b && y === a)).map((pair) => [Math.min(...pair), Math.max(...pair)]).filter((pair) => {
411
+ const key = `${pair[0]}-${pair[1]}`;
412
+ return !existing.has(key);
413
+ });
414
+ return {
415
+ selectedTeeth,
416
+ bridges: [...state.bridges, ...newBridges]
417
+ };
418
+ }
419
+ case "select-upper": {
420
+ const upper = teethData.filter((t) => t.position === "upper").map((t) => t.id);
421
+ return selectionReducer(state, { type: "select-range", teethIds: upper });
422
+ }
423
+ case "select-lower": {
424
+ const lower = teethData.filter((t) => t.position === "lower").map((t) => t.id);
425
+ return selectionReducer(state, { type: "select-range", teethIds: lower });
426
+ }
427
+ case "clear-all":
428
+ return initialState;
429
+ case "toggle-bridge": {
430
+ const { fromId, toId } = action;
431
+ if (!state.selectedTeeth.includes(fromId) || !state.selectedTeeth.includes(toId)) {
432
+ return {
433
+ selectedTeeth: [...state.selectedTeeth, fromId, toId],
434
+ bridges: [
435
+ ...state.bridges,
436
+ [Math.min(fromId, toId), Math.max(fromId, toId)]
437
+ ]
438
+ };
439
+ }
440
+ const key = (a, b) => `${Math.min(a, b)}-${Math.max(a, b)}`;
441
+ const exists = state.bridges.some(([a, b]) => key(a, b) === key(fromId, toId));
442
+ const bridges = exists ? state.bridges.filter(([a, b]) => key(a, b) !== key(fromId, toId)) : [...state.bridges, [Math.min(fromId, toId), Math.max(fromId, toId)]];
443
+ return { ...state, bridges };
444
+ }
445
+ case "set":
446
+ return action.state;
447
+ default:
448
+ return state;
449
+ }
450
+ }
451
+ var TeethSelectorContext = React.createContext(null);
452
+ function useTeethSelectorContext() {
453
+ const context = React.useContext(TeethSelectorContext);
454
+ if (!context) {
455
+ throw new Error("useTeethSelectorContext must be used within a TeethSelector");
456
+ }
457
+ return context;
458
+ }
459
+ var initialDragState = {
460
+ start: null,
461
+ current: null,
462
+ teethInRange: []
463
+ };
464
+ var TeethSelector = React.forwardRef((props, ref) => {
465
+ const {
466
+ defaultSelectedTeeth = [],
467
+ defaultBridges = [],
468
+ selectedTeeth: controlledSelectedTeeth,
469
+ bridges: controlledBridges,
470
+ onSelectionChange,
471
+ onBridgeChange,
472
+ onChange,
473
+ children
474
+ } = props;
475
+ const isControlled = controlledSelectedTeeth !== undefined || controlledBridges !== undefined;
476
+ const [internalState, dispatch] = React.useReducer(selectionReducer, {
477
+ selectedTeeth: defaultSelectedTeeth,
478
+ bridges: defaultBridges
479
+ });
480
+ const [dragState, setDragState] = React.useState(initialDragState);
481
+ const state = isControlled ? {
482
+ selectedTeeth: controlledSelectedTeeth ?? internalState.selectedTeeth,
483
+ bridges: controlledBridges ?? internalState.bridges
484
+ } : internalState;
485
+ const prevStateRef = React.useRef(state);
486
+ React.useEffect(() => {
487
+ if (prevStateRef.current.selectedTeeth !== state.selectedTeeth || prevStateRef.current.bridges !== state.bridges) {
488
+ const sorted = teethData.filter((t) => state.selectedTeeth.includes(t.id)).sort((a, b) => a.order - b.order);
489
+ onSelectionChange?.(sorted);
490
+ onBridgeChange?.(state.bridges);
491
+ onChange?.(sorted, state.bridges);
492
+ prevStateRef.current = state;
493
+ }
494
+ }, [state, onSelectionChange, onBridgeChange, onChange]);
495
+ const toggleTooth = React.useCallback((toothId) => {
496
+ dispatch({ type: "toggle-tooth", toothId });
497
+ }, []);
498
+ const toggleBridge = React.useCallback((fromId, toId) => {
499
+ dispatch({ type: "toggle-bridge", fromId, toId });
500
+ }, []);
501
+ const selectUpper = React.useCallback(() => {
502
+ dispatch({ type: "select-upper" });
503
+ }, []);
504
+ const selectLower = React.useCallback(() => {
505
+ dispatch({ type: "select-lower" });
506
+ }, []);
507
+ const selectRange = React.useCallback((teethIds) => {
508
+ dispatch({ type: "select-range", teethIds });
509
+ }, []);
510
+ const clearAll = React.useCallback(() => {
511
+ dispatch({ type: "clear-all" });
512
+ }, []);
513
+ const isSelected = React.useCallback((toothId) => state.selectedTeeth.includes(toothId), [state.selectedTeeth]);
514
+ const hasBridge = React.useCallback((fromId, toId) => {
515
+ const key = (a, b) => `${Math.min(a, b)}-${Math.max(a, b)}`;
516
+ return state.bridges.some(([a, b]) => key(a, b) === key(fromId, toId));
517
+ }, [state.bridges]);
518
+ const getSelectedTeethData = React.useCallback(() => teethData.filter((t) => state.selectedTeeth.includes(t.id)).sort((a, b) => a.order - b.order), [state.selectedTeeth]);
519
+ const renderProps = {
520
+ ...state,
521
+ toggleTooth,
522
+ toggleBridge,
523
+ selectUpper,
524
+ selectLower,
525
+ selectRange,
526
+ clearAll,
527
+ isSelected,
528
+ hasBridge,
529
+ getSelectedTeethData
530
+ };
531
+ return /* @__PURE__ */ jsxDEV(TeethSelectorContext.Provider, {
532
+ value: { state, dispatch, dragState, setDragState },
533
+ children: /* @__PURE__ */ jsxDEV("div", {
534
+ ref,
535
+ children: children(renderProps)
536
+ }, undefined, false, undefined, this)
537
+ }, undefined, false, undefined, this);
538
+ });
539
+ TeethSelector.displayName = "TeethSelector";
540
+ var TeethChart = React.forwardRef((props, ref) => {
541
+ const {
542
+ width = 300,
543
+ height = 500,
544
+ className,
545
+ renderTooth,
546
+ renderBridge,
547
+ enableDragSelection = true
548
+ } = props;
549
+ const { state, dispatch, dragState, setDragState } = useTeethSelectorContext();
550
+ const svgRef = React.useRef(null);
551
+ const toothRefs = React.useRef(new Map);
552
+ const holdRef = React.useRef(null);
553
+ React.useImperativeHandle(ref, () => svgRef.current);
554
+ React.useEffect(() => {
555
+ if (!svgRef.current)
556
+ return;
557
+ const map = new Map;
558
+ teethData.forEach(({ id }) => {
559
+ map.set(id, svgRef.current.querySelector(`#teeth-${id}`));
560
+ });
561
+ toothRefs.current = map;
562
+ }, []);
563
+ const getBridgePos = React.useCallback((a, b) => {
564
+ const from = toothRefs.current.get(a);
565
+ const to = toothRefs.current.get(b);
566
+ if (!from || !to)
567
+ return null;
568
+ const boxA = from.getBBox();
569
+ const boxB = to.getBBox();
570
+ const x1 = boxA.x + boxA.width / 2;
571
+ const y1 = boxA.y + boxA.height / 2;
572
+ const x2 = boxB.x + boxB.width / 2;
573
+ const y2 = boxB.y + boxB.height / 2;
574
+ return { x1, y1, x2, y2, midX: (x1 + x2) / 2, midY: (y1 + y2) / 2 };
575
+ }, []);
576
+ const handleClick = React.useCallback((id) => {
577
+ if (holdRef.current)
578
+ clearTimeout(holdRef.current);
579
+ dispatch({ type: "toggle-tooth", toothId: id });
580
+ }, [dispatch]);
581
+ const handleDown = React.useCallback((id) => {
582
+ if (!enableDragSelection)
583
+ return;
584
+ holdRef.current = setTimeout(() => {
585
+ setDragState({
586
+ start: id,
587
+ current: id,
588
+ teethInRange: [id]
589
+ });
590
+ }, 100);
591
+ }, [enableDragSelection, setDragState]);
592
+ const handleEnter = React.useCallback((id) => {
593
+ if (dragState.start === null)
594
+ return;
595
+ if (holdRef.current)
596
+ clearTimeout(holdRef.current);
597
+ const range = getTeethInRange(dragState.start, id);
598
+ setDragState((prev) => ({
599
+ ...prev,
600
+ current: id,
601
+ teethInRange: range
602
+ }));
603
+ }, [dragState.start, setDragState]);
604
+ const handleUp = React.useCallback(() => {
605
+ if (dragState.start === null)
606
+ return;
607
+ if (holdRef.current)
608
+ clearTimeout(holdRef.current);
609
+ if (dragState.teethInRange.length > 1) {
610
+ dispatch({ type: "select-range", teethIds: dragState.teethInRange });
611
+ }
612
+ setDragState(initialDragState);
613
+ }, [dragState, dispatch, setDragState]);
614
+ const handleTouchMove = React.useCallback((e) => {
615
+ if (!enableDragSelection)
616
+ return;
617
+ const touch = e.touches[0];
618
+ const element = document.elementFromPoint(touch.clientX, touch.clientY);
619
+ if (element?.id?.startsWith("teeth-")) {
620
+ const toothId = parseInt(element.id.replace("teeth-", ""), 10);
621
+ if (dragState.start === null) {
622
+ setDragState({
623
+ start: toothId,
624
+ current: toothId,
625
+ teethInRange: [toothId]
626
+ });
627
+ }
628
+ handleEnter(toothId);
629
+ }
630
+ }, [enableDragSelection, dragState.start, handleEnter, setDragState]);
631
+ return /* @__PURE__ */ jsxDEV("svg", {
632
+ ref: svgRef,
633
+ viewBox: "0 0 450 750",
634
+ width,
635
+ height,
636
+ className,
637
+ style: { touchAction: "none" },
638
+ onMouseUp: handleUp,
639
+ onTouchMove: handleTouchMove,
640
+ onTouchEnd: handleUp,
641
+ children: [
642
+ teethData.map((tooth) => {
643
+ const isSelected = state.selectedTeeth.includes(tooth.id);
644
+ const isInDragRange = dragState.teethInRange.includes(tooth.id);
645
+ const defaultElement = /* @__PURE__ */ jsxDEV("path", {
646
+ id: `teeth-${tooth.id}`,
647
+ d: tooth.d,
648
+ "data-selected": isSelected,
649
+ "data-in-range": isInDragRange,
650
+ onClick: () => handleClick(tooth.id),
651
+ onMouseDown: () => handleDown(tooth.id),
652
+ onMouseEnter: () => handleEnter(tooth.id)
653
+ }, tooth.id, false, undefined, this);
654
+ if (renderTooth) {
655
+ return renderTooth({ tooth, isSelected, isInDragRange }, defaultElement);
656
+ }
657
+ return defaultElement;
658
+ }),
659
+ /* @__PURE__ */ jsxDEV("g", {
660
+ "data-bridges": true,
661
+ children: adjacentTeethPairs.map((pair, i) => {
662
+ const pos = getBridgePos(pair[0], pair[1]);
663
+ if (!pos)
664
+ return null;
665
+ const exists = state.bridges.some(([a, b]) => a === pair[0] && b === pair[1] || a === pair[1] && b === pair[0]);
666
+ const defaultElement = /* @__PURE__ */ jsxDEV("g", {
667
+ children: [
668
+ exists && /* @__PURE__ */ jsxDEV("line", {
669
+ x1: pos.x1,
670
+ y1: pos.y1,
671
+ x2: pos.x2,
672
+ y2: pos.y2,
673
+ "data-bridge-line": true
674
+ }, undefined, false, undefined, this),
675
+ /* @__PURE__ */ jsxDEV("circle", {
676
+ cx: pos.midX,
677
+ cy: pos.midY,
678
+ r: 8,
679
+ "data-bridge-button": true,
680
+ "data-exists": exists,
681
+ onClick: () => dispatch({
682
+ type: "toggle-bridge",
683
+ fromId: pair[0],
684
+ toId: pair[1]
685
+ })
686
+ }, undefined, false, undefined, this)
687
+ ]
688
+ }, i, true, undefined, this);
689
+ if (renderBridge) {
690
+ return renderBridge({ fromId: pair[0], toId: pair[1], exists, position: pos }, defaultElement);
691
+ }
692
+ return defaultElement;
693
+ })
694
+ }, undefined, false, undefined, this)
695
+ ]
696
+ }, undefined, true, undefined, this);
697
+ });
698
+ TeethChart.displayName = "TeethChart";
699
+ // src/components/teeth-preview.tsx
700
+ import * as React2 from "react";
701
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
702
+ "use client";
703
+ var TeethPreview = React2.forwardRef((props, ref) => {
704
+ const {
705
+ teeth,
706
+ width = 150,
707
+ height = 250,
708
+ className,
709
+ renderTooth,
710
+ renderBridge
711
+ } = props;
712
+ const [refsReady, setRefsReady] = React2.useState(false);
713
+ const svgRef = React2.useRef(null);
714
+ const toothRefs = React2.useRef(new Map);
715
+ const { selectedTeeth, bridges } = React2.useMemo(() => parseTeethSelection(teeth), [teeth]);
716
+ React2.useImperativeHandle(ref, () => svgRef.current);
717
+ const getBridgePos = React2.useCallback((a, b) => {
718
+ const from = toothRefs.current.get(a);
719
+ const to = toothRefs.current.get(b);
720
+ if (!from || !to)
721
+ return null;
722
+ const boxA = from.getBBox();
723
+ const boxB = to.getBBox();
724
+ const x1 = boxA.x + boxA.width / 2;
725
+ const y1 = boxA.y + boxA.height / 2;
726
+ const x2 = boxB.x + boxB.width / 2;
727
+ const y2 = boxB.y + boxB.height / 2;
728
+ return { x1, y1, x2, y2, midX: (x1 + x2) / 2, midY: (y1 + y2) / 2 };
729
+ }, []);
730
+ React2.useLayoutEffect(() => {
731
+ if (!svgRef.current)
732
+ return;
733
+ const map = new Map;
734
+ teethData.forEach(({ id }) => {
735
+ map.set(id, svgRef.current.querySelector(`#teeth-preview-${id}`));
736
+ });
737
+ toothRefs.current = map;
738
+ setRefsReady(true);
739
+ }, []);
740
+ return /* @__PURE__ */ jsxDEV2("svg", {
741
+ ref: svgRef,
742
+ viewBox: "0 0 450 750",
743
+ width,
744
+ height,
745
+ className,
746
+ children: [
747
+ teethData.map((tooth) => {
748
+ const isSelected = selectedTeeth.includes(tooth.id);
749
+ const defaultElement = /* @__PURE__ */ jsxDEV2("path", {
750
+ id: `teeth-preview-${tooth.id}`,
751
+ d: tooth.d,
752
+ "data-selected": isSelected
753
+ }, tooth.id, false, undefined, this);
754
+ if (renderTooth) {
755
+ return renderTooth({ tooth, isSelected }, defaultElement);
756
+ }
757
+ return defaultElement;
758
+ }),
759
+ /* @__PURE__ */ jsxDEV2("g", {
760
+ "data-bridges": true,
761
+ children: refsReady && bridges.map((pair, i) => {
762
+ const pos = getBridgePos(pair[0], pair[1]);
763
+ if (!pos)
764
+ return null;
765
+ const defaultElement = /* @__PURE__ */ jsxDEV2("g", {
766
+ children: [
767
+ /* @__PURE__ */ jsxDEV2("line", {
768
+ x1: pos.x1,
769
+ y1: pos.y1,
770
+ x2: pos.x2,
771
+ y2: pos.y2,
772
+ "data-bridge-line": true
773
+ }, undefined, false, undefined, this),
774
+ /* @__PURE__ */ jsxDEV2("circle", {
775
+ cx: pos.midX,
776
+ cy: pos.midY,
777
+ r: 8,
778
+ "data-bridge-marker": true
779
+ }, undefined, false, undefined, this)
780
+ ]
781
+ }, i, true, undefined, this);
782
+ if (renderBridge) {
783
+ return renderBridge({ fromId: pair[0], toId: pair[1], position: pos }, defaultElement);
784
+ }
785
+ return defaultElement;
786
+ })
787
+ }, undefined, false, undefined, this)
788
+ ]
789
+ }, undefined, true, undefined, this);
790
+ });
791
+ TeethPreview.displayName = "TeethPreview";
792
+ export {
793
+ useTeethSelectorContext,
794
+ TeethSelector,
795
+ TeethPreview,
796
+ TeethChart
797
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salloomd/teeth-selector",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -10,6 +10,16 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.js",
12
12
  "default": "./dist/index.js"
13
+ },
14
+ "./ui": {
15
+ "types": "./dist/ui.d.ts",
16
+ "import": "./dist/ui.js",
17
+ "default": "./dist/ui.js"
18
+ },
19
+ "./lib": {
20
+ "types": "./dist/lib.d.ts",
21
+ "import": "./dist/lib.js",
22
+ "default": "./dist/lib.js"
13
23
  }
14
24
  },
15
25
  "files": [
@@ -17,7 +27,7 @@
17
27
  ],
18
28
  "sideEffects": false,
19
29
  "scripts": {
20
- "build": "bun build src/index.ts --outdir dist --external react --external react-dom && tsc -p tsconfig.build.json",
30
+ "build": "bun build src/index.ts src/ui.ts src/lib.ts --outdir dist --external react --external react-dom && tsc -p tsconfig.build.json",
21
31
  "storybook": "storybook dev -p 6006",
22
32
  "build-storybook": "storybook build"
23
33
  },