@alfadocs/ui-kit-debug 0.58.0 → 0.59.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 (145) hide show
  1. package/dist/_chunks/{bishop-score-CMQxsdy4.js → bishop-score-B9tvgoIq.js} +2 -2
  2. package/dist/_chunks/{bishop-score-CMQxsdy4.js.map → bishop-score-B9tvgoIq.js.map} +1 -1
  3. package/dist/_chunks/{bmi-calculator-DuUneHuZ.js → bmi-calculator-DA2NGmVK.js} +2 -2
  4. package/dist/_chunks/{bmi-calculator-DuUneHuZ.js.map → bmi-calculator-DA2NGmVK.js.map} +1 -1
  5. package/dist/_chunks/{calendar-oYWOCrnf.js → calendar-CkuJIg3s.js} +1 -1
  6. package/dist/_chunks/{calendar-oYWOCrnf.js.map → calendar-CkuJIg3s.js.map} +1 -1
  7. package/dist/_chunks/{cycle-calculator-vTtZZAmn.js → cycle-calculator-C_t1Hs8V.js} +2 -2
  8. package/dist/_chunks/{cycle-calculator-vTtZZAmn.js.map → cycle-calculator-C_t1Hs8V.js.map} +1 -1
  9. package/dist/_chunks/date-picker-variants-CXEAx3O_.js.map +1 -1
  10. package/dist/_chunks/{due-date-calculator-CUm5KJbf.js → due-date-calculator-Q8MIwEVV.js} +2 -2
  11. package/dist/_chunks/{due-date-calculator-CUm5KJbf.js.map → due-date-calculator-Q8MIwEVV.js.map} +1 -1
  12. package/dist/_chunks/{fetal-weight-Xf8-ZoDy.js → fetal-weight-D1a6BmM-.js} +2 -2
  13. package/dist/_chunks/{fetal-weight-Xf8-ZoDy.js.map → fetal-weight-D1a6BmM-.js.map} +1 -1
  14. package/dist/_chunks/{gestational-age-calculator-830KJql3.js → gestational-age-calculator-AkNFfZYs.js} +2 -2
  15. package/dist/_chunks/{gestational-age-calculator-830KJql3.js.map → gestational-age-calculator-AkNFfZYs.js.map} +1 -1
  16. package/dist/_chunks/{hcg-doubling-kVOpDfny.js → hcg-doubling-Dg0Hr7ey.js} +2 -2
  17. package/dist/_chunks/{hcg-doubling-kVOpDfny.js.map → hcg-doubling-Dg0Hr7ey.js.map} +1 -1
  18. package/dist/_chunks/{insert-result-njqBthzT.js → insert-result-C1SYdueh.js} +125 -115
  19. package/dist/_chunks/insert-result-C1SYdueh.js.map +1 -0
  20. package/dist/_chunks/{pregnancy-dating-BA37LSkF.js → pregnancy-dating-Dg6dTe1p.js} +2 -2
  21. package/dist/_chunks/{pregnancy-dating-BA37LSkF.js.map → pregnancy-dating-Dg6dTe1p.js.map} +1 -1
  22. package/dist/_chunks/{pregnancy-weight-gain-BMRBeA8V.js → pregnancy-weight-gain-DI7X-0JX.js} +2 -2
  23. package/dist/_chunks/{pregnancy-weight-gain-BMRBeA8V.js.map → pregnancy-weight-gain-DI7X-0JX.js.map} +1 -1
  24. package/dist/_chunks/rich-text-editor-B03qM22-.js +334 -0
  25. package/dist/_chunks/rich-text-editor-B03qM22-.js.map +1 -0
  26. package/dist/_chunks/{unit-converter-BQ6lEYvd.js → unit-converter-3sINXO3m.js} +2 -2
  27. package/dist/_chunks/{unit-converter-BQ6lEYvd.js.map → unit-converter-3sINXO3m.js.map} +1 -1
  28. package/dist/agent-catalog.json +2 -2
  29. package/dist/components/_shared/safe-html.d.ts +1 -1
  30. package/dist/components/bishop-score/index.js +1 -1
  31. package/dist/components/bmi-calculator/index.js +1 -1
  32. package/dist/components/calendar/index.js +1 -1
  33. package/dist/components/cycle-calculator/index.js +1 -1
  34. package/dist/components/due-date-calculator/index.js +1 -1
  35. package/dist/components/fetal-weight/index.js +1 -1
  36. package/dist/components/gestational-age-calculator/index.js +1 -1
  37. package/dist/components/hcg-doubling/index.js +1 -1
  38. package/dist/components/pregnancy-dating/index.js +1 -1
  39. package/dist/components/pregnancy-weight-gain/index.js +1 -1
  40. package/dist/components/rich-text-editor/index.d.ts +2 -2
  41. package/dist/components/rich-text-editor/index.d.ts.map +1 -1
  42. package/dist/components/rich-text-editor/index.js +4 -7
  43. package/dist/components/rich-text-editor/rich-text-editor.agent.d.ts.map +1 -1
  44. package/dist/components/rich-text-editor/rich-text-editor.d.ts +21 -15
  45. package/dist/components/rich-text-editor/rich-text-editor.d.ts.map +1 -1
  46. package/dist/components/unit-converter/index.js +1 -1
  47. package/dist/i18n/locales/ar.d.ts +1 -0
  48. package/dist/i18n/locales/ar.d.ts.map +1 -1
  49. package/dist/i18n/locales/ar.js +1 -0
  50. package/dist/i18n/locales/ar.js.map +1 -1
  51. package/dist/i18n/locales/de.d.ts +1 -0
  52. package/dist/i18n/locales/de.d.ts.map +1 -1
  53. package/dist/i18n/locales/de.js +1 -0
  54. package/dist/i18n/locales/de.js.map +1 -1
  55. package/dist/i18n/locales/el.d.ts +1 -0
  56. package/dist/i18n/locales/el.d.ts.map +1 -1
  57. package/dist/i18n/locales/el.js +1 -0
  58. package/dist/i18n/locales/el.js.map +1 -1
  59. package/dist/i18n/locales/en.d.ts +1 -0
  60. package/dist/i18n/locales/en.d.ts.map +1 -1
  61. package/dist/i18n/locales/en.js +1 -0
  62. package/dist/i18n/locales/en.js.map +1 -1
  63. package/dist/i18n/locales/es.d.ts +1 -0
  64. package/dist/i18n/locales/es.d.ts.map +1 -1
  65. package/dist/i18n/locales/es.js +1 -0
  66. package/dist/i18n/locales/es.js.map +1 -1
  67. package/dist/i18n/locales/fr.d.ts +1 -0
  68. package/dist/i18n/locales/fr.d.ts.map +1 -1
  69. package/dist/i18n/locales/fr.js +1 -0
  70. package/dist/i18n/locales/fr.js.map +1 -1
  71. package/dist/i18n/locales/hi.d.ts +1 -0
  72. package/dist/i18n/locales/hi.d.ts.map +1 -1
  73. package/dist/i18n/locales/hi.js +1 -0
  74. package/dist/i18n/locales/hi.js.map +1 -1
  75. package/dist/i18n/locales/it.d.ts +1 -0
  76. package/dist/i18n/locales/it.d.ts.map +1 -1
  77. package/dist/i18n/locales/it.js +1 -0
  78. package/dist/i18n/locales/it.js.map +1 -1
  79. package/dist/i18n/locales/ja.d.ts +1 -0
  80. package/dist/i18n/locales/ja.d.ts.map +1 -1
  81. package/dist/i18n/locales/ja.js +1 -0
  82. package/dist/i18n/locales/ja.js.map +1 -1
  83. package/dist/i18n/locales/nl.d.ts +1 -0
  84. package/dist/i18n/locales/nl.d.ts.map +1 -1
  85. package/dist/i18n/locales/nl.js +1 -0
  86. package/dist/i18n/locales/nl.js.map +1 -1
  87. package/dist/i18n/locales/pl.d.ts +1 -0
  88. package/dist/i18n/locales/pl.d.ts.map +1 -1
  89. package/dist/i18n/locales/pl.js +1 -0
  90. package/dist/i18n/locales/pl.js.map +1 -1
  91. package/dist/i18n/locales/pt.d.ts +1 -0
  92. package/dist/i18n/locales/pt.d.ts.map +1 -1
  93. package/dist/i18n/locales/pt.js +1 -0
  94. package/dist/i18n/locales/pt.js.map +1 -1
  95. package/dist/i18n/locales/ro.d.ts +1 -0
  96. package/dist/i18n/locales/ro.d.ts.map +1 -1
  97. package/dist/i18n/locales/ro.js +1 -0
  98. package/dist/i18n/locales/ro.js.map +1 -1
  99. package/dist/i18n/locales/ru.d.ts +1 -0
  100. package/dist/i18n/locales/ru.d.ts.map +1 -1
  101. package/dist/i18n/locales/ru.js +1 -0
  102. package/dist/i18n/locales/ru.js.map +1 -1
  103. package/dist/i18n/locales/sq.d.ts +1 -0
  104. package/dist/i18n/locales/sq.d.ts.map +1 -1
  105. package/dist/i18n/locales/sq.js +1 -0
  106. package/dist/i18n/locales/sq.js.map +1 -1
  107. package/dist/i18n/locales/sv.d.ts +1 -0
  108. package/dist/i18n/locales/sv.d.ts.map +1 -1
  109. package/dist/i18n/locales/sv.js +1 -0
  110. package/dist/i18n/locales/sv.js.map +1 -1
  111. package/dist/i18n/locales/tr.d.ts +1 -0
  112. package/dist/i18n/locales/tr.d.ts.map +1 -1
  113. package/dist/i18n/locales/tr.js +1 -0
  114. package/dist/i18n/locales/tr.js.map +1 -1
  115. package/dist/i18n/locales/zh.d.ts +1 -0
  116. package/dist/i18n/locales/zh.d.ts.map +1 -1
  117. package/dist/i18n/locales/zh.js +1 -0
  118. package/dist/i18n/locales/zh.js.map +1 -1
  119. package/dist/index.js +143 -146
  120. package/dist/locales/ar.json +1 -0
  121. package/dist/locales/de.json +1 -0
  122. package/dist/locales/el.json +1 -0
  123. package/dist/locales/en.json +1 -0
  124. package/dist/locales/es.json +1 -0
  125. package/dist/locales/fr.json +1 -0
  126. package/dist/locales/hi.json +1 -0
  127. package/dist/locales/it.json +1 -0
  128. package/dist/locales/ja.json +1 -0
  129. package/dist/locales/nl.json +1 -0
  130. package/dist/locales/pl.json +1 -0
  131. package/dist/locales/pt.json +1 -0
  132. package/dist/locales/ro.json +1 -0
  133. package/dist/locales/ru.json +1 -0
  134. package/dist/locales/sq.json +1 -0
  135. package/dist/locales/sv.json +1 -0
  136. package/dist/locales/tr.json +1 -0
  137. package/dist/locales/zh.json +1 -0
  138. package/dist/safe-html/index.js.map +1 -1
  139. package/dist/tokens.css +23 -25
  140. package/package.json +2 -52
  141. package/dist/_chunks/image-C6RM5hfF.js +0 -16
  142. package/dist/_chunks/image-C6RM5hfF.js.map +0 -1
  143. package/dist/_chunks/insert-result-njqBthzT.js.map +0 -1
  144. package/dist/_chunks/rich-text-editor-DhGIBd4a.js +0 -921
  145. package/dist/_chunks/rich-text-editor-DhGIBd4a.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"pregnancy-weight-gain-BMRBeA8V.js","sources":["../../src/components/pregnancy-weight-gain/weight-gain.ts","../../src/components/pregnancy-weight-gain/pregnancy-weight-gain.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Pregnancy weight-gain guidance — pure, framework-free, testable. */\n/* */\n/* Recommended ranges follow the US IOM/NAM (2009) guidelines, keyed off */\n/* pre-pregnancy BMI category. Singletons use the published total-gain */\n/* band plus a first-trimester gain (≤13 wk) and a per-week 2nd/3rd- */\n/* trimester rate. Twins use the IOM twin total bands, pro-rated linearly */\n/* for the \"to date\" estimate (the IOM does not publish a twin weekly */\n/* rate). The IOM provides NO range for triplets, and none for */\n/* underweight twins — those cases return `null` so the UI can say so */\n/* rather than invent a number. */\n/* */\n/* Guidance only — not a substitute for clinical judgement. */\n/* ------------------------------------------------------------------ */\n\nimport { type BmiCategory, bmiCategory } from '../bmi-calculator/bmi';\nimport type { Plurality } from '../_shared/obstetrics';\n\nexport interface GainBand {\n /** Lower bound, kg. */\n min: number;\n /** Upper bound, kg. */\n max: number;\n}\n\nexport type GainStatus = 'below' | 'within' | 'above';\n\n/** IOM (2009) total recommended gain for a singleton pregnancy, kg. */\nexport const TOTAL_GAIN_BANDS: Record<BmiCategory, GainBand> = {\n underweight: { min: 12.5, max: 18 },\n normal: { min: 11.5, max: 16 },\n overweight: { min: 7, max: 11.5 },\n obese: { min: 5, max: 9 },\n};\n\n/**\n * IOM (2009) total recommended gain for a TWIN pregnancy, kg. The IOM gives\n * no firm range for underweight twins (insufficient evidence), so that\n * category is intentionally absent and resolves to `null`. There is no IOM\n * range for triplets at all.\n */\nexport const TWIN_TOTAL_GAIN_BANDS: Partial<Record<BmiCategory, GainBand>> = {\n normal: { min: 16.8, max: 24.5 },\n overweight: { min: 14.1, max: 22.7 },\n obese: { min: 11.3, max: 19.1 },\n};\n\n/** Resolve the total-gain band for a category + plurality, or `null` if the IOM gives none. */\nexport function totalGainBand(\n category: BmiCategory,\n plurality: Plurality = 'singleton',\n): GainBand | null {\n if (plurality === 'triplet') return null; // no IOM guideline for triplets\n if (plurality === 'twin') return TWIN_TOTAL_GAIN_BANDS[category] ?? null;\n return TOTAL_GAIN_BANDS[category];\n}\n\n/** IOM (2009) mean rate of gain in the 2nd/3rd trimester, kg per week. */\nexport const RATE_BANDS: Record<BmiCategory, GainBand> = {\n underweight: { min: 0.44, max: 0.58 },\n normal: { min: 0.35, max: 0.5 },\n overweight: { min: 0.23, max: 0.33 },\n obese: { min: 0.17, max: 0.27 },\n};\n\n/** First-trimester total gain band, kg (category-independent in IOM). */\nexport const FIRST_TRIMESTER_GAIN: GainBand = { min: 0.5, max: 2 };\n/** Completed weeks counted as the first trimester for gain purposes. */\nexport const FIRST_TRIMESTER_WEEKS = 13;\n\nexport interface WeightGainInput {\n /** Pre-pregnancy BMI (kg/m²) — drives the category. */\n prePregnancyBmi: number;\n /** Pre-pregnancy weight, kg. */\n prePregnancyWeightKg: number;\n /** Current weight, kg. */\n currentWeightKg: number;\n /** Completed gestational weeks. */\n gestationalWeeks: number;\n /** Number of babies. Defaults to `'singleton'`. */\n plurality?: Plurality;\n}\n\nexport interface WeightGainResult {\n category: BmiCategory;\n /** Number of babies the assessment was computed for. */\n plurality: Plurality;\n /**\n * Recommended TOTAL gain across the whole pregnancy, or `null` when the IOM\n * publishes no range (triplets, and underweight twins).\n */\n totalRange: GainBand | null;\n /** Recommended gain accumulated by the current week, or `null` (see above). */\n recommendedToDate: GainBand | null;\n /** Actual gain so far (current − pre-pregnancy), kg. */\n actualGainKg: number;\n /** Where the actual gain sits against the to-date recommendation, or `null`. */\n status: GainStatus | null;\n /**\n * Estimated 2nd/3rd-trimester average weekly gain to date (kg/week), or `null`\n * before week 14 / when no singleton rate band applies. A snapshot estimate\n * (assumes the mid first-trimester gain), not a between-visit measurement.\n */\n weeklyRate: number | null;\n /** Where the weekly rate sits against the IOM rate band, or `null`. */\n rateStatus: GainStatus | null;\n}\n\n/** Mid-point of the category-independent first-trimester gain band, kg. */\nconst FIRST_TRIMESTER_MIDPOINT =\n (FIRST_TRIMESTER_GAIN.min + FIRST_TRIMESTER_GAIN.max) / 2;\n\n/* Singleton (default) keeps a non-null return; multiples may be null. */\nexport function recommendedGainToDate(\n category: BmiCategory,\n weeks: number,\n plurality?: 'singleton',\n): GainBand;\nexport function recommendedGainToDate(\n category: BmiCategory,\n weeks: number,\n plurality: Plurality,\n): GainBand | null;\n/** Recommended cumulative gain by a given gestational week. */\nexport function recommendedGainToDate(\n category: BmiCategory,\n weeks: number,\n plurality: Plurality = 'singleton',\n): GainBand | null {\n if (plurality !== 'singleton') {\n // Twins/triplets: no IOM weekly rate is published, so pro-rate the total\n // band linearly across the 40 weeks. `null` total ⇒ no guideline.\n const total = totalGainBand(category, plurality);\n if (!total) return null;\n if (weeks <= 0) return { min: 0, max: 0 };\n const frac = Math.min(weeks, 40) / 40;\n return { min: total.min * frac, max: total.max * frac };\n }\n if (weeks <= 0) return { min: 0, max: 0 };\n if (weeks <= FIRST_TRIMESTER_WEEKS) {\n const frac = weeks / FIRST_TRIMESTER_WEEKS;\n return {\n min: FIRST_TRIMESTER_GAIN.min * frac,\n max: FIRST_TRIMESTER_GAIN.max * frac,\n };\n }\n const extraWeeks = Math.min(weeks, 40) - FIRST_TRIMESTER_WEEKS;\n const rate = RATE_BANDS[category];\n return {\n min: FIRST_TRIMESTER_GAIN.min + rate.min * extraWeeks,\n max: FIRST_TRIMESTER_GAIN.max + rate.max * extraWeeks,\n };\n}\n\nexport function assessWeightGain(input: WeightGainInput): WeightGainResult {\n const category = bmiCategory(input.prePregnancyBmi);\n const plurality = input.plurality ?? 'singleton';\n const totalRange = totalGainBand(category, plurality);\n const recommendedToDate = totalRange\n ? recommendedGainToDate(category, input.gestationalWeeks, plurality)\n : null;\n const actualGainKg = input.currentWeightKg - input.prePregnancyWeightKg;\n const status: GainStatus | null = recommendedToDate\n ? actualGainKg < recommendedToDate.min\n ? 'below'\n : actualGainKg > recommendedToDate.max\n ? 'above'\n : 'within'\n : null;\n\n // 2nd/3rd-trimester average weekly rate to date — singleton only (the IOM\n // publishes weekly rate bands only for singletons), and only after week 13.\n let weeklyRate: number | null = null;\n let rateStatus: GainStatus | null = null;\n if (\n plurality === 'singleton' &&\n input.gestationalWeeks > FIRST_TRIMESTER_WEEKS\n ) {\n const weeks = input.gestationalWeeks - FIRST_TRIMESTER_WEEKS;\n weeklyRate = (actualGainKg - FIRST_TRIMESTER_MIDPOINT) / weeks;\n const band = RATE_BANDS[category];\n rateStatus =\n weeklyRate < band.min\n ? 'below'\n : weeklyRate > band.max\n ? 'above'\n : 'within';\n }\n\n return {\n category,\n plurality,\n totalRange,\n recommendedToDate,\n actualGainKg,\n status,\n weeklyRate,\n rateStatus,\n };\n}\n","/* ------------------------------------------------------------------ */\n/* PregnancyWeightGain — actual vs IOM-recommended gestational weight */\n/* gain, keyed off pre-pregnancy BMI. */\n/* */\n/* Composes the BMI maths from bmi-calculator and the IOM bands in */\n/* `./weight-gain` (both pure, separately tested). Metric only. */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { RadioGroup, Radio } from '../radio-group';\nimport { FormField } from '../form-field';\nimport { NumberInput } from '../number-input';\nimport { Card } from '../card';\nimport { Badge } from '../badge';\nimport {\n InsertButton,\n type InsertPayload,\n type InsertVariant,\n type InsertMode,\n} from '../_shared/insert-result';\nimport { computeBmi } from '../bmi-calculator/bmi';\nimport type { Plurality } from '../_shared/obstetrics';\nimport {\n type WeightGainResult,\n type GainStatus,\n type GainBand,\n assessWeightGain,\n} from './weight-gain';\n\nconst rootVariants = cva('ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]', {\n variants: {\n width: { full: 'ds:w-full', auto: 'ds:inline-flex' },\n },\n defaultVariants: { width: 'full' },\n});\n\nconst STATUS_BADGE: Record<GainStatus, 'info' | 'success' | 'warning'> = {\n below: 'warning',\n within: 'success',\n above: 'warning',\n};\n\n/**\n * DS token NAME backing each gain-status chip — the solid-fill companion of the\n * on-screen `STATUS_BADGE` variant (Badge `warning` fills `--warning`,\n * `success` fills `--success`). Passed as the card's `highlightToken` so the\n * inserted PNG chip matches the on-screen status badge; `InsertButton` resolves\n * the name to a concrete colour at raster time.\n */\nconst STATUS_HIGHLIGHT_TOKEN: Record<GainStatus, string> = {\n below: '--warning-readable',\n within: '--success',\n above: '--warning-readable',\n};\n\n/**\n * Gain-status chip token. The `below` / `above` statuses resolve to\n * `--warning-readable`, which the result card's colour probe resolves in the live\n * themed DOM: orange-600 in light, non-accessible mode (where bare `--warning`\n * yellow only reaches ~3.2:1 on the white card) and `--warning` elsewhere, whose\n * deepened ramp already clears contrast. Resolving the token NAME at the card\n * means the choice tracks the live theme regardless of where the theme class\n * lives. The `within` (`--success`) status is unchanged.\n */\nfunction statusHighlightToken(status: GainStatus): string {\n return STATUS_HIGHLIGHT_TOKEN[status];\n}\n\nexport interface PregnancyWeightGainProps extends VariantProps<\n typeof rootVariants\n> {\n /** Initial number of babies. Defaults to `'singleton'`. */\n defaultPlurality?: Plurality;\n /** Pre-fill the height (cm) — e.g. shared from a BMI measurement. */\n defaultHeightCm?: number;\n /** Pre-fill the pre-pregnancy weight (kg) from the patient record. */\n defaultPrePregnancyKg?: number;\n /** Fires whenever a result can be computed (and `null` when it can't). */\n onResultChange?: (result: WeightGainResult | null) => void;\n /** When provided, shows the result-action buttons that emit / copy the result. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Verb the result-action buttons perform. Defaults to `'insert'` (editor\n * extension). Pass `'copy'` from an app shell to copy the result to the\n * clipboard instead.\n */\n insertVariant?: InsertVariant;\n /** `copy` variant only — fired after a successful clipboard write. */\n onCopy?: (mode: InsertMode) => void;\n /** `copy` variant only — fired if the clipboard write can't proceed. */\n onError?: (error: unknown) => void;\n /**\n * Brand wordmark printed in the inserted/copied result-card footer.\n * Omitted → no brand line (and no footer hairline); a string → that custom\n * brand; `false` → no brand line. Brand is opt-in.\n */\n insertBrand?: string | false;\n /** Opaque instance id, emitted as `data-component-id`. */\n id?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\nexport const PregnancyWeightGain = forwardRef<\n HTMLDivElement,\n PregnancyWeightGainProps\n>(\n (\n {\n defaultPlurality = 'singleton',\n defaultHeightCm,\n defaultPrePregnancyKg,\n onResultChange,\n onInsert,\n insertVariant = 'insert',\n onCopy,\n onError,\n insertBrand,\n id,\n width,\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n\n const [heightCm, setHeightCm] = useState<number | null>(\n defaultHeightCm ?? null,\n );\n const [prePregnancyKg, setPrePregnancyKg] = useState<number | null>(\n defaultPrePregnancyKg ?? null,\n );\n const [currentKg, setCurrentKg] = useState<number | null>(null);\n const [weeks, setWeeks] = useState<number | null>(null);\n const [plurality, setPlurality] = useState<Plurality>(defaultPlurality);\n\n const prePregnancyBmi = computeBmi(prePregnancyKg, heightCm);\n\n const result = useMemo<WeightGainResult | null>(() => {\n if (\n prePregnancyBmi === null ||\n prePregnancyKg === null ||\n currentKg === null ||\n weeks === null\n ) {\n return null;\n }\n return assessWeightGain({\n prePregnancyBmi,\n prePregnancyWeightKg: prePregnancyKg,\n currentWeightKg: currentKg,\n gestationalWeeks: weeks,\n plurality,\n });\n }, [prePregnancyBmi, prePregnancyKg, currentKg, weeks, plurality]);\n\n const kg = useMemo(\n () =>\n new Intl.NumberFormat(i18n.language, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }),\n [i18n.language],\n );\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n const unit = t('pregnancyWeightGain.units.kg');\n const band = (b: GainBand): string =>\n `${kg.format(b.min)} – ${kg.format(b.max)} ${unit}`;\n\n return (\n <div\n ref={ref}\n data-component=\"pregnancy-weight-gain\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <FormField\n label={`${t('pregnancyWeightGain.height')} (${t('pregnancyWeightGain.units.cm')})`}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.5}\n value={heightCm}\n onChange={setHeightCm}\n />\n </FormField>\n <FormField\n label={`${t('pregnancyWeightGain.prePregnancyWeight')} (${unit})`}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.1}\n value={prePregnancyKg}\n onChange={setPrePregnancyKg}\n />\n </FormField>\n <FormField\n label={`${t('pregnancyWeightGain.currentWeight')} (${unit})`}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.1}\n value={currentKg}\n onChange={setCurrentKg}\n />\n </FormField>\n <FormField label={t('pregnancyWeightGain.gestationalWeeks')}>\n <NumberInput\n mode=\"integer\"\n min={0}\n max={42}\n value={weeks}\n onChange={setWeeks}\n />\n </FormField>\n </div>\n\n <RadioGroup\n label={t('pregnancyWeightGain.plurality.label')}\n variant=\"horizontal\"\n value={plurality}\n onValueChange={(next) => setPlurality(next as Plurality)}\n >\n <Radio\n label={t('pregnancyWeightGain.plurality.singleton')}\n value=\"singleton\"\n />\n <Radio label={t('pregnancyWeightGain.plurality.twin')} value=\"twin\" />\n <Radio\n label={t('pregnancyWeightGain.plurality.triplet')}\n value=\"triplet\"\n />\n </RadioGroup>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result\n ? `${t('pregnancyWeightGain.actualGain')}: ${kg.format(\n result.actualGainKg,\n )} ${unit}.${\n result.status\n ? ` ${t(`pregnancyWeightGain.status.${result.status}`)}.`\n : ` ${t('pregnancyWeightGain.noGuideline')}`\n }`\n : ''}\n </p>\n\n {result ? (\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <dl className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.prePregnancyBmi')}\n </dt>\n <dd className=\"type-body ds:text-foreground\">\n {kg.format(prePregnancyBmi ?? 0)} ·{' '}\n {t(`pregnancyWeightGain.category.${result.category}`)}\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.actualGain')}\n </dt>\n <dd className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-xs)]\">\n <span className=\"type-metric ds:text-foreground\">\n {kg.format(result.actualGainKg)} {unit}\n </span>\n {result.status ? (\n <Badge variant={STATUS_BADGE[result.status]} size=\"lg\">\n {t(`pregnancyWeightGain.status.${result.status}`)}\n </Badge>\n ) : null}\n </dd>\n </div>\n {result.recommendedToDate && result.totalRange ? (\n <>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.recommendedToDate')}\n </dt>\n <dd className=\"type-body ds:text-foreground\">\n {band(result.recommendedToDate)}\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.recommendedTotal')}\n </dt>\n <dd className=\"type-body ds:text-foreground\">\n {band(result.totalRange)}\n </dd>\n </div>\n </>\n ) : null}\n </dl>\n {result.totalRange ? null : (\n <p className=\"type-body ds:text-foreground\">\n {t('pregnancyWeightGain.noGuideline')}\n </p>\n )}\n {result.weeklyRate !== null && result.rateStatus ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.weeklyRate')}\n </span>\n <span className=\"type-body ds:text-foreground\">\n {t('pregnancyWeightGain.perWeek', {\n value: kg.format(result.weeklyRate),\n })}\n </span>\n <Badge variant={STATUS_BADGE[result.rateStatus]}>\n {t(`pregnancyWeightGain.status.${result.rateStatus}`)}\n </Badge>\n </div>\n ) : null}\n <p className=\"type-body-sm ds:text-muted-foreground\">\n {t(\n plurality === 'singleton'\n ? 'pregnancyWeightGain.disclaimer'\n : 'pregnancyWeightGain.disclaimerMultiple',\n )}\n </p>\n {result.status &&\n result.recommendedToDate &&\n result.totalRange &&\n (insertVariant === 'copy' || onInsert) ? (\n <InsertButton\n onInsert={onInsert}\n variant={insertVariant}\n onCopy={onCopy}\n onError={onError}\n card={{\n title: t('insert.title.weightGain'),\n icon: 'baby',\n highlight: t(`pregnancyWeightGain.status.${result.status}`),\n // Chip shares the gain-status semantic token so the\n // inserted PNG chip matches the on-screen status badge —\n // with the contrast-safe orange override for the\n // `--warning` below/above statuses.\n highlightToken: statusHighlightToken(result.status),\n brand: insertBrand,\n fields: [\n {\n // Actual gain is the headline trend → trending-up glyph.\n icon: 'trending-up',\n label: t('pregnancyWeightGain.actualGain'),\n value: `${kg.format(result.actualGainKg)} ${unit}`,\n },\n {\n // Recommended weight band → scale glyph.\n icon: 'scale',\n label: t('pregnancyWeightGain.recommendedToDate'),\n value: band(result.recommendedToDate),\n },\n {\n // Recommended total weight band → scale glyph.\n icon: 'scale',\n label: t('pregnancyWeightGain.recommendedTotal'),\n value: band(result.totalRange),\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('pregnancyWeightGain.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nPregnancyWeightGain.displayName = 'PregnancyWeightGain';\n"],"names":["TOTAL_GAIN_BANDS","TWIN_TOTAL_GAIN_BANDS","totalGainBand","category","plurality","RATE_BANDS","FIRST_TRIMESTER_GAIN","FIRST_TRIMESTER_WEEKS","FIRST_TRIMESTER_MIDPOINT","recommendedGainToDate","weeks","total","frac","extraWeeks","rate","assessWeightGain","input","bmiCategory","totalRange","recommendedToDate","actualGainKg","status","weeklyRate","rateStatus","band","rootVariants","cva","STATUS_BADGE","STATUS_HIGHLIGHT_TOKEN","statusHighlightToken","PregnancyWeightGain","forwardRef","defaultPlurality","defaultHeightCm","defaultPrePregnancyKg","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","t","i18n","useTranslation","heightCm","setHeightCm","useState","prePregnancyKg","setPrePregnancyKg","currentKg","setCurrentKg","setWeeks","setPlurality","prePregnancyBmi","computeBmi","result","useMemo","kg","useEffect","unit","b","jsxs","jsx","FormField","NumberInput","RadioGroup","next","Radio","Card","Badge","Fragment","InsertButton"],"mappings":"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"}
1
+ {"version":3,"file":"pregnancy-weight-gain-DI7X-0JX.js","sources":["../../src/components/pregnancy-weight-gain/weight-gain.ts","../../src/components/pregnancy-weight-gain/pregnancy-weight-gain.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Pregnancy weight-gain guidance — pure, framework-free, testable. */\n/* */\n/* Recommended ranges follow the US IOM/NAM (2009) guidelines, keyed off */\n/* pre-pregnancy BMI category. Singletons use the published total-gain */\n/* band plus a first-trimester gain (≤13 wk) and a per-week 2nd/3rd- */\n/* trimester rate. Twins use the IOM twin total bands, pro-rated linearly */\n/* for the \"to date\" estimate (the IOM does not publish a twin weekly */\n/* rate). The IOM provides NO range for triplets, and none for */\n/* underweight twins — those cases return `null` so the UI can say so */\n/* rather than invent a number. */\n/* */\n/* Guidance only — not a substitute for clinical judgement. */\n/* ------------------------------------------------------------------ */\n\nimport { type BmiCategory, bmiCategory } from '../bmi-calculator/bmi';\nimport type { Plurality } from '../_shared/obstetrics';\n\nexport interface GainBand {\n /** Lower bound, kg. */\n min: number;\n /** Upper bound, kg. */\n max: number;\n}\n\nexport type GainStatus = 'below' | 'within' | 'above';\n\n/** IOM (2009) total recommended gain for a singleton pregnancy, kg. */\nexport const TOTAL_GAIN_BANDS: Record<BmiCategory, GainBand> = {\n underweight: { min: 12.5, max: 18 },\n normal: { min: 11.5, max: 16 },\n overweight: { min: 7, max: 11.5 },\n obese: { min: 5, max: 9 },\n};\n\n/**\n * IOM (2009) total recommended gain for a TWIN pregnancy, kg. The IOM gives\n * no firm range for underweight twins (insufficient evidence), so that\n * category is intentionally absent and resolves to `null`. There is no IOM\n * range for triplets at all.\n */\nexport const TWIN_TOTAL_GAIN_BANDS: Partial<Record<BmiCategory, GainBand>> = {\n normal: { min: 16.8, max: 24.5 },\n overweight: { min: 14.1, max: 22.7 },\n obese: { min: 11.3, max: 19.1 },\n};\n\n/** Resolve the total-gain band for a category + plurality, or `null` if the IOM gives none. */\nexport function totalGainBand(\n category: BmiCategory,\n plurality: Plurality = 'singleton',\n): GainBand | null {\n if (plurality === 'triplet') return null; // no IOM guideline for triplets\n if (plurality === 'twin') return TWIN_TOTAL_GAIN_BANDS[category] ?? null;\n return TOTAL_GAIN_BANDS[category];\n}\n\n/** IOM (2009) mean rate of gain in the 2nd/3rd trimester, kg per week. */\nexport const RATE_BANDS: Record<BmiCategory, GainBand> = {\n underweight: { min: 0.44, max: 0.58 },\n normal: { min: 0.35, max: 0.5 },\n overweight: { min: 0.23, max: 0.33 },\n obese: { min: 0.17, max: 0.27 },\n};\n\n/** First-trimester total gain band, kg (category-independent in IOM). */\nexport const FIRST_TRIMESTER_GAIN: GainBand = { min: 0.5, max: 2 };\n/** Completed weeks counted as the first trimester for gain purposes. */\nexport const FIRST_TRIMESTER_WEEKS = 13;\n\nexport interface WeightGainInput {\n /** Pre-pregnancy BMI (kg/m²) — drives the category. */\n prePregnancyBmi: number;\n /** Pre-pregnancy weight, kg. */\n prePregnancyWeightKg: number;\n /** Current weight, kg. */\n currentWeightKg: number;\n /** Completed gestational weeks. */\n gestationalWeeks: number;\n /** Number of babies. Defaults to `'singleton'`. */\n plurality?: Plurality;\n}\n\nexport interface WeightGainResult {\n category: BmiCategory;\n /** Number of babies the assessment was computed for. */\n plurality: Plurality;\n /**\n * Recommended TOTAL gain across the whole pregnancy, or `null` when the IOM\n * publishes no range (triplets, and underweight twins).\n */\n totalRange: GainBand | null;\n /** Recommended gain accumulated by the current week, or `null` (see above). */\n recommendedToDate: GainBand | null;\n /** Actual gain so far (current − pre-pregnancy), kg. */\n actualGainKg: number;\n /** Where the actual gain sits against the to-date recommendation, or `null`. */\n status: GainStatus | null;\n /**\n * Estimated 2nd/3rd-trimester average weekly gain to date (kg/week), or `null`\n * before week 14 / when no singleton rate band applies. A snapshot estimate\n * (assumes the mid first-trimester gain), not a between-visit measurement.\n */\n weeklyRate: number | null;\n /** Where the weekly rate sits against the IOM rate band, or `null`. */\n rateStatus: GainStatus | null;\n}\n\n/** Mid-point of the category-independent first-trimester gain band, kg. */\nconst FIRST_TRIMESTER_MIDPOINT =\n (FIRST_TRIMESTER_GAIN.min + FIRST_TRIMESTER_GAIN.max) / 2;\n\n/* Singleton (default) keeps a non-null return; multiples may be null. */\nexport function recommendedGainToDate(\n category: BmiCategory,\n weeks: number,\n plurality?: 'singleton',\n): GainBand;\nexport function recommendedGainToDate(\n category: BmiCategory,\n weeks: number,\n plurality: Plurality,\n): GainBand | null;\n/** Recommended cumulative gain by a given gestational week. */\nexport function recommendedGainToDate(\n category: BmiCategory,\n weeks: number,\n plurality: Plurality = 'singleton',\n): GainBand | null {\n if (plurality !== 'singleton') {\n // Twins/triplets: no IOM weekly rate is published, so pro-rate the total\n // band linearly across the 40 weeks. `null` total ⇒ no guideline.\n const total = totalGainBand(category, plurality);\n if (!total) return null;\n if (weeks <= 0) return { min: 0, max: 0 };\n const frac = Math.min(weeks, 40) / 40;\n return { min: total.min * frac, max: total.max * frac };\n }\n if (weeks <= 0) return { min: 0, max: 0 };\n if (weeks <= FIRST_TRIMESTER_WEEKS) {\n const frac = weeks / FIRST_TRIMESTER_WEEKS;\n return {\n min: FIRST_TRIMESTER_GAIN.min * frac,\n max: FIRST_TRIMESTER_GAIN.max * frac,\n };\n }\n const extraWeeks = Math.min(weeks, 40) - FIRST_TRIMESTER_WEEKS;\n const rate = RATE_BANDS[category];\n return {\n min: FIRST_TRIMESTER_GAIN.min + rate.min * extraWeeks,\n max: FIRST_TRIMESTER_GAIN.max + rate.max * extraWeeks,\n };\n}\n\nexport function assessWeightGain(input: WeightGainInput): WeightGainResult {\n const category = bmiCategory(input.prePregnancyBmi);\n const plurality = input.plurality ?? 'singleton';\n const totalRange = totalGainBand(category, plurality);\n const recommendedToDate = totalRange\n ? recommendedGainToDate(category, input.gestationalWeeks, plurality)\n : null;\n const actualGainKg = input.currentWeightKg - input.prePregnancyWeightKg;\n const status: GainStatus | null = recommendedToDate\n ? actualGainKg < recommendedToDate.min\n ? 'below'\n : actualGainKg > recommendedToDate.max\n ? 'above'\n : 'within'\n : null;\n\n // 2nd/3rd-trimester average weekly rate to date — singleton only (the IOM\n // publishes weekly rate bands only for singletons), and only after week 13.\n let weeklyRate: number | null = null;\n let rateStatus: GainStatus | null = null;\n if (\n plurality === 'singleton' &&\n input.gestationalWeeks > FIRST_TRIMESTER_WEEKS\n ) {\n const weeks = input.gestationalWeeks - FIRST_TRIMESTER_WEEKS;\n weeklyRate = (actualGainKg - FIRST_TRIMESTER_MIDPOINT) / weeks;\n const band = RATE_BANDS[category];\n rateStatus =\n weeklyRate < band.min\n ? 'below'\n : weeklyRate > band.max\n ? 'above'\n : 'within';\n }\n\n return {\n category,\n plurality,\n totalRange,\n recommendedToDate,\n actualGainKg,\n status,\n weeklyRate,\n rateStatus,\n };\n}\n","/* ------------------------------------------------------------------ */\n/* PregnancyWeightGain — actual vs IOM-recommended gestational weight */\n/* gain, keyed off pre-pregnancy BMI. */\n/* */\n/* Composes the BMI maths from bmi-calculator and the IOM bands in */\n/* `./weight-gain` (both pure, separately tested). Metric only. */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { RadioGroup, Radio } from '../radio-group';\nimport { FormField } from '../form-field';\nimport { NumberInput } from '../number-input';\nimport { Card } from '../card';\nimport { Badge } from '../badge';\nimport {\n InsertButton,\n type InsertPayload,\n type InsertVariant,\n type InsertMode,\n} from '../_shared/insert-result';\nimport { computeBmi } from '../bmi-calculator/bmi';\nimport type { Plurality } from '../_shared/obstetrics';\nimport {\n type WeightGainResult,\n type GainStatus,\n type GainBand,\n assessWeightGain,\n} from './weight-gain';\n\nconst rootVariants = cva('ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]', {\n variants: {\n width: { full: 'ds:w-full', auto: 'ds:inline-flex' },\n },\n defaultVariants: { width: 'full' },\n});\n\nconst STATUS_BADGE: Record<GainStatus, 'info' | 'success' | 'warning'> = {\n below: 'warning',\n within: 'success',\n above: 'warning',\n};\n\n/**\n * DS token NAME backing each gain-status chip — the solid-fill companion of the\n * on-screen `STATUS_BADGE` variant (Badge `warning` fills `--warning`,\n * `success` fills `--success`). Passed as the card's `highlightToken` so the\n * inserted PNG chip matches the on-screen status badge; `InsertButton` resolves\n * the name to a concrete colour at raster time.\n */\nconst STATUS_HIGHLIGHT_TOKEN: Record<GainStatus, string> = {\n below: '--warning-readable',\n within: '--success',\n above: '--warning-readable',\n};\n\n/**\n * Gain-status chip token. The `below` / `above` statuses resolve to\n * `--warning-readable`, which the result card's colour probe resolves in the live\n * themed DOM: orange-600 in light, non-accessible mode (where bare `--warning`\n * yellow only reaches ~3.2:1 on the white card) and `--warning` elsewhere, whose\n * deepened ramp already clears contrast. Resolving the token NAME at the card\n * means the choice tracks the live theme regardless of where the theme class\n * lives. The `within` (`--success`) status is unchanged.\n */\nfunction statusHighlightToken(status: GainStatus): string {\n return STATUS_HIGHLIGHT_TOKEN[status];\n}\n\nexport interface PregnancyWeightGainProps extends VariantProps<\n typeof rootVariants\n> {\n /** Initial number of babies. Defaults to `'singleton'`. */\n defaultPlurality?: Plurality;\n /** Pre-fill the height (cm) — e.g. shared from a BMI measurement. */\n defaultHeightCm?: number;\n /** Pre-fill the pre-pregnancy weight (kg) from the patient record. */\n defaultPrePregnancyKg?: number;\n /** Fires whenever a result can be computed (and `null` when it can't). */\n onResultChange?: (result: WeightGainResult | null) => void;\n /** When provided, shows the result-action buttons that emit / copy the result. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Verb the result-action buttons perform. Defaults to `'insert'` (editor\n * extension). Pass `'copy'` from an app shell to copy the result to the\n * clipboard instead.\n */\n insertVariant?: InsertVariant;\n /** `copy` variant only — fired after a successful clipboard write. */\n onCopy?: (mode: InsertMode) => void;\n /** `copy` variant only — fired if the clipboard write can't proceed. */\n onError?: (error: unknown) => void;\n /**\n * Brand wordmark printed in the inserted/copied result-card footer.\n * Omitted → no brand line (and no footer hairline); a string → that custom\n * brand; `false` → no brand line. Brand is opt-in.\n */\n insertBrand?: string | false;\n /** Opaque instance id, emitted as `data-component-id`. */\n id?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\nexport const PregnancyWeightGain = forwardRef<\n HTMLDivElement,\n PregnancyWeightGainProps\n>(\n (\n {\n defaultPlurality = 'singleton',\n defaultHeightCm,\n defaultPrePregnancyKg,\n onResultChange,\n onInsert,\n insertVariant = 'insert',\n onCopy,\n onError,\n insertBrand,\n id,\n width,\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n\n const [heightCm, setHeightCm] = useState<number | null>(\n defaultHeightCm ?? null,\n );\n const [prePregnancyKg, setPrePregnancyKg] = useState<number | null>(\n defaultPrePregnancyKg ?? null,\n );\n const [currentKg, setCurrentKg] = useState<number | null>(null);\n const [weeks, setWeeks] = useState<number | null>(null);\n const [plurality, setPlurality] = useState<Plurality>(defaultPlurality);\n\n const prePregnancyBmi = computeBmi(prePregnancyKg, heightCm);\n\n const result = useMemo<WeightGainResult | null>(() => {\n if (\n prePregnancyBmi === null ||\n prePregnancyKg === null ||\n currentKg === null ||\n weeks === null\n ) {\n return null;\n }\n return assessWeightGain({\n prePregnancyBmi,\n prePregnancyWeightKg: prePregnancyKg,\n currentWeightKg: currentKg,\n gestationalWeeks: weeks,\n plurality,\n });\n }, [prePregnancyBmi, prePregnancyKg, currentKg, weeks, plurality]);\n\n const kg = useMemo(\n () =>\n new Intl.NumberFormat(i18n.language, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }),\n [i18n.language],\n );\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n const unit = t('pregnancyWeightGain.units.kg');\n const band = (b: GainBand): string =>\n `${kg.format(b.min)} – ${kg.format(b.max)} ${unit}`;\n\n return (\n <div\n ref={ref}\n data-component=\"pregnancy-weight-gain\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <FormField\n label={`${t('pregnancyWeightGain.height')} (${t('pregnancyWeightGain.units.cm')})`}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.5}\n value={heightCm}\n onChange={setHeightCm}\n />\n </FormField>\n <FormField\n label={`${t('pregnancyWeightGain.prePregnancyWeight')} (${unit})`}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.1}\n value={prePregnancyKg}\n onChange={setPrePregnancyKg}\n />\n </FormField>\n <FormField\n label={`${t('pregnancyWeightGain.currentWeight')} (${unit})`}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.1}\n value={currentKg}\n onChange={setCurrentKg}\n />\n </FormField>\n <FormField label={t('pregnancyWeightGain.gestationalWeeks')}>\n <NumberInput\n mode=\"integer\"\n min={0}\n max={42}\n value={weeks}\n onChange={setWeeks}\n />\n </FormField>\n </div>\n\n <RadioGroup\n label={t('pregnancyWeightGain.plurality.label')}\n variant=\"horizontal\"\n value={plurality}\n onValueChange={(next) => setPlurality(next as Plurality)}\n >\n <Radio\n label={t('pregnancyWeightGain.plurality.singleton')}\n value=\"singleton\"\n />\n <Radio label={t('pregnancyWeightGain.plurality.twin')} value=\"twin\" />\n <Radio\n label={t('pregnancyWeightGain.plurality.triplet')}\n value=\"triplet\"\n />\n </RadioGroup>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result\n ? `${t('pregnancyWeightGain.actualGain')}: ${kg.format(\n result.actualGainKg,\n )} ${unit}.${\n result.status\n ? ` ${t(`pregnancyWeightGain.status.${result.status}`)}.`\n : ` ${t('pregnancyWeightGain.noGuideline')}`\n }`\n : ''}\n </p>\n\n {result ? (\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <dl className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.prePregnancyBmi')}\n </dt>\n <dd className=\"type-body ds:text-foreground\">\n {kg.format(prePregnancyBmi ?? 0)} ·{' '}\n {t(`pregnancyWeightGain.category.${result.category}`)}\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.actualGain')}\n </dt>\n <dd className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-xs)]\">\n <span className=\"type-metric ds:text-foreground\">\n {kg.format(result.actualGainKg)} {unit}\n </span>\n {result.status ? (\n <Badge variant={STATUS_BADGE[result.status]} size=\"lg\">\n {t(`pregnancyWeightGain.status.${result.status}`)}\n </Badge>\n ) : null}\n </dd>\n </div>\n {result.recommendedToDate && result.totalRange ? (\n <>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.recommendedToDate')}\n </dt>\n <dd className=\"type-body ds:text-foreground\">\n {band(result.recommendedToDate)}\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.recommendedTotal')}\n </dt>\n <dd className=\"type-body ds:text-foreground\">\n {band(result.totalRange)}\n </dd>\n </div>\n </>\n ) : null}\n </dl>\n {result.totalRange ? null : (\n <p className=\"type-body ds:text-foreground\">\n {t('pregnancyWeightGain.noGuideline')}\n </p>\n )}\n {result.weeklyRate !== null && result.rateStatus ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('pregnancyWeightGain.weeklyRate')}\n </span>\n <span className=\"type-body ds:text-foreground\">\n {t('pregnancyWeightGain.perWeek', {\n value: kg.format(result.weeklyRate),\n })}\n </span>\n <Badge variant={STATUS_BADGE[result.rateStatus]}>\n {t(`pregnancyWeightGain.status.${result.rateStatus}`)}\n </Badge>\n </div>\n ) : null}\n <p className=\"type-body-sm ds:text-muted-foreground\">\n {t(\n plurality === 'singleton'\n ? 'pregnancyWeightGain.disclaimer'\n : 'pregnancyWeightGain.disclaimerMultiple',\n )}\n </p>\n {result.status &&\n result.recommendedToDate &&\n result.totalRange &&\n (insertVariant === 'copy' || onInsert) ? (\n <InsertButton\n onInsert={onInsert}\n variant={insertVariant}\n onCopy={onCopy}\n onError={onError}\n card={{\n title: t('insert.title.weightGain'),\n icon: 'baby',\n highlight: t(`pregnancyWeightGain.status.${result.status}`),\n // Chip shares the gain-status semantic token so the\n // inserted PNG chip matches the on-screen status badge —\n // with the contrast-safe orange override for the\n // `--warning` below/above statuses.\n highlightToken: statusHighlightToken(result.status),\n brand: insertBrand,\n fields: [\n {\n // Actual gain is the headline trend → trending-up glyph.\n icon: 'trending-up',\n label: t('pregnancyWeightGain.actualGain'),\n value: `${kg.format(result.actualGainKg)} ${unit}`,\n },\n {\n // Recommended weight band → scale glyph.\n icon: 'scale',\n label: t('pregnancyWeightGain.recommendedToDate'),\n value: band(result.recommendedToDate),\n },\n {\n // Recommended total weight band → scale glyph.\n icon: 'scale',\n label: t('pregnancyWeightGain.recommendedTotal'),\n value: band(result.totalRange),\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('pregnancyWeightGain.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nPregnancyWeightGain.displayName = 'PregnancyWeightGain';\n"],"names":["TOTAL_GAIN_BANDS","TWIN_TOTAL_GAIN_BANDS","totalGainBand","category","plurality","RATE_BANDS","FIRST_TRIMESTER_GAIN","FIRST_TRIMESTER_WEEKS","FIRST_TRIMESTER_MIDPOINT","recommendedGainToDate","weeks","total","frac","extraWeeks","rate","assessWeightGain","input","bmiCategory","totalRange","recommendedToDate","actualGainKg","status","weeklyRate","rateStatus","band","rootVariants","cva","STATUS_BADGE","STATUS_HIGHLIGHT_TOKEN","statusHighlightToken","PregnancyWeightGain","forwardRef","defaultPlurality","defaultHeightCm","defaultPrePregnancyKg","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","t","i18n","useTranslation","heightCm","setHeightCm","useState","prePregnancyKg","setPrePregnancyKg","currentKg","setCurrentKg","setWeeks","setPlurality","prePregnancyBmi","computeBmi","result","useMemo","kg","useEffect","unit","b","jsxs","jsx","FormField","NumberInput","RadioGroup","next","Radio","Card","Badge","Fragment","InsertButton"],"mappings":"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"}
@@ -0,0 +1,334 @@
1
+ import { jsx as S } from "react/jsx-runtime";
2
+ import { forwardRef as Y, useId as K, useMemo as N, useRef as l, useState as ee, useEffect as p, useImperativeHandle as te, useCallback as re } from "react";
3
+ import { c as A } from "./index-D2ZczOXr.js";
4
+ import { useTranslation as ne } from "react-i18next";
5
+ import { sanitiseHtml as oe } from "../safe-html/index.js";
6
+ import { u as ie } from "./registry-nPAVE19X.js";
7
+ const se = {
8
+ id: "rich-text-editor",
9
+ capabilities: ["edit_inline"],
10
+ state: {
11
+ html: {
12
+ type: "string",
13
+ description: "Sanitised HTML representation of the editor content.",
14
+ read: (t) => t.getHTML()
15
+ },
16
+ markdown: {
17
+ type: "string",
18
+ description: "Markdown representation of the editor content. The Redactor engine has no native Markdown serialiser, so this is always empty — read `html` instead.",
19
+ read: (t) => t.getMarkdown()
20
+ }
21
+ },
22
+ actions: {
23
+ set_content: {
24
+ safety: "destructive",
25
+ argsType: "{ html: string }",
26
+ description: "Replace the editor content. Loses unsaved input.",
27
+ invoke: (t, n) => {
28
+ t.setContent(n.html);
29
+ }
30
+ },
31
+ clear: {
32
+ safety: "destructive",
33
+ description: "Empty the editor. Loses unsaved input.",
34
+ invoke: (t) => {
35
+ t.clear();
36
+ }
37
+ },
38
+ focus: {
39
+ safety: "read",
40
+ description: "Move keyboard focus to the editor surface.",
41
+ invoke: (t) => {
42
+ t.focus();
43
+ }
44
+ }
45
+ },
46
+ domHooks: {
47
+ root: { attr: "data-component", value: "rich-text-editor" },
48
+ instanceId: {
49
+ attr: "data-component-id",
50
+ sourceProp: "id",
51
+ description: "Sourced from the id prop."
52
+ }
53
+ }
54
+ };
55
+ function u(t) {
56
+ return oe(t, "rich-text");
57
+ }
58
+ function z(t = "") {
59
+ const n = String(t).trim();
60
+ if (n === "" || n === "<p><br></p>" || n === "<p>&nbsp;</p>")
61
+ return "";
62
+ const i = n.replace(/<br\s*\/?>/gi, "").replace(/&nbsp;/gi, "").replace(/<[^>]+>/g, "").trim();
63
+ return i === "" && /<img\b[^>]*>/i.test(n) ? n : i === "" ? "" : n;
64
+ }
65
+ function ae(t) {
66
+ return t.replace(
67
+ /<img(?![^>]*\sloading=)([^>]*)>/gi,
68
+ '<img loading="lazy"$1>'
69
+ );
70
+ }
71
+ function ce(t) {
72
+ if (t) return t;
73
+ if (typeof window > "u") return null;
74
+ const n = window;
75
+ return n.$R ?? n.Redactor ?? null;
76
+ }
77
+ const de = {
78
+ minimal: ["bold", "italic", "link"],
79
+ standard: ["format", "bold", "italic", "link", "lists"],
80
+ full: [
81
+ "format",
82
+ "bold",
83
+ "italic",
84
+ "deleted",
85
+ "link",
86
+ "lists",
87
+ "image",
88
+ "table",
89
+ "line"
90
+ ]
91
+ }, le = {
92
+ sm: "8rem",
93
+ md: "16rem",
94
+ lg: "24rem"
95
+ }, ue = {
96
+ zh: "zh_cn",
97
+ pt: "pt_br"
98
+ };
99
+ function fe(t) {
100
+ const n = t.split("-")[0];
101
+ return ue[n] ?? n ?? "en";
102
+ }
103
+ const me = A(
104
+ "redactor-theme-alfadocs ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]"
105
+ ), pe = A(
106
+ [
107
+ "ds:block ds:[inline-size:100%]",
108
+ "ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--border)]",
109
+ "ds:bg-[var(--background)] ds:text-[var(--foreground)]",
110
+ "ds:font-[family-name:var(--font-sans)] ds:text-[length:var(--font-size-base)]",
111
+ "ds:p-[var(--spacing-md)] ds:[min-block-size:10rem]",
112
+ "ds:focus-visible:outline-[length:var(--focus-ring-width)]",
113
+ "ds:focus-visible:outline-solid",
114
+ "ds:focus-visible:outline-[var(--ring)]",
115
+ "ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]",
116
+ "ds:forced-colors:focus-visible:outline-[CanvasText]"
117
+ ].join(" ")
118
+ ), ge = Y(
119
+ ({
120
+ id: t,
121
+ defaultValue: n,
122
+ value: i,
123
+ onChange: R,
124
+ toolbar: $ = "standard",
125
+ placeholder: E,
126
+ readOnly: g = !1,
127
+ ariaLabel: O,
128
+ minHeight: H,
129
+ className: B,
130
+ redactor: P,
131
+ options: h,
132
+ plugins: V,
133
+ lang: j,
134
+ imageUploadUrl: L,
135
+ onKeyDown: y
136
+ }, G) => {
137
+ const { t: k, i18n: D } = ne(), M = K(), C = N(
138
+ () => `rte-${M.replace(/[^a-zA-Z0-9-_]/g, "")}`,
139
+ [M]
140
+ ), o = `#${C}`, s = l(null), a = l(null), T = l(!1), [f, _] = ee(!1), m = l(null), c = l(R), b = l(y);
141
+ p(() => {
142
+ c.current = R, b.current = y;
143
+ }, [R, y]);
144
+ const F = l(
145
+ (() => {
146
+ const e = i ?? n ?? "";
147
+ return e ? u(e) : "";
148
+ })()
149
+ );
150
+ p(() => {
151
+ const e = s.current;
152
+ if (!e) return;
153
+ e.value = F.current;
154
+ const r = ce(P);
155
+ if (!r) {
156
+ _(!0);
157
+ return;
158
+ }
159
+ a.current = r;
160
+ const d = {
161
+ lang: j ?? fe(D.language || "en"),
162
+ buttons: de[$],
163
+ buttonsHide: ["html"],
164
+ plugins: V ?? [],
165
+ placeholder: E ?? k("editor.placeholder"),
166
+ toolbarFixed: !1,
167
+ imageEditable: !1,
168
+ imageResizable: !0
169
+ };
170
+ H && (d.minHeight = le[H]);
171
+ const q = L ? { imageUpload: L } : {}, J = (h == null ? void 0 : h.callbacks) ?? {}, Q = {
172
+ ...d,
173
+ ...q,
174
+ ...h ?? {},
175
+ // Re-pin safety-critical keys LAST so consumer `options` can't override
176
+ // the sanitising change handler or re-enable image editing.
177
+ imageEditable: !1,
178
+ callbacks: {
179
+ ...J,
180
+ changed(w) {
181
+ const v = z(ae(w)), X = u(v);
182
+ T.current = !0, c.current && (m.current && clearTimeout(m.current), m.current = setTimeout(() => {
183
+ var I;
184
+ (I = c.current) == null || I.call(c, { html: X, markdown: "" });
185
+ }, 100));
186
+ },
187
+ keydown(w) {
188
+ var v;
189
+ (v = b.current) == null || v.call(b, w);
190
+ }
191
+ }
192
+ };
193
+ try {
194
+ if (r(o, Q), g)
195
+ try {
196
+ r(o, "enableReadonly");
197
+ } catch {
198
+ }
199
+ } catch {
200
+ _(!0);
201
+ }
202
+ return () => {
203
+ m.current && clearTimeout(m.current);
204
+ try {
205
+ r(o, "destroy");
206
+ } catch {
207
+ }
208
+ a.current = null;
209
+ };
210
+ }, []), p(() => {
211
+ const e = a.current;
212
+ if (!e || i === void 0) return;
213
+ if (T.current) {
214
+ T.current = !1;
215
+ return;
216
+ }
217
+ const r = u(z(i));
218
+ let d = "";
219
+ try {
220
+ d = z(
221
+ String(e(o, "source.getCode") ?? "")
222
+ );
223
+ } catch {
224
+ return;
225
+ }
226
+ if (d !== r)
227
+ try {
228
+ e(o, "source.setCode", r);
229
+ } catch {
230
+ }
231
+ }, [i, o]), p(() => {
232
+ const e = a.current;
233
+ if (e)
234
+ try {
235
+ e(o, g ? "enableReadonly" : "disableReadonly");
236
+ } catch {
237
+ }
238
+ }, [g, o]), p(() => {
239
+ if (!f || i === void 0) return;
240
+ const e = s.current;
241
+ if (!e) return;
242
+ const r = u(i);
243
+ e.value !== r && document.activeElement !== e && (e.value = r);
244
+ }, [f, i]);
245
+ const x = N(
246
+ () => ({
247
+ getHTML() {
248
+ var r;
249
+ const e = a.current;
250
+ if (e)
251
+ try {
252
+ return String(e(o, "source.getCode") ?? "");
253
+ } catch {
254
+ }
255
+ return ((r = s.current) == null ? void 0 : r.value) ?? "";
256
+ },
257
+ getMarkdown() {
258
+ return "";
259
+ },
260
+ setContent(e) {
261
+ const r = u(e), d = a.current;
262
+ if (d)
263
+ try {
264
+ d(o, "source.setCode", r);
265
+ return;
266
+ } catch {
267
+ }
268
+ s.current && (s.current.value = r);
269
+ },
270
+ focus() {
271
+ var r;
272
+ const e = a.current;
273
+ if (e)
274
+ try {
275
+ e(o, "editor.focus");
276
+ return;
277
+ } catch {
278
+ }
279
+ (r = s.current) == null || r.focus();
280
+ },
281
+ clear() {
282
+ const e = a.current;
283
+ if (e)
284
+ try {
285
+ e(o, "source.setCode", "");
286
+ return;
287
+ } catch {
288
+ }
289
+ s.current && (s.current.value = "");
290
+ }
291
+ }),
292
+ [o]
293
+ );
294
+ te(G, () => x, [x]), ie(se, x, t);
295
+ const U = re(
296
+ (e) => {
297
+ var r;
298
+ (r = c.current) == null || r.call(c, {
299
+ html: u(e.target.value),
300
+ markdown: ""
301
+ });
302
+ },
303
+ []
304
+ ), W = O ?? k("editor.regionLabel"), Z = E ?? k("editor.placeholder");
305
+ return /* @__PURE__ */ S(
306
+ "div",
307
+ {
308
+ className: [me(), B].filter(Boolean).join(" "),
309
+ "data-component": "rich-text-editor",
310
+ "data-component-id": t,
311
+ children: /* @__PURE__ */ S(
312
+ "textarea",
313
+ {
314
+ ref: s,
315
+ id: C,
316
+ "aria-label": W,
317
+ "aria-multiline": "true",
318
+ placeholder: Z,
319
+ onChange: f ? U : void 0,
320
+ readOnly: f ? g : void 0,
321
+ className: f ? pe() : void 0
322
+ }
323
+ )
324
+ }
325
+ );
326
+ }
327
+ );
328
+ ge.displayName = "RichTextEditor";
329
+ export {
330
+ ge as R,
331
+ se as r,
332
+ me as w
333
+ };
334
+ //# sourceMappingURL=rich-text-editor-B03qM22-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rich-text-editor-B03qM22-.js","sources":["../../src/components/rich-text-editor/rich-text-editor.agent.ts","../../src/components/rich-text-editor/rich-text-editor.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { RichTextEditorHandle } from './rich-text-editor';\n\nexport const richTextEditorAgent: AgentAdapter<RichTextEditorHandle> = {\n id: 'rich-text-editor',\n capabilities: ['edit_inline'],\n state: {\n html: {\n type: 'string',\n description: 'Sanitised HTML representation of the editor content.',\n read: (handle) => handle.getHTML(),\n },\n markdown: {\n type: 'string',\n description:\n 'Markdown representation of the editor content. The Redactor engine has no native Markdown serialiser, so this is always empty — read `html` instead.',\n read: (handle) => handle.getMarkdown(),\n },\n },\n actions: {\n set_content: {\n safety: 'destructive',\n argsType: '{ html: string }',\n description: 'Replace the editor content. Loses unsaved input.',\n invoke: (handle, args: { html: string }) => {\n handle.setContent(args.html);\n },\n },\n clear: {\n safety: 'destructive',\n description: 'Empty the editor. Loses unsaved input.',\n invoke: (handle) => {\n handle.clear();\n },\n },\n focus: {\n safety: 'read',\n description: 'Move keyboard focus to the editor surface.',\n invoke: (handle) => {\n handle.focus();\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'rich-text-editor' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","/* ------------------------------------------------------------------ */\n/* RichTextEditor — thin, secure wrapper over Redactor (Imperavi). */\n/* */\n/* LICENCE-SAFETY MODEL (read first): */\n/* Redactor is a COMMERCIAL editor and `@alfadocs/ui-kit` publishes */\n/* to PUBLIC npm — so redactor.js bytes MUST NOT enter `dist/`. This */\n/* wrapper never imports Redactor; it resolves a Redactor factory at */\n/* runtime from a `redactor` prop or the UMD globals `window.$R` / */\n/* `window.Redactor` that the CONSUMER's licensed build sets */\n/* (platform ships `web/static/redactor/redactor.js`). With no */\n/* factory found, the editor renders a graceful plain <textarea>. */\n/* Storybook loads a vendored dev-only build via preview-head.html. */\n/* */\n/* - Toolbar/buttons/dropdowns/shortcuts are owned entirely by Redactor */\n/* itself. The kit only skins them via `redactor-theme.css` and feeds */\n/* button presets + lang strings. */\n/* - Security: all inbound HTML (value/defaultValue/setContent) and the */\n/* editor's outbound `changed` HTML run through DOMPurify with an */\n/* explicit FORBID list. Outbound HTML also gets `loading=\"lazy\"` on */\n/* images and platform's empty-placeholder normalisation. */\n/* - Backend-coupled features (image upload, help-me-write, */\n/* voice-recording) are CONSUMER-PROVIDED via `imageUploadUrl` / */\n/* `plugins` / `options`; the kit never bakes a backend route in. */\n/* - i18n: placeholder/aria via `t('editor.placeholder')`; Redactor's */\n/* own `lang` is derived from the active locale (not hardcoded). */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n} from 'react';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\n\nimport '../../tokens/themes/bridges/redactor-theme.css';\n\nimport { sanitiseHtml as sanitiseShared } from '../_shared/safe-html';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { richTextEditorAgent } from './rich-text-editor.agent';\n\n/* ------------------------------------------------------------------ */\n/* Sanitisation */\n/* ------------------------------------------------------------------ */\n\n// Single sanitiser surface (constraints §8): the editor routes every inbound\n// and outbound HTML string through the shared `rich-text` DOMPurify profile in\n// `_shared/safe-html.tsx`, so one audit covers the whole kit. The wrapper adds\n// only editor-specific shaping (normalizeEditorValue + withLazyImages) around it.\nfunction sanitiseHtml(input: string): string {\n return sanitiseShared(input, 'rich-text');\n}\n\n// Mirror platform's RedactorWysiwyg `normalizeEditorValue` (RedactorWysiwyg.js\n// :26-44): map Redactor's empty placeholders to '' for the emptiness test, but\n// preserve image-only HTML so embedded-image notes aren't dropped on save.\nfunction normalizeEditorValue(html = ''): string {\n const normalized = String(html).trim();\n if (\n normalized === '' ||\n normalized === '<p><br></p>' ||\n normalized === '<p>&nbsp;</p>'\n ) {\n return '';\n }\n const contentWithoutTags = normalized\n .replace(/<br\\s*\\/?>/gi, '')\n .replace(/&nbsp;/gi, '')\n .replace(/<[^>]+>/g, '')\n .trim();\n if (contentWithoutTags === '' && /<img\\b[^>]*>/i.test(normalized)) {\n return normalized;\n }\n return contentWithoutTags === '' ? '' : normalized;\n}\n\n// Inject `loading=\"lazy\"` on any <img> lacking it (platform parity).\nfunction withLazyImages(html: string): string {\n return html.replace(\n /<img(?![^>]*\\sloading=)([^>]*)>/gi,\n '<img loading=\"lazy\"$1>',\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Redactor factory resolution (external — never bundled) */\n/* ------------------------------------------------------------------ */\n\nexport type RedactorFactory = (\n selector: string | HTMLElement,\n command?: unknown,\n ...args: unknown[]\n) => unknown;\n\nfunction resolveRedactor(injected?: RedactorFactory): RedactorFactory | null {\n if (injected) return injected;\n if (typeof window === 'undefined') return null;\n const w = window as unknown as {\n $R?: RedactorFactory;\n Redactor?: RedactorFactory;\n };\n return w.$R ?? w.Redactor ?? null;\n}\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport type RichTextEditorToolbar = 'minimal' | 'standard' | 'full';\n\nexport interface RichTextEditorProps {\n /** Opaque instance id — emitted as `data-component-id` for the agent registry. */\n id?: string;\n /** HTML initial value (uncontrolled). Runs through DOMPurify. */\n defaultValue?: string;\n /** Controlled value (HTML). Sanitised; synced with a skip-once caret guard. */\n value?: string;\n /** Emits on every change — debounced 100ms. `markdown` is '' (Redactor has no serializer). */\n onChange?: (payload: { html: string; markdown: string }) => void;\n /** Toolbar preset → Redactor `buttons`. */\n toolbar?: RichTextEditorToolbar;\n /** Placeholder text — translates via useTranslation if omitted. */\n placeholder?: string;\n /** Read-only mode. */\n readOnly?: boolean;\n /** Accessible name for the editing region. */\n ariaLabel?: string;\n /** Size variant for the minimum block-size of the editing region. */\n minHeight?: 'sm' | 'md' | 'lg';\n /** Extra class names on the wrapper. */\n className?: string;\n\n /* --- Redactor-specific seams (all CONSUMER-PROVIDED) --- */\n /** Inject the Redactor factory explicitly; else `window.$R` / `window.Redactor`. */\n redactor?: RedactorFactory;\n /** Raw Redactor options, deep-merged LAST over the kit defaults (callbacks reserved). */\n options?: Record<string, unknown>;\n /** Redactor plugin names to enable (the consumer must register/load them). */\n plugins?: string[];\n /** Redactor lang code; default derived from the active i18n locale. */\n lang?: string;\n /** Consumer image-upload endpoint → Redactor `imageUpload`. Omitted if absent. */\n imageUploadUrl?: string;\n /** App keyboard-shortcut seam (e.g. Ctrl/Cmd+Enter to save). */\n onKeyDown?: (event: KeyboardEvent) => void;\n}\n\nexport interface RichTextEditorHandle {\n getHTML: () => string;\n /** Always '' — Redactor has no native Markdown serializer (see MDX). */\n getMarkdown: () => string;\n /** HTML goes through DOMPurify before reaching Redactor. */\n setContent: (html: string) => void;\n focus: () => void;\n clear: () => void;\n}\n\n/* ------------------------------------------------------------------ */\n/* Toolbar presets → Redactor buttons (UI-only; backend buttons are */\n/* consumer-supplied via `plugins` + `options`, never in a preset). */\n/* ------------------------------------------------------------------ */\n\nconst TOOLBAR_BUTTONS: Record<RichTextEditorToolbar, string[]> = {\n minimal: ['bold', 'italic', 'link'],\n standard: ['format', 'bold', 'italic', 'link', 'lists'],\n full: [\n 'format',\n 'bold',\n 'italic',\n 'deleted',\n 'link',\n 'lists',\n 'image',\n 'table',\n 'line',\n ],\n};\n\nconst MIN_HEIGHT_REM: Record<\n NonNullable<RichTextEditorProps['minHeight']>,\n string\n> = {\n sm: '8rem',\n md: '16rem',\n lg: '24rem',\n};\n\n// Map the kit's locale codes to Redactor's lang-file codes where they differ\n// (Redactor ships zh_cn / zh_tw / pt_br; the kit uses zh / pt). RTL (ar/fa/he)\n// pass through — direction is handled by the document dir, not the lang code.\nconst REDACTOR_LANG: Record<string, string> = {\n zh: 'zh_cn',\n pt: 'pt_br',\n};\n\nfunction toRedactorLang(locale: string): string {\n const base = locale.split('-')[0];\n return REDACTOR_LANG[base] ?? base ?? 'en';\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst wrapperVariants = cva(\n 'redactor-theme-alfadocs ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n);\n\n// Degraded-mode plain textarea (Redactor not found) — looks like a kit field.\nconst degradedTextareaVariants = cva(\n [\n 'ds:block ds:[inline-size:100%]',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--border)]',\n 'ds:bg-[var(--background)] ds:text-[var(--foreground)]',\n 'ds:font-[family-name:var(--font-sans)] ds:text-[length:var(--font-size-base)]',\n 'ds:p-[var(--spacing-md)] ds:[min-block-size:10rem]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[var(--ring)]',\n 'ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' '),\n);\n\n/* ------------------------------------------------------------------ */\n/* RichTextEditor */\n/* ------------------------------------------------------------------ */\n\nexport const RichTextEditor = forwardRef<\n RichTextEditorHandle,\n RichTextEditorProps\n>(\n (\n {\n id,\n defaultValue,\n value,\n onChange,\n toolbar = 'standard',\n placeholder,\n readOnly = false,\n ariaLabel,\n minHeight,\n className,\n redactor,\n options,\n plugins,\n lang,\n imageUploadUrl,\n onKeyDown,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const rawId = useId();\n const editorId = useMemo(\n () => `rte-${rawId.replace(/[^a-zA-Z0-9-_]/g, '')}`,\n [rawId],\n );\n const selector = `#${editorId}`;\n\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const redactorRef = useRef<RedactorFactory | null>(null);\n // Skip-once guard: Redactor's `changed` already pushed this value out via\n // onChange, so the controlled-value effect must not echo it back with\n // setCode (which resets the caret to position 0). Mirrors platform.\n const skipNextPropSyncRef = useRef(false);\n const [degraded, setDegraded] = useState(false);\n\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const onChangeRef = useRef<RichTextEditorProps['onChange']>(onChange);\n const onKeyDownRef = useRef<RichTextEditorProps['onKeyDown']>(onKeyDown);\n useEffect(() => {\n onChangeRef.current = onChange;\n onKeyDownRef.current = onKeyDown;\n }, [onChange, onKeyDown]);\n\n // Initial content is computed once (controlled `value` is then synced by\n // the effect below). Sanitised before it ever reaches Redactor.\n const initialContentRef = useRef<string>(\n (() => {\n const raw = value ?? defaultValue ?? '';\n return raw ? sanitiseHtml(raw) : '';\n })(),\n );\n\n /* ---- Init Redactor once (or degrade gracefully) -------------- */\n useEffect(() => {\n const ta = textareaRef.current;\n if (!ta) return undefined;\n // Seed the host textarea imperatively: Redactor reads it on init, and in\n // degraded mode it stays as the textarea's shown value. (Done here, not\n // via `defaultValue`, because `degraded` flips after mount so a\n // `defaultValue` prop would never apply.)\n ta.value = initialContentRef.current;\n const R = resolveRedactor(redactor);\n if (!R) {\n setDegraded(true);\n if (import.meta.env.DEV) {\n console.warn(\n '[RichTextEditor] Redactor not found on window.$R / window.Redactor and no `redactor` prop supplied; rendering a plain textarea.',\n );\n }\n return undefined;\n }\n redactorRef.current = R;\n\n const kitDefaults: Record<string, unknown> = {\n lang: lang ?? toRedactorLang(i18n.language || 'en'),\n buttons: TOOLBAR_BUTTONS[toolbar],\n buttonsHide: ['html'],\n plugins: plugins ?? [],\n placeholder: placeholder ?? t('editor.placeholder'),\n toolbarFixed: false,\n imageEditable: false,\n imageResizable: true,\n };\n if (minHeight) kitDefaults.minHeight = MIN_HEIGHT_REM[minHeight];\n const imageUploadOption = imageUploadUrl\n ? { imageUpload: imageUploadUrl }\n : {};\n\n const consumerCallbacks =\n (options?.callbacks as Record<string, unknown> | undefined) ?? {};\n\n const finalOptions: Record<string, unknown> = {\n ...kitDefaults,\n ...imageUploadOption,\n ...(options ?? {}),\n // Re-pin safety-critical keys LAST so consumer `options` can't override\n // the sanitising change handler or re-enable image editing.\n imageEditable: false,\n callbacks: {\n ...consumerCallbacks,\n changed(html: string): void {\n const normalized = normalizeEditorValue(withLazyImages(html));\n const safe = sanitiseHtml(normalized);\n skipNextPropSyncRef.current = true;\n if (!onChangeRef.current) return;\n if (debounceRef.current) clearTimeout(debounceRef.current);\n debounceRef.current = setTimeout(() => {\n onChangeRef.current?.({ html: safe, markdown: '' });\n }, 100);\n },\n keydown(event: KeyboardEvent): void {\n onKeyDownRef.current?.(event);\n },\n },\n };\n\n try {\n R(selector, finalOptions);\n // Redactor renders its own toolbar; readonly is a best-effort command\n // (engine-version dependent) — guarded so an unknown command is inert.\n if (readOnly) {\n try {\n R(selector, 'enableReadonly');\n } catch {\n /* readonly command unsupported in this Redactor build */\n }\n }\n } catch {\n setDegraded(true);\n }\n\n return () => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n try {\n // Platform forgets to destroy and leaks; we clean up on unmount.\n R(selector, 'destroy');\n } catch {\n /* already torn down */\n }\n redactorRef.current = null;\n };\n // Init once — option changes after mount are not re-applied (matches the\n // editor's uncontrolled-internals model); value/readOnly sync below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n /* ---- Keep controlled `value` in sync (skip-once caret guard) - */\n useEffect(() => {\n const R = redactorRef.current;\n if (!R || value === undefined) return;\n if (skipNextPropSyncRef.current) {\n skipNextPropSyncRef.current = false;\n return;\n }\n const safe = sanitiseHtml(normalizeEditorValue(value));\n let current = '';\n try {\n current = normalizeEditorValue(\n String(R(selector, 'source.getCode') ?? ''),\n );\n } catch {\n return;\n }\n if (current !== safe) {\n try {\n R(selector, 'source.setCode', safe);\n } catch {\n /* ignore */\n }\n }\n }, [value, selector]);\n\n /* ---- Keep readOnly in sync ----------------------------------- */\n useEffect(() => {\n const R = redactorRef.current;\n if (!R) return;\n try {\n R(selector, readOnly ? 'enableReadonly' : 'disableReadonly');\n } catch {\n /* readonly command unsupported in this Redactor build */\n }\n }, [readOnly, selector]);\n\n /* ---- Degraded fallback: reflect controlled `value` ----------- */\n // Without Redactor the host textarea is uncontrolled (seeded once on init),\n // so it would otherwise ignore external `value` updates. Sync imperatively\n // (a controlled `value=` would warn when `degraded` flips after mount), and\n // skip while focused so we never reset the user's caret mid-type.\n useEffect(() => {\n if (!degraded || value === undefined) return;\n const ta = textareaRef.current;\n if (!ta) return;\n const safe = sanitiseHtml(value);\n if (ta.value !== safe && document.activeElement !== ta) {\n ta.value = safe;\n }\n }, [degraded, value]);\n\n /* ---- Imperative handle --------------------------------------- */\n const handle = useMemo<RichTextEditorHandle>(\n () => ({\n getHTML(): string {\n const R = redactorRef.current;\n if (R) {\n try {\n return String(R(selector, 'source.getCode') ?? '');\n } catch {\n /* fall through */\n }\n }\n return textareaRef.current?.value ?? '';\n },\n getMarkdown(): string {\n return '';\n },\n setContent(html: string): void {\n const safe = sanitiseHtml(html);\n const R = redactorRef.current;\n if (R) {\n try {\n R(selector, 'source.setCode', safe);\n return;\n } catch {\n /* fall through */\n }\n }\n if (textareaRef.current) textareaRef.current.value = safe;\n },\n focus(): void {\n const R = redactorRef.current;\n if (R) {\n try {\n R(selector, 'editor.focus');\n return;\n } catch {\n /* fall through */\n }\n }\n textareaRef.current?.focus();\n },\n clear(): void {\n const R = redactorRef.current;\n if (R) {\n try {\n R(selector, 'source.setCode', '');\n return;\n } catch {\n /* fall through */\n }\n }\n if (textareaRef.current) textareaRef.current.value = '';\n },\n }),\n [selector],\n );\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(richTextEditorAgent, handle, id);\n\n /* ---- Degraded-mode change handler ---------------------------- */\n const handleDegradedChange = useCallback(\n (event: ChangeEvent<HTMLTextAreaElement>): void => {\n onChangeRef.current?.({\n html: sanitiseHtml(event.target.value),\n markdown: '',\n });\n },\n [],\n );\n\n // Accessible name for the editing region — a dedicated label, not the\n // placeholder, so the region's name and its placeholder hint don't collide.\n const resolvedLabel = ariaLabel ?? t('editor.regionLabel');\n const resolvedPlaceholder = placeholder ?? t('editor.placeholder');\n\n return (\n <div\n className={[wrapperVariants(), className].filter(Boolean).join(' ')}\n data-component=\"rich-text-editor\"\n data-component-id={id}\n >\n <textarea\n ref={textareaRef}\n id={editorId}\n aria-label={resolvedLabel}\n aria-multiline=\"true\"\n placeholder={resolvedPlaceholder}\n onChange={degraded ? handleDegradedChange : undefined}\n readOnly={degraded ? readOnly : undefined}\n className={degraded ? degradedTextareaVariants() : undefined}\n />\n </div>\n );\n },\n);\n\nRichTextEditor.displayName = 'RichTextEditor';\n\nexport { wrapperVariants };\n"],"names":["richTextEditorAgent","handle","args","sanitiseHtml","input","sanitiseShared","normalizeEditorValue","html","normalized","contentWithoutTags","withLazyImages","resolveRedactor","injected","w","TOOLBAR_BUTTONS","MIN_HEIGHT_REM","REDACTOR_LANG","toRedactorLang","locale","base","wrapperVariants","cva","degradedTextareaVariants","RichTextEditor","forwardRef","id","defaultValue","value","onChange","toolbar","placeholder","readOnly","ariaLabel","minHeight","className","redactor","options","plugins","lang","imageUploadUrl","onKeyDown","ref","t","i18n","useTranslation","rawId","useId","editorId","useMemo","selector","textareaRef","useRef","redactorRef","skipNextPropSyncRef","degraded","setDegraded","useState","debounceRef","onChangeRef","onKeyDownRef","useEffect","initialContentRef","raw","ta","R","kitDefaults","imageUploadOption","consumerCallbacks","finalOptions","safe","_a","event","current","useImperativeHandle","useAgentRegistration","handleDegradedChange","useCallback","resolvedLabel","resolvedPlaceholder","jsx"],"mappings":"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"}
@@ -5,7 +5,7 @@ import { useTranslation as j } from "react-i18next";
5
5
  import { F as v } from "./form-field-BOm9hK35.js";
6
6
  import { N as B } from "./number-input-Dj5L3pXK.js";
7
7
  import { S as C } from "./select-hsCaJSX3.js";
8
- import { I as L } from "./insert-result-njqBthzT.js";
8
+ import { I as L } from "./insert-result-C1SYdueh.js";
9
9
  const _ = {
10
10
  // weight → base kg
11
11
  kg: { id: "kg", category: "weight", factor: 1, offset: 0 },
@@ -193,4 +193,4 @@ export {
193
193
  P as b,
194
194
  K as c
195
195
  };
196
- //# sourceMappingURL=unit-converter-BQ6lEYvd.js.map
196
+ //# sourceMappingURL=unit-converter-3sINXO3m.js.map