@alfadocs/ui-kit 0.75.0 → 0.78.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 (91) hide show
  1. package/dist/_chunks/{address-fields-COyjgqxA.js → address-fields-7k59FnJ6.js} +2 -2
  2. package/dist/_chunks/{booking-B1Qkcl3c.js → booking-CLmeXtGv.js} +2 -2
  3. package/dist/_chunks/{country-select-DYHDLEjG.js → country-select-BN9nedAp.js} +2 -2
  4. package/dist/_chunks/document-preview-nwfulDEs.js +290 -0
  5. package/dist/_chunks/{file-manager-UF8dkQF1.js → file-manager-DfTG7bxx.js} +394 -399
  6. package/dist/_chunks/flag-Cnmivlrl.js +641 -0
  7. package/dist/_chunks/{header-settings-4cwQzeFc.js → header-settings-DMfdx0Vq.js} +2 -2
  8. package/dist/_chunks/{locale-picker-GNF3lo8K.js → locale-picker-EyvPrwDc.js} +2 -2
  9. package/dist/_chunks/{marketplace-app-shell-D_1tF4La.js → marketplace-app-shell-Bxq1PYAj.js} +2 -2
  10. package/dist/_chunks/{patient-details-DP4wKyq4.js → patient-details-zy76MRh7.js} +3 -3
  11. package/dist/_chunks/periodontal-chart-CYBhr9vc.js +1305 -0
  12. package/dist/_chunks/{phone-input-dtnrvnZf.js → phone-input-D00ZBAm-.js} +2 -2
  13. package/dist/agent-catalog.json +149 -1
  14. package/dist/agent-i18n/en.json +2 -0
  15. package/dist/components/address-fields/index.js +1 -1
  16. package/dist/components/booking/index.js +1 -1
  17. package/dist/components/country-select/index.js +1 -1
  18. package/dist/components/document-preview/document-preview.d.ts +35 -2
  19. package/dist/components/document-preview/index.d.ts +1 -1
  20. package/dist/components/document-preview/index.js +1 -1
  21. package/dist/components/file-manager/index.js +1 -1
  22. package/dist/components/flag/index.js +1 -1
  23. package/dist/components/header-settings/index.js +1 -1
  24. package/dist/components/locale-picker/index.js +1 -1
  25. package/dist/components/patient-details/index.js +1 -1
  26. package/dist/components/periodontal-chart/index.d.ts +2 -2
  27. package/dist/components/periodontal-chart/index.js +8 -7
  28. package/dist/components/periodontal-chart/periodontal-chart-data.d.ts +45 -0
  29. package/dist/components/periodontal-chart/periodontal-chart.d.ts +3 -1
  30. package/dist/components/phone-input/index.js +1 -1
  31. package/dist/i18n/locales/ar.d.ts +8 -1
  32. package/dist/i18n/locales/ar.js +8 -1
  33. package/dist/i18n/locales/de.d.ts +8 -1
  34. package/dist/i18n/locales/de.js +8 -1
  35. package/dist/i18n/locales/el.d.ts +8 -1
  36. package/dist/i18n/locales/el.js +8 -1
  37. package/dist/i18n/locales/en.d.ts +8 -1
  38. package/dist/i18n/locales/en.js +8 -1
  39. package/dist/i18n/locales/es.d.ts +8 -1
  40. package/dist/i18n/locales/es.js +8 -1
  41. package/dist/i18n/locales/fr.d.ts +8 -1
  42. package/dist/i18n/locales/fr.js +8 -1
  43. package/dist/i18n/locales/hi.d.ts +8 -1
  44. package/dist/i18n/locales/hi.js +8 -1
  45. package/dist/i18n/locales/it.d.ts +8 -1
  46. package/dist/i18n/locales/it.js +8 -1
  47. package/dist/i18n/locales/ja.d.ts +8 -1
  48. package/dist/i18n/locales/ja.js +8 -1
  49. package/dist/i18n/locales/nl.d.ts +8 -1
  50. package/dist/i18n/locales/nl.js +8 -1
  51. package/dist/i18n/locales/pl.d.ts +8 -1
  52. package/dist/i18n/locales/pl.js +8 -1
  53. package/dist/i18n/locales/pt.d.ts +8 -1
  54. package/dist/i18n/locales/pt.js +8 -1
  55. package/dist/i18n/locales/ro.d.ts +8 -1
  56. package/dist/i18n/locales/ro.js +8 -1
  57. package/dist/i18n/locales/ru.d.ts +8 -1
  58. package/dist/i18n/locales/ru.js +8 -1
  59. package/dist/i18n/locales/sq.d.ts +8 -1
  60. package/dist/i18n/locales/sq.js +8 -1
  61. package/dist/i18n/locales/sv.d.ts +8 -1
  62. package/dist/i18n/locales/sv.js +8 -1
  63. package/dist/i18n/locales/tr.d.ts +8 -1
  64. package/dist/i18n/locales/tr.js +8 -1
  65. package/dist/i18n/locales/zh.d.ts +8 -1
  66. package/dist/i18n/locales/zh.js +8 -1
  67. package/dist/index.js +217 -216
  68. package/dist/locales/ar.json +8 -1
  69. package/dist/locales/de.json +8 -1
  70. package/dist/locales/el.json +8 -1
  71. package/dist/locales/en.json +8 -1
  72. package/dist/locales/es.json +8 -1
  73. package/dist/locales/fr.json +8 -1
  74. package/dist/locales/hi.json +8 -1
  75. package/dist/locales/it.json +8 -1
  76. package/dist/locales/ja.json +8 -1
  77. package/dist/locales/nl.json +8 -1
  78. package/dist/locales/pl.json +8 -1
  79. package/dist/locales/pt.json +8 -1
  80. package/dist/locales/ro.json +8 -1
  81. package/dist/locales/ru.json +8 -1
  82. package/dist/locales/sq.json +8 -1
  83. package/dist/locales/sv.json +8 -1
  84. package/dist/locales/tr.json +8 -1
  85. package/dist/locales/zh.json +8 -1
  86. package/dist/patterns/marketplace-app-shell/index.js +1 -1
  87. package/dist/tokens.css +2 -1
  88. package/package.json +1 -1
  89. package/dist/_chunks/document-preview-CGSM_au9.js +0 -181
  90. package/dist/_chunks/flag-DZ6V7-hU.js +0 -637
  91. package/dist/_chunks/periodontal-chart-D8hOFmde.js +0 -1173
@@ -0,0 +1,1305 @@
1
+ import { jsx as n, jsxs as m } from "react/jsx-runtime";
2
+ import { forwardRef as et, useRef as Re, useState as tt, useCallback as O, useMemo as Ie, useImperativeHandle as st } from "react";
3
+ import { c as qe } from "./index-D2ZczOXr.js";
4
+ import { useTranslation as rt } from "react-i18next";
5
+ import { u as nt } from "./registry-DvAUVLHh.js";
6
+ import { P as ve, f as ye, e as at, d as ot, F as it, t as lt } from "./tooth-data-BwuoKFf4.js";
7
+ import { P as Fe } from "./periodontal-chart-card-WVjJAheq.js";
8
+ const F = {
9
+ type: "string",
10
+ description: "FDI tooth id ('11'–'48' permanent, '51'–'85' primary)."
11
+ }, $ = {
12
+ enum: ["db", "b", "mb", "dl", "l", "ml"],
13
+ description: "Probing site: (m/d)b = mesio-/disto-/mid buccal, (m/d)l = mesio-/disto-/mid lingual."
14
+ }, fe = {
15
+ type: "integer",
16
+ description: "Whole millimetres — perio probing is recorded in integer mm."
17
+ }, ct = {
18
+ type: "object",
19
+ properties: {
20
+ meanPocketDepthMm: { type: ["number", "null"] },
21
+ meanAttachmentLevelMm: { type: ["number", "null"] },
22
+ plaquePercent: { type: ["number", "null"] },
23
+ bleedingPercent: { type: ["number", "null"] },
24
+ scoredSiteCount: { type: "number" },
25
+ deepPocketCount: { type: "number" }
26
+ }
27
+ }, dt = {
28
+ type: "array",
29
+ description: "One entry per flagged tooth, in clinical order; each tooth’s pockets are deepest-first.",
30
+ items: {
31
+ type: "object",
32
+ properties: {
33
+ fdi: F,
34
+ status: {
35
+ enum: ["present", "missing", "implant"],
36
+ description: "Structural status of the tooth."
37
+ },
38
+ severity: {
39
+ enum: ["normal", "moderate", "severe"],
40
+ description: "Worst pocket severity band across the tooth."
41
+ },
42
+ maxPocketDepthMm: { type: ["integer", "null"] },
43
+ pockets: {
44
+ type: "array",
45
+ description: "Sites at/above the moderate threshold, deepest first.",
46
+ items: {
47
+ type: "object",
48
+ properties: {
49
+ site: $,
50
+ pocketDepthMm: fe,
51
+ severity: { enum: ["moderate", "severe"] }
52
+ }
53
+ }
54
+ },
55
+ bleedingSites: { type: "array", items: $ },
56
+ plaqueSites: { type: "array", items: $ },
57
+ suppurationSites: { type: "array", items: $ },
58
+ maxRecessionMm: { type: ["number", "null"] },
59
+ mobility: {
60
+ type: ["integer", "null"],
61
+ description: "Miller mobility grade 1–3, or null when none."
62
+ },
63
+ furcation: { type: "array", items: { enum: ["buccal", "lingual"] } }
64
+ }
65
+ }
66
+ };
67
+ function ut(s) {
68
+ if (!s) return null;
69
+ const t = { ...s };
70
+ return delete t.note, t;
71
+ }
72
+ const pt = {
73
+ id: "periodontal-chart",
74
+ // pick — focus a measurement input (transient); set_value / edit_inline —
75
+ // record per-site probing readings and per-tooth flags.
76
+ capabilities: ["pick", "set_value", "edit_inline"],
77
+ state: {
78
+ indices: {
79
+ type: "object",
80
+ descriptionKey: "ui.agent.periodontalChart.state.indices",
81
+ description: "Derived whole-mouth indices: mean probing depth, mean attachment level, plaque %, bleeding %, scored-site and deep-pocket counts. Aggregates only — no per-site or patient data.",
82
+ read: (s) => s.getIndices()
83
+ },
84
+ findings: {
85
+ type: "object[]",
86
+ descriptionKey: "ui.agent.periodontalChart.state.findings",
87
+ description: "Narratable per-tooth problem list — one entry per flagged tooth (missing, implant, a pocket at/above the moderate threshold, bleeding, suppuration, mobility ≥ 1, or furcation), in clinical order with each tooth’s pockets deepest-first. Each entry carries fdi, status, severity, maxPocketDepthMm, pockets, bleeding/plaque/suppuration sites, maxRecessionMm, mobility and furcation. Read this to summarise the chart aloud. Structural readings + FDI ids only — never the free-text note or patient identity.",
88
+ read: (s) => s.getFindings()
89
+ },
90
+ missingTeeth: {
91
+ type: "string[]",
92
+ descriptionKey: "ui.agent.periodontalChart.state.missingTeeth",
93
+ description: "FDI ids of every tooth flagged as missing.",
94
+ read: (s) => s.missingTeeth()
95
+ },
96
+ chartedTeeth: {
97
+ type: "string[]",
98
+ descriptionKey: "ui.agent.periodontalChart.state.chartedTeeth",
99
+ description: "FDI ids of teeth carrying at least one probing reading or flag. Identifiers only — never patient identity or the free-text note.",
100
+ read: (s) => Object.entries(s.getValue()).filter(([, t]) => t.missing || t.implant || t.mobility != null || t.furcation && Object.keys(t.furcation).length > 0 ? !0 : (t.sites ? Object.values(t.sites) : []).some(
101
+ (d) => d != null && (d.pocketDepthMm != null || d.recessionMm != null || d.bleeding || d.plaque || d.suppuration)
102
+ )).map(([t]) => t)
103
+ }
104
+ },
105
+ actions: {
106
+ focus_site: {
107
+ safety: "read",
108
+ argsType: '{ fdi: string; site: PeriodontalSiteId; field: "pocketDepthMm" | "recessionMm" }',
109
+ argsSchema: {
110
+ type: "object",
111
+ properties: {
112
+ fdi: F,
113
+ site: $,
114
+ field: { enum: ["pocketDepthMm", "recessionMm"] }
115
+ },
116
+ required: ["fdi", "site", "field"]
117
+ },
118
+ idempotent: !0,
119
+ descriptionKey: "ui.agent.periodontalChart.actions.focusSite",
120
+ description: "Move focus to a specific probing-measurement input.",
121
+ invoke: (s, t) => {
122
+ s.focusSite(t.fdi, t.site, t.field);
123
+ }
124
+ },
125
+ get_tooth: {
126
+ safety: "read",
127
+ argsType: "{ fdi: string }",
128
+ argsSchema: {
129
+ type: "object",
130
+ properties: { fdi: F },
131
+ required: ["fdi"]
132
+ },
133
+ returns: "object | null",
134
+ returnsSchema: { type: ["object", "null"] },
135
+ idempotent: !0,
136
+ descriptionKey: "ui.agent.periodontalChart.actions.getTooth",
137
+ description: "Read one tooth’s structural readings (sites, flags, mobility, furcation). The free-text note is never returned.",
138
+ invoke: (s, t) => ut(s.getTooth(t.fdi))
139
+ },
140
+ get_indices: {
141
+ safety: "read",
142
+ argsType: "{}",
143
+ argsSchema: { type: "object", properties: {} },
144
+ returns: "PeriodontalIndices",
145
+ returnsSchema: ct,
146
+ idempotent: !0,
147
+ descriptionKey: "ui.agent.periodontalChart.actions.getIndices",
148
+ description: "Read the derived whole-mouth summary indices.",
149
+ invoke: (s) => s.getIndices()
150
+ },
151
+ get_findings: {
152
+ safety: "read",
153
+ argsType: "{}",
154
+ argsSchema: { type: "object", properties: {} },
155
+ returns: "PeriodontalToothFinding[]",
156
+ returnsSchema: dt,
157
+ idempotent: !0,
158
+ descriptionKey: "ui.agent.periodontalChart.actions.getFindings",
159
+ description: "Read the narratable per-tooth problem list (flagged teeth only). Use it to summarise the chart aloud — e.g. “46: 8 mm disto-buccal pocket, bleeding, grade 2 mobility”. Structural readings and FDI ids only; the free-text note is never returned.",
160
+ invoke: (s) => s.getFindings()
161
+ },
162
+ set_depth: {
163
+ safety: "write",
164
+ argsType: "{ fdi: string; site: PeriodontalSiteId; mm: number }",
165
+ argsSchema: {
166
+ type: "object",
167
+ properties: { fdi: F, site: $, mm: fe },
168
+ required: ["fdi", "site", "mm"]
169
+ },
170
+ idempotent: !0,
171
+ descriptionKey: "ui.agent.periodontalChart.actions.setDepth",
172
+ description: "Record the probing pocket depth (mm) at a site.",
173
+ invoke: (s, t) => {
174
+ s.setSite(t.fdi, t.site, {
175
+ pocketDepthMm: Math.max(0, t.mm)
176
+ });
177
+ }
178
+ },
179
+ set_recession: {
180
+ safety: "write",
181
+ argsType: "{ fdi: string; site: PeriodontalSiteId; mm: number }",
182
+ argsSchema: {
183
+ type: "object",
184
+ properties: { fdi: F, site: $, mm: fe },
185
+ required: ["fdi", "site", "mm"]
186
+ },
187
+ idempotent: !0,
188
+ descriptionKey: "ui.agent.periodontalChart.actions.setRecession",
189
+ description: "Record the gingival recession (mm) at a site.",
190
+ invoke: (s, t) => {
191
+ s.setSite(t.fdi, t.site, { recessionMm: t.mm });
192
+ }
193
+ },
194
+ toggle_bleeding: {
195
+ safety: "write",
196
+ argsType: "{ fdi: string; site: PeriodontalSiteId; value: boolean }",
197
+ argsSchema: {
198
+ type: "object",
199
+ properties: {
200
+ fdi: F,
201
+ site: $,
202
+ value: { type: "boolean" }
203
+ },
204
+ required: ["fdi", "site", "value"]
205
+ },
206
+ idempotent: !0,
207
+ descriptionKey: "ui.agent.periodontalChart.actions.toggleBleeding",
208
+ description: "Set bleeding on probing at a site.",
209
+ invoke: (s, t) => {
210
+ s.setSite(t.fdi, t.site, { bleeding: t.value });
211
+ }
212
+ },
213
+ toggle_plaque: {
214
+ safety: "write",
215
+ argsType: "{ fdi: string; site: PeriodontalSiteId; value: boolean }",
216
+ argsSchema: {
217
+ type: "object",
218
+ properties: {
219
+ fdi: F,
220
+ site: $,
221
+ value: { type: "boolean" }
222
+ },
223
+ required: ["fdi", "site", "value"]
224
+ },
225
+ idempotent: !0,
226
+ descriptionKey: "ui.agent.periodontalChart.actions.togglePlaque",
227
+ description: "Set plaque presence at a site.",
228
+ invoke: (s, t) => {
229
+ s.setSite(t.fdi, t.site, { plaque: t.value });
230
+ }
231
+ },
232
+ set_mobility: {
233
+ safety: "write",
234
+ argsType: "{ fdi: string; grade: 0 | 1 | 2 | 3 }",
235
+ argsSchema: {
236
+ type: "object",
237
+ properties: {
238
+ fdi: F,
239
+ grade: { enum: [0, 1, 2, 3] }
240
+ },
241
+ required: ["fdi", "grade"]
242
+ },
243
+ idempotent: !0,
244
+ descriptionKey: "ui.agent.periodontalChart.actions.setMobility",
245
+ description: "Set a tooth’s Miller mobility grade (0–3).",
246
+ invoke: (s, t) => {
247
+ s.setTooth(t.fdi, { mobility: t.grade });
248
+ }
249
+ },
250
+ set_missing: {
251
+ safety: "write",
252
+ argsType: "{ fdi: string; value: boolean }",
253
+ argsSchema: {
254
+ type: "object",
255
+ properties: { fdi: F, value: { type: "boolean" } },
256
+ required: ["fdi", "value"]
257
+ },
258
+ idempotent: !0,
259
+ descriptionKey: "ui.agent.periodontalChart.actions.setMissing",
260
+ description: "Flag a tooth present or absent.",
261
+ invoke: (s, t) => {
262
+ s.setTooth(t.fdi, { missing: t.value });
263
+ }
264
+ },
265
+ clear_tooth: {
266
+ safety: "destructive",
267
+ argsType: "{ fdi: string }",
268
+ argsSchema: {
269
+ type: "object",
270
+ properties: { fdi: F },
271
+ required: ["fdi"]
272
+ },
273
+ idempotent: !0,
274
+ descriptionKey: "ui.agent.periodontalChart.actions.clearTooth",
275
+ description: "Clear every reading from a tooth.",
276
+ invoke: (s, t) => {
277
+ s.clearTooth(t.fdi);
278
+ }
279
+ }
280
+ },
281
+ domHooks: {
282
+ root: {
283
+ attr: "data-component",
284
+ value: "periodontal-chart",
285
+ description: "Marks the PeriodontalChart root group."
286
+ },
287
+ instanceId: {
288
+ attr: "data-component-id",
289
+ sourceProp: "id",
290
+ description: "Sourced from the id prop."
291
+ },
292
+ item: {
293
+ attr: "data-fdi",
294
+ description: "Each tooth column header carries its FDI id as `data-fdi`. Every measurement input also carries `data-fdi` + `data-site` + `data-field`. The root `data-mode` exposes interactive vs display."
295
+ }
296
+ }
297
+ }, be = ["db", "b", "mb"], mt = ["dl", "l", "ml"];
298
+ function Ae(s) {
299
+ return s === "buccal" ? be : mt;
300
+ }
301
+ const xe = 4, ie = 6, ht = 20, gt = 5;
302
+ function je(s, t = xe, h = ie) {
303
+ return s == null || !Number.isFinite(s) ? "normal" : s >= h ? "severe" : s >= t ? "moderate" : "normal";
304
+ }
305
+ const ft = {
306
+ 18: { buccal: 1, lingual: 2 },
307
+ 17: { buccal: 1, lingual: 2 },
308
+ 16: { buccal: 1, lingual: 2 },
309
+ 15: { buccal: 2, lingual: 2 },
310
+ 14: { buccal: 0, lingual: 2 },
311
+ 24: { buccal: 0, lingual: 2 },
312
+ 25: { buccal: 2, lingual: 2 },
313
+ 26: { buccal: 1, lingual: 2 },
314
+ 27: { buccal: 1, lingual: 2 },
315
+ 28: { buccal: 1, lingual: 2 },
316
+ 48: { buccal: 1, lingual: 1 },
317
+ 47: { buccal: 1, lingual: 1 },
318
+ 46: { buccal: 1, lingual: 1 },
319
+ 36: { buccal: 1, lingual: 1 },
320
+ 37: { buccal: 1, lingual: 1 },
321
+ 38: { buccal: 1, lingual: 1 }
322
+ };
323
+ function bt(s, t) {
324
+ var h;
325
+ return ((h = ft[s]) == null ? void 0 : h[t]) ?? 0;
326
+ }
327
+ const vt = ve.slice(0, 16), yt = ve.slice(16), xt = ye.slice(0, 10), Nt = ye.slice(10);
328
+ function Oe(s) {
329
+ return s === "primary" ? { upper: xt, lower: Nt } : {
330
+ upper: vt,
331
+ lower: yt
332
+ };
333
+ }
334
+ function _e(s, t) {
335
+ return t === "universal" ? at[s] ?? s : t === "palmer" ? ot[s] ?? s : s;
336
+ }
337
+ function oe(s) {
338
+ const t = Math.round(s * 10) / 10;
339
+ return t === 0 ? 0 : t;
340
+ }
341
+ function Le(s) {
342
+ let t = 0, h = 0, d = 0, f = 0, E = 0, S = 0;
343
+ for (const L of Object.values(s))
344
+ if (!(L.missing || !L.sites))
345
+ for (const P of Object.values(L.sites)) {
346
+ if (!P) continue;
347
+ const y = P.pocketDepthMm;
348
+ y == null || !Number.isFinite(y) || (d += 1, t += y, h += y + (P.recessionMm ?? 0), y >= ie && (S += 1), P.bleeding && (f += 1), P.plaque && (E += 1));
349
+ }
350
+ return d === 0 ? {
351
+ meanPocketDepthMm: null,
352
+ meanAttachmentLevelMm: null,
353
+ plaquePercent: null,
354
+ bleedingPercent: null,
355
+ scoredSiteCount: 0,
356
+ deepPocketCount: 0
357
+ } : {
358
+ meanPocketDepthMm: oe(t / d),
359
+ meanAttachmentLevelMm: oe(h / d),
360
+ plaquePercent: oe(E / d * 100),
361
+ bleedingPercent: oe(f / d * 100),
362
+ scoredSiteCount: d,
363
+ deepPocketCount: S
364
+ };
365
+ }
366
+ const Mt = [...ve, ...ye];
367
+ function kt(s, t) {
368
+ var L, P;
369
+ const h = (t == null ? void 0 : t.moderate) ?? xe, d = (t == null ? void 0 : t.severe) ?? ie, f = /* @__PURE__ */ new Map();
370
+ Mt.forEach((y, C) => f.set(y, C));
371
+ const E = Object.keys(s).sort(
372
+ (y, C) => (f.get(y) ?? Number.MAX_SAFE_INTEGER) - (f.get(C) ?? Number.MAX_SAFE_INTEGER)
373
+ ), S = [];
374
+ for (const y of E) {
375
+ const C = s[y];
376
+ if (!C) continue;
377
+ if (C.missing) {
378
+ S.push({
379
+ fdi: y,
380
+ status: "missing",
381
+ severity: "normal",
382
+ maxPocketDepthMm: null,
383
+ pockets: [],
384
+ bleedingSites: [],
385
+ plaqueSites: [],
386
+ suppurationSites: [],
387
+ maxRecessionMm: null,
388
+ mobility: null,
389
+ furcation: []
390
+ });
391
+ continue;
392
+ }
393
+ const G = C.implant ? "implant" : "present", q = [], Z = [], te = [], z = [];
394
+ let Y = null, X = null;
395
+ for (const M of Fe) {
396
+ const V = (L = C.sites) == null ? void 0 : L[M];
397
+ if (!V) continue;
398
+ const { pocketDepthMm: K, recessionMm: B, bleeding: b, plaque: H, suppuration: D } = V;
399
+ if (K != null && Number.isFinite(K)) {
400
+ Y = Y == null ? K : Math.max(Y, K);
401
+ const j = je(K, h, d);
402
+ j !== "normal" && q.push({ site: M, pocketDepthMm: K, severity: j });
403
+ }
404
+ B != null && Number.isFinite(B) && B > 0 && (X = X == null ? B : Math.max(X, B)), b && Z.push(M), H && te.push(M), D && z.push(M);
405
+ }
406
+ q.sort((M, V) => V.pocketDepthMm - M.pocketDepthMm);
407
+ const se = C.mobility != null && C.mobility >= 1 ? C.mobility : null, o = [];
408
+ if (C.furcation)
409
+ for (const M of ["buccal", "lingual"])
410
+ (P = C.furcation[M]) != null && P.some(Boolean) && o.push(M);
411
+ const le = q.some(
412
+ (M) => M.severity === "severe"
413
+ ) ? "severe" : q.length > 0 ? "moderate" : "normal";
414
+ (G === "implant" || q.length > 0 || Z.length > 0 || z.length > 0 || se != null || o.length > 0) && S.push({
415
+ fdi: y,
416
+ status: G,
417
+ severity: le,
418
+ maxPocketDepthMm: Y,
419
+ pockets: q,
420
+ bleedingSites: Z,
421
+ plaqueSites: te,
422
+ suppurationSites: z,
423
+ maxRecessionMm: X,
424
+ mobility: se,
425
+ furcation: o
426
+ });
427
+ }
428
+ return S;
429
+ }
430
+ function jt(s = "permanent") {
431
+ const { upper: t, lower: h } = Oe(s), d = {};
432
+ for (const f of [...t, ...h]) d[f] = {};
433
+ return d;
434
+ }
435
+ function Ot(s) {
436
+ const t = [];
437
+ for (const [h, d] of Object.entries(s))
438
+ if (!(d.missing || !d.sites))
439
+ for (const f of Fe) {
440
+ const E = d.sites[f];
441
+ if (!E) continue;
442
+ const { pocketDepthMm: S, recessionMm: L, bleeding: P, suppuration: y } = E;
443
+ S == null && L == null && !P && !y || t.push({
444
+ tooth: h,
445
+ site: f,
446
+ pocketDepthMm: S,
447
+ recessionMm: L,
448
+ bleeding: P,
449
+ suppuration: y
450
+ });
451
+ }
452
+ return t;
453
+ }
454
+ function $t(s) {
455
+ const t = {};
456
+ for (const [h, d] of Object.entries(s))
457
+ d.mobility != null && (t[h] = d.mobility);
458
+ return t;
459
+ }
460
+ const Ct = qe(
461
+ "ds:w-full ds:flex ds:flex-col ds:gap-[var(--spacing-lg)] ds:text-[color:var(--foreground)]",
462
+ {
463
+ variants: {
464
+ // Disabled is conveyed by the inputs' own `disabled` state (native
465
+ // disabled controls are excluded from the axe contrast check). We must
466
+ // NOT dim the whole root with opacity — that would drop the legend,
467
+ // captions and headers below the AA contrast floor.
468
+ disabled: { true: "ds:cursor-not-allowed", false: "" }
469
+ },
470
+ defaultVariants: { disabled: !1 }
471
+ }
472
+ ), wt = qe(
473
+ "ds:h-6 ds:w-[3.5ch] ds:rounded-[var(--radius-sm)] ds:border ds:bg-[var(--background)] ds:text-center ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50",
474
+ {
475
+ variants: {
476
+ severity: {
477
+ normal: "ds:border-[color:var(--input)] ds:text-[color:var(--foreground)]",
478
+ moderate: "ds:border-[color:var(--warning)] ds:font-semibold ds:text-[color:var(--warning-foreground)]",
479
+ severe: "ds:border-[color:var(--destructive)] ds:font-bold ds:text-[color:var(--destructive)]"
480
+ }
481
+ },
482
+ defaultVariants: { severity: "normal" }
483
+ }
484
+ );
485
+ function St(s, t, h) {
486
+ return { ...s, [t]: { ...s[t], ...h } };
487
+ }
488
+ function Pt(s, t, h, d) {
489
+ const f = s[t] ?? {}, E = f.sites ?? {};
490
+ return {
491
+ ...s,
492
+ [t]: {
493
+ ...f,
494
+ sites: { ...E, [h]: { ...E[h], ...d } }
495
+ }
496
+ };
497
+ }
498
+ function Tt(s) {
499
+ return s === "severe" ? "ds:font-bold ds:text-[color:var(--destructive)]" : s === "moderate" ? "ds:font-semibold ds:text-[color:var(--warning-foreground)]" : "";
500
+ }
501
+ const Et = {
502
+ db: "disto-buccal",
503
+ b: "buccal",
504
+ mb: "mesio-buccal",
505
+ dl: "disto-lingual",
506
+ l: "lingual",
507
+ ml: "mesio-lingual"
508
+ }, Dt = et(
509
+ ({
510
+ value: s,
511
+ defaultValue: t,
512
+ onChange: h,
513
+ mode: d = "interactive",
514
+ disabled: f = !1,
515
+ dentition: E = "permanent",
516
+ numbering: S = "fdi",
517
+ showTeeth: L = !0,
518
+ assetBaseUrl: P = "/toothscheme/teeth",
519
+ showCurves: y = !0,
520
+ showIndices: C = !0,
521
+ moderatePocketMm: G = xe,
522
+ severePocketMm: q = ie,
523
+ locale: Z,
524
+ "aria-label": te,
525
+ id: z,
526
+ className: Y,
527
+ ...X
528
+ }, se) => {
529
+ const { t: o, i18n: le } = rt(), ce = Z ?? le.language ?? "en", M = Re(null), V = s !== void 0, [K, B] = tt(
530
+ () => t ?? {}
531
+ ), b = V ? s : K, H = d === "display", D = Re(b);
532
+ D.current = b;
533
+ const j = O(
534
+ (e) => {
535
+ D.current = e, V || B(e), h == null || h(e);
536
+ },
537
+ [V, h]
538
+ ), re = O(
539
+ (e, r, a) => j(Pt(D.current, e, r, a)),
540
+ [j]
541
+ ), Q = O(
542
+ (e, r) => j(St(D.current, e, r)),
543
+ [j]
544
+ ), Ne = O(
545
+ (e) => {
546
+ const r = { ...D.current };
547
+ delete r[e], j(r);
548
+ },
549
+ [j]
550
+ ), Me = O(
551
+ (e, r, a) => {
552
+ var i, l;
553
+ try {
554
+ (l = (i = M.current) == null ? void 0 : i.querySelector(
555
+ `[data-perio-input][data-fdi="${e}"][data-site="${r}"][data-field="${a}"]`
556
+ )) == null || l.focus();
557
+ } catch {
558
+ }
559
+ },
560
+ []
561
+ ), R = Ie(
562
+ () => Le(b),
563
+ [b]
564
+ ), de = Ie(
565
+ () => ({
566
+ getValue: () => D.current,
567
+ getTooth: (e) => D.current[e],
568
+ getIndices: () => Le(D.current),
569
+ getFindings: () => kt(D.current, {
570
+ moderate: G,
571
+ severe: q
572
+ }),
573
+ missingTeeth: () => Object.entries(D.current).filter(([, e]) => e.missing).map(([e]) => e),
574
+ setSite: re,
575
+ setTooth: Q,
576
+ clearTooth: Ne,
577
+ focusSite: Me
578
+ }),
579
+ [
580
+ re,
581
+ Q,
582
+ Ne,
583
+ Me,
584
+ G,
585
+ q
586
+ ]
587
+ );
588
+ st(se, () => de, [de]), nt(pt, de, z);
589
+ const ke = Oe(E), $e = te ?? o("periodontalChart.ariaLabel", "Periodontal chart"), ee = O(
590
+ (e) => {
591
+ try {
592
+ return new Intl.NumberFormat(ce, {
593
+ maximumFractionDigits: 1
594
+ }).format(e);
595
+ } catch {
596
+ return String(e);
597
+ }
598
+ },
599
+ [ce]
600
+ ), Ce = O(
601
+ (e) => o(`periodontalChart.site.${e}`, {
602
+ defaultValue: Et[e]
603
+ }),
604
+ [o]
605
+ ), U = O(
606
+ (e) => _e(e, S),
607
+ [S]
608
+ ), I = /* @__PURE__ */ n(
609
+ "span",
610
+ {
611
+ "aria-hidden": "true",
612
+ className: "ds:text-[color:var(--muted-foreground)]",
613
+ children: o("periodontalChart.cell.empty", "–")
614
+ }
615
+ ), Ve = O((e) => {
616
+ var k;
617
+ const r = e.target;
618
+ if (!((k = r.matches) != null && k.call(r, "[data-perio-input]"))) return;
619
+ const a = e.key;
620
+ if (a !== "ArrowLeft" && a !== "ArrowRight" && a !== "ArrowUp" && a !== "ArrowDown")
621
+ return;
622
+ e.preventDefault();
623
+ const i = M.current;
624
+ if (!i) return;
625
+ if (a === "ArrowUp" || a === "ArrowDown") {
626
+ const v = r.getAttribute("data-fdi"), A = r.getAttribute("data-site"), N = r.getAttribute("data-field") === "pocketDepthMm" ? "recessionMm" : "pocketDepthMm", _ = i.querySelector(
627
+ `[data-perio-input][data-fdi="${v}"][data-site="${A}"][data-field="${N}"]`
628
+ );
629
+ _ == null || _.focus(), _ == null || _.select();
630
+ return;
631
+ }
632
+ const l = Array.from(
633
+ i.querySelectorAll("[data-perio-input]")
634
+ ), p = l.indexOf(r);
635
+ if (p === -1) return;
636
+ const c = l[p + (a === "ArrowRight" ? 1 : -1)];
637
+ c == null || c.focus(), c == null || c.select();
638
+ }, []), T = "ds:px-[var(--spacing-2xs)] ds:py-[var(--spacing-2xs)] ds:text-center ds:border-b ds:border-[color:var(--border)]", W = "ds:px-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)] ds:text-start ds:font-medium ds:border-b ds:border-[color:var(--border)] ds:whitespace-nowrap", Ke = (e, r, a) => {
639
+ var c, k, v;
640
+ const i = (v = (k = (c = b[e]) == null ? void 0 : c.sites) == null ? void 0 : k[r]) == null ? void 0 : v[a], l = a === "pocketDepthMm" ? je(i, G, q) : "normal", p = o(`periodontalChart.input.${a}`, {
641
+ tooth: U(e),
642
+ site: Ce(r),
643
+ defaultValue: a === "pocketDepthMm" ? "Tooth {{tooth}} {{site}} pocket depth, millimetres" : "Tooth {{tooth}} {{site}} recession, millimetres"
644
+ }), g = l === "severe" ? o("periodontalChart.flag.severe", "severe pocket") : l === "moderate" ? o("periodontalChart.flag.moderate", "moderate pocket") : "";
645
+ return H ? i == null || !Number.isFinite(i) ? /* @__PURE__ */ n("span", { className: "ds:inline-block ds:w-[3.5ch]", children: I }) : /* @__PURE__ */ m(
646
+ "span",
647
+ {
648
+ className: `ds:inline-block ds:w-[3.5ch] ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ${Tt(
649
+ l
650
+ )}`,
651
+ children: [
652
+ /* @__PURE__ */ n("span", { children: ee(i) }),
653
+ g ? /* @__PURE__ */ m("span", { className: "ds:sr-only", children: [
654
+ " ",
655
+ g
656
+ ] }) : null
657
+ ]
658
+ }
659
+ ) : /* @__PURE__ */ n(
660
+ "input",
661
+ {
662
+ type: "text",
663
+ inputMode: "numeric",
664
+ autoComplete: "off",
665
+ disabled: f,
666
+ "aria-label": g ? `${p}, ${g}` : p,
667
+ "data-perio-input": "",
668
+ "data-fdi": e,
669
+ "data-site": r,
670
+ "data-field": a,
671
+ value: i == null ? "" : String(i),
672
+ onFocus: (A) => A.currentTarget.select(),
673
+ onChange: (A) => {
674
+ const x = A.target.value.trim();
675
+ let N;
676
+ if (x === "" || x === "-")
677
+ N = void 0;
678
+ else {
679
+ const _ = Number(x);
680
+ if (!Number.isFinite(_)) return;
681
+ N = a === "pocketDepthMm" ? Math.max(0, _) : _;
682
+ }
683
+ re(
684
+ e,
685
+ r,
686
+ a === "pocketDepthMm" ? { pocketDepthMm: N } : { recessionMm: N }
687
+ );
688
+ },
689
+ className: wt({ severity: l })
690
+ }
691
+ );
692
+ }, He = (e, r, a) => {
693
+ var g, c, k;
694
+ const i = !!((k = (c = (g = b[e]) == null ? void 0 : g.sites) == null ? void 0 : c[r]) != null && k[a]), l = o(`periodontalChart.toggle.${a}`, {
695
+ tooth: U(e),
696
+ site: Ce(r),
697
+ defaultValue: a === "bleeding" ? "Tooth {{tooth}} {{site}} bleeding on probing" : "Tooth {{tooth}} {{site}} plaque"
698
+ }), p = a === "bleeding" ? "ds:accent-[var(--destructive)]" : "ds:accent-[var(--warning)]";
699
+ return H ? i ? /* @__PURE__ */ n(
700
+ "span",
701
+ {
702
+ role: "img",
703
+ "aria-label": l,
704
+ className: `ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${a === "bleeding" ? "ds:bg-[var(--destructive)]" : "ds:bg-[var(--warning)]"}`
705
+ }
706
+ ) : /* @__PURE__ */ n("span", { className: "ds:inline-block ds:size-6", children: I }) : /* @__PURE__ */ m("label", { className: "ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center", children: [
707
+ /* @__PURE__ */ n("span", { className: "ds:sr-only", children: l }),
708
+ /* @__PURE__ */ n(
709
+ "input",
710
+ {
711
+ type: "checkbox",
712
+ disabled: f,
713
+ checked: i,
714
+ onChange: (v) => re(
715
+ e,
716
+ r,
717
+ a === "bleeding" ? { bleeding: v.target.checked } : { plaque: v.target.checked }
718
+ ),
719
+ className: `ds:size-4 ${p} ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50`
720
+ }
721
+ )
722
+ ] });
723
+ }, ze = (e, r) => {
724
+ var p;
725
+ const a = b[e], i = bt(e, r);
726
+ if (a != null && a.missing || i === 0) return I;
727
+ const l = ((p = a == null ? void 0 : a.furcation) == null ? void 0 : p[r]) ?? [];
728
+ return /* @__PURE__ */ n("span", { className: "ds:inline-flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]", children: Array.from({ length: i }, (g, c) => {
729
+ const k = !!l[c], v = o("periodontalChart.toggle.furcation", {
730
+ tooth: U(e),
731
+ side: o(`periodontalChart.side.${r}`, {
732
+ defaultValue: r === "buccal" ? "buccal" : "lingual"
733
+ }),
734
+ n: c + 1,
735
+ defaultValue: "Tooth {{tooth}} {{side}} furcation {{n}}"
736
+ });
737
+ return H ? k ? /* @__PURE__ */ n(
738
+ "span",
739
+ {
740
+ role: "img",
741
+ "aria-label": v,
742
+ className: "ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--primary)]"
743
+ },
744
+ c
745
+ ) : /* @__PURE__ */ n(
746
+ "span",
747
+ {
748
+ "aria-hidden": "true",
749
+ className: "ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:border ds:border-[color:var(--border)]"
750
+ },
751
+ c
752
+ ) : /* @__PURE__ */ m(
753
+ "label",
754
+ {
755
+ className: "ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center",
756
+ children: [
757
+ /* @__PURE__ */ n("span", { className: "ds:sr-only", children: v }),
758
+ /* @__PURE__ */ n(
759
+ "input",
760
+ {
761
+ type: "checkbox",
762
+ disabled: f,
763
+ checked: k,
764
+ onChange: (A) => {
765
+ var N;
766
+ const x = [...((N = a == null ? void 0 : a.furcation) == null ? void 0 : N[r]) ?? []];
767
+ for (; x.length < i; ) x.push(!1);
768
+ x[c] = A.target.checked, Q(e, {
769
+ furcation: { ...a == null ? void 0 : a.furcation, [r]: x }
770
+ });
771
+ },
772
+ className: "ds:size-4 ds:accent-[var(--primary)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50"
773
+ }
774
+ )
775
+ ]
776
+ },
777
+ c
778
+ );
779
+ }) });
780
+ }, ne = (e, r, a, i, l) => /* @__PURE__ */ m("tr", { children: [
781
+ /* @__PURE__ */ n("th", { scope: "row", className: W, children: o(r, a) }),
782
+ e.map((p) => {
783
+ var g;
784
+ return /* @__PURE__ */ n("td", { className: T, children: (g = b[p]) != null && g.missing ? I : /* @__PURE__ */ n("span", { className: "ds:flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]", children: Ae(i).map((c) => /* @__PURE__ */ n("span", { children: Ke(p, c, l) }, c)) }) }, p);
785
+ })
786
+ ] }), ae = (e, r, a, i, l) => /* @__PURE__ */ m("tr", { children: [
787
+ /* @__PURE__ */ n("th", { scope: "row", className: W, children: o(r, a) }),
788
+ e.map((p) => {
789
+ var g;
790
+ return /* @__PURE__ */ n("td", { className: T, children: (g = b[p]) != null && g.missing ? I : /* @__PURE__ */ n("span", { className: "ds:flex ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)]", children: Ae(i).map((c) => /* @__PURE__ */ n("span", { children: He(p, c, l) }, c)) }) }, p);
791
+ })
792
+ ] }), we = (e, r) => /* @__PURE__ */ m("tr", { children: [
793
+ /* @__PURE__ */ n("th", { scope: "row", className: W, children: o("periodontalChart.row.furcation", "Furcation") }),
794
+ e.map((a) => /* @__PURE__ */ n("td", { className: T, children: ze(a, r) }, a))
795
+ ] }), Se = (e, r, a, i) => /* @__PURE__ */ m("tr", { children: [
796
+ /* @__PURE__ */ n("th", { scope: "row", className: W, children: o(r, a) }),
797
+ e.map((l) => {
798
+ var c, k;
799
+ if (i === "implant" && ((c = b[l]) != null && c.missing))
800
+ return /* @__PURE__ */ n("td", { className: T, children: I }, l);
801
+ const p = !!((k = b[l]) != null && k[i]), g = o(`periodontalChart.flag.${i}Label`, {
802
+ tooth: U(l),
803
+ defaultValue: i === "missing" ? "Tooth {{tooth}} missing" : "Tooth {{tooth}} implant"
804
+ });
805
+ return /* @__PURE__ */ n("td", { className: T, children: H ? p ? /* @__PURE__ */ n(
806
+ "span",
807
+ {
808
+ role: "img",
809
+ "aria-label": g,
810
+ className: "type-meta ds:font-semibold",
811
+ children: i === "missing" ? o("periodontalChart.flag.missingShort", "×") : o("periodontalChart.flag.implantShort", "I")
812
+ }
813
+ ) : I : /* @__PURE__ */ m("label", { className: "ds:inline-flex ds:size-6 ds:cursor-pointer ds:items-center ds:justify-center", children: [
814
+ /* @__PURE__ */ n("span", { className: "ds:sr-only", children: g }),
815
+ /* @__PURE__ */ n(
816
+ "input",
817
+ {
818
+ type: "checkbox",
819
+ disabled: f,
820
+ checked: p,
821
+ onChange: (v) => Q(
822
+ l,
823
+ i === "missing" ? { missing: v.target.checked } : { implant: v.target.checked }
824
+ ),
825
+ className: "ds:size-4 ds:accent-[var(--primary)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50"
826
+ }
827
+ )
828
+ ] }) }, l);
829
+ })
830
+ ] }), Be = (e) => /* @__PURE__ */ m("tr", { children: [
831
+ /* @__PURE__ */ n("th", { scope: "row", className: W, children: o("periodontalChart.row.mobility", "Mobility") }),
832
+ e.map((r) => {
833
+ var l, p;
834
+ if ((l = b[r]) != null && l.missing)
835
+ return /* @__PURE__ */ n("td", { className: T, children: I }, r);
836
+ const a = (p = b[r]) == null ? void 0 : p.mobility, i = o("periodontalChart.mobility.label", {
837
+ tooth: U(r),
838
+ defaultValue: "Tooth {{tooth}} mobility grade"
839
+ });
840
+ return H ? /* @__PURE__ */ n("td", { className: T, children: a == null ? I : /* @__PURE__ */ n("span", { role: "img", "aria-label": i, className: "type-meta", children: a }) }, r) : /* @__PURE__ */ n("td", { className: T, children: /* @__PURE__ */ m(
841
+ "select",
842
+ {
843
+ disabled: f,
844
+ "aria-label": i,
845
+ value: a == null ? "" : String(a),
846
+ onChange: (g) => Q(r, {
847
+ mobility: g.target.value === "" ? void 0 : Number(g.target.value)
848
+ }),
849
+ className: "ds:h-6 ds:rounded-[var(--radius-sm)] ds:border ds:border-[color:var(--input)] ds:bg-[var(--background)] ds:text-[color:var(--foreground)] ds:text-center ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50",
850
+ children: [
851
+ /* @__PURE__ */ n("option", { value: "", children: o("periodontalChart.mobility.none", "–") }),
852
+ /* @__PURE__ */ n("option", { value: "0", children: "0" }),
853
+ /* @__PURE__ */ n("option", { value: "1", children: "1" }),
854
+ /* @__PURE__ */ n("option", { value: "2", children: "2" }),
855
+ /* @__PURE__ */ n("option", { value: "3", children: "3" })
856
+ ]
857
+ }
858
+ ) }, r);
859
+ })
860
+ ] }), Ue = (e) => /* @__PURE__ */ m("tr", { children: [
861
+ /* @__PURE__ */ n("th", { scope: "row", className: W, children: o("periodontalChart.row.note", "Note") }),
862
+ e.map((r) => {
863
+ var l, p;
864
+ if ((l = b[r]) != null && l.missing)
865
+ return /* @__PURE__ */ n("td", { className: T, children: I }, r);
866
+ const a = ((p = b[r]) == null ? void 0 : p.note) ?? "", i = o("periodontalChart.note.label", {
867
+ tooth: U(r),
868
+ defaultValue: "Tooth {{tooth}} note"
869
+ });
870
+ return H ? /* @__PURE__ */ n("td", { className: `${T} ds:text-start`, children: a ? /* @__PURE__ */ n("span", { className: "type-meta", children: a }) : I }, r) : /* @__PURE__ */ n("td", { className: T, children: /* @__PURE__ */ n(
871
+ "input",
872
+ {
873
+ type: "text",
874
+ disabled: f,
875
+ "aria-label": i,
876
+ value: a,
877
+ onChange: (g) => Q(r, { note: g.target.value }),
878
+ className: "ds:h-6 ds:w-[6ch] ds:rounded-[var(--radius-sm)] ds:border ds:border-[color:var(--input)] ds:bg-[var(--background)] ds:text-[color:var(--foreground)] ds:px-[var(--spacing-2xs)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:disabled:cursor-not-allowed ds:disabled:opacity-50"
879
+ }
880
+ ) }, r);
881
+ })
882
+ ] }), We = (e) => /* @__PURE__ */ m("tr", { children: [
883
+ /* @__PURE__ */ n("th", { scope: "row", className: W, children: o("periodontalChart.row.tooth", "Tooth") }),
884
+ e.map((r) => {
885
+ const a = b[r], i = it[r];
886
+ if (a != null && a.missing || !i)
887
+ return /* @__PURE__ */ n("td", { className: T, children: I }, r);
888
+ const l = a != null && a.implant ? "implant" : "default";
889
+ return /* @__PURE__ */ n("td", { className: T, children: /* @__PURE__ */ n(
890
+ "img",
891
+ {
892
+ src: lt(i, P, l, "side"),
893
+ alt: "",
894
+ "aria-hidden": "true",
895
+ loading: "lazy",
896
+ className: "ds:mx-auto ds:h-12 ds:w-auto ds:max-w-full ds:object-contain"
897
+ }
898
+ ) }, r);
899
+ })
900
+ ] }), Ge = (e) => {
901
+ var Te, Ee, De;
902
+ const c = (u) => 38 + Math.min(Math.max(0, u), 13) * 6, k = (u) => 38 - Math.min(Math.max(0, u), 5) * 6, v = [], A = [];
903
+ let x = [], N = [];
904
+ const _ = () => {
905
+ x.length && v.push(x), N.length && A.push(N), x = [], N = [];
906
+ };
907
+ let ue = 12;
908
+ for (const u of e) {
909
+ const w = (Te = b[u]) == null ? void 0 : Te.missing;
910
+ for (const Ze of be) {
911
+ const J = w || (De = (Ee = b[u]) == null ? void 0 : Ee.sites) == null ? void 0 : De[Ze], he = J == null ? void 0 : J.pocketDepthMm, ge = J == null ? void 0 : J.recessionMm;
912
+ he != null && Number.isFinite(he) ? x.push({ x: ue, y: c(he) }) : x.length && (v.push(x), x = []), ge != null && Number.isFinite(ge) ? N.push({ x: ue, y: k(ge) }) : N.length && (A.push(N), N = []), ue += 24;
913
+ }
914
+ }
915
+ _();
916
+ const pe = e.length * be.length * 24, me = (u) => u.map((w) => `${w.x},${w.y}`).join(" "), Qe = v.flat(), Je = A.flat();
917
+ return /* @__PURE__ */ m(
918
+ "svg",
919
+ {
920
+ viewBox: `0 0 ${pe} 124`,
921
+ className: "ds:h-[132px] ds:w-full",
922
+ preserveAspectRatio: "none",
923
+ "aria-hidden": "true",
924
+ focusable: "false",
925
+ children: [
926
+ [3, 6, 9, 12].map((u) => /* @__PURE__ */ n(
927
+ "line",
928
+ {
929
+ x1: 0,
930
+ x2: pe,
931
+ y1: c(u),
932
+ y2: c(u),
933
+ stroke: "var(--border)",
934
+ strokeWidth: 1,
935
+ strokeDasharray: "2 4"
936
+ },
937
+ u
938
+ )),
939
+ /* @__PURE__ */ n(
940
+ "line",
941
+ {
942
+ x1: 0,
943
+ x2: pe,
944
+ y1: 38,
945
+ y2: 38,
946
+ stroke: "var(--border)",
947
+ strokeWidth: 1.5
948
+ }
949
+ ),
950
+ v.map(
951
+ (u, w) => u.length >= 2 ? /* @__PURE__ */ n(
952
+ "polygon",
953
+ {
954
+ points: `${u[0].x},38 ${me(u)} ${u[u.length - 1].x},38`,
955
+ fill: "var(--success)",
956
+ fillOpacity: 0.14
957
+ },
958
+ `fill-${w}`
959
+ ) : null
960
+ ),
961
+ v.map(
962
+ (u, w) => u.length >= 2 ? /* @__PURE__ */ n(
963
+ "polyline",
964
+ {
965
+ points: me(u),
966
+ fill: "none",
967
+ stroke: "var(--success)",
968
+ strokeWidth: 2.5,
969
+ strokeLinejoin: "round",
970
+ strokeLinecap: "round"
971
+ },
972
+ `depth-${w}`
973
+ ) : null
974
+ ),
975
+ A.map(
976
+ (u, w) => u.length >= 2 ? /* @__PURE__ */ n(
977
+ "polyline",
978
+ {
979
+ points: me(u),
980
+ fill: "none",
981
+ stroke: "var(--accent)",
982
+ strokeWidth: 2.5,
983
+ strokeLinejoin: "round",
984
+ strokeLinecap: "round"
985
+ },
986
+ `margin-${w}`
987
+ ) : null
988
+ ),
989
+ Qe.map((u, w) => /* @__PURE__ */ n(
990
+ "circle",
991
+ {
992
+ cx: u.x,
993
+ cy: u.y,
994
+ r: 2,
995
+ fill: "var(--success)"
996
+ },
997
+ `dp-${w}`
998
+ )),
999
+ Je.map((u, w) => /* @__PURE__ */ n(
1000
+ "circle",
1001
+ {
1002
+ cx: u.x,
1003
+ cy: u.y,
1004
+ r: 2,
1005
+ fill: "var(--accent)"
1006
+ },
1007
+ `mp-${w}`
1008
+ ))
1009
+ ]
1010
+ }
1011
+ );
1012
+ }, Pe = (e, r, a) => /* @__PURE__ */ n("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]", children: /* @__PURE__ */ m(
1013
+ "div",
1014
+ {
1015
+ tabIndex: 0,
1016
+ role: "group",
1017
+ dir: "ltr",
1018
+ "aria-label": o(r, a),
1019
+ className: "ds:overflow-x-auto ds:rounded-[var(--radius-sm)] ds:outline-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]",
1020
+ children: [
1021
+ /* @__PURE__ */ m("table", { className: "type-body-sm ds:w-full ds:border-collapse", children: [
1022
+ /* @__PURE__ */ n("caption", { className: "type-label ds:text-start ds:mb-[var(--spacing-2xs)] ds:text-[color:var(--muted-foreground)]", children: o(r, a) }),
1023
+ /* @__PURE__ */ n("thead", { children: /* @__PURE__ */ m("tr", { children: [
1024
+ /* @__PURE__ */ n(
1025
+ "th",
1026
+ {
1027
+ scope: "col",
1028
+ className: "ds:px-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)] ds:text-start ds:border-b ds:border-[color:var(--border)]",
1029
+ children: o("periodontalChart.column.measure", "Measurement")
1030
+ }
1031
+ ),
1032
+ e.map((i) => /* @__PURE__ */ m(
1033
+ "th",
1034
+ {
1035
+ scope: "col",
1036
+ "data-fdi": i,
1037
+ className: "ds:px-[var(--spacing-2xs)] ds:py-[var(--spacing-2xs)] ds:text-center ds:border-b ds:border-[color:var(--border)] ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)]",
1038
+ children: [
1039
+ /* @__PURE__ */ n("span", { className: "ds:sr-only", children: o("periodontalChart.toothHeader", {
1040
+ tooth: U(i),
1041
+ defaultValue: "Tooth {{tooth}}"
1042
+ }) }),
1043
+ /* @__PURE__ */ n("span", { "aria-hidden": "true", children: _e(i, S) })
1044
+ ]
1045
+ },
1046
+ i
1047
+ ))
1048
+ ] }) }),
1049
+ /* @__PURE__ */ m("tbody", { children: [
1050
+ Se(
1051
+ e,
1052
+ "periodontalChart.row.missing",
1053
+ "Missing",
1054
+ "missing"
1055
+ ),
1056
+ Se(
1057
+ e,
1058
+ "periodontalChart.row.implant",
1059
+ "Implant",
1060
+ "implant"
1061
+ ),
1062
+ Be(e),
1063
+ ne(
1064
+ e,
1065
+ "periodontalChart.row.pocketDepthBuccal",
1066
+ "Pocket depth — buccal",
1067
+ "buccal",
1068
+ "pocketDepthMm"
1069
+ ),
1070
+ ne(
1071
+ e,
1072
+ "periodontalChart.row.recessionBuccal",
1073
+ "Recession — buccal",
1074
+ "buccal",
1075
+ "recessionMm"
1076
+ ),
1077
+ ae(
1078
+ e,
1079
+ "periodontalChart.row.bleedingBuccal",
1080
+ "Bleeding — buccal",
1081
+ "buccal",
1082
+ "bleeding"
1083
+ ),
1084
+ ae(
1085
+ e,
1086
+ "periodontalChart.row.plaqueBuccal",
1087
+ "Plaque — buccal",
1088
+ "buccal",
1089
+ "plaque"
1090
+ ),
1091
+ we(e, "buccal"),
1092
+ L ? We(e) : null,
1093
+ ne(
1094
+ e,
1095
+ "periodontalChart.row.pocketDepthLingual",
1096
+ "Pocket depth — lingual",
1097
+ "lingual",
1098
+ "pocketDepthMm"
1099
+ ),
1100
+ ne(
1101
+ e,
1102
+ "periodontalChart.row.recessionLingual",
1103
+ "Recession — lingual",
1104
+ "lingual",
1105
+ "recessionMm"
1106
+ ),
1107
+ ae(
1108
+ e,
1109
+ "periodontalChart.row.bleedingLingual",
1110
+ "Bleeding — lingual",
1111
+ "lingual",
1112
+ "bleeding"
1113
+ ),
1114
+ ae(
1115
+ e,
1116
+ "periodontalChart.row.plaqueLingual",
1117
+ "Plaque — lingual",
1118
+ "lingual",
1119
+ "plaque"
1120
+ ),
1121
+ we(e, "lingual"),
1122
+ Ue(e)
1123
+ ] })
1124
+ ] }),
1125
+ y ? /* @__PURE__ */ n(
1126
+ "figure",
1127
+ {
1128
+ className: "ds:m-0 ds:mt-[var(--spacing-2xs)]",
1129
+ "aria-label": o("periodontalChart.curve.label", {
1130
+ arch: o(r, a),
1131
+ defaultValue: "{{arch}} probing depth and recession profile"
1132
+ }),
1133
+ children: Ge(e)
1134
+ }
1135
+ ) : null
1136
+ ]
1137
+ }
1138
+ ) }), Ye = () => {
1139
+ const e = [
1140
+ {
1141
+ key: "meanPd",
1142
+ label: o("periodontalChart.index.meanPocketDepth", "Mean PD"),
1143
+ value: R.meanPocketDepthMm == null ? "–" : o("periodontalChart.unit.mm", {
1144
+ value: ee(R.meanPocketDepthMm),
1145
+ defaultValue: "{{value}} mm"
1146
+ }),
1147
+ elevated: !1
1148
+ },
1149
+ {
1150
+ key: "meanCal",
1151
+ label: o("periodontalChart.index.meanAttachment", "Mean CAL"),
1152
+ value: R.meanAttachmentLevelMm == null ? "–" : o("periodontalChart.unit.mm", {
1153
+ value: ee(R.meanAttachmentLevelMm),
1154
+ defaultValue: "{{value}} mm"
1155
+ }),
1156
+ elevated: !1
1157
+ },
1158
+ {
1159
+ key: "plaque",
1160
+ label: o("periodontalChart.index.plaque", "Plaque"),
1161
+ value: R.plaquePercent == null ? "–" : o("periodontalChart.unit.percent", {
1162
+ value: ee(R.plaquePercent),
1163
+ defaultValue: "{{value}}%"
1164
+ }),
1165
+ elevated: R.plaquePercent != null && R.plaquePercent > ht
1166
+ },
1167
+ {
1168
+ key: "bleeding",
1169
+ label: o("periodontalChart.index.bleeding", "BoP"),
1170
+ value: R.bleedingPercent == null ? "–" : o("periodontalChart.unit.percent", {
1171
+ value: ee(R.bleedingPercent),
1172
+ defaultValue: "{{value}}%"
1173
+ }),
1174
+ elevated: R.bleedingPercent != null && R.bleedingPercent > gt
1175
+ }
1176
+ ];
1177
+ return /* @__PURE__ */ n(
1178
+ "dl",
1179
+ {
1180
+ className: "type-body-sm ds:flex ds:flex-wrap ds:gap-[var(--spacing-lg)] ds:m-0",
1181
+ "aria-label": o("periodontalChart.index.label", "Summary indices"),
1182
+ children: e.map((r) => /* @__PURE__ */ m(
1183
+ "div",
1184
+ {
1185
+ className: "ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]",
1186
+ children: [
1187
+ /* @__PURE__ */ n("dt", { className: "type-meta ds:text-[color:var(--muted-foreground)]", children: r.label }),
1188
+ /* @__PURE__ */ m(
1189
+ "dd",
1190
+ {
1191
+ className: `ds:m-0 ds:tabular-nums ds:[font-variant-numeric:var(--font-feature-tabular)] ${r.elevated ? "ds:font-bold ds:text-[color:var(--destructive)]" : "ds:text-[color:var(--foreground)]"}`,
1192
+ children: [
1193
+ /* @__PURE__ */ n("span", { children: r.value }),
1194
+ r.elevated ? /* @__PURE__ */ m("span", { className: "ds:sr-only", children: [
1195
+ " ",
1196
+ o("periodontalChart.index.elevated", "elevated")
1197
+ ] }) : null
1198
+ ]
1199
+ }
1200
+ )
1201
+ ]
1202
+ },
1203
+ r.key
1204
+ ))
1205
+ }
1206
+ );
1207
+ }, Xe = /* @__PURE__ */ m(
1208
+ "div",
1209
+ {
1210
+ role: "group",
1211
+ "aria-label": o("periodontalChart.legend.label", "Chart legend"),
1212
+ className: "type-meta ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-md)] ds:text-[color:var(--muted-foreground)]",
1213
+ children: [
1214
+ /* @__PURE__ */ m("span", { className: "ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]", children: [
1215
+ /* @__PURE__ */ n(
1216
+ "span",
1217
+ {
1218
+ "aria-hidden": "true",
1219
+ className: "ds:inline-block ds:h-[2px] ds:w-4 ds:bg-[var(--success)]"
1220
+ }
1221
+ ),
1222
+ o("periodontalChart.legend.depth", "Pocket depth")
1223
+ ] }),
1224
+ /* @__PURE__ */ m("span", { className: "ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]", children: [
1225
+ /* @__PURE__ */ n(
1226
+ "span",
1227
+ {
1228
+ "aria-hidden": "true",
1229
+ className: "ds:inline-block ds:h-[2px] ds:w-4 ds:bg-[var(--accent)]"
1230
+ }
1231
+ ),
1232
+ o("periodontalChart.legend.recession", "Recession")
1233
+ ] }),
1234
+ /* @__PURE__ */ m("span", { className: "ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]", children: [
1235
+ /* @__PURE__ */ n(
1236
+ "span",
1237
+ {
1238
+ "aria-hidden": "true",
1239
+ className: "ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--destructive)]"
1240
+ }
1241
+ ),
1242
+ o("periodontalChart.legend.bleeding", "Bleeding on probing")
1243
+ ] }),
1244
+ /* @__PURE__ */ m("span", { className: "ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)]", children: [
1245
+ /* @__PURE__ */ n(
1246
+ "span",
1247
+ {
1248
+ "aria-hidden": "true",
1249
+ className: "ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ds:bg-[var(--warning)]"
1250
+ }
1251
+ ),
1252
+ o("periodontalChart.legend.plaque", "Plaque")
1253
+ ] })
1254
+ ]
1255
+ }
1256
+ );
1257
+ return (
1258
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- roving-tabindex arrow-key nav across the chart group; each input is individually focusable and labelled
1259
+ /* @__PURE__ */ m(
1260
+ "div",
1261
+ {
1262
+ ref: M,
1263
+ role: "group",
1264
+ "aria-label": $e,
1265
+ "data-component": "periodontal-chart",
1266
+ "data-component-id": z,
1267
+ "data-mode": d,
1268
+ id: z,
1269
+ ...X,
1270
+ onKeyDown: Ve,
1271
+ className: Ct({ disabled: f, className: Y }),
1272
+ children: [
1273
+ C ? Ye() : null,
1274
+ Pe(ke.upper, "periodontalChart.arch.upper", "Upper arch"),
1275
+ Pe(ke.lower, "periodontalChart.arch.lower", "Lower arch"),
1276
+ Xe
1277
+ ]
1278
+ }
1279
+ )
1280
+ );
1281
+ }
1282
+ );
1283
+ Dt.displayName = "PeriodontalChart";
1284
+ export {
1285
+ gt as B,
1286
+ xe as M,
1287
+ be as P,
1288
+ ie as S,
1289
+ ft as a,
1290
+ mt as b,
1291
+ ht as c,
1292
+ Dt as d,
1293
+ kt as e,
1294
+ Le as f,
1295
+ jt as g,
1296
+ bt as h,
1297
+ pt as i,
1298
+ _e as j,
1299
+ je as k,
1300
+ Ot as l,
1301
+ Oe as p,
1302
+ Ae as s,
1303
+ $t as t
1304
+ };
1305
+ //# sourceMappingURL=periodontal-chart-CYBhr9vc.js.map