@alfadocs/ui-kit 1.4.17 → 1.6.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.
Files changed (92) hide show
  1. package/dist/_chunks/{booking-CGKof8QU.js → booking-D84O6H2X.js} +553 -546
  2. package/dist/_chunks/{care-plan-card-C83Hb9EE.js → care-plan-card-CehAU7F_.js} +2 -2
  3. package/dist/_chunks/{care-plan-entry-card-CHMPSTCz.js → care-plan-entry-card-ChWRMfMw.js} +2 -2
  4. package/dist/_chunks/{periodontal-chart-brakjj0s.js → periodontal-chart-C0nNQKKK.js} +121 -121
  5. package/dist/_chunks/practice-profile-hero-Bdd0bFqC.js +187 -0
  6. package/dist/_chunks/{practice-results-DWUZRm2x.js → practice-results-CKoLD_x5.js} +220 -206
  7. package/dist/_chunks/tooth-data-DR8Kdnup.js +903 -0
  8. package/dist/_chunks/tooth-finding-palette-vqC-D2Ye.js +708 -0
  9. package/dist/_chunks/tooth-scheme-BPXE1_3N.js +2051 -0
  10. package/dist/agent-catalog.json +497 -56
  11. package/dist/agent-i18n/en.json +29 -11
  12. package/dist/components/booking/booking-types.d.ts +9 -0
  13. package/dist/components/booking/index.js +1 -1
  14. package/dist/components/care-plan-card/index.js +1 -1
  15. package/dist/components/care-plan-entry-card/index.js +1 -1
  16. package/dist/components/index.d.ts +2 -0
  17. package/dist/components/periodontal-chart/index.js +1 -1
  18. package/dist/components/practice-profile-hero/index.d.ts +4 -0
  19. package/dist/components/practice-profile-hero/index.js +6 -0
  20. package/dist/components/practice-profile-hero/practice-profile-hero.agent.d.ts +4 -0
  21. package/dist/components/practice-profile-hero/practice-profile-hero.d.ts +60 -0
  22. package/dist/components/practice-results/index.js +1 -1
  23. package/dist/components/practice-results/practice-results.d.ts +15 -1
  24. package/dist/components/tooth-finding-palette/index.d.ts +4 -0
  25. package/dist/components/tooth-finding-palette/index.js +9 -0
  26. package/dist/components/tooth-finding-palette/tooth-finding-palette.agent.d.ts +4 -0
  27. package/dist/components/tooth-finding-palette/tooth-finding-palette.d.ts +89 -0
  28. package/dist/components/tooth-scheme/index.d.ts +2 -2
  29. package/dist/components/tooth-scheme/index.js +57 -30
  30. package/dist/components/tooth-scheme/tooth-data.d.ts +279 -10
  31. package/dist/components/tooth-scheme/tooth-scheme.agent.d.ts +2 -2
  32. package/dist/components/tooth-scheme/tooth-scheme.d.ts +30 -3
  33. package/dist/host-compat.css +1 -4
  34. package/dist/i18n/locales/ar.d.ts +41 -1
  35. package/dist/i18n/locales/ar.js +42 -2
  36. package/dist/i18n/locales/de.d.ts +41 -1
  37. package/dist/i18n/locales/de.js +42 -2
  38. package/dist/i18n/locales/el.d.ts +41 -1
  39. package/dist/i18n/locales/el.js +42 -2
  40. package/dist/i18n/locales/en.d.ts +41 -1
  41. package/dist/i18n/locales/en.js +42 -2
  42. package/dist/i18n/locales/es.d.ts +41 -1
  43. package/dist/i18n/locales/es.js +42 -2
  44. package/dist/i18n/locales/fr.d.ts +41 -1
  45. package/dist/i18n/locales/fr.js +42 -2
  46. package/dist/i18n/locales/hi.d.ts +41 -1
  47. package/dist/i18n/locales/hi.js +42 -2
  48. package/dist/i18n/locales/it.d.ts +41 -1
  49. package/dist/i18n/locales/it.js +42 -2
  50. package/dist/i18n/locales/ja.d.ts +41 -1
  51. package/dist/i18n/locales/ja.js +42 -2
  52. package/dist/i18n/locales/nl.d.ts +41 -1
  53. package/dist/i18n/locales/nl.js +42 -2
  54. package/dist/i18n/locales/pl.d.ts +41 -1
  55. package/dist/i18n/locales/pl.js +42 -2
  56. package/dist/i18n/locales/pt.d.ts +41 -1
  57. package/dist/i18n/locales/pt.js +42 -2
  58. package/dist/i18n/locales/ro.d.ts +41 -1
  59. package/dist/i18n/locales/ro.js +42 -2
  60. package/dist/i18n/locales/ru.d.ts +41 -1
  61. package/dist/i18n/locales/ru.js +42 -2
  62. package/dist/i18n/locales/sq.d.ts +41 -1
  63. package/dist/i18n/locales/sq.js +42 -2
  64. package/dist/i18n/locales/sv.d.ts +41 -1
  65. package/dist/i18n/locales/sv.js +42 -2
  66. package/dist/i18n/locales/tr.d.ts +41 -1
  67. package/dist/i18n/locales/tr.js +42 -2
  68. package/dist/i18n/locales/zh.d.ts +41 -1
  69. package/dist/i18n/locales/zh.js +42 -2
  70. package/dist/index.js +561 -525
  71. package/dist/locales/ar.json +42 -2
  72. package/dist/locales/de.json +42 -2
  73. package/dist/locales/el.json +42 -2
  74. package/dist/locales/en.json +42 -2
  75. package/dist/locales/es.json +42 -2
  76. package/dist/locales/fr.json +42 -2
  77. package/dist/locales/hi.json +42 -2
  78. package/dist/locales/it.json +42 -2
  79. package/dist/locales/ja.json +42 -2
  80. package/dist/locales/nl.json +42 -2
  81. package/dist/locales/pl.json +42 -2
  82. package/dist/locales/pt.json +42 -2
  83. package/dist/locales/ro.json +42 -2
  84. package/dist/locales/ru.json +42 -2
  85. package/dist/locales/sq.json +42 -2
  86. package/dist/locales/sv.json +42 -2
  87. package/dist/locales/tr.json +42 -2
  88. package/dist/locales/zh.json +42 -2
  89. package/dist/tokens.css +1 -1
  90. package/package.json +9 -1
  91. package/dist/_chunks/tooth-data-BwuoKFf4.js +0 -660
  92. package/dist/_chunks/tooth-scheme-s0ipC0-Z.js +0 -1483
@@ -0,0 +1,2051 @@
1
+ import { jsxs as k, jsx as s, Fragment as Bo } from "react/jsx-runtime";
2
+ import { forwardRef as Go, useState as K, useRef as qe, useEffect as Ce, useMemo as Oe, useCallback as D, useImperativeHandle as Qo } from "react";
3
+ import { createPortal as Zo } from "react-dom";
4
+ import * as E from "@radix-ui/react-tooltip";
5
+ import { c as ie } from "./index-D2ZczOXr.js";
6
+ import { useTranslation as io } from "react-i18next";
7
+ import { u as Xo } from "./registry-DvAUVLHh.js";
8
+ import { I as Jo } from "./icon-button-9c9Gp41m.js";
9
+ import { P as rt } from "./popover-Btj6N25H.js";
10
+ import { S as en } from "./select-BW5ecXlX.js";
11
+ import { S as at } from "./switch-OILsBLWo.js";
12
+ import { o as zt, K as tn, a0 as on, J as Ke, R as Ue, u as ro, U as ao, G as nn, S as rn, w as Ve, X as F, f as de, $ as st, _ as Vt, E as ft, a1 as ce, H as Bt, z as ut, y as an, l as ct, A as so, L as sn, t as pt, W as dt, s as co, Y as lo, x as cn, V as dn, a2 as ln, a3 as $, a4 as U, a5 as hn, Z as un, C as ho, Q as pn, a6 as fn, k as Gt, T as mn, O as gn, q as vn, N as bn } from "./tooth-data-DR8Kdnup.js";
13
+ import { S as yn } from "./settings-d25ju_6L.js";
14
+ const ye = () => (t) => t, Sn = ye()([
15
+ "caries",
16
+ "filled",
17
+ "crowned",
18
+ "temporaryCrown",
19
+ "bridge",
20
+ "missing",
21
+ "implant",
22
+ "implantExtraction",
23
+ "stub",
24
+ "destroyed",
25
+ "rootCanal",
26
+ "pin",
27
+ "rootAmputation",
28
+ "anchorage",
29
+ "hook",
30
+ "attachment",
31
+ "mucogingivalSurgery",
32
+ "periodontalSurgery",
33
+ "curettage",
34
+ "medication",
35
+ "xray"
36
+ ]), xn = ye()([
37
+ "mesial",
38
+ "distal",
39
+ "occlusal",
40
+ "buccal",
41
+ "lingual"
42
+ ]), wn = ye()([
43
+ "mesial",
44
+ "distal",
45
+ "palatal",
46
+ "buccal",
47
+ "single"
48
+ ]), kn = ye()(["element", "surface", "root"]), In = ye()(["planned", "executed"]), Tn = ye()([
49
+ "periapical",
50
+ "bitewing",
51
+ "panoramic",
52
+ "other"
53
+ ]), W = {
54
+ type: "string",
55
+ description: "FDI tooth id ('11'–'48' permanent, '51'–'85' primary) or a special-element id ('Q1'–'Q4' quadrants, 'S1'–'S6' sextants, 'AS'/'AI' arches, 'general'; primary mirrors with an 'm' prefix)."
56
+ }, Ye = {
57
+ enum: [...Sn],
58
+ description: "Built-in finding id. 'destroyed' is the retained root (*radice persistente*); 'xray' is the chartable radiograph SYMBOL, distinct from the image archive reached via get_radiographs."
59
+ }, uo = {
60
+ type: "string",
61
+ description: "Finding id — either a built-in (the enum on set_finding) or a practice-defined id from the host's `conditionRegistry`."
62
+ }, po = {
63
+ enum: [...xn],
64
+ description: "Tooth surface, named anatomically."
65
+ }, An = {
66
+ enum: [...wn],
67
+ description: "Root, named ANATOMICALLY (not by screen side) — 'palatal' exists only on upper molars, 'single' covers every one-rooted tooth. On flank-mounted markers (anchorage, hook) 'mesial' / 'distal' select the side of the tooth instead."
68
+ }, Dn = {
69
+ enum: [...kn],
70
+ description: "Anatomical layer the finding marks. Omit to take the condition's natural part (caries and fillings are 'surface', root canals are 'root', crowns and implants are 'element')."
71
+ }, _n = {
72
+ enum: [...In],
73
+ description: "Execution status — 'planned' (proposed treatment) vs 'executed' (already done). Omit when the finding carries no plan/executed distinction."
74
+ }, Qt = {
75
+ type: "object",
76
+ properties: {
77
+ id: {
78
+ type: "string",
79
+ description: "Provenance id — the host's care-plan / finding entry this symbol came from. Present on symbols the host seeded; supply it to update that exact entry."
80
+ },
81
+ condition: uo,
82
+ part: Dn,
83
+ surfaces: { type: "array", items: po },
84
+ roots: { type: "array", items: An },
85
+ status: _n,
86
+ color: {
87
+ type: "string",
88
+ description: "Per-practice colour override — a CSS colour or `var(--token)`. Omit to take the condition's own colour."
89
+ },
90
+ note: {
91
+ type: "string",
92
+ description: "Free-text clinical note. PHI — never in a state snapshot."
93
+ }
94
+ },
95
+ required: ["condition"]
96
+ }, Cn = {
97
+ type: "object",
98
+ properties: {
99
+ id: { type: "string" },
100
+ url: { type: "string" },
101
+ thumbnailUrl: { type: "string" },
102
+ date: { type: "string", description: "ISO acquisition date (YYYY-MM-DD)." },
103
+ note: { type: "string" },
104
+ kind: { enum: [...Tn] },
105
+ alt: { type: "string" }
106
+ },
107
+ required: ["id", "url"]
108
+ };
109
+ function On(t) {
110
+ return {
111
+ id: t.id,
112
+ condition: t.condition,
113
+ part: t.part,
114
+ surfaces: t.surfaces,
115
+ roots: t.roots,
116
+ status: t.status,
117
+ color: t.color
118
+ };
119
+ }
120
+ const $n = {
121
+ id: "tooth-scheme",
122
+ // pick — focus/select a tooth (`focus_tooth`, transient, not persisted);
123
+ // set_value / edit_inline — `set_finding` records a built-in finding and
124
+ // marks the affected surfaces (set-surface), `set_symbol` records the full
125
+ // per-symbol form (part / roots / status / colour / note), `remove_finding`
126
+ // clears one; view_change — the side/plan projection + dentition are view
127
+ // props.
128
+ // `view_change` is deliberately NOT claimed: projection / dentition /
129
+ // numbering are props, and the component's own view state only takes effect
130
+ // when `controls` is on, so a `set_view` action would silently no-op for most
131
+ // consumers. The view is readable below; changing it stays the host's job.
132
+ capabilities: ["pick", "set_value", "edit_inline"],
133
+ state: {
134
+ chart: {
135
+ // A9: the always-on snapshot carries FDI / element ids plus finding
136
+ // ENUMS only — never the free-text clinical notes (`ToothState.notes`,
137
+ // `ToothSymbol.note`), which are PHI. Those are fetched deliberately,
138
+ // per tooth, via get_notes / get_symbols (both safety: 'phi').
139
+ type: "Record<FdiId, { conditions: ToothCondition[]; surfaces: Surface[]; symbols: { id?: string; condition: ConditionId; part?: SymbolPart; surfaces?: Surface[]; roots?: Root[]; status?: SymbolStatus; color?: string }[] }>",
140
+ descriptionKey: "ui.agent.toothScheme.state.chart",
141
+ description: "Current dental chart keyed by FDI id, or by a special-element id (quadrant / sextant / arch / general) for region-spanning findings. Each entry carries the flat `conditions` + `surfaces` pair AND the normalised per-symbol list, where every finding scopes its own surfaces, roots, planned/executed status and colour. Free-text clinical notes are PHI and omitted — read them per tooth via get_symbols / get_notes.",
142
+ read: (t) => Object.fromEntries(
143
+ Object.entries(t.getChart()).map(([n, a]) => [
144
+ n,
145
+ {
146
+ conditions: a.conditions,
147
+ surfaces: a.surfaces,
148
+ // Read back through the handle so a tooth charted the legacy
149
+ // way (flat conditions + surfaces) reports the same symbol
150
+ // shape as one charted per-symbol.
151
+ symbols: t.getSymbols(n).map(On)
152
+ }
153
+ ])
154
+ )
155
+ },
156
+ chartedTeeth: {
157
+ type: "string[]",
158
+ descriptionKey: "ui.agent.toothScheme.state.chartedTeeth",
159
+ description: "Ids carrying at least one finding — FDI tooth ids and/or special-element ids (quadrant / sextant / arch / general).",
160
+ read: (t) => Object.keys(t.getChart())
161
+ },
162
+ radiographs: {
163
+ // A9: the always-on state snapshot carries identifiers + enums only —
164
+ // never PHI free-text (the radiograph `note`), service dates, or the
165
+ // consumer-hosted image URLs. Those are fetched deliberately, per tooth,
166
+ // via the audited `get_radiographs` action.
167
+ type: "Record<string, { id: string; kind?: string }[]>",
168
+ descriptionKey: "ui.agent.toothScheme.state.radiographs",
169
+ description: "Structural references (id + kind) to the radiographs on each tooth, keyed by FDI id. Notes, dates and image URLs are not exposed here — fetch them on demand via get_radiographs.",
170
+ read: (t) => Object.fromEntries(
171
+ t.teethWithRadiographs().map((n) => [
172
+ n,
173
+ t.getRadiographs(n).map((a) => ({ id: a.id, kind: a.kind ?? "periapical" }))
174
+ ])
175
+ )
176
+ },
177
+ teethWithRadiographs: {
178
+ type: "string[]",
179
+ descriptionKey: "ui.agent.toothScheme.state.teethWithRadiographs",
180
+ description: "FDI ids of every tooth carrying at least one radiograph.",
181
+ read: (t) => t.teethWithRadiographs()
182
+ }
183
+ },
184
+ actions: {
185
+ focus_tooth: {
186
+ safety: "read",
187
+ argsType: "{ id: FdiId }",
188
+ argsSchema: {
189
+ type: "object",
190
+ properties: { id: W },
191
+ required: ["id"]
192
+ },
193
+ idempotent: !0,
194
+ errors: [
195
+ "In the `both` view the registered instance renders no SVG of its own (the four sub-charts do, and deliberately do not register), so this action has no effect there."
196
+ ],
197
+ descriptionKey: "ui.agent.toothScheme.actions.focusTooth",
198
+ description: "Move focus to a specific tooth by FDI id.",
199
+ invoke: (t, n) => {
200
+ t.focusTooth(n.id);
201
+ }
202
+ },
203
+ get_tooth: {
204
+ safety: "read",
205
+ argsType: "{ id: FdiId }",
206
+ argsSchema: {
207
+ type: "object",
208
+ properties: { id: W },
209
+ required: ["id"]
210
+ },
211
+ returns: "ToothCondition[]",
212
+ returnsSchema: { type: "array", items: Ye },
213
+ idempotent: !0,
214
+ descriptionKey: "ui.agent.toothScheme.actions.getTooth",
215
+ description: "Read the BUILT-IN findings recorded on one tooth (FDI id), as a flat list of condition ids. Practice-defined findings, per-finding surfaces, roots and status are not included — use get_symbols for the tooth's full record.",
216
+ invoke: (t, n) => t.getTooth(n.id)
217
+ },
218
+ get_symbols: {
219
+ // phi (not read): a symbol carries a free-text clinical `note`, which is
220
+ // PHI. PRS §26 §3 — phi outranks read so the host gates it behind
221
+ // stricter confirm + audit. The always-on state.chart snapshot stays
222
+ // note-free.
223
+ safety: "phi",
224
+ argsType: "{ id: FdiId }",
225
+ argsSchema: {
226
+ type: "object",
227
+ properties: { id: W },
228
+ required: ["id"]
229
+ },
230
+ returns: "ToothSymbol[]",
231
+ returnsSchema: { type: "array", items: Qt },
232
+ idempotent: !0,
233
+ descriptionKey: "ui.agent.toothScheme.actions.getSymbols",
234
+ description: "Read one tooth's full finding record (incl. notes) — one entry per symbol, each with its own surfaces, roots, planned/executed status, colour override, provenance id and free-text note. Normalised, so a tooth charted with the flat conditions/surfaces pair reports the same shape.",
235
+ invoke: (t, n) => t.getSymbols(n.id)
236
+ },
237
+ get_notes: {
238
+ // phi (not read): resolves the tooth-level free-text clinical note.
239
+ // Same posture as get_symbols — the note is out of state.chart and only
240
+ // reachable through this audited action.
241
+ safety: "phi",
242
+ argsType: "{ id: FdiId }",
243
+ argsSchema: {
244
+ type: "object",
245
+ properties: { id: W },
246
+ required: ["id"]
247
+ },
248
+ returns: "string | null",
249
+ returnsSchema: { type: ["string", "null"] },
250
+ idempotent: !0,
251
+ descriptionKey: "ui.agent.toothScheme.actions.getNotes",
252
+ description: "Read the tooth-level free-text clinical note, or null when there is none. Per-finding notes are separate — they come back from get_symbols.",
253
+ invoke: (t, n) => {
254
+ var a;
255
+ return ((a = t.getChart()[n.id]) == null ? void 0 : a.notes) ?? null;
256
+ }
257
+ },
258
+ teeth_with: {
259
+ safety: "read",
260
+ argsType: "{ condition: ToothCondition }",
261
+ argsSchema: {
262
+ type: "object",
263
+ properties: { condition: Ye },
264
+ required: ["condition"]
265
+ },
266
+ returns: "FdiId[]",
267
+ returnsSchema: { type: "array", items: W },
268
+ idempotent: !0,
269
+ descriptionKey: "ui.agent.toothScheme.actions.teethWith",
270
+ description: "List the ids carrying a given built-in finding — FDI tooth ids and/or special-element ids (e.g. all teeth with caries). For a practice-defined finding use teeth_with_symbol.",
271
+ invoke: (t, n) => t.teethWith(n.condition)
272
+ },
273
+ teeth_with_symbol: {
274
+ safety: "read",
275
+ argsType: "{ condition: ConditionId }",
276
+ argsSchema: {
277
+ type: "object",
278
+ properties: { condition: uo },
279
+ required: ["condition"]
280
+ },
281
+ returns: "FdiId[]",
282
+ returnsSchema: { type: "array", items: W },
283
+ idempotent: !0,
284
+ descriptionKey: "ui.agent.toothScheme.actions.teethWithSymbol",
285
+ description: "As teeth_with, but open to the practice-defined ids in the host's `conditionRegistry` as well as the built-ins.",
286
+ invoke: (t, n) => t.teethWithSymbol(n.condition)
287
+ },
288
+ get_radiographs: {
289
+ // phi (not read): resolves the radiograph `note` (free-text clinical
290
+ // PHI) alongside the acquisition date and the consumer-hosted image
291
+ // urls. PRS §26 §3 — phi outranks read so the host gates it behind
292
+ // stricter confirm + audit; the always-on state.radiographs snapshot
293
+ // stays structural (id + kind).
294
+ safety: "phi",
295
+ argsType: "{ id: FdiId }",
296
+ argsSchema: {
297
+ type: "object",
298
+ properties: { id: W },
299
+ required: ["id"]
300
+ },
301
+ returns: "Radiograph[]",
302
+ returnsSchema: { type: "array", items: Cn },
303
+ idempotent: !0,
304
+ descriptionKey: "ui.agent.toothScheme.actions.getRadiographs",
305
+ description: "Read the radiographs (x-rays) attached to one tooth, including their notes, dates and image urls.",
306
+ invoke: (t, n) => t.getRadiographs(n.id)
307
+ },
308
+ open_radiographs: {
309
+ safety: "read",
310
+ argsType: "{ id: FdiId }",
311
+ argsSchema: {
312
+ type: "object",
313
+ properties: { id: W },
314
+ required: ["id"]
315
+ },
316
+ idempotent: !0,
317
+ errors: [
318
+ "In the `both` view the registered instance renders no SVG of its own (the four sub-charts do, and deliberately do not register), so this action has no effect there."
319
+ ],
320
+ descriptionKey: "ui.agent.toothScheme.actions.openRadiographs",
321
+ description: "Focus a tooth and open its radiograph archive (fires the host's onOpenRadiographs).",
322
+ invoke: (t, n) => {
323
+ t.openRadiographs(n.id);
324
+ }
325
+ },
326
+ set_finding: {
327
+ safety: "write",
328
+ argsType: "{ id: FdiId; condition: ToothCondition; surfaces?: Surface[] }",
329
+ argsSchema: {
330
+ type: "object",
331
+ properties: {
332
+ id: W,
333
+ condition: Ye,
334
+ surfaces: { type: "array", items: po }
335
+ },
336
+ required: ["id", "condition"]
337
+ },
338
+ idempotent: !0,
339
+ descriptionKey: "ui.agent.toothScheme.actions.setFinding",
340
+ description: "Record a built-in finding on a tooth or region element (crown, implant, root canal, caries, …). Optionally mark the affected surfaces for a surface finding. Structural only — to set a finding’s roots, planned/executed status, colour or note, or to chart a practice-defined finding, use set_symbol.",
341
+ invoke: (t, n) => {
342
+ t.setFinding(n.id, n.condition, n.surfaces);
343
+ }
344
+ },
345
+ set_symbol: {
346
+ // phi (not write): the symbol may carry a free-text clinical `note`,
347
+ // which is PHI. PRS §26 §3 — a PHI write must never be auto-confirmed.
348
+ // The structural-only path stays available as set_finding ('write').
349
+ safety: "phi",
350
+ argsType: "{ id: FdiId; symbol: ToothSymbol }",
351
+ argsSchema: {
352
+ type: "object",
353
+ properties: { id: W, symbol: Qt },
354
+ required: ["id", "symbol"]
355
+ },
356
+ idempotent: !0,
357
+ descriptionKey: "ui.agent.toothScheme.actions.setSymbol",
358
+ description: "Add or replace one finding on a tooth or region element, with its own part, surfaces, roots, planned/executed status, colour override and note. Matched by `symbol.id` when the symbol carries provenance, otherwise by its condition; appended when nothing matches. Accepts practice-defined `conditionRegistry` ids as well as the built-ins.",
359
+ examples: [
360
+ {
361
+ args: {
362
+ id: "16",
363
+ symbol: {
364
+ condition: "filled",
365
+ surfaces: ["mesial"],
366
+ status: "executed"
367
+ }
368
+ },
369
+ note: "Record an executed mesial filling on tooth 16, leaving any other finding on that tooth untouched."
370
+ },
371
+ {
372
+ args: {
373
+ id: "26",
374
+ symbol: {
375
+ condition: "rootCanal",
376
+ roots: ["palatal"],
377
+ status: "planned"
378
+ }
379
+ },
380
+ note: "Plan a root canal on the palatal root of tooth 26 ('palatal' is the anatomical root, not a screen side)."
381
+ }
382
+ ],
383
+ invoke: (t, n) => {
384
+ t.setSymbol(n.id, n.symbol);
385
+ }
386
+ },
387
+ remove_finding: {
388
+ safety: "write",
389
+ argsType: "{ id: FdiId; condition: ToothCondition }",
390
+ argsSchema: {
391
+ type: "object",
392
+ properties: { id: W, condition: Ye },
393
+ required: ["id", "condition"]
394
+ },
395
+ idempotent: !0,
396
+ descriptionKey: "ui.agent.toothScheme.actions.removeFinding",
397
+ description: "Remove a finding from a tooth — every symbol carrying that condition goes, whatever its surfaces or roots. Built-in ids only.",
398
+ invoke: (t, n) => {
399
+ t.removeFinding(n.id, n.condition);
400
+ }
401
+ },
402
+ clear_tooth: {
403
+ safety: "destructive",
404
+ argsType: "{ id: FdiId }",
405
+ argsSchema: {
406
+ type: "object",
407
+ properties: { id: W },
408
+ required: ["id"]
409
+ },
410
+ idempotent: !0,
411
+ descriptionKey: "ui.agent.toothScheme.actions.clearTooth",
412
+ description: "Clear every finding from a tooth.",
413
+ invoke: (t, n) => {
414
+ t.clearTooth(n.id);
415
+ }
416
+ }
417
+ },
418
+ domHooks: {
419
+ root: { attr: "data-component", value: "tooth-scheme" },
420
+ instanceId: {
421
+ attr: "data-component-id",
422
+ sourceProp: "id",
423
+ description: "Sourced from the id prop."
424
+ },
425
+ item: {
426
+ attr: "data-fdi",
427
+ description: "Each rendered tooth `<g>` carries its FDI id as `data-fdi`, its first finding as `data-condition`, the full space-separated list as `data-conditions`, the finding count as `data-symbol-count` and the first finding's status as `data-status`; selectable special-element slots carry their id in `data-fdi` too (plus `data-special` naming the kind). Per-surface pick targets carry `data-zone`, per-root pick targets `data-root` (the anatomical root name). The `data-projection` / `data-view-mode` attributes on the root expose the side/plan/both view."
428
+ }
429
+ }
430
+ }, Nn = ie(
431
+ [
432
+ "ds:tooth-scheme-alfadocs",
433
+ "ds:flex ds:flex-col",
434
+ "ds:gap-[var(--spacing-sm)]",
435
+ "ds:bg-[var(--background)] ds:text-[var(--foreground)]",
436
+ // Block-level flex column so the SVG's w-full can grow to fill the frame.
437
+ // min-w-fit on the SVG floors it at natural size, so narrow frames scroll
438
+ // here rather than shrinking the per-tooth hit target below the 44px floor.
439
+ "ds:max-w-full ds:overflow-x-auto",
440
+ // When the scroll container itself is the tab stop (display mode, see
441
+ // scrollTabIndex) it needs a focus ring. Use an outline (not box-shadow):
442
+ // forced-colors / high-contrast mode zeroes box-shadow, which would leave
443
+ // HCM keyboard users with no visible focus. outline survives, and the
444
+ // CanvasText fallback repaints it in the system palette.
445
+ "ds:outline-none",
446
+ "ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid",
447
+ "ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]",
448
+ "ds:forced-colors:focus-visible:outline-[CanvasText]"
449
+ ].join(" ")
450
+ ), jn = ie(["ds:flex ds:flex-col ds:gap-0"].join(" ")), Fn = ie(
451
+ [
452
+ "ds:block",
453
+ "ds:w-full ds:min-w-fit ds:h-auto",
454
+ "ds:text-[var(--foreground)]"
455
+ ].join(" ")
456
+ ), Zt = ie(
457
+ [
458
+ "ds:group ds:cursor-pointer",
459
+ "ds:focus:outline-none",
460
+ "ds:transition-[fill,stroke,opacity] ds:duration-[var(--animation-duration)]",
461
+ "ds:motion-reduce:transition-none"
462
+ ].join(" ")
463
+ ), Xt = ie(
464
+ [
465
+ "ds:flex ds:flex-wrap ds:items-center",
466
+ "ds:gap-[var(--spacing-sm)]",
467
+ "type-meta ds:text-[var(--muted-foreground)]"
468
+ ].join(" ")
469
+ ), Jt = ie(
470
+ ["ds:inline-flex ds:items-center", "ds:gap-[var(--spacing-xs)]"].join(" ")
471
+ ), Mn = ie(["ds:sr-only"].join(" ")), eo = ie(
472
+ [
473
+ "ds:cursor-pointer ds:outline-none",
474
+ "ds:fill-transparent ds:stroke-transparent",
475
+ "ds:transition-[fill] ds:duration-[var(--animation-duration)]",
476
+ "ds:motion-reduce:transition-none",
477
+ "ds:hover:fill-[color-mix(in_srgb,var(--accent)_18%,transparent)]",
478
+ // `:focus` as well as `:focus-visible` — a pointer drill-in moves focus
479
+ // programmatically, so the browser's focus-visible heuristic does not match
480
+ // and the newly focused zone would otherwise show nothing at all.
481
+ "ds:focus:fill-[color-mix(in_srgb,var(--accent)_22%,transparent)]",
482
+ "ds:focus:stroke-[var(--ring)]",
483
+ "ds:focus-visible:fill-[color-mix(in_srgb,var(--accent)_22%,transparent)]",
484
+ "ds:focus-visible:stroke-[var(--ring)]",
485
+ "ds:forced-colors:focus:stroke-[CanvasText]",
486
+ "ds:forced-colors:focus-visible:stroke-[CanvasText]"
487
+ ].join(" ")
488
+ );
489
+ function lt(t) {
490
+ return t.value !== void 0;
491
+ }
492
+ function Ln() {
493
+ return { conditions: [], surfaces: [] };
494
+ }
495
+ function to(t, n, a) {
496
+ const d = n ?? "caries", h = {
497
+ condition: d,
498
+ part: Ve(d, a)
499
+ };
500
+ return n ? t.some((f) => f.condition === d) ? t.filter((f) => f.condition !== d) : [...t, h] : t.length === 0 ? [h] : [];
501
+ }
502
+ function En(t, n, a, d) {
503
+ const h = (b) => a === "surface" ? (b.surfaces ?? []).includes(d) : (b.roots ?? []).includes(d), f = (b) => b.condition === n, x = t.findIndex(
504
+ (b) => f(b) && h(b)
505
+ ), P = t.findIndex(
506
+ (b) => f(b) && b.id === void 0
507
+ );
508
+ if (x < 0 && P >= 0)
509
+ return oo(t, P, a, d);
510
+ if (x < 0) {
511
+ const b = a === "surface" ? { condition: n, part: a, surfaces: [d] } : { condition: n, part: a, roots: [d] };
512
+ return [...t, b];
513
+ }
514
+ return oo(t, x, a, d);
515
+ }
516
+ function oo(t, n, a, d) {
517
+ const h = t[n], f = a === "surface" ? h.surfaces ?? [] : h.roots ?? [], x = f.includes(d) ? f.filter((b) => b !== d) : [...f, d];
518
+ if (x.length === 0) return t.filter((b, _) => _ !== n);
519
+ const P = a === "surface" ? { ...h, part: a, surfaces: x } : { ...h, part: a, roots: x };
520
+ return t.map((b, _) => _ === n ? P : b);
521
+ }
522
+ function Wn(t, n) {
523
+ const a = [], d = [];
524
+ for (const h of t) {
525
+ ft(h.condition) && !a.includes(h.condition) && a.push(h.condition);
526
+ for (const f of h.surfaces ?? [])
527
+ d.includes(f) || d.push(f);
528
+ }
529
+ return { ...n ?? Ln(), conditions: a, surfaces: d, symbols: t };
530
+ }
531
+ function no(t, n) {
532
+ switch (t.labelKey) {
533
+ case "quadrant":
534
+ return n("toothScheme.special.quadrant", { n: t.labelIndex });
535
+ case "sextant":
536
+ return n("toothScheme.special.sextant", { n: t.labelIndex });
537
+ case "archUpper":
538
+ return n("toothScheme.special.archUpper");
539
+ case "archLower":
540
+ return n("toothScheme.special.archLower");
541
+ default:
542
+ return n("toothScheme.special.general");
543
+ }
544
+ }
545
+ function Rn(t, n, a) {
546
+ var h;
547
+ return a ? ((h = {
548
+ top: { ArrowDown: "center" },
549
+ bottom: { ArrowUp: "center" },
550
+ left: { ArrowRight: "center" },
551
+ right: { ArrowLeft: "center" },
552
+ center: {
553
+ ArrowUp: "top",
554
+ ArrowDown: "bottom",
555
+ ArrowLeft: "left",
556
+ ArrowRight: "right"
557
+ }
558
+ }[t]) == null ? void 0 : h[n]) ?? t : n === "ArrowUp" ? "top" : n === "ArrowDown" ? "bottom" : n === "ArrowLeft" ? "left" : n === "ArrowRight" ? "right" : t;
559
+ }
560
+ function Pn(t) {
561
+ const n = (a) => a === "mesial" || a === "distal" ? bn(t, a) === "left" ? 0 : 2 : 1;
562
+ return [...gn(t)].sort((a, d) => n(a) - n(d));
563
+ }
564
+ function ze(t) {
565
+ const n = Pn(t);
566
+ return n.length > 1 ? n : [];
567
+ }
568
+ function Hn(t, n, a) {
569
+ const d = a.indexOf(t);
570
+ return d < 0 ? t : n === "ArrowRight" ? a[Math.min(a.length - 1, d + 1)] : n === "ArrowLeft" ? a[Math.max(0, d - 1)] : t;
571
+ }
572
+ function qn(t, n, a) {
573
+ const d = a.w / n;
574
+ return { x: a.x + t * d, y: a.y, w: d, h: a.h };
575
+ }
576
+ function fo({
577
+ glyph: t,
578
+ colour: n,
579
+ cx: a,
580
+ cy: d,
581
+ opacity: h,
582
+ testId: f
583
+ }) {
584
+ const x = 9 + t.length * 5;
585
+ return /* @__PURE__ */ k(
586
+ "g",
587
+ {
588
+ transform: `translate(${a - x / 2}, ${d - 7})`,
589
+ "data-testid": f,
590
+ pointerEvents: "none",
591
+ "aria-hidden": "true",
592
+ children: [
593
+ /* @__PURE__ */ s(
594
+ "rect",
595
+ {
596
+ width: x,
597
+ height: 14,
598
+ rx: "3.5",
599
+ fill: "var(--card)",
600
+ stroke: n,
601
+ strokeWidth: "1.5",
602
+ opacity: h
603
+ }
604
+ ),
605
+ /* @__PURE__ */ s(
606
+ "text",
607
+ {
608
+ x: x / 2,
609
+ y: 10,
610
+ textAnchor: "middle",
611
+ fontSize: "9",
612
+ fontWeight: "600",
613
+ fill: "var(--foreground)",
614
+ className: "ds:select-none",
615
+ children: t
616
+ }
617
+ )
618
+ ]
619
+ }
620
+ );
621
+ }
622
+ function mo({
623
+ status: t,
624
+ cx: n,
625
+ cy: a,
626
+ r: d,
627
+ strokeWidth: h,
628
+ testId: f
629
+ }) {
630
+ return /* @__PURE__ */ s(
631
+ "circle",
632
+ {
633
+ cx: n,
634
+ cy: a,
635
+ r: d,
636
+ fill: t === "executed" ? Gt.executed : "var(--card)",
637
+ stroke: Gt[t],
638
+ strokeWidth: h,
639
+ "data-testid": f,
640
+ pointerEvents: "none",
641
+ "aria-hidden": "true"
642
+ }
643
+ );
644
+ }
645
+ const go = ["planned", "executed"];
646
+ function Kn({
647
+ condition: t,
648
+ assetBaseUrl: n,
649
+ registry: a
650
+ }) {
651
+ const d = de[16], h = ut(t, a), f = t === "missing", x = vn(t, a), P = !h && !f && pt(t, "side", a), b = 36 * 0.9, _ = 62 * 0.9, V = (36 - b) / 2, N = (62 - _) / 2;
652
+ return /* @__PURE__ */ k(
653
+ "svg",
654
+ {
655
+ width: "22",
656
+ height: "38",
657
+ viewBox: "0 0 36 62",
658
+ "aria-hidden": "true",
659
+ className: "ds:block ds:shrink-0",
660
+ children: [
661
+ /* @__PURE__ */ s(
662
+ "image",
663
+ {
664
+ href: lo(d, n),
665
+ x: V,
666
+ y: N,
667
+ width: b,
668
+ height: _,
669
+ preserveAspectRatio: "xMidYMid meet",
670
+ opacity: f ? 0.25 : 1
671
+ }
672
+ ),
673
+ h ? /* @__PURE__ */ s(
674
+ "image",
675
+ {
676
+ href: so(d, n, h),
677
+ x: V,
678
+ y: N,
679
+ width: b,
680
+ height: _,
681
+ preserveAspectRatio: "xMidYMid meet"
682
+ }
683
+ ) : null,
684
+ P ? /* @__PURE__ */ s(
685
+ "rect",
686
+ {
687
+ x: 3,
688
+ y: 2,
689
+ width: 30,
690
+ height: 58,
691
+ fill: "none",
692
+ stroke: x,
693
+ strokeWidth: 2,
694
+ strokeDasharray: t === "filled" ? "4 2" : void 0,
695
+ rx: 6
696
+ }
697
+ ) : null,
698
+ f ? /* @__PURE__ */ s(
699
+ "path",
700
+ {
701
+ d: "M7 9 L29 53 M29 9 L7 53",
702
+ stroke: ho.missing,
703
+ strokeWidth: 2,
704
+ fill: "none"
705
+ }
706
+ ) : null,
707
+ !h && !P && !f ? /* @__PURE__ */ s(
708
+ fo,
709
+ {
710
+ glyph: co(t, a),
711
+ colour: x,
712
+ cx: 18,
713
+ cy: 31
714
+ }
715
+ ) : null
716
+ ]
717
+ }
718
+ );
719
+ }
720
+ function Un({
721
+ assetBaseUrl: t,
722
+ registry: n
723
+ }) {
724
+ const { t: a } = io(), d = [
725
+ ...mn,
726
+ ...(n ?? []).map((h) => h.id).filter((h) => !ft(h))
727
+ ];
728
+ return /* @__PURE__ */ k(
729
+ "section",
730
+ {
731
+ "aria-label": a("toothScheme.legendLabel"),
732
+ "data-testid": "tooth-scheme-legend",
733
+ className: "ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:rounded-[var(--radius-md)] ds:bg-[var(--card)] ds:border ds:border-[color:var(--card-border)] ds:p-[var(--spacing-sm)] ds:shadow-[var(--shadow-card)] ds:[.theme-accessible_&]:border-2",
734
+ children: [
735
+ /* @__PURE__ */ s("span", { className: "type-eyebrow ds:text-[color:var(--muted-foreground)]", children: a("toothScheme.legendLabel") }),
736
+ /* @__PURE__ */ s("ul", { className: Xt(), children: d.map((h) => {
737
+ const f = ro(h, n);
738
+ return /* @__PURE__ */ k(
739
+ "li",
740
+ {
741
+ className: Jt(),
742
+ "data-condition": h,
743
+ "data-testid": `tooth-scheme-legend-item-${h}`,
744
+ children: [
745
+ /* @__PURE__ */ s(
746
+ Kn,
747
+ {
748
+ condition: h,
749
+ assetBaseUrl: t,
750
+ registry: n
751
+ }
752
+ ),
753
+ /* @__PURE__ */ s("span", { children: f.kind === "key" ? a(f.key) : f.text })
754
+ ]
755
+ },
756
+ h
757
+ );
758
+ }) }),
759
+ /* @__PURE__ */ s("span", { className: "type-eyebrow ds:text-[color:var(--muted-foreground)]", children: a("toothScheme.legendStatusLabel") }),
760
+ /* @__PURE__ */ s("ul", { className: Xt(), children: go.map((h) => /* @__PURE__ */ k(
761
+ "li",
762
+ {
763
+ className: Jt(),
764
+ "data-status": h,
765
+ "data-testid": `tooth-scheme-legend-status-${h}`,
766
+ children: [
767
+ /* @__PURE__ */ s(
768
+ "svg",
769
+ {
770
+ width: "14",
771
+ height: "14",
772
+ viewBox: "0 0 14 14",
773
+ "aria-hidden": "true",
774
+ className: "ds:block ds:shrink-0",
775
+ children: /* @__PURE__ */ s(
776
+ mo,
777
+ {
778
+ status: h,
779
+ cx: 7,
780
+ cy: 7,
781
+ r: 5,
782
+ strokeWidth: 1.75
783
+ }
784
+ )
785
+ }
786
+ ),
787
+ /* @__PURE__ */ s("span", { children: a(ao(h)) })
788
+ ]
789
+ },
790
+ h
791
+ )) })
792
+ ]
793
+ }
794
+ );
795
+ }
796
+ function ht({
797
+ label: t,
798
+ value: n,
799
+ options: a,
800
+ onValueChange: d,
801
+ container: h
802
+ }) {
803
+ return /* @__PURE__ */ k("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]", children: [
804
+ /* @__PURE__ */ s(
805
+ "span",
806
+ {
807
+ "aria-hidden": "true",
808
+ className: "type-label ds:text-[color:var(--muted-foreground)]",
809
+ children: t
810
+ }
811
+ ),
812
+ /* @__PURE__ */ s(
813
+ en,
814
+ {
815
+ size: "md",
816
+ "aria-label": t,
817
+ value: n,
818
+ options: a,
819
+ onValueChange: d,
820
+ container: h
821
+ }
822
+ )
823
+ ] });
824
+ }
825
+ function Yn(t, n) {
826
+ return t.date === n.date ? 0 : t.date ? n.date && t.date < n.date ? 1 : -1 : 1;
827
+ }
828
+ const $e = Go(
829
+ ({
830
+ id: t,
831
+ dentition: n = "permanent",
832
+ numbering: a = "fdi",
833
+ mode: d = "interactive",
834
+ projection: h = "side",
835
+ specialElements: f,
836
+ value: x,
837
+ defaultValue: P,
838
+ onChange: b,
839
+ onSelect: _,
840
+ conditions: V,
841
+ radiographs: N,
842
+ radiographDisplay: mt = "badge",
843
+ onOpenRadiographs: le,
844
+ activeCondition: j,
845
+ conditionRegistry: u,
846
+ assetBaseUrl: re = "/toothscheme/teeth",
847
+ legend: Ne = !1,
848
+ controls: H = !1,
849
+ controlsContainer: je,
850
+ ariaLabel: vo,
851
+ className: bo,
852
+ _arch: he,
853
+ _numbersPosition: Se = "between",
854
+ _inner: Fe = !1,
855
+ _contentWidth: yo
856
+ }, So) => {
857
+ const { t: l } = io(), [xo, gt] = K(n), [wo, vt] = K(a), [ko, bt] = K(h), [Io, yt] = K(Ne), [To, St] = K(
858
+ f
859
+ ), [Be, Ao] = K(null), L = H ? xo : n, xe = H ? wo : a, xt = H ? Io : Ne, we = H ? ko : h, O = we === "plan" ? "plan" : "side", Me = we === "both" && !Fe, wt = we === "both", B = H ? To ?? wt : f ?? wt, kt = qe(H);
860
+ Ce(() => {
861
+ H && !kt.current && (gt(n), vt(a), bt(h), yt(Ne), St(f)), kt.current = H;
862
+ }, [
863
+ H,
864
+ n,
865
+ a,
866
+ h,
867
+ Ne,
868
+ f
869
+ ]);
870
+ const [Do, It] = K(() => x !== void 0 ? x : P !== void 0 ? P : V ? zt(V) : {}), g = lt({ value: x }) ? x : Do;
871
+ Ce(() => {
872
+ lt({ value: x }) || V !== void 0 && It(zt(V));
873
+ }, [V, x]);
874
+ const w = O === "plan" ? 44 : on, M = Oe(
875
+ () => tn(L, w, he ? { arch: he } : {}),
876
+ [L, w, he]
877
+ ), ke = Oe(() => {
878
+ if (!B) return [];
879
+ if (he)
880
+ return Ke(
881
+ Ue(L, he, O),
882
+ M.width
883
+ ).specials;
884
+ const e = Ke(
885
+ Ue(L, "upper", O),
886
+ M.width
887
+ ).specials, o = w + fn, r = Ke(
888
+ Ue(L, "lower", O),
889
+ M.width
890
+ ).specials.map((c) => ({ ...c, y: o }));
891
+ return [...e, ...r];
892
+ }, [B, he, L, O, M.width, w]), _o = Oe(() => {
893
+ if (!Me) return;
894
+ if (!B) return M.width;
895
+ let e = M.width;
896
+ for (const o of ["upper", "lower"])
897
+ for (const r of ["side", "plan"]) {
898
+ const c = Ke(
899
+ Ue(L, o, r),
900
+ M.width
901
+ ).width;
902
+ c > e && (e = c);
903
+ }
904
+ return e;
905
+ }, [Me, B, L, M.width]), ae = Oe(
906
+ () => [
907
+ ...M.teeth.map((e) => e.fdi),
908
+ // Special slots join keyboard nav only when interactive — in display /
909
+ // radiograph-open mode they are decorative (role="img"), so the
910
+ // radiograph-open keyboard path never reaches a region id.
911
+ ...d === "interactive" ? ke.map((e) => e.element.id) : []
912
+ ],
913
+ [M, ke, d]
914
+ ), Ge = ae[0], [Ie, Y] = K(Ge);
915
+ Ce(() => {
916
+ (!Ie || !ae.includes(Ie)) && Y(Ge);
917
+ }, [Ge, Ie, ae]);
918
+ const [Co, Oo] = K(""), Qe = qe(null), Te = D(
919
+ (e) => {
920
+ lt({ value: x }) || It(e), b == null || b(e);
921
+ },
922
+ [b, x]
923
+ ), R = D((e) => {
924
+ const o = Qe.current;
925
+ if (!o) return;
926
+ const r = o.querySelector(`g[data-fdi="${e}"]`);
927
+ r && (r.focus(), Y(e));
928
+ }, []), Tt = D(
929
+ (e) => {
930
+ const o = ro(e, u);
931
+ return o.kind === "key" ? l(o.key) : o.text;
932
+ },
933
+ [l, u]
934
+ ), G = D(
935
+ (e, o = !0) => {
936
+ var v;
937
+ const r = Tt(e.condition), c = o ? ((v = e.roots) != null && v.length ? e.roots.map((m) => l(`toothScheme.root.${m}`)) : (e.surfaces ?? []).map(
938
+ (m) => l(`toothScheme.surface.${m}`)
939
+ )).join(", ") : "", p = e.status ? l(ao(e.status)) : "";
940
+ return c && p ? l("toothScheme.symbolWithScopeAndStatus", {
941
+ condition: r,
942
+ scope: c,
943
+ status: p
944
+ }) : c ? l("toothScheme.symbolWithScope", { condition: r, scope: c }) : p ? l("toothScheme.symbolWithStatus", { condition: r, status: p }) : r;
945
+ },
946
+ [Tt, l]
947
+ ), At = D(
948
+ (e, o, r) => e.filter(
949
+ (c) => o === "surface" ? (c.surfaces ?? []).includes(r) : (c.roots ?? []).includes(r)
950
+ ).map((c) => `, ${G(c, !1)}`).join(""),
951
+ [G]
952
+ ), Dt = D(
953
+ (e, o) => {
954
+ const r = o.map((c) => G(c)).join(", ");
955
+ if (nn(e)) {
956
+ const c = no(rn[e], l);
957
+ return o.length > 0 ? l("toothScheme.elementSelected", {
958
+ name: c,
959
+ conditions: r
960
+ }) : l("toothScheme.elementDeselected", { name: c });
961
+ }
962
+ return o.length > 0 ? l("toothScheme.selected", { id: e, conditions: r }) : l("toothScheme.deselected", { id: e });
963
+ },
964
+ [G, l]
965
+ ), ue = D(
966
+ (e, o) => {
967
+ const r = { ...g };
968
+ o.length === 0 ? delete r[e] : r[e] = Wn(o, g[e]), Te(r);
969
+ },
970
+ [g, Te]
971
+ ), Q = D(
972
+ (e, o) => {
973
+ ue(e, o), Oo(Dt(e, o)), _ == null || _(e);
974
+ },
975
+ [Dt, _, ue]
976
+ ), _t = j ? Ve(j, u) : void 0, q = O === "plan" && d === "interactive" && _t === "surface", se = O === "side" && d === "interactive" && _t === "root", [y, z] = K(null);
977
+ Ce(() => {
978
+ if (!y) return;
979
+ (!(y.part === "surface" ? q : se) || !ae.includes(y.fdi)) && z(null);
980
+ }, [q, se, y, ae]);
981
+ const Ct = D((e) => {
982
+ var r, c;
983
+ const o = e.part === "surface" ? `path[data-fdi="${e.fdi}"][data-zone="${e.zone}"]` : `rect[data-fdi="${e.fdi}"][data-root="${e.root}"]`;
984
+ (c = (r = Qe.current) == null ? void 0 : r.querySelector(o)) == null || c.focus();
985
+ }, []), Le = qe(null);
986
+ Ce(() => {
987
+ if (y) {
988
+ Ct(y);
989
+ return;
990
+ }
991
+ const e = Le.current;
992
+ Le.current = null, e && R(e);
993
+ }, [y, Ct, R]);
994
+ const Ot = D(
995
+ (e, o, r) => j ? F(g[e], u).some(
996
+ (c) => c.condition === j && (o === "surface" ? (c.surfaces ?? []).includes(r) : (c.roots ?? []).includes(r))
997
+ ) : !1,
998
+ [j, g, u]
999
+ ), pe = D(
1000
+ (e, o, r) => {
1001
+ if (!j || (o === "surface" ? !q : !se)) return;
1002
+ const c = F(g[e], u);
1003
+ Q(
1004
+ e,
1005
+ En(c, j, o, r)
1006
+ );
1007
+ },
1008
+ [
1009
+ j,
1010
+ g,
1011
+ Q,
1012
+ u,
1013
+ se,
1014
+ q
1015
+ ]
1016
+ ), Ze = D(
1017
+ (e) => {
1018
+ const o = de[e];
1019
+ if (!o) return;
1020
+ if (Y(e), q) {
1021
+ z({ fdi: e, part: "surface", zone: st(o)[0] });
1022
+ return;
1023
+ }
1024
+ const r = ze(o);
1025
+ r.length > 0 && z({ fdi: e, part: "root", root: r[0] });
1026
+ },
1027
+ [q]
1028
+ ), $o = D(
1029
+ (e, o, r) => {
1030
+ const c = de[o];
1031
+ if (c)
1032
+ switch (e.key) {
1033
+ case "ArrowUp":
1034
+ case "ArrowDown":
1035
+ case "ArrowLeft":
1036
+ case "ArrowRight": {
1037
+ e.preventDefault(), e.stopPropagation();
1038
+ const p = st(c).includes("center"), v = Rn(r, e.key, p);
1039
+ v !== r && z({ fdi: o, part: "surface", zone: v });
1040
+ return;
1041
+ }
1042
+ case " ":
1043
+ case "Enter": {
1044
+ e.preventDefault(), e.stopPropagation(), pe(o, "surface", Vt(c, r));
1045
+ return;
1046
+ }
1047
+ case "Escape": {
1048
+ e.preventDefault(), e.stopPropagation(), Le.current = o, z(null);
1049
+ return;
1050
+ }
1051
+ default:
1052
+ return;
1053
+ }
1054
+ },
1055
+ [pe]
1056
+ ), No = D(
1057
+ (e, o, r) => {
1058
+ const c = de[o];
1059
+ if (c)
1060
+ switch (e.key) {
1061
+ case "ArrowUp":
1062
+ case "ArrowDown":
1063
+ case "ArrowLeft":
1064
+ case "ArrowRight": {
1065
+ e.preventDefault(), e.stopPropagation();
1066
+ const p = Hn(r, e.key, ze(c));
1067
+ p !== r && z({ fdi: o, part: "root", root: p });
1068
+ return;
1069
+ }
1070
+ case " ":
1071
+ case "Enter": {
1072
+ e.preventDefault(), e.stopPropagation(), pe(o, "root", r);
1073
+ return;
1074
+ }
1075
+ case "Escape": {
1076
+ e.preventDefault(), e.stopPropagation(), Le.current = o, z(null);
1077
+ return;
1078
+ }
1079
+ default:
1080
+ return;
1081
+ }
1082
+ },
1083
+ [pe]
1084
+ ), jo = qe(null), Ee = d === "display" && typeof le == "function", fe = d === "interactive" || Ee, Ae = D(
1085
+ (e) => {
1086
+ Y(e), _ == null || _(e), le == null || le(e);
1087
+ },
1088
+ [_, le]
1089
+ ), Xe = Oe(
1090
+ () => ({
1091
+ focusTooth: (e) => {
1092
+ R(e);
1093
+ },
1094
+ getChart: () => g,
1095
+ getTooth: (e) => F(g[e], u).map((o) => o.condition).filter(ft),
1096
+ getSymbols: (e) => F(g[e], u),
1097
+ teethWith: (e) => Object.keys(g).filter(
1098
+ (o) => F(g[o], u).some(
1099
+ (r) => r.condition === e
1100
+ )
1101
+ ),
1102
+ teethWithSymbol: (e) => Object.keys(g).filter(
1103
+ (o) => F(g[o], u).some(
1104
+ (r) => r.condition === e
1105
+ )
1106
+ ),
1107
+ setFinding: (e, o, r) => {
1108
+ const c = F(g[e], u), p = c.findIndex(
1109
+ (m) => m.condition === o
1110
+ );
1111
+ if (p >= 0 && !r) return;
1112
+ const v = Ve(o, u);
1113
+ ue(
1114
+ e,
1115
+ p >= 0 ? c.map(
1116
+ (m, T) => T === p ? { ...m, surfaces: r } : m
1117
+ ) : [...c, { condition: o, part: v, surfaces: r }]
1118
+ );
1119
+ },
1120
+ setSymbol: (e, o) => {
1121
+ const r = F(g[e], u), c = o.id ? r.findIndex((p) => p.id === o.id) : r.findIndex(
1122
+ (p) => p.condition === o.condition
1123
+ );
1124
+ ue(
1125
+ e,
1126
+ c >= 0 ? r.map((p, v) => v === c ? o : p) : [...r, o]
1127
+ );
1128
+ },
1129
+ removeFinding: (e, o) => {
1130
+ const r = F(g[e], u), c = r.filter(
1131
+ (p) => p.condition !== o
1132
+ );
1133
+ c.length !== r.length && ue(e, c);
1134
+ },
1135
+ clearTooth: (e) => {
1136
+ if (!g[e]) return;
1137
+ const o = { ...g };
1138
+ delete o[e], Te(o);
1139
+ },
1140
+ getRadiographs: (e) => (N == null ? void 0 : N[e]) ?? [],
1141
+ teethWithRadiographs: () => Object.keys(N ?? {}).filter(
1142
+ (e) => {
1143
+ var o;
1144
+ return (((o = N == null ? void 0 : N[e]) == null ? void 0 : o.length) ?? 0) > 0;
1145
+ }
1146
+ ),
1147
+ openRadiographs: (e) => {
1148
+ R(e), Ae(e);
1149
+ }
1150
+ }),
1151
+ [
1152
+ g,
1153
+ Te,
1154
+ u,
1155
+ R,
1156
+ N,
1157
+ Ae,
1158
+ ue
1159
+ ]
1160
+ );
1161
+ Qo(So, () => Xe, [Xe]), Xo(
1162
+ $n,
1163
+ Xe,
1164
+ Fe ? void 0 : t
1165
+ );
1166
+ const $t = D(
1167
+ (e, o) => {
1168
+ if (!fe) return;
1169
+ const r = de[o], c = ae, p = c.indexOf(o);
1170
+ switch (e.key) {
1171
+ case "ArrowRight": {
1172
+ e.preventDefault();
1173
+ const v = Math.min(c.length - 1, p + 1);
1174
+ R(c[v]);
1175
+ return;
1176
+ }
1177
+ case "ArrowLeft": {
1178
+ e.preventDefault();
1179
+ const v = Math.max(0, p - 1);
1180
+ R(c[v]);
1181
+ return;
1182
+ }
1183
+ case "ArrowUp":
1184
+ case "ArrowDown": {
1185
+ if (e.preventDefault(), !r) return;
1186
+ const m = {
1187
+ 1: 4,
1188
+ 4: 1,
1189
+ 2: 3,
1190
+ 3: 2,
1191
+ 5: 8,
1192
+ 8: 5,
1193
+ 6: 7,
1194
+ 7: 6
1195
+ }[r.quadrant];
1196
+ if (!m) return;
1197
+ const T = `${m}${r.positionInQuadrant}`;
1198
+ c.includes(T) && R(T);
1199
+ return;
1200
+ }
1201
+ case "Home": {
1202
+ if (e.preventDefault(), !r) return;
1203
+ const v = c.filter(
1204
+ (m) => {
1205
+ var T;
1206
+ return ((T = de[m]) == null ? void 0 : T.quadrant) === r.quadrant;
1207
+ }
1208
+ );
1209
+ v.length > 0 && R(v[0]);
1210
+ return;
1211
+ }
1212
+ case "End": {
1213
+ if (e.preventDefault(), !r) return;
1214
+ const v = c.filter(
1215
+ (m) => {
1216
+ var T;
1217
+ return ((T = de[m]) == null ? void 0 : T.quadrant) === r.quadrant;
1218
+ }
1219
+ );
1220
+ v.length > 0 && R(v[v.length - 1]);
1221
+ return;
1222
+ }
1223
+ case " ":
1224
+ case "Enter": {
1225
+ if (e.preventDefault(), Ee) {
1226
+ Ae(o);
1227
+ return;
1228
+ }
1229
+ if (r && (q || se && ze(r).length > 0)) {
1230
+ Ze(o);
1231
+ return;
1232
+ }
1233
+ Q(
1234
+ o,
1235
+ to(
1236
+ F(g[o], u),
1237
+ j,
1238
+ u
1239
+ )
1240
+ );
1241
+ return;
1242
+ }
1243
+ default:
1244
+ return;
1245
+ }
1246
+ },
1247
+ [
1248
+ j,
1249
+ g,
1250
+ Q,
1251
+ u,
1252
+ Ze,
1253
+ R,
1254
+ Ae,
1255
+ Ee,
1256
+ se,
1257
+ q,
1258
+ fe,
1259
+ ae
1260
+ ]
1261
+ ), Nt = D(
1262
+ (e) => {
1263
+ d === "interactive" && (Y(e), Q(
1264
+ e,
1265
+ to(
1266
+ F(g[e], u),
1267
+ j,
1268
+ u
1269
+ )
1270
+ ));
1271
+ },
1272
+ [j, g, Q, u, d]
1273
+ ), Je = L === "primary" ? "mGeneral" : "general", jt = F(g[Je], u).length > 0, Fo = D(
1274
+ (e) => {
1275
+ const o = j ?? "caries";
1276
+ Q(
1277
+ Je,
1278
+ e ? [{ condition: o, part: Ve(o, u) }] : []
1279
+ );
1280
+ },
1281
+ [j, Q, u, Je]
1282
+ ), Ft = Se === "above" ? 16 : 2, Mo = Se === "below" ? 16 : 2, Lo = ke.length > 0 ? Math.max(...ke.map((e) => e.x)) + ce : 0, Mt = Math.max(
1283
+ yo ?? 0,
1284
+ M.width,
1285
+ Lo
1286
+ ) + 4, Lt = M.height + Ft + Mo, et = vo ?? l("toothScheme.ariaLabel"), Eo = fe ? void 0 : 0, Et = Me ? null : /* @__PURE__ */ k(
1287
+ "svg",
1288
+ {
1289
+ ref: Qe,
1290
+ viewBox: `0 ${-Ft} ${Mt} ${Lt}`,
1291
+ width: Mt,
1292
+ height: Lt,
1293
+ className: Fn(),
1294
+ role: "group",
1295
+ "aria-label": et,
1296
+ focusable: "false",
1297
+ "data-testid": "tooth-scheme-svg",
1298
+ xmlns: "http://www.w3.org/2000/svg",
1299
+ children: [
1300
+ M.teeth.map((e) => {
1301
+ var Ut, Yt;
1302
+ const o = F(g[e.fdi], u), r = (Ut = o[0]) == null ? void 0 : Ut.condition, c = l(`toothScheme.anatomy.${e.meta.anatomy}`), p = o.map((i) => `, ${G(i)}`).join(""), v = (N == null ? void 0 : N[e.fdi]) ?? [], m = v.length, T = m > 0, me = T ? [...v].sort(Yn)[0] : void 0, ge = mt === "thumbnail" && me !== void 0, tt = T ? `, ${l("toothScheme.radiographBadge", { count: m })}` : "", Pe = Fe ? `, ${l(O === "plan" ? "toothScheme.view.occlusal" : "toothScheme.view.side")}` : "", Z = l("toothScheme.toothLabel", {
1303
+ id: Bt(e.fdi, xe),
1304
+ anatomy: c,
1305
+ conditionSuffix: p
1306
+ }) + tt + Pe, ot = fe ? y ? -1 : e.fdi === Ie ? 0 : -1 : void 0, X = o.some(
1307
+ (i) => i.condition === "missing"
1308
+ ), Po = o.flatMap((i, S) => {
1309
+ var C;
1310
+ const A = O === "side" ? ut(i.condition, u) : an(i.condition, u);
1311
+ return A ? ((C = i.roots) != null && C.length ? i.roots : [void 0]).map((ne) => ({
1312
+ key: `${S}-${i.condition}-${ne ?? "whole"}`,
1313
+ url: O === "side" ? so(e.meta, re, A, {
1314
+ root: ne,
1315
+ region: i.region
1316
+ }) : sn(e.meta, re, A, {
1317
+ root: ne,
1318
+ region: i.region
1319
+ }),
1320
+ opacity: i.status ? ct[i.status] : 1
1321
+ })) : [];
1322
+ }), Ho = o.filter(
1323
+ (i) => !pt(
1324
+ i.condition,
1325
+ O,
1326
+ u
1327
+ )
1328
+ ).map((i, S) => ({
1329
+ key: `${i.condition}-${S}`,
1330
+ condition: i.condition,
1331
+ glyph: co(i.condition, u),
1332
+ colour: dt(i, u),
1333
+ opacity: i.status ? ct[i.status] : 1
1334
+ })), De = o.find(
1335
+ (i) => i.condition !== "missing" && !ut(i.condition, u) && pt(i.condition, "side", u)
1336
+ ), Pt = De ? dt(De, u) : void 0, qo = go.filter(
1337
+ (i) => o.some((S) => S.status === i)
1338
+ ), Ko = (o.length > 0 ? `${c} · ${o.map((i) => G(i)).join(" · ")}` : c) + (T ? ` · ${l("toothScheme.radiographBadge", { count: m })}` : ""), nt = Bt(e.fdi, xe), Uo = lo(
1339
+ e.meta,
1340
+ re,
1341
+ "default",
1342
+ O
1343
+ ), Yo = ge && me ? me.thumbnailUrl ?? me.url : Uo, zo = O === "plan" ? o.flatMap((i, S) => {
1344
+ const A = cn(
1345
+ i.condition,
1346
+ u
1347
+ );
1348
+ return A ? (i.surfaces ?? []).map((I) => ({
1349
+ key: `${S}-${i.condition}-${I}`,
1350
+ url: dn(
1351
+ e.meta,
1352
+ re,
1353
+ A,
1354
+ I
1355
+ ),
1356
+ opacity: i.status ? ct[i.status] : 1
1357
+ })) : [];
1358
+ }) : [], Ht = (e.meta.dentition === "primary" ? ln : 1) * 0.9, J = U * Ht, ee = w * Ht, ve = (U - J) / 2, be = (w - ee) / 2, te = (y == null ? void 0 : y.part) === "surface" && y.fdi === e.fdi, oe = (y == null ? void 0 : y.part) === "root" && y.fdi === e.fdi, _e = te || oe, it = q ? st(e.meta) : [], Vo = it.includes("center"), He = se ? ze(e.meta) : [], qt = it.length > 0 || He.length > 0, Kt = Se === "above" ? -4 : Se === "below" ? w + 12 : Se === "none" ? null : e.meta.arch === "upper" ? w + 12 : -4;
1359
+ return /* @__PURE__ */ k(E.Root, { children: [
1360
+ /* @__PURE__ */ s(E.Trigger, { asChild: !0, children: /* @__PURE__ */ k(
1361
+ "g",
1362
+ {
1363
+ "data-fdi": e.fdi,
1364
+ "data-anatomy": e.meta.anatomy,
1365
+ "data-arch": e.meta.arch,
1366
+ "data-side": e.meta.side,
1367
+ "data-quadrant": e.meta.quadrant,
1368
+ "data-condition": r ?? "none",
1369
+ "data-symbol-count": o.length,
1370
+ "data-conditions": o.length > 0 ? o.map((i) => i.condition).join(" ") : void 0,
1371
+ "data-status": (Yt = o[0]) == null ? void 0 : Yt.status,
1372
+ "data-testid": `tooth-${e.fdi}`,
1373
+ transform: `translate(${e.x}, ${e.y})`,
1374
+ tabIndex: ot,
1375
+ role: _e ? "group" : fe ? "button" : "img",
1376
+ "aria-label": Z,
1377
+ "aria-pressed": _e || qt ? void 0 : d === "interactive" ? r !== void 0 : void 0,
1378
+ "aria-expanded": !_e && qt && fe ? !1 : void 0,
1379
+ className: Zt(),
1380
+ onClick: _e ? void 0 : d === "interactive" ? q || He.length > 0 ? () => Ze(e.fdi) : () => Nt(e.fdi) : Ee ? () => Ae(e.fdi) : void 0,
1381
+ onFocus: () => Y(e.fdi),
1382
+ onKeyDown: _e ? void 0 : (i) => $t(i, e.fdi),
1383
+ children: [
1384
+ /* @__PURE__ */ s(
1385
+ "rect",
1386
+ {
1387
+ x: -$,
1388
+ y: -$,
1389
+ width: U + $ * 2,
1390
+ height: w + $ * 2,
1391
+ fill: "transparent"
1392
+ }
1393
+ ),
1394
+ /* @__PURE__ */ s(
1395
+ "image",
1396
+ {
1397
+ href: Yo,
1398
+ x: ve,
1399
+ y: be,
1400
+ width: J,
1401
+ height: ee,
1402
+ preserveAspectRatio: ge ? "xMidYMid slice" : "xMidYMid meet",
1403
+ opacity: X && !ge ? 0.25 : 1,
1404
+ "aria-hidden": "true"
1405
+ }
1406
+ ),
1407
+ Po.map(({ key: i, url: S, opacity: A }) => {
1408
+ const I = O === "plan" ? hn(S) : 1, C = J * I, ne = ee * I;
1409
+ return /* @__PURE__ */ s(
1410
+ "image",
1411
+ {
1412
+ href: S,
1413
+ x: ve - (C - J) / 2,
1414
+ y: be - (ne - ee) / 2,
1415
+ width: C,
1416
+ height: ne,
1417
+ preserveAspectRatio: "xMidYMid meet",
1418
+ opacity: A,
1419
+ "aria-hidden": "true",
1420
+ pointerEvents: "none"
1421
+ },
1422
+ i
1423
+ );
1424
+ }),
1425
+ zo.map(({ key: i, url: S, opacity: A }) => /* @__PURE__ */ s(
1426
+ "image",
1427
+ {
1428
+ href: S,
1429
+ x: ve,
1430
+ y: be,
1431
+ width: J,
1432
+ height: ee,
1433
+ preserveAspectRatio: "xMidYMid meet",
1434
+ opacity: A,
1435
+ "aria-hidden": "true",
1436
+ pointerEvents: "none"
1437
+ },
1438
+ i
1439
+ )),
1440
+ Ho.map(
1441
+ ({ key: i, condition: S, glyph: A, colour: I, opacity: C }, ne) => /* @__PURE__ */ s(
1442
+ fo,
1443
+ {
1444
+ glyph: A,
1445
+ colour: I,
1446
+ opacity: C,
1447
+ cx: U / 2,
1448
+ cy: w / 2 + ne * 16,
1449
+ testId: `tooth-${e.fdi}-glyph-${S}`
1450
+ },
1451
+ i
1452
+ )
1453
+ ),
1454
+ it.map((i) => {
1455
+ const S = Vt(e.meta, i), A = Ot(
1456
+ e.fdi,
1457
+ "surface",
1458
+ S
1459
+ );
1460
+ return /* @__PURE__ */ s(
1461
+ "path",
1462
+ {
1463
+ d: un(
1464
+ i,
1465
+ { x: ve, y: be, w: J, h: ee },
1466
+ Vo
1467
+ ),
1468
+ "data-fdi": e.fdi,
1469
+ "data-zone": i,
1470
+ "data-surface": S,
1471
+ "data-testid": `tooth-${e.fdi}-zone-${i}`,
1472
+ role: te ? "button" : void 0,
1473
+ tabIndex: te ? (y == null ? void 0 : y.part) === "surface" && y.zone === i ? 0 : -1 : void 0,
1474
+ "aria-label": te ? l("toothScheme.toothLabel", {
1475
+ id: nt,
1476
+ anatomy: l(`toothScheme.surface.${S}`),
1477
+ conditionSuffix: At(
1478
+ o,
1479
+ "surface",
1480
+ S
1481
+ )
1482
+ }) : void 0,
1483
+ "aria-pressed": te ? A : void 0,
1484
+ strokeWidth: "var(--focus-ring-width)",
1485
+ className: eo(),
1486
+ onClick: (I) => {
1487
+ I.stopPropagation(), te || Y(e.fdi), pe(e.fdi, "surface", S);
1488
+ },
1489
+ onKeyDown: te ? (I) => $o(I, e.fdi, i) : void 0,
1490
+ onFocus: te ? () => z(
1491
+ (I) => I && I.part === "surface" && I.fdi === e.fdi && I.zone === i ? I : { fdi: e.fdi, part: "surface", zone: i }
1492
+ ) : void 0
1493
+ },
1494
+ i
1495
+ );
1496
+ }),
1497
+ He.map((i, S) => {
1498
+ const A = qn(S, He.length, {
1499
+ x: ve,
1500
+ y: be,
1501
+ w: J,
1502
+ h: ee
1503
+ }), I = Ot(e.fdi, "root", i);
1504
+ return /* @__PURE__ */ s(
1505
+ "rect",
1506
+ {
1507
+ x: A.x,
1508
+ y: A.y,
1509
+ width: A.w,
1510
+ height: A.h,
1511
+ rx: "2",
1512
+ "data-fdi": e.fdi,
1513
+ "data-root": i,
1514
+ "data-testid": `tooth-${e.fdi}-root-${i}`,
1515
+ role: oe ? "button" : void 0,
1516
+ tabIndex: oe ? (y == null ? void 0 : y.part) === "root" && y.root === i ? 0 : -1 : void 0,
1517
+ "aria-label": oe ? l("toothScheme.toothLabel", {
1518
+ id: nt,
1519
+ anatomy: l(`toothScheme.root.${i}`),
1520
+ conditionSuffix: At(
1521
+ o,
1522
+ "root",
1523
+ i
1524
+ )
1525
+ }) : void 0,
1526
+ "aria-pressed": oe ? I : void 0,
1527
+ strokeWidth: "var(--focus-ring-width)",
1528
+ className: eo(),
1529
+ onClick: (C) => {
1530
+ C.stopPropagation(), oe || Y(e.fdi), pe(e.fdi, "root", i);
1531
+ },
1532
+ onKeyDown: oe ? (C) => No(C, e.fdi, i) : void 0,
1533
+ onFocus: oe ? () => z(
1534
+ (C) => C && C.part === "root" && C.fdi === e.fdi && C.root === i ? C : { fdi: e.fdi, part: "root", root: i }
1535
+ ) : void 0
1536
+ },
1537
+ i
1538
+ );
1539
+ }),
1540
+ O === "side" && Pt ? /* @__PURE__ */ s(
1541
+ "rect",
1542
+ {
1543
+ x: 3,
1544
+ y: 2,
1545
+ width: U - 6,
1546
+ height: w - 4,
1547
+ fill: "none",
1548
+ stroke: Pt,
1549
+ strokeWidth: "2",
1550
+ strokeDasharray: (De == null ? void 0 : De.condition) === "filled" ? "4 2" : void 0,
1551
+ rx: "6",
1552
+ pointerEvents: "none"
1553
+ }
1554
+ ) : null,
1555
+ qo.map((i, S) => /* @__PURE__ */ s(
1556
+ mo,
1557
+ {
1558
+ status: i,
1559
+ cx: 5 + S * 9,
1560
+ cy: 5,
1561
+ r: 3.25,
1562
+ strokeWidth: 1.25,
1563
+ testId: `tooth-${e.fdi}-status-${i}`
1564
+ },
1565
+ i
1566
+ )),
1567
+ X ? /* @__PURE__ */ s(
1568
+ "path",
1569
+ {
1570
+ d: `M7 9 L${U - 7} ${w - 9} M${U - 7} 9 L7 ${w - 9}`,
1571
+ stroke: ho.missing,
1572
+ strokeWidth: "2",
1573
+ fill: "none",
1574
+ pointerEvents: "none"
1575
+ }
1576
+ ) : null,
1577
+ ge ? /* @__PURE__ */ s(
1578
+ "rect",
1579
+ {
1580
+ x: ve,
1581
+ y: be,
1582
+ width: J,
1583
+ height: ee,
1584
+ fill: "none",
1585
+ stroke: "var(--border)",
1586
+ strokeWidth: "1",
1587
+ rx: "3",
1588
+ pointerEvents: "none"
1589
+ }
1590
+ ) : null,
1591
+ T ? /* @__PURE__ */ k(
1592
+ "g",
1593
+ {
1594
+ "data-testid": `tooth-${e.fdi}-radiograph-badge`,
1595
+ "data-radiograph-count": m,
1596
+ transform: `translate(${U - (m >= 10 ? 19 : 15)}, ${w - 13})`,
1597
+ pointerEvents: "none",
1598
+ "aria-hidden": "true",
1599
+ children: [
1600
+ /* @__PURE__ */ s(
1601
+ "rect",
1602
+ {
1603
+ width: m >= 10 ? 17 : 13,
1604
+ height: 12,
1605
+ rx: "3.5",
1606
+ fill: "var(--card)",
1607
+ stroke: "var(--info-foreground)",
1608
+ strokeWidth: "0.75"
1609
+ }
1610
+ ),
1611
+ /* @__PURE__ */ s(
1612
+ "text",
1613
+ {
1614
+ x: (m >= 10 ? 17 : 13) / 2,
1615
+ y: 9,
1616
+ textAnchor: "middle",
1617
+ fontSize: "9",
1618
+ fontWeight: "600",
1619
+ fill: "var(--info-foreground)",
1620
+ className: "ds:select-none",
1621
+ children: m
1622
+ }
1623
+ )
1624
+ ]
1625
+ }
1626
+ ) : null,
1627
+ /* @__PURE__ */ s(
1628
+ "rect",
1629
+ {
1630
+ x: -$,
1631
+ y: -$,
1632
+ width: U + $ * 2,
1633
+ height: w + $ * 2,
1634
+ fill: "none",
1635
+ stroke: "var(--ring)",
1636
+ strokeWidth: "var(--focus-ring-width)",
1637
+ className: [
1638
+ "ds:opacity-0",
1639
+ "ds:group-focus-visible:opacity-100",
1640
+ "ds:forced-colors:stroke-[CanvasText]"
1641
+ ].join(" "),
1642
+ "data-testid": `tooth-${e.fdi}-focus-ring`,
1643
+ pointerEvents: "none",
1644
+ rx: "4"
1645
+ }
1646
+ ),
1647
+ Kt !== null ? /* @__PURE__ */ s(
1648
+ "text",
1649
+ {
1650
+ x: U / 2,
1651
+ y: Kt,
1652
+ textAnchor: "middle",
1653
+ fontSize: "10",
1654
+ fill: "var(--muted-foreground)",
1655
+ className: "ds:select-none",
1656
+ "aria-hidden": "true",
1657
+ children: nt
1658
+ }
1659
+ ) : null
1660
+ ]
1661
+ }
1662
+ ) }),
1663
+ /* @__PURE__ */ s(E.Portal, { children: /* @__PURE__ */ k(
1664
+ E.Content,
1665
+ {
1666
+ side: "top",
1667
+ sideOffset: 6,
1668
+ className: "ds:z-[var(--z-tooltip)] ds:rounded-[var(--radius-sm)] ds:bg-[var(--foreground)] ds:text-[var(--background)] ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[calc(var(--spacing-xs)/2)] ds:pb-[calc(var(--spacing-xs)/2)] ds:text-[length:var(--font-size-xs)]",
1669
+ children: [
1670
+ Ko,
1671
+ /* @__PURE__ */ s(E.Arrow, { className: "ds:fill-[var(--foreground)]" })
1672
+ ]
1673
+ }
1674
+ ) })
1675
+ ] }, e.fdi);
1676
+ }),
1677
+ ke.map(({ element: e, x: o, y: r }) => {
1678
+ const c = F(
1679
+ g[e.id],
1680
+ u
1681
+ ), p = c[0], v = p == null ? void 0 : p.condition, m = v !== void 0, T = no(e, l), me = c.map((X) => `, ${G(X)}`).join(""), ge = l("toothScheme.elementLabel", {
1682
+ name: T,
1683
+ conditionSuffix: me
1684
+ }), tt = p ? dt(p, u) : void 0, Pe = pn(e, re), Z = d === "interactive", ot = Z ? e.id === Ie ? 0 : -1 : void 0;
1685
+ return /* @__PURE__ */ k(E.Root, { children: [
1686
+ /* @__PURE__ */ s(E.Trigger, { asChild: !0, children: /* @__PURE__ */ k(
1687
+ "g",
1688
+ {
1689
+ "data-fdi": e.id,
1690
+ "data-special": e.kind,
1691
+ "data-arch": e.arch,
1692
+ "data-condition": v ?? "none",
1693
+ "data-testid": `element-${e.id}`,
1694
+ transform: `translate(${o}, ${r})`,
1695
+ tabIndex: ot,
1696
+ role: Z ? "button" : "img",
1697
+ "aria-label": ge,
1698
+ "aria-pressed": Z ? m : void 0,
1699
+ className: Zt(),
1700
+ onClick: Z ? () => Nt(e.id) : void 0,
1701
+ onFocus: Z ? () => Y(e.id) : void 0,
1702
+ onKeyDown: Z ? (X) => $t(X, e.id) : void 0,
1703
+ children: [
1704
+ /* @__PURE__ */ s(
1705
+ "rect",
1706
+ {
1707
+ x: -$,
1708
+ y: -$,
1709
+ width: ce + $ * 2,
1710
+ height: w + $ * 2,
1711
+ fill: "transparent"
1712
+ }
1713
+ ),
1714
+ /* @__PURE__ */ s(
1715
+ "rect",
1716
+ {
1717
+ x: 0,
1718
+ y: 0,
1719
+ width: ce,
1720
+ height: w,
1721
+ rx: 6,
1722
+ fill: m ? "var(--accent)" : "var(--card)",
1723
+ fillOpacity: m ? 0.16 : 1,
1724
+ stroke: tt ?? "var(--border)",
1725
+ strokeWidth: m ? 2 : 1
1726
+ }
1727
+ ),
1728
+ Pe ? /* @__PURE__ */ k(Bo, { children: [
1729
+ /* @__PURE__ */ s(
1730
+ "image",
1731
+ {
1732
+ href: Pe,
1733
+ x: 3,
1734
+ y: 3,
1735
+ width: ce - 6,
1736
+ height: w - 16,
1737
+ preserveAspectRatio: "xMidYMid meet",
1738
+ "aria-hidden": "true",
1739
+ pointerEvents: "none"
1740
+ }
1741
+ ),
1742
+ /* @__PURE__ */ s(
1743
+ "text",
1744
+ {
1745
+ x: ce / 2,
1746
+ y: w - 4,
1747
+ textAnchor: "middle",
1748
+ fontSize: "9",
1749
+ fontWeight: "600",
1750
+ fill: "var(--muted-foreground)",
1751
+ className: "ds:select-none",
1752
+ "aria-hidden": "true",
1753
+ children: e.code
1754
+ }
1755
+ )
1756
+ ] }) : /* @__PURE__ */ s(
1757
+ "text",
1758
+ {
1759
+ x: ce / 2,
1760
+ y: w / 2,
1761
+ textAnchor: "middle",
1762
+ dominantBaseline: "central",
1763
+ fontSize: "11",
1764
+ fontWeight: "600",
1765
+ fill: "var(--foreground)",
1766
+ className: "ds:select-none",
1767
+ "aria-hidden": "true",
1768
+ children: e.code
1769
+ }
1770
+ ),
1771
+ /* @__PURE__ */ s(
1772
+ "rect",
1773
+ {
1774
+ x: -$,
1775
+ y: -$,
1776
+ width: ce + $ * 2,
1777
+ height: w + $ * 2,
1778
+ fill: "none",
1779
+ stroke: "var(--ring)",
1780
+ strokeWidth: "var(--focus-ring-width)",
1781
+ className: [
1782
+ "ds:opacity-0",
1783
+ "ds:group-focus-visible:opacity-100",
1784
+ "ds:forced-colors:stroke-[CanvasText]"
1785
+ ].join(" "),
1786
+ "data-testid": `element-${e.id}-focus-ring`,
1787
+ pointerEvents: "none",
1788
+ rx: "6"
1789
+ }
1790
+ )
1791
+ ]
1792
+ }
1793
+ ) }),
1794
+ /* @__PURE__ */ s(E.Portal, { children: /* @__PURE__ */ k(
1795
+ E.Content,
1796
+ {
1797
+ side: "top",
1798
+ sideOffset: 6,
1799
+ className: "ds:z-[var(--z-tooltip)] ds:rounded-[var(--radius-sm)] ds:bg-[var(--foreground)] ds:text-[var(--background)] ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[calc(var(--spacing-xs)/2)] ds:pb-[calc(var(--spacing-xs)/2)] ds:text-[length:var(--font-size-xs)]",
1800
+ children: [
1801
+ c.length > 0 ? `${T} · ${c.map((X) => G(X)).join(" · ")}` : T,
1802
+ /* @__PURE__ */ s(E.Arrow, { className: "ds:fill-[var(--foreground)]" })
1803
+ ]
1804
+ }
1805
+ ) })
1806
+ ] }, e.id);
1807
+ })
1808
+ ]
1809
+ }
1810
+ ), Wt = /* @__PURE__ */ s(
1811
+ "div",
1812
+ {
1813
+ className: Mn(),
1814
+ "aria-live": "polite",
1815
+ "aria-atomic": "true",
1816
+ "data-testid": "tooth-scheme-live",
1817
+ children: Co
1818
+ }
1819
+ );
1820
+ if (Fe)
1821
+ return /* @__PURE__ */ k(E.Provider, { delayDuration: 200, children: [
1822
+ Et,
1823
+ Wt
1824
+ ] });
1825
+ const Wo = (e) => l(e === "upper" ? "toothScheme.arch.upper" : "toothScheme.arch.lower"), We = (e, o) => `${et} — ${Wo(e)}, ${l(o === "plan" ? "toothScheme.view.occlusal" : "toothScheme.view.side")}`, Re = {
1826
+ dentition: L,
1827
+ numbering: xe,
1828
+ mode: d,
1829
+ value: g,
1830
+ onChange: Te,
1831
+ onSelect: _,
1832
+ activeCondition: j,
1833
+ assetBaseUrl: re,
1834
+ radiographs: N,
1835
+ radiographDisplay: mt,
1836
+ onOpenRadiographs: le,
1837
+ conditionRegistry: u,
1838
+ specialElements: B,
1839
+ _inner: !0,
1840
+ _contentWidth: _o
1841
+ }, Ro = /* @__PURE__ */ k("div", { className: jn(), "data-testid": "tooth-scheme-both", children: [
1842
+ /* @__PURE__ */ s(
1843
+ $e,
1844
+ {
1845
+ ...Re,
1846
+ projection: "side",
1847
+ _arch: "upper",
1848
+ _numbersPosition: "above",
1849
+ ariaLabel: We("upper", "side")
1850
+ }
1851
+ ),
1852
+ /* @__PURE__ */ s(
1853
+ $e,
1854
+ {
1855
+ ...Re,
1856
+ projection: "plan",
1857
+ _arch: "upper",
1858
+ _numbersPosition: "none",
1859
+ ariaLabel: We("upper", "plan")
1860
+ }
1861
+ ),
1862
+ /* @__PURE__ */ s(
1863
+ $e,
1864
+ {
1865
+ ...Re,
1866
+ projection: "plan",
1867
+ _arch: "lower",
1868
+ _numbersPosition: "none",
1869
+ ariaLabel: We("lower", "plan")
1870
+ }
1871
+ ),
1872
+ /* @__PURE__ */ s(
1873
+ $e,
1874
+ {
1875
+ ...Re,
1876
+ projection: "side",
1877
+ _arch: "lower",
1878
+ _numbersPosition: "below",
1879
+ ariaLabel: We("lower", "side")
1880
+ }
1881
+ )
1882
+ ] }), Rt = H ? /* @__PURE__ */ s(
1883
+ "div",
1884
+ {
1885
+ className: je != null ? "ds:flex ds:justify-end" : "ds:flex ds:w-full ds:justify-end",
1886
+ "data-testid": "tooth-scheme-controls",
1887
+ children: /* @__PURE__ */ k(rt.Root, { children: [
1888
+ /* @__PURE__ */ s(rt.Trigger, { asChild: !0, children: /* @__PURE__ */ s(
1889
+ Jo,
1890
+ {
1891
+ size: "sm",
1892
+ intent: "outline",
1893
+ icon: /* @__PURE__ */ s(yn, { "aria-hidden": "true" }),
1894
+ "aria-label": l("toothScheme.options.label"),
1895
+ tooltip: l("toothScheme.options.label")
1896
+ }
1897
+ ) }),
1898
+ /* @__PURE__ */ s(
1899
+ rt.Content,
1900
+ {
1901
+ ref: Ao,
1902
+ align: "end",
1903
+ size: "md",
1904
+ "aria-label": l("toothScheme.options.label"),
1905
+ children: /* @__PURE__ */ k("div", { className: "ds:flex ds:w-[var(--popover-size-sm)] ds:max-w-full ds:flex-col ds:gap-[var(--spacing-md)]", children: [
1906
+ /* @__PURE__ */ k("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:border-b ds:border-[color:var(--border)] ds:pb-[var(--spacing-sm)] ds:[&>*]:w-full ds:[&>*]:justify-between", children: [
1907
+ /* @__PURE__ */ s(
1908
+ at,
1909
+ {
1910
+ label: l("toothScheme.options.legend"),
1911
+ labelSide: "start",
1912
+ checked: xt,
1913
+ onCheckedChange: yt
1914
+ }
1915
+ ),
1916
+ /* @__PURE__ */ s(
1917
+ at,
1918
+ {
1919
+ label: l("toothScheme.options.specialElements"),
1920
+ labelSide: "start",
1921
+ checked: B,
1922
+ onCheckedChange: St
1923
+ }
1924
+ )
1925
+ ] }),
1926
+ /* @__PURE__ */ s(
1927
+ ht,
1928
+ {
1929
+ container: Be,
1930
+ label: l("toothScheme.options.dentition"),
1931
+ value: L,
1932
+ onValueChange: (e) => gt(e),
1933
+ options: [
1934
+ {
1935
+ value: "permanent",
1936
+ label: l("toothScheme.dentition.permanent")
1937
+ },
1938
+ {
1939
+ value: "primary",
1940
+ label: l("toothScheme.dentition.primary")
1941
+ },
1942
+ {
1943
+ value: "mixed",
1944
+ label: l("toothScheme.dentition.mixed")
1945
+ }
1946
+ ]
1947
+ }
1948
+ ),
1949
+ /* @__PURE__ */ s(
1950
+ ht,
1951
+ {
1952
+ container: Be,
1953
+ label: l("toothScheme.options.numbering"),
1954
+ value: xe,
1955
+ onValueChange: (e) => vt(e),
1956
+ options: [
1957
+ {
1958
+ value: "fdi",
1959
+ label: l("toothScheme.numbering.fdi")
1960
+ },
1961
+ {
1962
+ value: "universal",
1963
+ label: l("toothScheme.numbering.universal")
1964
+ },
1965
+ {
1966
+ value: "palmer",
1967
+ label: l("toothScheme.numbering.palmer")
1968
+ }
1969
+ ]
1970
+ }
1971
+ ),
1972
+ /* @__PURE__ */ s(
1973
+ ht,
1974
+ {
1975
+ container: Be,
1976
+ label: l("toothScheme.options.view"),
1977
+ value: we,
1978
+ onValueChange: (e) => bt(e),
1979
+ options: [
1980
+ {
1981
+ value: "side",
1982
+ label: l("toothScheme.view.side")
1983
+ },
1984
+ {
1985
+ value: "plan",
1986
+ label: l("toothScheme.view.occlusal")
1987
+ },
1988
+ {
1989
+ value: "both",
1990
+ label: l("toothScheme.view.both")
1991
+ }
1992
+ ]
1993
+ }
1994
+ )
1995
+ ] })
1996
+ }
1997
+ )
1998
+ ] })
1999
+ }
2000
+ ) : null;
2001
+ return /* @__PURE__ */ s(E.Provider, { delayDuration: 200, children: /* @__PURE__ */ k(
2002
+ "div",
2003
+ {
2004
+ ref: jo,
2005
+ role: "region",
2006
+ "aria-label": et,
2007
+ tabIndex: Eo,
2008
+ className: [Nn(), bo].filter(Boolean).join(" "),
2009
+ "data-component": "tooth-scheme",
2010
+ "data-component-id": t,
2011
+ "data-testid": "tooth-scheme-root",
2012
+ "data-dentition": L,
2013
+ "data-numbering": xe,
2014
+ "data-mode": d,
2015
+ "data-projection": O,
2016
+ "data-view-mode": we,
2017
+ "data-special-elements": B ? "true" : "false",
2018
+ children: [
2019
+ je === void 0 ? Rt : je ? Zo(Rt, je) : null,
2020
+ Me ? Ro : Et,
2021
+ B && (d === "interactive" || jt) ? /* @__PURE__ */ s(
2022
+ "div",
2023
+ {
2024
+ className: "ds:flex ds:w-full ds:items-center ds:pt-[var(--spacing-2xs)]",
2025
+ "data-testid": "tooth-scheme-general",
2026
+ children: /* @__PURE__ */ s(
2027
+ at,
2028
+ {
2029
+ label: l("toothScheme.special.general"),
2030
+ labelSide: "end",
2031
+ checked: jt,
2032
+ disabled: d !== "interactive",
2033
+ onCheckedChange: Fo
2034
+ }
2035
+ )
2036
+ }
2037
+ ) : null,
2038
+ xt ? /* @__PURE__ */ s(Un, { assetBaseUrl: re, registry: u }) : null,
2039
+ Wt
2040
+ ]
2041
+ }
2042
+ ) });
2043
+ }
2044
+ );
2045
+ $e.displayName = "ToothScheme";
2046
+ export {
2047
+ $e as T,
2048
+ Nn as r,
2049
+ $n as t
2050
+ };
2051
+ //# sourceMappingURL=tooth-scheme-BPXE1_3N.js.map