@alfadocs/ui-kit-debug 0.50.0 → 0.50.1

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 (38) hide show
  1. package/dist/_chunks/{bishop-score-2MzAz8NE.js → bishop-score-CMQxsdy4.js} +2 -2
  2. package/dist/_chunks/{bishop-score-2MzAz8NE.js.map → bishop-score-CMQxsdy4.js.map} +1 -1
  3. package/dist/_chunks/{bmi-calculator-DdylQzT6.js → bmi-calculator-DuUneHuZ.js} +2 -2
  4. package/dist/_chunks/{bmi-calculator-DdylQzT6.js.map → bmi-calculator-DuUneHuZ.js.map} +1 -1
  5. package/dist/_chunks/{cycle-calculator-oOkj5wlB.js → cycle-calculator-vTtZZAmn.js} +2 -2
  6. package/dist/_chunks/{cycle-calculator-oOkj5wlB.js.map → cycle-calculator-vTtZZAmn.js.map} +1 -1
  7. package/dist/_chunks/{due-date-calculator-DykajWzh.js → due-date-calculator-CUm5KJbf.js} +2 -2
  8. package/dist/_chunks/{due-date-calculator-DykajWzh.js.map → due-date-calculator-CUm5KJbf.js.map} +1 -1
  9. package/dist/_chunks/{fetal-weight-Da-B4P4k.js → fetal-weight-Xf8-ZoDy.js} +2 -2
  10. package/dist/_chunks/{fetal-weight-Da-B4P4k.js.map → fetal-weight-Xf8-ZoDy.js.map} +1 -1
  11. package/dist/_chunks/{gestational-age-calculator-Bc55hq6t.js → gestational-age-calculator-830KJql3.js} +2 -2
  12. package/dist/_chunks/{gestational-age-calculator-Bc55hq6t.js.map → gestational-age-calculator-830KJql3.js.map} +1 -1
  13. package/dist/_chunks/{hcg-doubling-DAnpbale.js → hcg-doubling-kVOpDfny.js} +2 -2
  14. package/dist/_chunks/{hcg-doubling-DAnpbale.js.map → hcg-doubling-kVOpDfny.js.map} +1 -1
  15. package/dist/_chunks/insert-result-njqBthzT.js +742 -0
  16. package/dist/_chunks/insert-result-njqBthzT.js.map +1 -0
  17. package/dist/_chunks/{pregnancy-dating-48Gfvod1.js → pregnancy-dating-DT_244bG.js} +2 -2
  18. package/dist/_chunks/{pregnancy-dating-48Gfvod1.js.map → pregnancy-dating-DT_244bG.js.map} +1 -1
  19. package/dist/_chunks/{pregnancy-weight-gain-CCGrvarh.js → pregnancy-weight-gain-BMRBeA8V.js} +2 -2
  20. package/dist/_chunks/{pregnancy-weight-gain-CCGrvarh.js.map → pregnancy-weight-gain-BMRBeA8V.js.map} +1 -1
  21. package/dist/_chunks/{unit-converter-jWp3Z85y.js → unit-converter-BQ6lEYvd.js} +2 -2
  22. package/dist/_chunks/{unit-converter-jWp3Z85y.js.map → unit-converter-BQ6lEYvd.js.map} +1 -1
  23. package/dist/agent-catalog.json +1 -1
  24. package/dist/components/_shared/insert-result.d.ts.map +1 -1
  25. package/dist/components/bishop-score/index.js +1 -1
  26. package/dist/components/bmi-calculator/index.js +1 -1
  27. package/dist/components/cycle-calculator/index.js +1 -1
  28. package/dist/components/due-date-calculator/index.js +1 -1
  29. package/dist/components/fetal-weight/index.js +1 -1
  30. package/dist/components/gestational-age-calculator/index.js +1 -1
  31. package/dist/components/hcg-doubling/index.js +1 -1
  32. package/dist/components/pregnancy-dating/index.js +1 -1
  33. package/dist/components/pregnancy-weight-gain/index.js +1 -1
  34. package/dist/components/unit-converter/index.js +1 -1
  35. package/dist/index.js +11 -11
  36. package/package.json +1 -1
  37. package/dist/_chunks/insert-result-DNdi_JYW.js +0 -683
  38. package/dist/_chunks/insert-result-DNdi_JYW.js.map +0 -1
@@ -6,7 +6,7 @@ import { F as I } from "./form-field-BOm9hK35.js";
6
6
  import { S as P } from "./select-hsCaJSX3.js";
7
7
  import { C as m } from "./card-DPmk26CL.js";
8
8
  import { B as T } from "./badge-zsf5i5bH.js";
9
- import { I as A } from "./insert-result-DNdi_JYW.js";
9
+ import { I as A } from "./insert-result-njqBthzT.js";
10
10
  const b = [
11
11
  "dilation",
12
12
  "effacement",
@@ -182,4 +182,4 @@ export {
182
182
  D as d,
183
183
  M as e
184
184
  };
185
- //# sourceMappingURL=bishop-score-2MzAz8NE.js.map
185
+ //# sourceMappingURL=bishop-score-CMQxsdy4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bishop-score-2MzAz8NE.js","sources":["../../src/components/bishop-score/bishop.ts","../../src/components/bishop-score/bishop-score.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Bishop score — cervical favourability for induction of labour. */\n/* */\n/* Pure, framework-free, unit-testable. Five components scored 0–3 */\n/* (dilation, effacement, station) or 0–2 (consistency, position), */\n/* summed to a 0–13 total. Bands per ACOG: ≤6 unfavourable (cervical */\n/* ripening advised), 7 intermediate, ≥8 favourable. */\n/* */\n/* Bishop EH, Obstet Gynecol 1964; thresholds per ACOG Practice */\n/* Bulletin 107 (Induction of Labor). */\n/* ------------------------------------------------------------------ */\n\nexport type BishopComponent =\n | 'dilation'\n | 'effacement'\n | 'station'\n | 'consistency'\n | 'position';\n\n/** The component fields in the order they are scored / displayed. */\nexport const BISHOP_COMPONENTS: readonly BishopComponent[] = [\n 'dilation',\n 'effacement',\n 'station',\n 'consistency',\n 'position',\n] as const;\n\nexport interface BishopOption {\n /** Stable value key → i18n lookup `bishopScore.<component>.<value>`. */\n value: string;\n /** Points this option contributes. */\n score: number;\n}\n\n/**\n * Scored options per component. Dilation / effacement / station run 0–3;\n * consistency / position run 0–2 (the classic Bishop weighting).\n */\nexport const BISHOP_OPTIONS: Record<BishopComponent, BishopOption[]> = {\n dilation: [\n { value: 'closed', score: 0 },\n { value: '1-2', score: 1 },\n { value: '3-4', score: 2 },\n { value: '5+', score: 3 },\n ],\n effacement: [\n { value: '0-30', score: 0 },\n { value: '40-50', score: 1 },\n { value: '60-70', score: 2 },\n { value: '80+', score: 3 },\n ],\n station: [\n { value: '-3', score: 0 },\n { value: '-2', score: 1 },\n { value: '-1-0', score: 2 },\n { value: '+1+2', score: 3 },\n ],\n consistency: [\n { value: 'firm', score: 0 },\n { value: 'medium', score: 1 },\n { value: 'soft', score: 2 },\n ],\n position: [\n { value: 'posterior', score: 0 },\n { value: 'mid', score: 1 },\n { value: 'anterior', score: 2 },\n ],\n};\n\nexport type BishopBand = 'unfavourable' | 'intermediate' | 'favourable';\n\n/** Highest attainable total (3 + 3 + 3 + 2 + 2). */\nexport const BISHOP_MAX_SCORE = 13;\n\n/** Classify a total into a favourability band (ACOG thresholds). */\nexport function bishopBand(total: number): BishopBand {\n if (total <= 6) return 'unfavourable';\n if (total >= 8) return 'favourable';\n return 'intermediate';\n}\n\nexport type BishopSelection = Partial<Record<BishopComponent, string>>;\n\nexport interface BishopResult {\n total: number;\n band: BishopBand;\n}\n\n/** Look up the score for a component's selected option, or `null` if unknown. */\nfunction scoreFor(component: BishopComponent, value: string): number | null {\n return (\n BISHOP_OPTIONS[component].find((o) => o.value === value)?.score ?? null\n );\n}\n\n/**\n * Sum the Bishop score. Returns `null` until every one of the five components\n * has a valid selection (a partial exam doesn't yield a meaningful total).\n */\nexport function bishopScore(selection: BishopSelection): BishopResult | null {\n let total = 0;\n for (const component of BISHOP_COMPONENTS) {\n const value = selection[component];\n if (value === undefined) return null;\n const score = scoreFor(component, value);\n if (score === null) return null;\n total += score;\n }\n return { total, band: bishopBand(total) };\n}\n","/* ------------------------------------------------------------------ */\n/* BishopScore — cervical favourability for induction of labour. */\n/* */\n/* Five enumerated cervical-exam findings → a 0–13 total with a */\n/* favourability band. Maths lives in `./bishop` (pure, tested). */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { FormField } from '../form-field';\nimport { Select } from '../select';\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 {\n type BishopComponent,\n type BishopSelection,\n type BishopResult,\n type BishopBand,\n BISHOP_COMPONENTS,\n BISHOP_OPTIONS,\n BISHOP_MAX_SCORE,\n bishopScore,\n} from './bishop';\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\n/** Band → on-screen Badge variant + inserted-card chip token. */\nconst BAND_BADGE: Record<BishopBand, 'warning' | 'info' | 'success'> = {\n unfavourable: 'warning',\n intermediate: 'info',\n favourable: 'success',\n};\nconst BAND_TOKEN: Record<BishopBand, string> = {\n unfavourable: '--warning-readable',\n intermediate: '--info',\n favourable: '--success',\n};\n\nexport interface BishopScoreProps extends VariantProps<typeof rootVariants> {\n /** Fires whenever all five findings are set (and `null` when not). */\n onResultChange?: (result: BishopResult | null) => void;\n /** When provided, shows the result-action buttons that emit / copy the result. */\n onInsert?: (payload: InsertPayload) => void;\n /** Which verb the result button performs. Defaults to `'insert'`. */\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 /** Brand wordmark printed in the inserted/copied result-card footer. */\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 BishopScore = forwardRef<HTMLDivElement, BishopScoreProps>(\n (\n {\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 } = useTranslation();\n\n const [selection, setSelection] = useState<BishopSelection>({});\n\n const result = useMemo(() => bishopScore(selection), [selection]);\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n const optionsFor = (component: BishopComponent) =>\n BISHOP_OPTIONS[component].map((o) => ({\n value: o.value,\n label: t(`bishopScore.${component}.${o.value}`),\n }));\n\n const bandLabel = (band: BishopBand): string =>\n t(`bishopScore.band.${band}`);\n\n return (\n <div\n ref={ref}\n data-component=\"bishop-score\"\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 {BISHOP_COMPONENTS.map((component) => (\n <FormField\n key={component}\n label={t(`bishopScore.${component}.label`)}\n >\n <Select\n options={optionsFor(component)}\n value={selection[component] ?? ''}\n onValueChange={(next) =>\n setSelection((prev) => ({ ...prev, [component]: next }))\n }\n />\n </FormField>\n ))}\n </div>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result\n ? `${t('bishopScore.totalLabel')}: ${t('bishopScore.scoreOf', {\n score: result.total,\n max: BISHOP_MAX_SCORE,\n })}. ${bandLabel(result.band)}.`\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:items-start ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('bishopScore.totalLabel')}\n </dt>\n <dd className=\"type-metric ds:text-foreground\">\n {t('bishopScore.scoreOf', {\n score: result.total,\n max: BISHOP_MAX_SCORE,\n })}\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-end ds:gap-[var(--spacing-xs)] ds:text-end\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('bishopScore.bandLabel')}\n </dt>\n <dd>\n <Badge variant={BAND_BADGE[result.band]} size=\"lg\">\n {bandLabel(result.band)}\n </Badge>\n </dd>\n </div>\n </dl>\n <p className=\"type-body-sm ds:text-muted-foreground\">\n {t(`bishopScore.interpretation.${result.band}`)}\n </p>\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.bishop'),\n icon: 'activity',\n highlight: bandLabel(result.band),\n highlightToken: BAND_TOKEN[result.band],\n brand: insertBrand,\n fields: [\n {\n icon: 'percent',\n label: t('bishopScore.totalLabel'),\n value: t('bishopScore.scoreOf', {\n score: result.total,\n max: BISHOP_MAX_SCORE,\n }),\n },\n {\n icon: 'tag',\n label: t('bishopScore.bandLabel'),\n value: bandLabel(result.band),\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('bishopScore.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nBishopScore.displayName = 'BishopScore';\n"],"names":["BISHOP_COMPONENTS","BISHOP_OPTIONS","BISHOP_MAX_SCORE","bishopBand","total","scoreFor","component","value","_a","o","bishopScore","selection","score","rootVariants","cva","BAND_BADGE","BAND_TOKEN","BishopScore","forwardRef","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","t","useTranslation","setSelection","useState","result","useMemo","useEffect","optionsFor","bandLabel","band","jsxs","jsx","FormField","Select","next","prev","Card","Badge","InsertButton"],"mappings":"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"}
1
+ {"version":3,"file":"bishop-score-CMQxsdy4.js","sources":["../../src/components/bishop-score/bishop.ts","../../src/components/bishop-score/bishop-score.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Bishop score — cervical favourability for induction of labour. */\n/* */\n/* Pure, framework-free, unit-testable. Five components scored 0–3 */\n/* (dilation, effacement, station) or 0–2 (consistency, position), */\n/* summed to a 0–13 total. Bands per ACOG: ≤6 unfavourable (cervical */\n/* ripening advised), 7 intermediate, ≥8 favourable. */\n/* */\n/* Bishop EH, Obstet Gynecol 1964; thresholds per ACOG Practice */\n/* Bulletin 107 (Induction of Labor). */\n/* ------------------------------------------------------------------ */\n\nexport type BishopComponent =\n | 'dilation'\n | 'effacement'\n | 'station'\n | 'consistency'\n | 'position';\n\n/** The component fields in the order they are scored / displayed. */\nexport const BISHOP_COMPONENTS: readonly BishopComponent[] = [\n 'dilation',\n 'effacement',\n 'station',\n 'consistency',\n 'position',\n] as const;\n\nexport interface BishopOption {\n /** Stable value key → i18n lookup `bishopScore.<component>.<value>`. */\n value: string;\n /** Points this option contributes. */\n score: number;\n}\n\n/**\n * Scored options per component. Dilation / effacement / station run 0–3;\n * consistency / position run 0–2 (the classic Bishop weighting).\n */\nexport const BISHOP_OPTIONS: Record<BishopComponent, BishopOption[]> = {\n dilation: [\n { value: 'closed', score: 0 },\n { value: '1-2', score: 1 },\n { value: '3-4', score: 2 },\n { value: '5+', score: 3 },\n ],\n effacement: [\n { value: '0-30', score: 0 },\n { value: '40-50', score: 1 },\n { value: '60-70', score: 2 },\n { value: '80+', score: 3 },\n ],\n station: [\n { value: '-3', score: 0 },\n { value: '-2', score: 1 },\n { value: '-1-0', score: 2 },\n { value: '+1+2', score: 3 },\n ],\n consistency: [\n { value: 'firm', score: 0 },\n { value: 'medium', score: 1 },\n { value: 'soft', score: 2 },\n ],\n position: [\n { value: 'posterior', score: 0 },\n { value: 'mid', score: 1 },\n { value: 'anterior', score: 2 },\n ],\n};\n\nexport type BishopBand = 'unfavourable' | 'intermediate' | 'favourable';\n\n/** Highest attainable total (3 + 3 + 3 + 2 + 2). */\nexport const BISHOP_MAX_SCORE = 13;\n\n/** Classify a total into a favourability band (ACOG thresholds). */\nexport function bishopBand(total: number): BishopBand {\n if (total <= 6) return 'unfavourable';\n if (total >= 8) return 'favourable';\n return 'intermediate';\n}\n\nexport type BishopSelection = Partial<Record<BishopComponent, string>>;\n\nexport interface BishopResult {\n total: number;\n band: BishopBand;\n}\n\n/** Look up the score for a component's selected option, or `null` if unknown. */\nfunction scoreFor(component: BishopComponent, value: string): number | null {\n return (\n BISHOP_OPTIONS[component].find((o) => o.value === value)?.score ?? null\n );\n}\n\n/**\n * Sum the Bishop score. Returns `null` until every one of the five components\n * has a valid selection (a partial exam doesn't yield a meaningful total).\n */\nexport function bishopScore(selection: BishopSelection): BishopResult | null {\n let total = 0;\n for (const component of BISHOP_COMPONENTS) {\n const value = selection[component];\n if (value === undefined) return null;\n const score = scoreFor(component, value);\n if (score === null) return null;\n total += score;\n }\n return { total, band: bishopBand(total) };\n}\n","/* ------------------------------------------------------------------ */\n/* BishopScore — cervical favourability for induction of labour. */\n/* */\n/* Five enumerated cervical-exam findings → a 0–13 total with a */\n/* favourability band. Maths lives in `./bishop` (pure, tested). */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { FormField } from '../form-field';\nimport { Select } from '../select';\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 {\n type BishopComponent,\n type BishopSelection,\n type BishopResult,\n type BishopBand,\n BISHOP_COMPONENTS,\n BISHOP_OPTIONS,\n BISHOP_MAX_SCORE,\n bishopScore,\n} from './bishop';\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\n/** Band → on-screen Badge variant + inserted-card chip token. */\nconst BAND_BADGE: Record<BishopBand, 'warning' | 'info' | 'success'> = {\n unfavourable: 'warning',\n intermediate: 'info',\n favourable: 'success',\n};\nconst BAND_TOKEN: Record<BishopBand, string> = {\n unfavourable: '--warning-readable',\n intermediate: '--info',\n favourable: '--success',\n};\n\nexport interface BishopScoreProps extends VariantProps<typeof rootVariants> {\n /** Fires whenever all five findings are set (and `null` when not). */\n onResultChange?: (result: BishopResult | null) => void;\n /** When provided, shows the result-action buttons that emit / copy the result. */\n onInsert?: (payload: InsertPayload) => void;\n /** Which verb the result button performs. Defaults to `'insert'`. */\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 /** Brand wordmark printed in the inserted/copied result-card footer. */\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 BishopScore = forwardRef<HTMLDivElement, BishopScoreProps>(\n (\n {\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 } = useTranslation();\n\n const [selection, setSelection] = useState<BishopSelection>({});\n\n const result = useMemo(() => bishopScore(selection), [selection]);\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n const optionsFor = (component: BishopComponent) =>\n BISHOP_OPTIONS[component].map((o) => ({\n value: o.value,\n label: t(`bishopScore.${component}.${o.value}`),\n }));\n\n const bandLabel = (band: BishopBand): string =>\n t(`bishopScore.band.${band}`);\n\n return (\n <div\n ref={ref}\n data-component=\"bishop-score\"\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 {BISHOP_COMPONENTS.map((component) => (\n <FormField\n key={component}\n label={t(`bishopScore.${component}.label`)}\n >\n <Select\n options={optionsFor(component)}\n value={selection[component] ?? ''}\n onValueChange={(next) =>\n setSelection((prev) => ({ ...prev, [component]: next }))\n }\n />\n </FormField>\n ))}\n </div>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result\n ? `${t('bishopScore.totalLabel')}: ${t('bishopScore.scoreOf', {\n score: result.total,\n max: BISHOP_MAX_SCORE,\n })}. ${bandLabel(result.band)}.`\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:items-start ds:gap-[var(--spacing-xs)]\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('bishopScore.totalLabel')}\n </dt>\n <dd className=\"type-metric ds:text-foreground\">\n {t('bishopScore.scoreOf', {\n score: result.total,\n max: BISHOP_MAX_SCORE,\n })}\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-end ds:gap-[var(--spacing-xs)] ds:text-end\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('bishopScore.bandLabel')}\n </dt>\n <dd>\n <Badge variant={BAND_BADGE[result.band]} size=\"lg\">\n {bandLabel(result.band)}\n </Badge>\n </dd>\n </div>\n </dl>\n <p className=\"type-body-sm ds:text-muted-foreground\">\n {t(`bishopScore.interpretation.${result.band}`)}\n </p>\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.bishop'),\n icon: 'activity',\n highlight: bandLabel(result.band),\n highlightToken: BAND_TOKEN[result.band],\n brand: insertBrand,\n fields: [\n {\n icon: 'percent',\n label: t('bishopScore.totalLabel'),\n value: t('bishopScore.scoreOf', {\n score: result.total,\n max: BISHOP_MAX_SCORE,\n }),\n },\n {\n icon: 'tag',\n label: t('bishopScore.bandLabel'),\n value: bandLabel(result.band),\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('bishopScore.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nBishopScore.displayName = 'BishopScore';\n"],"names":["BISHOP_COMPONENTS","BISHOP_OPTIONS","BISHOP_MAX_SCORE","bishopBand","total","scoreFor","component","value","_a","o","bishopScore","selection","score","rootVariants","cva","BAND_BADGE","BAND_TOKEN","BishopScore","forwardRef","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","t","useTranslation","setSelection","useState","result","useMemo","useEffect","optionsFor","bandLabel","band","jsxs","jsx","FormField","Select","next","prev","Card","Badge","InsertButton"],"mappings":"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"}
@@ -8,7 +8,7 @@ import { F as D } from "./form-field-BOm9hK35.js";
8
8
  import { N as p } from "./number-input-Dj5L3pXK.js";
9
9
  import { C as M } from "./card-DPmk26CL.js";
10
10
  import { B as na } from "./badge-zsf5i5bH.js";
11
- import { B as ra, I as sa, b as oa } from "./insert-result-DNdi_JYW.js";
11
+ import { B as ra, I as sa, b as oa } from "./insert-result-njqBthzT.js";
12
12
  import { b as ca, e as ma, c as da, f as O, l as W, k as ua } from "./bmi-BxD-tFzU.js";
13
13
  const ga = la("ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]", {
14
14
  variants: {
@@ -255,4 +255,4 @@ pa.displayName = "BmiCalculator";
255
255
  export {
256
256
  pa as B
257
257
  };
258
- //# sourceMappingURL=bmi-calculator-DdylQzT6.js.map
258
+ //# sourceMappingURL=bmi-calculator-DuUneHuZ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bmi-calculator-DdylQzT6.js","sources":["../../src/components/bmi-calculator/bmi-calculator.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* BmiCalculator — height + weight → WHO body-mass-index, shown on a */\n/* banded-linear gauge. */\n/* */\n/* - Maths + unit conversion live in `./bmi` (pure, separately tested). */\n/* - Metric ⇄ imperial toggle persists the entered figures across the */\n/* switch by converting them (a patient measured in lb/ft doesn't */\n/* lose their numbers when a clinician flips to kg/cm). */\n/* - The gauge is the shared `BandedGauge`: a horizontal WHO-banded bar */\n/* (15–40) with a marker at the BMI's x-position. The SAME geometry */\n/* bakes into the inserted result-card PNG (banded variant), so the */\n/* on-screen widget and the rasterised card never drift. The band */\n/* fills come from semantic tokens (`--info` / `--success` / */\n/* `--warning-readable` / `--destructive`) — the contrast-safe */\n/* `--warning-readable` token handles the overweight orange in every */\n/* theme, so no `getComputedStyle` arc-colour sampling is needed. */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useId, 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 { BandedGauge, bmiBands } from '../_shared/banded-gauge';\nimport {\n type UnitSystem,\n type BmiCategory,\n computeBmi,\n bmiCategory,\n cmToFtIn,\n ftInToCm,\n kgToLb,\n lbToKg,\n} from './bmi';\n\n/* ------------------------------------------------------------------ */\n/* Result payload emitted to consumers */\n/* ------------------------------------------------------------------ */\n\nexport interface BmiResult {\n /** BMI in kg/m². */\n bmi: number;\n /** WHO category the BMI falls into. */\n category: BmiCategory;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rootVariants = cva('ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]', {\n variants: {\n width: {\n full: 'ds:w-full',\n auto: 'ds:inline-flex',\n },\n },\n defaultVariants: { width: 'full' },\n});\n\n/* ------------------------------------------------------------------ */\n/* Category → semantic intent for the Stat headline */\n/* ------------------------------------------------------------------ */\n\nconst CATEGORY_BADGE: Record<\n BmiCategory,\n 'info' | 'success' | 'warning' | 'error'\n> = {\n underweight: 'info',\n normal: 'success',\n overweight: 'warning',\n obese: 'error',\n};\n\n/* ------------------------------------------------------------------ */\n/* Category → semantic band token. */\n/* */\n/* The four WHO bands carry these tokens both on-screen (the shared */\n/* `bmiBands()` definition fills each zone with the same name) and in */\n/* the card chip's `highlightToken`. `overweight` uses */\n/* `--warning-readable` — the kit's contrast-safe orange that already */\n/* resolves correctly per theme (orange-600 in light/non-accessible, */\n/* `--warning` elsewhere), so NO `getComputedStyle` arc sampling or */\n/* `document.documentElement` override is needed. */\n/* ------------------------------------------------------------------ */\n\nconst CATEGORY_TOKEN: Record<BmiCategory, string> = {\n underweight: '--info',\n normal: '--success',\n overweight: '--warning-readable',\n obese: '--destructive',\n};\n\nexport interface BmiCalculatorProps extends VariantProps<typeof rootVariants> {\n /** Initial unit system. Defaults to `'metric'`. */\n defaultUnitSystem?: UnitSystem;\n /** Fires whenever a valid BMI can be computed (and with `null` when it can't). */\n onResultChange?: (result: BmiResult | null) => void;\n /** When provided, shows an \"Insert\" button that emits the result for an editor. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Which verb the result button performs. Defaults to `'insert'`.\n * Use `'copy'` in an app-shell surface (no editor to insert into) — the\n * button writes the result to the clipboard as a multi-format `ClipboardItem`.\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\n/** Round to one decimal place — used when seeding a unit-switch. */\nconst round1 = (n: number): number => Math.round(n * 10) / 10;\n\nexport const BmiCalculator = forwardRef<HTMLDivElement, BmiCalculatorProps>(\n (\n {\n defaultUnitSystem = 'metric',\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 const heightGroupId = useId();\n\n const [unitSystem, setUnitSystem] = useState<UnitSystem>(defaultUnitSystem);\n\n // Raw per-system inputs. Metric is canonical for cm/kg; imperial keeps\n // ft + in + lb so typing isn't fought by rounding round-trips.\n const [heightCm, setHeightCm] = useState<number | null>(null);\n const [weightKg, setWeightKg] = useState<number | null>(null);\n const [heightFt, setHeightFt] = useState<number | null>(null);\n const [heightIn, setHeightIn] = useState<number | null>(null);\n const [weightLb, setWeightLb] = useState<number | null>(null);\n\n const handleUnitChange = (next: string): void => {\n const target = next as UnitSystem;\n if (target === unitSystem) return;\n if (target === 'imperial') {\n if (heightCm !== null) {\n const { ft, in: inches } = cmToFtIn(heightCm);\n setHeightFt(ft);\n setHeightIn(round1(inches));\n }\n if (weightKg !== null) setWeightLb(round1(kgToLb(weightKg)));\n } else {\n if (heightFt !== null || heightIn !== null) {\n setHeightCm(round1(ftInToCm(heightFt ?? 0, heightIn ?? 0)));\n }\n if (weightLb !== null) setWeightKg(round1(lbToKg(weightLb)));\n }\n setUnitSystem(target);\n };\n\n /* Canonical metric figures for the active system. */\n const canonicalHeightCm =\n unitSystem === 'metric'\n ? heightCm\n : heightFt !== null || heightIn !== null\n ? ftInToCm(heightFt ?? 0, heightIn ?? 0)\n : null;\n const canonicalWeightKg =\n unitSystem === 'metric'\n ? weightKg\n : weightLb !== null\n ? lbToKg(weightLb)\n : null;\n\n const bmi = computeBmi(canonicalWeightKg, canonicalHeightCm);\n const category = bmi !== null ? bmiCategory(bmi) : null;\n\n const bmiFormatter = useMemo(\n () =>\n new Intl.NumberFormat(i18n.language, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }),\n [i18n.language],\n );\n\n // Notify consumers without re-firing on unrelated re-renders.\n useEffect(() => {\n onResultChange?.(\n bmi !== null && category !== null ? { bmi, category } : null,\n );\n }, [bmi, category, onResultChange]);\n\n const isMetric = unitSystem === 'metric';\n\n // Pre-translated gauge labels, shared by the on-screen widget and the\n // inserted card so both surfaces read identically. The shared `BandedGauge`\n // is i18n-agnostic — every visible string arrives already through `t()`.\n const gauge = useMemo(() => {\n if (bmi === null || category === null) return null;\n const { bands, min, max, ticks } = bmiBands();\n const valueLabel = bmiFormatter.format(bmi);\n const categoryLabel = t(`bmiCalculator.category.${category}`);\n const rangeLabel = t(`bmiCalculator.range.${category}`);\n const ariaLabel = t('bmiCalculator.gaugeAria', {\n bmi: valueLabel,\n category: categoryLabel,\n range: rangeLabel,\n });\n return {\n value: bmi,\n bands,\n min,\n max,\n ticks,\n valueLabel,\n categoryLabel,\n rangeLabel,\n ariaLabel,\n };\n }, [bmi, category, bmiFormatter, t]);\n\n return (\n <div\n ref={ref}\n data-component=\"bmi-calculator\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <RadioGroup\n label={t('bmiCalculator.unitSystem.label')}\n variant=\"horizontal\"\n value={unitSystem}\n onValueChange={handleUnitChange}\n >\n <Radio label={t('bmiCalculator.unitSystem.metric')} value=\"metric\" />\n <Radio\n label={t('bmiCalculator.unitSystem.imperial')}\n value=\"imperial\"\n />\n </RadioGroup>\n\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n {isMetric ? (\n <FormField\n label={`${t('bmiCalculator.height')} (${t('bmiCalculator.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 ) : (\n <div role=\"group\" aria-labelledby={heightGroupId}>\n <span\n id={heightGroupId}\n className=\"type-label ds:mb-[var(--spacing-xs)] ds:block ds:text-foreground\"\n >\n {t('bmiCalculator.height')}\n </span>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <NumberInput\n mode=\"integer\"\n min={0}\n value={heightFt}\n onChange={setHeightFt}\n aria-label={t('bmiCalculator.feet')}\n />\n <span className=\"type-label ds:text-muted-foreground\">\n {t('bmiCalculator.units.ft')}\n </span>\n <NumberInput\n mode=\"decimal\"\n min={0}\n max={11.9}\n step={0.5}\n value={heightIn}\n onChange={setHeightIn}\n aria-label={t('bmiCalculator.inches')}\n />\n <span className=\"type-label ds:text-muted-foreground\">\n {t('bmiCalculator.units.in')}\n </span>\n </div>\n </div>\n )}\n\n <FormField\n label={`${t('bmiCalculator.weight')} (${t(\n isMetric ? 'bmiCalculator.units.kg' : 'bmiCalculator.units.lb',\n )})`}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={isMetric ? 0.1 : 0.5}\n value={isMetric ? weightKg : weightLb}\n onChange={isMetric ? setWeightKg : setWeightLb}\n />\n </FormField>\n </div>\n\n {/* Concise polite announcement when the result resolves — the gauge\n itself stays out of the live region to avoid re-reading on every\n keystroke. */}\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {gauge ? gauge.ariaLabel : ''}\n </p>\n\n {bmi !== null && category !== null && gauge !== null ? (\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)] ds:text-center\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('bmiCalculator.category.label')}\n </span>\n <Badge variant={CATEGORY_BADGE[category]} size=\"lg\">\n {t(`bmiCalculator.category.${category}`)}\n </Badge>\n <p className=\"type-body ds:text-muted-foreground\">\n {gauge.rangeLabel}\n </p>\n </div>\n {/* `data-bmi-category` exposes the WHO band so the gauge's\n semantic colour is assertable without sampling pixels. The\n shared BandedGauge fills each zone via `var(<token>)`, so a\n theme switch repaints the bands with no JS re-sample. */}\n <div\n data-bmi-category={category}\n className=\"ds:w-full ds:max-w-[320px]\"\n >\n <BandedGauge model={gauge} className=\"ds:w-full\" />\n </div>\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.bmi'),\n icon: 'scale',\n highlight: t(`bmiCalculator.category.${category}`),\n // Chip shares the WHO-category semantic token so the\n // inserted PNG chip matches the on-screen band colour.\n highlightToken: CATEGORY_TOKEN[category],\n brand: insertBrand,\n // Banded gauge: the SAME geometry + WHO bands as the\n // on-screen widget, baked into the card PNG with\n // probe-resolved theme colours.\n gauge: {\n variant: 'banded',\n value: bmi,\n bands: gauge.bands,\n min: gauge.min,\n max: gauge.max,\n valueLabel: gauge.valueLabel,\n categoryLabel: gauge.categoryLabel,\n rangeLabel: gauge.rangeLabel,\n ariaLabel: gauge.ariaLabel,\n ticks: gauge.ticks,\n },\n fields: [\n {\n // Mirrors the on-screen scale glyph + header icon.\n icon: 'scale',\n label: t('insert.title.bmi'),\n value: gauge.valueLabel,\n },\n {\n // WHO band is a classification → tag glyph.\n icon: 'tag',\n label: t('bmiCalculator.category.label'),\n value: gauge.categoryLabel,\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('bmiCalculator.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nBmiCalculator.displayName = 'BmiCalculator';\n"],"names":["rootVariants","cva","CATEGORY_BADGE","CATEGORY_TOKEN","round1","n","BmiCalculator","forwardRef","defaultUnitSystem","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","t","i18n","useTranslation","heightGroupId","useId","unitSystem","setUnitSystem","useState","heightCm","setHeightCm","weightKg","setWeightKg","heightFt","setHeightFt","heightIn","setHeightIn","weightLb","setWeightLb","handleUnitChange","next","target","ft","inches","cmToFtIn","kgToLb","ftInToCm","lbToKg","canonicalHeightCm","canonicalWeightKg","bmi","computeBmi","category","bmiCategory","bmiFormatter","useMemo","useEffect","isMetric","gauge","bands","min","max","ticks","bmiBands","valueLabel","categoryLabel","rangeLabel","ariaLabel","jsxs","RadioGroup","jsx","Radio","FormField","NumberInput","Card","Badge","BandedGauge","InsertButton"],"mappings":";;;;;;;;;;;;AA2DA,MAAMA,KAAeC,GAAI,kDAAkD;AAAA,EACzE,UAAU;AAAA,IACR,OAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,EACR;AAAA,EAEF,iBAAiB,EAAE,OAAO,OAAA;AAC5B,CAAC,GAMKC,KAGF;AAAA,EACF,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AACT,GAcMC,KAA8C;AAAA,EAClD,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AACT,GAgCMC,IAAS,CAACC,MAAsB,KAAK,MAAMA,IAAI,EAAE,IAAI,IAE9CC,KAAgBC;AAAA,EAC3B,CACE;AAAA,IACE,mBAAAC,IAAoB;AAAA,IACpB,gBAAAC;AAAA,IACA,UAAAC;AAAA,IACA,eAAAC,IAAgB;AAAA,IAChB,QAAAC;AAAA,IACA,SAAAC;AAAA,IACA,aAAAC;AAAA,IACA,IAAAC;AAAA,IACA,OAAAC;AAAA,IACA,WAAAC;AAAA,EAAA,GAEFC,MACG;AACH,UAAM,EAAE,GAAAC,GAAG,MAAAC,EAAA,IAASC,GAAA,GACdC,IAAgBC,GAAA,GAEhB,CAACC,GAAYC,CAAa,IAAIC,EAAqBlB,CAAiB,GAIpE,CAACmB,GAAUC,CAAW,IAAIF,EAAwB,IAAI,GACtD,CAACG,GAAUC,CAAW,IAAIJ,EAAwB,IAAI,GACtD,CAACK,GAAUC,CAAW,IAAIN,EAAwB,IAAI,GACtD,CAACO,GAAUC,CAAW,IAAIR,EAAwB,IAAI,GACtD,CAACS,GAAUC,CAAW,IAAIV,EAAwB,IAAI,GAEtDW,IAAmB,CAACC,MAAuB;AAC/C,YAAMC,IAASD;AACf,UAAIC,MAAWf,GACf;AAAA,YAAIe,MAAW,YAAY;AACzB,cAAIZ,MAAa,MAAM;AACrB,kBAAM,EAAE,IAAAa,GAAI,IAAIC,EAAA,IAAWC,GAASf,CAAQ;AAC5C,YAAAK,EAAYQ,CAAE,GACdN,EAAY9B,EAAOqC,CAAM,CAAC;AAAA,UAC5B;AACA,UAAIZ,MAAa,QAAMO,EAAYhC,EAAOuC,GAAOd,CAAQ,CAAC,CAAC;AAAA,QAC7D;AACE,WAAIE,MAAa,QAAQE,MAAa,SACpCL,EAAYxB,EAAOwC,EAASb,KAAY,GAAGE,KAAY,CAAC,CAAC,CAAC,GAExDE,MAAa,QAAML,EAAY1B,EAAOyC,EAAOV,CAAQ,CAAC,CAAC;AAE7D,QAAAV,EAAcc,CAAM;AAAA;AAAA,IACtB,GAGMO,IACJtB,MAAe,WACXG,IACAI,MAAa,QAAQE,MAAa,OAChCW,EAASb,KAAY,GAAGE,KAAY,CAAC,IACrC,MACFc,IACJvB,MAAe,WACXK,IACAM,MAAa,OACXU,EAAOV,CAAQ,IACf,MAEFa,IAAMC,GAAWF,GAAmBD,CAAiB,GACrDI,IAAWF,MAAQ,OAAOG,GAAYH,CAAG,IAAI,MAE7CI,IAAeC;AAAA,MACnB,MACE,IAAI,KAAK,aAAajC,EAAK,UAAU;AAAA,QACnC,uBAAuB;AAAA,QACvB,uBAAuB;AAAA,MAAA,CACxB;AAAA,MACH,CAACA,EAAK,QAAQ;AAAA,IAAA;AAIhB,IAAAkC,GAAU,MAAM;AACd,MAAA7C,KAAA,QAAAA;AAAA,QACEuC,MAAQ,QAAQE,MAAa,OAAO,EAAE,KAAAF,GAAK,UAAAE,MAAa;AAAA;AAAA,IAE5D,GAAG,CAACF,GAAKE,GAAUzC,CAAc,CAAC;AAElC,UAAM8C,IAAW/B,MAAe,UAK1BgC,IAAQH,EAAQ,MAAM;AAC1B,UAAIL,MAAQ,QAAQE,MAAa,KAAM,QAAO;AAC9C,YAAM,EAAE,OAAAO,GAAO,KAAAC,GAAK,KAAAC,GAAK,OAAAC,EAAA,IAAUC,GAAA,GAC7BC,IAAaV,EAAa,OAAOJ,CAAG,GACpCe,IAAgB5C,EAAE,0BAA0B+B,CAAQ,EAAE,GACtDc,IAAa7C,EAAE,uBAAuB+B,CAAQ,EAAE,GAChDe,IAAY9C,EAAE,2BAA2B;AAAA,QAC7C,KAAK2C;AAAA,QACL,UAAUC;AAAA,QACV,OAAOC;AAAA,MAAA,CACR;AACD,aAAO;AAAA,QACL,OAAOhB;AAAA,QACP,OAAAS;AAAA,QACA,KAAAC;AAAA,QACA,KAAAC;AAAA,QACA,OAAAC;AAAA,QACA,YAAAE;AAAA,QACA,eAAAC;AAAA,QACA,YAAAC;AAAA,QACA,WAAAC;AAAA,MAAA;AAAA,IAEJ,GAAG,CAACjB,GAAKE,GAAUE,GAAcjC,CAAC,CAAC;AAEnC,WACE,gBAAA+C;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAhD;AAAA,QACA,kBAAe;AAAA,QACf,qBAAmBH;AAAA,QACnB,WAAWf,GAAa,EAAE,OAAAgB,GAAO,WAAAC,GAAW;AAAA,QAE5C,UAAA;AAAA,UAAA,gBAAAiD;AAAA,YAACC;AAAA,YAAA;AAAA,cACC,OAAOhD,EAAE,gCAAgC;AAAA,cACzC,SAAQ;AAAA,cACR,OAAOK;AAAA,cACP,eAAea;AAAA,cAEf,UAAA;AAAA,gBAAA,gBAAA+B,EAACC,KAAM,OAAOlD,EAAE,iCAAiC,GAAG,OAAM,UAAS;AAAA,gBACnE,gBAAAiD;AAAA,kBAACC;AAAA,kBAAA;AAAA,oBACC,OAAOlD,EAAE,mCAAmC;AAAA,oBAC5C,OAAM;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACR;AAAA,YAAA;AAAA,UAAA;AAAA,UAGF,gBAAA+C,EAAC,OAAA,EAAI,WAAU,uEACZ,UAAA;AAAA,YAAAX,IACC,gBAAAa;AAAA,cAACE;AAAA,cAAA;AAAA,gBACC,OAAO,GAAGnD,EAAE,sBAAsB,CAAC,KAAKA,EAAE,wBAAwB,CAAC;AAAA,gBAEnE,UAAA,gBAAAiD;AAAA,kBAACG;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,KAAK;AAAA,oBACL,MAAM;AAAA,oBACN,OAAO5C;AAAA,oBACP,UAAUC;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACZ;AAAA,YAAA,IAGF,gBAAAsC,EAAC,OAAA,EAAI,MAAK,SAAQ,mBAAiB5C,GACjC,UAAA;AAAA,cAAA,gBAAA8C;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,IAAI9C;AAAA,kBACJ,WAAU;AAAA,kBAET,YAAE,sBAAsB;AAAA,gBAAA;AAAA,cAAA;AAAA,cAE3B,gBAAA4C,EAAC,OAAA,EAAI,WAAU,sDACb,UAAA;AAAA,gBAAA,gBAAAE;AAAA,kBAACG;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,KAAK;AAAA,oBACL,OAAOxC;AAAA,oBACP,UAAUC;AAAA,oBACV,cAAYb,EAAE,oBAAoB;AAAA,kBAAA;AAAA,gBAAA;AAAA,kCAEnC,QAAA,EAAK,WAAU,uCACb,UAAAA,EAAE,wBAAwB,GAC7B;AAAA,gBACA,gBAAAiD;AAAA,kBAACG;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,KAAK;AAAA,oBACL,KAAK;AAAA,oBACL,MAAM;AAAA,oBACN,OAAOtC;AAAA,oBACP,UAAUC;AAAA,oBACV,cAAYf,EAAE,sBAAsB;AAAA,kBAAA;AAAA,gBAAA;AAAA,kCAErC,QAAA,EAAK,WAAU,uCACb,UAAAA,EAAE,wBAAwB,EAAA,CAC7B;AAAA,cAAA,EAAA,CACF;AAAA,YAAA,GACF;AAAA,YAGF,gBAAAiD;AAAA,cAACE;AAAA,cAAA;AAAA,gBACC,OAAO,GAAGnD,EAAE,sBAAsB,CAAC,KAAKA;AAAA,kBACtCoC,IAAW,2BAA2B;AAAA,gBAAA,CACvC;AAAA,gBAED,UAAA,gBAAAa;AAAA,kBAACG;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,KAAK;AAAA,oBACL,MAAMhB,IAAW,MAAM;AAAA,oBACvB,OAAOA,IAAW1B,IAAWM;AAAA,oBAC7B,UAAUoB,IAAWzB,IAAcM;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACrC;AAAA,YAAA;AAAA,UACF,GACF;AAAA,UAKA,gBAAAgC,EAAC,KAAA,EAAE,WAAU,cAAa,MAAK,UAAS,aAAU,UAC/C,UAAAZ,IAAQA,EAAM,YAAY,GAAA,CAC7B;AAAA,UAECR,MAAQ,QAAQE,MAAa,QAAQM,MAAU,OAC9C,gBAAAY,EAACI,GAAA,EAAK,SAAQ,YACZ,UAAA,gBAAAN,EAACM,EAAK,MAAL,EAAU,WAAU,kEACnB,UAAA;AAAA,YAAA,gBAAAN,EAAC,OAAA,EAAI,WAAU,iFACb,UAAA;AAAA,cAAA,gBAAAE,EAAC,QAAA,EAAK,WAAU,uCACb,UAAAjD,EAAE,8BAA8B,GACnC;AAAA,cACA,gBAAAiD,EAACK,IAAA,EAAM,SAASvE,GAAegD,CAAQ,GAAG,MAAK,MAC5C,UAAA/B,EAAE,0BAA0B+B,CAAQ,EAAE,EAAA,CACzC;AAAA,cACA,gBAAAkB,EAAC,KAAA,EAAE,WAAU,sCACV,YAAM,WAAA,CACT;AAAA,YAAA,GACF;AAAA,YAKA,gBAAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,qBAAmBlB;AAAA,gBACnB,WAAU;AAAA,gBAEV,UAAA,gBAAAkB,EAACM,IAAA,EAAY,OAAOlB,GAAO,WAAU,YAAA,CAAY;AAAA,cAAA;AAAA,YAAA;AAAA,YAElD7C,MAAkB,UAAUD,IAC3B,gBAAA0D;AAAA,cAACO;AAAA,cAAA;AAAA,gBACC,UAAAjE;AAAA,gBACA,SAASC;AAAA,gBACT,QAAAC;AAAA,gBACA,SAAAC;AAAA,gBACA,MAAM;AAAA,kBACJ,OAAOM,EAAE,kBAAkB;AAAA,kBAC3B,MAAM;AAAA,kBACN,WAAWA,EAAE,0BAA0B+B,CAAQ,EAAE;AAAA;AAAA;AAAA,kBAGjD,gBAAgB/C,GAAe+C,CAAQ;AAAA,kBACvC,OAAOpC;AAAA;AAAA;AAAA;AAAA,kBAIP,OAAO;AAAA,oBACL,SAAS;AAAA,oBACT,OAAOkC;AAAA,oBACP,OAAOQ,EAAM;AAAA,oBACb,KAAKA,EAAM;AAAA,oBACX,KAAKA,EAAM;AAAA,oBACX,YAAYA,EAAM;AAAA,oBAClB,eAAeA,EAAM;AAAA,oBACrB,YAAYA,EAAM;AAAA,oBAClB,WAAWA,EAAM;AAAA,oBACjB,OAAOA,EAAM;AAAA,kBAAA;AAAA,kBAEf,QAAQ;AAAA,oBACN;AAAA;AAAA,sBAEE,MAAM;AAAA,sBACN,OAAOrC,EAAE,kBAAkB;AAAA,sBAC3B,OAAOqC,EAAM;AAAA,oBAAA;AAAA,oBAEf;AAAA;AAAA,sBAEE,MAAM;AAAA,sBACN,OAAOrC,EAAE,8BAA8B;AAAA,sBACvC,OAAOqC,EAAM;AAAA,oBAAA;AAAA,kBACf;AAAA,gBACF;AAAA,cACF;AAAA,YAAA,IAEA;AAAA,UAAA,EAAA,CACN,EAAA,CACF,IAEA,gBAAAY,EAAC,KAAA,EAAE,WAAU,sCACV,UAAAjD,EAAE,qBAAqB,EAAA,CAC1B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEAb,GAAc,cAAc;"}
1
+ {"version":3,"file":"bmi-calculator-DuUneHuZ.js","sources":["../../src/components/bmi-calculator/bmi-calculator.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* BmiCalculator — height + weight → WHO body-mass-index, shown on a */\n/* banded-linear gauge. */\n/* */\n/* - Maths + unit conversion live in `./bmi` (pure, separately tested). */\n/* - Metric ⇄ imperial toggle persists the entered figures across the */\n/* switch by converting them (a patient measured in lb/ft doesn't */\n/* lose their numbers when a clinician flips to kg/cm). */\n/* - The gauge is the shared `BandedGauge`: a horizontal WHO-banded bar */\n/* (15–40) with a marker at the BMI's x-position. The SAME geometry */\n/* bakes into the inserted result-card PNG (banded variant), so the */\n/* on-screen widget and the rasterised card never drift. The band */\n/* fills come from semantic tokens (`--info` / `--success` / */\n/* `--warning-readable` / `--destructive`) — the contrast-safe */\n/* `--warning-readable` token handles the overweight orange in every */\n/* theme, so no `getComputedStyle` arc-colour sampling is needed. */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useId, 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 { BandedGauge, bmiBands } from '../_shared/banded-gauge';\nimport {\n type UnitSystem,\n type BmiCategory,\n computeBmi,\n bmiCategory,\n cmToFtIn,\n ftInToCm,\n kgToLb,\n lbToKg,\n} from './bmi';\n\n/* ------------------------------------------------------------------ */\n/* Result payload emitted to consumers */\n/* ------------------------------------------------------------------ */\n\nexport interface BmiResult {\n /** BMI in kg/m². */\n bmi: number;\n /** WHO category the BMI falls into. */\n category: BmiCategory;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rootVariants = cva('ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]', {\n variants: {\n width: {\n full: 'ds:w-full',\n auto: 'ds:inline-flex',\n },\n },\n defaultVariants: { width: 'full' },\n});\n\n/* ------------------------------------------------------------------ */\n/* Category → semantic intent for the Stat headline */\n/* ------------------------------------------------------------------ */\n\nconst CATEGORY_BADGE: Record<\n BmiCategory,\n 'info' | 'success' | 'warning' | 'error'\n> = {\n underweight: 'info',\n normal: 'success',\n overweight: 'warning',\n obese: 'error',\n};\n\n/* ------------------------------------------------------------------ */\n/* Category → semantic band token. */\n/* */\n/* The four WHO bands carry these tokens both on-screen (the shared */\n/* `bmiBands()` definition fills each zone with the same name) and in */\n/* the card chip's `highlightToken`. `overweight` uses */\n/* `--warning-readable` — the kit's contrast-safe orange that already */\n/* resolves correctly per theme (orange-600 in light/non-accessible, */\n/* `--warning` elsewhere), so NO `getComputedStyle` arc sampling or */\n/* `document.documentElement` override is needed. */\n/* ------------------------------------------------------------------ */\n\nconst CATEGORY_TOKEN: Record<BmiCategory, string> = {\n underweight: '--info',\n normal: '--success',\n overweight: '--warning-readable',\n obese: '--destructive',\n};\n\nexport interface BmiCalculatorProps extends VariantProps<typeof rootVariants> {\n /** Initial unit system. Defaults to `'metric'`. */\n defaultUnitSystem?: UnitSystem;\n /** Fires whenever a valid BMI can be computed (and with `null` when it can't). */\n onResultChange?: (result: BmiResult | null) => void;\n /** When provided, shows an \"Insert\" button that emits the result for an editor. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Which verb the result button performs. Defaults to `'insert'`.\n * Use `'copy'` in an app-shell surface (no editor to insert into) — the\n * button writes the result to the clipboard as a multi-format `ClipboardItem`.\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\n/** Round to one decimal place — used when seeding a unit-switch. */\nconst round1 = (n: number): number => Math.round(n * 10) / 10;\n\nexport const BmiCalculator = forwardRef<HTMLDivElement, BmiCalculatorProps>(\n (\n {\n defaultUnitSystem = 'metric',\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 const heightGroupId = useId();\n\n const [unitSystem, setUnitSystem] = useState<UnitSystem>(defaultUnitSystem);\n\n // Raw per-system inputs. Metric is canonical for cm/kg; imperial keeps\n // ft + in + lb so typing isn't fought by rounding round-trips.\n const [heightCm, setHeightCm] = useState<number | null>(null);\n const [weightKg, setWeightKg] = useState<number | null>(null);\n const [heightFt, setHeightFt] = useState<number | null>(null);\n const [heightIn, setHeightIn] = useState<number | null>(null);\n const [weightLb, setWeightLb] = useState<number | null>(null);\n\n const handleUnitChange = (next: string): void => {\n const target = next as UnitSystem;\n if (target === unitSystem) return;\n if (target === 'imperial') {\n if (heightCm !== null) {\n const { ft, in: inches } = cmToFtIn(heightCm);\n setHeightFt(ft);\n setHeightIn(round1(inches));\n }\n if (weightKg !== null) setWeightLb(round1(kgToLb(weightKg)));\n } else {\n if (heightFt !== null || heightIn !== null) {\n setHeightCm(round1(ftInToCm(heightFt ?? 0, heightIn ?? 0)));\n }\n if (weightLb !== null) setWeightKg(round1(lbToKg(weightLb)));\n }\n setUnitSystem(target);\n };\n\n /* Canonical metric figures for the active system. */\n const canonicalHeightCm =\n unitSystem === 'metric'\n ? heightCm\n : heightFt !== null || heightIn !== null\n ? ftInToCm(heightFt ?? 0, heightIn ?? 0)\n : null;\n const canonicalWeightKg =\n unitSystem === 'metric'\n ? weightKg\n : weightLb !== null\n ? lbToKg(weightLb)\n : null;\n\n const bmi = computeBmi(canonicalWeightKg, canonicalHeightCm);\n const category = bmi !== null ? bmiCategory(bmi) : null;\n\n const bmiFormatter = useMemo(\n () =>\n new Intl.NumberFormat(i18n.language, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }),\n [i18n.language],\n );\n\n // Notify consumers without re-firing on unrelated re-renders.\n useEffect(() => {\n onResultChange?.(\n bmi !== null && category !== null ? { bmi, category } : null,\n );\n }, [bmi, category, onResultChange]);\n\n const isMetric = unitSystem === 'metric';\n\n // Pre-translated gauge labels, shared by the on-screen widget and the\n // inserted card so both surfaces read identically. The shared `BandedGauge`\n // is i18n-agnostic — every visible string arrives already through `t()`.\n const gauge = useMemo(() => {\n if (bmi === null || category === null) return null;\n const { bands, min, max, ticks } = bmiBands();\n const valueLabel = bmiFormatter.format(bmi);\n const categoryLabel = t(`bmiCalculator.category.${category}`);\n const rangeLabel = t(`bmiCalculator.range.${category}`);\n const ariaLabel = t('bmiCalculator.gaugeAria', {\n bmi: valueLabel,\n category: categoryLabel,\n range: rangeLabel,\n });\n return {\n value: bmi,\n bands,\n min,\n max,\n ticks,\n valueLabel,\n categoryLabel,\n rangeLabel,\n ariaLabel,\n };\n }, [bmi, category, bmiFormatter, t]);\n\n return (\n <div\n ref={ref}\n data-component=\"bmi-calculator\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <RadioGroup\n label={t('bmiCalculator.unitSystem.label')}\n variant=\"horizontal\"\n value={unitSystem}\n onValueChange={handleUnitChange}\n >\n <Radio label={t('bmiCalculator.unitSystem.metric')} value=\"metric\" />\n <Radio\n label={t('bmiCalculator.unitSystem.imperial')}\n value=\"imperial\"\n />\n </RadioGroup>\n\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n {isMetric ? (\n <FormField\n label={`${t('bmiCalculator.height')} (${t('bmiCalculator.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 ) : (\n <div role=\"group\" aria-labelledby={heightGroupId}>\n <span\n id={heightGroupId}\n className=\"type-label ds:mb-[var(--spacing-xs)] ds:block ds:text-foreground\"\n >\n {t('bmiCalculator.height')}\n </span>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <NumberInput\n mode=\"integer\"\n min={0}\n value={heightFt}\n onChange={setHeightFt}\n aria-label={t('bmiCalculator.feet')}\n />\n <span className=\"type-label ds:text-muted-foreground\">\n {t('bmiCalculator.units.ft')}\n </span>\n <NumberInput\n mode=\"decimal\"\n min={0}\n max={11.9}\n step={0.5}\n value={heightIn}\n onChange={setHeightIn}\n aria-label={t('bmiCalculator.inches')}\n />\n <span className=\"type-label ds:text-muted-foreground\">\n {t('bmiCalculator.units.in')}\n </span>\n </div>\n </div>\n )}\n\n <FormField\n label={`${t('bmiCalculator.weight')} (${t(\n isMetric ? 'bmiCalculator.units.kg' : 'bmiCalculator.units.lb',\n )})`}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={isMetric ? 0.1 : 0.5}\n value={isMetric ? weightKg : weightLb}\n onChange={isMetric ? setWeightKg : setWeightLb}\n />\n </FormField>\n </div>\n\n {/* Concise polite announcement when the result resolves — the gauge\n itself stays out of the live region to avoid re-reading on every\n keystroke. */}\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {gauge ? gauge.ariaLabel : ''}\n </p>\n\n {bmi !== null && category !== null && gauge !== null ? (\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)] ds:text-center\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('bmiCalculator.category.label')}\n </span>\n <Badge variant={CATEGORY_BADGE[category]} size=\"lg\">\n {t(`bmiCalculator.category.${category}`)}\n </Badge>\n <p className=\"type-body ds:text-muted-foreground\">\n {gauge.rangeLabel}\n </p>\n </div>\n {/* `data-bmi-category` exposes the WHO band so the gauge's\n semantic colour is assertable without sampling pixels. The\n shared BandedGauge fills each zone via `var(<token>)`, so a\n theme switch repaints the bands with no JS re-sample. */}\n <div\n data-bmi-category={category}\n className=\"ds:w-full ds:max-w-[320px]\"\n >\n <BandedGauge model={gauge} className=\"ds:w-full\" />\n </div>\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.bmi'),\n icon: 'scale',\n highlight: t(`bmiCalculator.category.${category}`),\n // Chip shares the WHO-category semantic token so the\n // inserted PNG chip matches the on-screen band colour.\n highlightToken: CATEGORY_TOKEN[category],\n brand: insertBrand,\n // Banded gauge: the SAME geometry + WHO bands as the\n // on-screen widget, baked into the card PNG with\n // probe-resolved theme colours.\n gauge: {\n variant: 'banded',\n value: bmi,\n bands: gauge.bands,\n min: gauge.min,\n max: gauge.max,\n valueLabel: gauge.valueLabel,\n categoryLabel: gauge.categoryLabel,\n rangeLabel: gauge.rangeLabel,\n ariaLabel: gauge.ariaLabel,\n ticks: gauge.ticks,\n },\n fields: [\n {\n // Mirrors the on-screen scale glyph + header icon.\n icon: 'scale',\n label: t('insert.title.bmi'),\n value: gauge.valueLabel,\n },\n {\n // WHO band is a classification → tag glyph.\n icon: 'tag',\n label: t('bmiCalculator.category.label'),\n value: gauge.categoryLabel,\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('bmiCalculator.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nBmiCalculator.displayName = 'BmiCalculator';\n"],"names":["rootVariants","cva","CATEGORY_BADGE","CATEGORY_TOKEN","round1","n","BmiCalculator","forwardRef","defaultUnitSystem","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","t","i18n","useTranslation","heightGroupId","useId","unitSystem","setUnitSystem","useState","heightCm","setHeightCm","weightKg","setWeightKg","heightFt","setHeightFt","heightIn","setHeightIn","weightLb","setWeightLb","handleUnitChange","next","target","ft","inches","cmToFtIn","kgToLb","ftInToCm","lbToKg","canonicalHeightCm","canonicalWeightKg","bmi","computeBmi","category","bmiCategory","bmiFormatter","useMemo","useEffect","isMetric","gauge","bands","min","max","ticks","bmiBands","valueLabel","categoryLabel","rangeLabel","ariaLabel","jsxs","RadioGroup","jsx","Radio","FormField","NumberInput","Card","Badge","BandedGauge","InsertButton"],"mappings":"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"}
@@ -7,7 +7,7 @@ import { D as z } from "./date-picker-BfHblqwA.js";
7
7
  import { N as D } from "./number-input-Dj5L3pXK.js";
8
8
  import { C as E } from "./card-DPmk26CL.js";
9
9
  import { B as P } from "./badge-zsf5i5bH.js";
10
- import { I as B } from "./insert-result-DNdi_JYW.js";
10
+ import { I as B } from "./insert-result-njqBthzT.js";
11
11
  import { a as i } from "./date-picker-variants-CXEAx3O_.js";
12
12
  import { C as G } from "./check-DPdL_Sm7.js";
13
13
  import { H as R } from "./heart-C0faivFf.js";
@@ -249,4 +249,4 @@ export {
249
249
  U as L,
250
250
  O as p
251
251
  };
252
- //# sourceMappingURL=cycle-calculator-oOkj5wlB.js.map
252
+ //# sourceMappingURL=cycle-calculator-vTtZZAmn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cycle-calculator-oOkj5wlB.js","sources":["../../node_modules/lucide-react/dist/esm/icons/droplet.js","../../src/components/cycle-calculator/cycle.ts","../../src/components/cycle-calculator/cycle-calculator.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z\",\n key: \"c7niix\"\n }\n ]\n];\nconst Droplet = createLucideIcon(\"droplet\", __iconNode);\n\nexport { __iconNode, Droplet as default };\n//# sourceMappingURL=droplet.js.map\n","/* ------------------------------------------------------------------ */\n/* Menstrual-cycle prediction — pure, framework-free, unit-testable. */\n/* */\n/* Uses the standard fixed-luteal-phase model: ovulation falls ~14 days */\n/* before the next period, so on a cycle of length L ovulation is day */\n/* (L − 14). The fertile window spans the 5 days before ovulation */\n/* (sperm survival) through 1 day after (oocyte viability). */\n/* ------------------------------------------------------------------ */\n\nimport { addDays } from 'date-fns';\n\n/** Standard menstrual-cycle length. */\nexport const DEFAULT_CYCLE_LENGTH = 28;\n/** Luteal phase is biologically near-constant at ~14 days. */\nexport const LUTEAL_PHASE_DAYS = 14;\n/** Sperm survive up to ~5 days before ovulation. */\nexport const FERTILE_DAYS_BEFORE = 5;\n/** The oocyte is viable ~1 day after ovulation. */\nexport const FERTILE_DAYS_AFTER = 1;\n\nexport interface CycleInput {\n /** First day of the most recent period. */\n lastPeriodStart: Date;\n /** Average cycle length in days. Defaults to 28. */\n cycleLength?: number;\n /** Period (bleed) length in days. Optional — used for the period range. */\n periodLength?: number;\n}\n\nexport interface DateRange {\n start: Date;\n end: Date;\n}\n\nexport interface CyclePrediction {\n /** Start date of the next period. */\n nextPeriod: Date;\n /** Estimated ovulation date. */\n ovulation: Date;\n /** Fertile window (5 days before ovulation → 1 day after). */\n fertileWindow: DateRange;\n /** Start dates of the next few periods (length = `occurrences`). */\n upcomingPeriods: Date[];\n /**\n * The current period's bleed range (first day → last day), or `null` when no\n * `periodLength` was given.\n */\n periodRange: DateRange | null;\n}\n\n/**\n * Predict ovulation, the fertile window and upcoming periods from the last\n * period's start date. `occurrences` controls how many future periods to\n * list (default 3).\n */\nexport function predictCycle(\n input: CycleInput,\n occurrences = 3,\n): CyclePrediction {\n const cycle = input.cycleLength ?? DEFAULT_CYCLE_LENGTH;\n const { lastPeriodStart } = input;\n\n const ovulation = addDays(lastPeriodStart, cycle - LUTEAL_PHASE_DAYS);\n const fertileWindow: DateRange = {\n start: addDays(ovulation, -FERTILE_DAYS_BEFORE),\n end: addDays(ovulation, FERTILE_DAYS_AFTER),\n };\n const nextPeriod = addDays(lastPeriodStart, cycle);\n const upcomingPeriods = Array.from({ length: occurrences }, (_, i) =>\n addDays(lastPeriodStart, cycle * (i + 1)),\n );\n const periodRange: DateRange | null =\n input.periodLength && input.periodLength > 0\n ? {\n start: lastPeriodStart,\n end: addDays(lastPeriodStart, input.periodLength - 1),\n }\n : null;\n\n return { nextPeriod, ovulation, fertileWindow, upcomingPeriods, periodRange };\n}\n","/* ------------------------------------------------------------------ */\n/* CycleCalculator — predict ovulation, fertile window and upcoming */\n/* periods from the last period's start date. */\n/* */\n/* Maths lives in `./cycle` (pure, separately tested). */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Heart, Check, Droplet } from 'lucide-react';\nimport { FormField } from '../form-field';\nimport { DatePicker } from '../date-picker';\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 {\n type CyclePrediction,\n predictCycle,\n DEFAULT_CYCLE_LENGTH,\n} from './cycle';\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\n/**\n * DS token NAME backing the result-card chip — the fertile window is the\n * headline fertility datum, shown on screen with the `accent` Badge variant\n * (which fills `--accent`). The card chip reuses that token so the inserted\n * PNG chip matches the on-screen fertile-window badge.\n */\nconst HIGHLIGHT_TOKEN = '--accent';\n\nexport interface CycleCalculatorProps extends VariantProps<\n typeof rootVariants\n> {\n /** Initial cycle length in days. Defaults to 28. */\n defaultCycleLength?: number;\n /** Fires whenever a prediction can be computed (and `null` when it can't). */\n onResultChange?: (result: CyclePrediction | null) => void;\n /** When provided, shows the result-action buttons that emit / copy the result. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Which verb the result button performs. Defaults to `'insert'`.\n * Use `'copy'` in an app-shell surface (no editor to insert into) — the\n * button writes the result to the clipboard as a multi-format `ClipboardItem`.\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 CycleCalculator = forwardRef<HTMLDivElement, CycleCalculatorProps>(\n (\n {\n defaultCycleLength = DEFAULT_CYCLE_LENGTH,\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 [lastPeriod, setLastPeriod] = useState<Date | undefined>(undefined);\n const [cycleLength, setCycleLength] = useState<number | null>(\n defaultCycleLength,\n );\n const [periodLength, setPeriodLength] = useState<number | null>(null);\n\n const result = useMemo<CyclePrediction | null>(() => {\n if (!lastPeriod) return null;\n return predictCycle({\n lastPeriodStart: lastPeriod,\n cycleLength: cycleLength ?? DEFAULT_CYCLE_LENGTH,\n periodLength: periodLength ?? undefined,\n });\n }, [lastPeriod, cycleLength, periodLength]);\n\n const dateFormatter = useMemo(\n () => new Intl.DateTimeFormat(i18n.language, { dateStyle: 'medium' }),\n [i18n.language],\n );\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n const today = useMemo(() => new Date(), []);\n\n const fmt = (d: Date): string => dateFormatter.format(d);\n\n return (\n <div\n ref={ref}\n data-component=\"cycle-calculator\"\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 label={t('cycleCalculator.lastPeriod')}>\n <DatePicker\n value={lastPeriod}\n onChange={setLastPeriod}\n maxDate={today}\n />\n </FormField>\n <FormField\n label={t('cycleCalculator.cycleLength')}\n description={t('cycleCalculator.cycleLengthHint')}\n >\n <NumberInput\n mode=\"integer\"\n min={20}\n max={45}\n value={cycleLength}\n onChange={setCycleLength}\n />\n </FormField>\n <FormField\n label={t('cycleCalculator.periodLength')}\n description={t('cycleCalculator.periodLengthHint')}\n >\n <NumberInput\n mode=\"integer\"\n min={1}\n max={14}\n value={periodLength}\n onChange={setPeriodLength}\n />\n </FormField>\n </div>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result\n ? `${t('cycleCalculator.ovulation')}: ${fmt(result.ovulation)}. ${t(\n 'cycleCalculator.nextPeriod',\n )}: ${fmt(result.nextPeriod)}.`\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-3\">\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)] ds:text-center\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.ovulation')}\n </dt>\n <dd>\n <Badge\n variant=\"success\"\n size=\"lg\"\n leading={<Check aria-hidden />}\n >\n {fmt(result.ovulation)}\n </Badge>\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)] ds:text-center\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.fertileWindow')}\n </dt>\n <dd>\n <Badge\n variant=\"accent\"\n size=\"lg\"\n leading={<Heart aria-hidden />}\n >\n {fmt(result.fertileWindow.start)} –{' '}\n {fmt(result.fertileWindow.end)}\n </Badge>\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)] ds:text-center\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.nextPeriod')}\n </dt>\n <dd>\n <Badge\n variant=\"error\"\n size=\"lg\"\n leading={<Droplet aria-hidden />}\n >\n {fmt(result.nextPeriod)}\n </Badge>\n </dd>\n </div>\n </dl>\n\n {result.periodRange ? (\n <div className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-2xs)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.periodRange')}\n </span>\n <span className=\"type-body ds:text-foreground\">\n {fmt(result.periodRange.start)} –{' '}\n {fmt(result.periodRange.end)}\n </span>\n </div>\n ) : null}\n\n <div className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-2xs)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.upcomingPeriods')}\n </span>\n <ul className=\"ds:flex ds:flex-wrap ds:gap-x-[var(--spacing-md)] ds:gap-y-[var(--spacing-2xs)]\">\n {result.upcomingPeriods.map((d) => (\n <li\n key={d.getTime()}\n className=\"type-body ds:text-foreground\"\n >\n {fmt(d)}\n </li>\n ))}\n </ul>\n </div>\n\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.cycle'),\n icon: 'heart',\n highlight: `${fmt(result.fertileWindow.start)} – ${fmt(\n result.fertileWindow.end,\n )}`,\n // Chip reuses the on-screen fertile-window badge's accent\n // token so the inserted PNG chip matches the screen.\n highlightToken: HIGHLIGHT_TOKEN,\n brand: insertBrand,\n fields: [\n {\n // Ovulation is a predicted date → calendar glyph (the\n // on-screen badge uses a Check tick, which ICON_GEOMETRY\n // does not carry, so the calendar reads the date meaning).\n icon: 'calendar',\n label: t('cycleCalculator.ovulation'),\n value: fmt(result.ovulation),\n },\n {\n // Fertile window → heart glyph, matching the on-screen\n // fertile-window badge's Heart icon.\n icon: 'heart',\n label: t('cycleCalculator.fertileWindow'),\n value: `${fmt(result.fertileWindow.start)} – ${fmt(\n result.fertileWindow.end,\n )}`,\n },\n {\n // Next period → droplets glyph, matching the on-screen\n // next-period badge's Droplet icon.\n icon: 'droplets',\n label: t('cycleCalculator.nextPeriod'),\n value: fmt(result.nextPeriod),\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('cycleCalculator.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nCycleCalculator.displayName = 'CycleCalculator';\n"],"names":["__iconNode","Droplet","createLucideIcon","DEFAULT_CYCLE_LENGTH","LUTEAL_PHASE_DAYS","FERTILE_DAYS_AFTER","predictCycle","input","occurrences","cycle","lastPeriodStart","ovulation","addDays","fertileWindow","nextPeriod","upcomingPeriods","_","i","periodRange","rootVariants","cva","HIGHLIGHT_TOKEN","CycleCalculator","forwardRef","defaultCycleLength","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","i18n","useTranslation","lastPeriod","setLastPeriod","useState","cycleLength","setCycleLength","periodLength","setPeriodLength","result","useMemo","dateFormatter","useEffect","today","fmt","d","jsxs","jsx","FormField","DatePicker","NumberInput","Card","Badge","Check","Heart","InsertButton"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"cycle-calculator-vTtZZAmn.js","sources":["../../node_modules/lucide-react/dist/esm/icons/droplet.js","../../src/components/cycle-calculator/cycle.ts","../../src/components/cycle-calculator/cycle-calculator.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z\",\n key: \"c7niix\"\n }\n ]\n];\nconst Droplet = createLucideIcon(\"droplet\", __iconNode);\n\nexport { __iconNode, Droplet as default };\n//# sourceMappingURL=droplet.js.map\n","/* ------------------------------------------------------------------ */\n/* Menstrual-cycle prediction — pure, framework-free, unit-testable. */\n/* */\n/* Uses the standard fixed-luteal-phase model: ovulation falls ~14 days */\n/* before the next period, so on a cycle of length L ovulation is day */\n/* (L − 14). The fertile window spans the 5 days before ovulation */\n/* (sperm survival) through 1 day after (oocyte viability). */\n/* ------------------------------------------------------------------ */\n\nimport { addDays } from 'date-fns';\n\n/** Standard menstrual-cycle length. */\nexport const DEFAULT_CYCLE_LENGTH = 28;\n/** Luteal phase is biologically near-constant at ~14 days. */\nexport const LUTEAL_PHASE_DAYS = 14;\n/** Sperm survive up to ~5 days before ovulation. */\nexport const FERTILE_DAYS_BEFORE = 5;\n/** The oocyte is viable ~1 day after ovulation. */\nexport const FERTILE_DAYS_AFTER = 1;\n\nexport interface CycleInput {\n /** First day of the most recent period. */\n lastPeriodStart: Date;\n /** Average cycle length in days. Defaults to 28. */\n cycleLength?: number;\n /** Period (bleed) length in days. Optional — used for the period range. */\n periodLength?: number;\n}\n\nexport interface DateRange {\n start: Date;\n end: Date;\n}\n\nexport interface CyclePrediction {\n /** Start date of the next period. */\n nextPeriod: Date;\n /** Estimated ovulation date. */\n ovulation: Date;\n /** Fertile window (5 days before ovulation → 1 day after). */\n fertileWindow: DateRange;\n /** Start dates of the next few periods (length = `occurrences`). */\n upcomingPeriods: Date[];\n /**\n * The current period's bleed range (first day → last day), or `null` when no\n * `periodLength` was given.\n */\n periodRange: DateRange | null;\n}\n\n/**\n * Predict ovulation, the fertile window and upcoming periods from the last\n * period's start date. `occurrences` controls how many future periods to\n * list (default 3).\n */\nexport function predictCycle(\n input: CycleInput,\n occurrences = 3,\n): CyclePrediction {\n const cycle = input.cycleLength ?? DEFAULT_CYCLE_LENGTH;\n const { lastPeriodStart } = input;\n\n const ovulation = addDays(lastPeriodStart, cycle - LUTEAL_PHASE_DAYS);\n const fertileWindow: DateRange = {\n start: addDays(ovulation, -FERTILE_DAYS_BEFORE),\n end: addDays(ovulation, FERTILE_DAYS_AFTER),\n };\n const nextPeriod = addDays(lastPeriodStart, cycle);\n const upcomingPeriods = Array.from({ length: occurrences }, (_, i) =>\n addDays(lastPeriodStart, cycle * (i + 1)),\n );\n const periodRange: DateRange | null =\n input.periodLength && input.periodLength > 0\n ? {\n start: lastPeriodStart,\n end: addDays(lastPeriodStart, input.periodLength - 1),\n }\n : null;\n\n return { nextPeriod, ovulation, fertileWindow, upcomingPeriods, periodRange };\n}\n","/* ------------------------------------------------------------------ */\n/* CycleCalculator — predict ovulation, fertile window and upcoming */\n/* periods from the last period's start date. */\n/* */\n/* Maths lives in `./cycle` (pure, separately tested). */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Heart, Check, Droplet } from 'lucide-react';\nimport { FormField } from '../form-field';\nimport { DatePicker } from '../date-picker';\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 {\n type CyclePrediction,\n predictCycle,\n DEFAULT_CYCLE_LENGTH,\n} from './cycle';\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\n/**\n * DS token NAME backing the result-card chip — the fertile window is the\n * headline fertility datum, shown on screen with the `accent` Badge variant\n * (which fills `--accent`). The card chip reuses that token so the inserted\n * PNG chip matches the on-screen fertile-window badge.\n */\nconst HIGHLIGHT_TOKEN = '--accent';\n\nexport interface CycleCalculatorProps extends VariantProps<\n typeof rootVariants\n> {\n /** Initial cycle length in days. Defaults to 28. */\n defaultCycleLength?: number;\n /** Fires whenever a prediction can be computed (and `null` when it can't). */\n onResultChange?: (result: CyclePrediction | null) => void;\n /** When provided, shows the result-action buttons that emit / copy the result. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Which verb the result button performs. Defaults to `'insert'`.\n * Use `'copy'` in an app-shell surface (no editor to insert into) — the\n * button writes the result to the clipboard as a multi-format `ClipboardItem`.\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 CycleCalculator = forwardRef<HTMLDivElement, CycleCalculatorProps>(\n (\n {\n defaultCycleLength = DEFAULT_CYCLE_LENGTH,\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 [lastPeriod, setLastPeriod] = useState<Date | undefined>(undefined);\n const [cycleLength, setCycleLength] = useState<number | null>(\n defaultCycleLength,\n );\n const [periodLength, setPeriodLength] = useState<number | null>(null);\n\n const result = useMemo<CyclePrediction | null>(() => {\n if (!lastPeriod) return null;\n return predictCycle({\n lastPeriodStart: lastPeriod,\n cycleLength: cycleLength ?? DEFAULT_CYCLE_LENGTH,\n periodLength: periodLength ?? undefined,\n });\n }, [lastPeriod, cycleLength, periodLength]);\n\n const dateFormatter = useMemo(\n () => new Intl.DateTimeFormat(i18n.language, { dateStyle: 'medium' }),\n [i18n.language],\n );\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n const today = useMemo(() => new Date(), []);\n\n const fmt = (d: Date): string => dateFormatter.format(d);\n\n return (\n <div\n ref={ref}\n data-component=\"cycle-calculator\"\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 label={t('cycleCalculator.lastPeriod')}>\n <DatePicker\n value={lastPeriod}\n onChange={setLastPeriod}\n maxDate={today}\n />\n </FormField>\n <FormField\n label={t('cycleCalculator.cycleLength')}\n description={t('cycleCalculator.cycleLengthHint')}\n >\n <NumberInput\n mode=\"integer\"\n min={20}\n max={45}\n value={cycleLength}\n onChange={setCycleLength}\n />\n </FormField>\n <FormField\n label={t('cycleCalculator.periodLength')}\n description={t('cycleCalculator.periodLengthHint')}\n >\n <NumberInput\n mode=\"integer\"\n min={1}\n max={14}\n value={periodLength}\n onChange={setPeriodLength}\n />\n </FormField>\n </div>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result\n ? `${t('cycleCalculator.ovulation')}: ${fmt(result.ovulation)}. ${t(\n 'cycleCalculator.nextPeriod',\n )}: ${fmt(result.nextPeriod)}.`\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-3\">\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)] ds:text-center\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.ovulation')}\n </dt>\n <dd>\n <Badge\n variant=\"success\"\n size=\"lg\"\n leading={<Check aria-hidden />}\n >\n {fmt(result.ovulation)}\n </Badge>\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)] ds:text-center\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.fertileWindow')}\n </dt>\n <dd>\n <Badge\n variant=\"accent\"\n size=\"lg\"\n leading={<Heart aria-hidden />}\n >\n {fmt(result.fertileWindow.start)} –{' '}\n {fmt(result.fertileWindow.end)}\n </Badge>\n </dd>\n </div>\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)] ds:text-center\">\n <dt className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.nextPeriod')}\n </dt>\n <dd>\n <Badge\n variant=\"error\"\n size=\"lg\"\n leading={<Droplet aria-hidden />}\n >\n {fmt(result.nextPeriod)}\n </Badge>\n </dd>\n </div>\n </dl>\n\n {result.periodRange ? (\n <div className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-2xs)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.periodRange')}\n </span>\n <span className=\"type-body ds:text-foreground\">\n {fmt(result.periodRange.start)} –{' '}\n {fmt(result.periodRange.end)}\n </span>\n </div>\n ) : null}\n\n <div className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-2xs)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('cycleCalculator.upcomingPeriods')}\n </span>\n <ul className=\"ds:flex ds:flex-wrap ds:gap-x-[var(--spacing-md)] ds:gap-y-[var(--spacing-2xs)]\">\n {result.upcomingPeriods.map((d) => (\n <li\n key={d.getTime()}\n className=\"type-body ds:text-foreground\"\n >\n {fmt(d)}\n </li>\n ))}\n </ul>\n </div>\n\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.cycle'),\n icon: 'heart',\n highlight: `${fmt(result.fertileWindow.start)} – ${fmt(\n result.fertileWindow.end,\n )}`,\n // Chip reuses the on-screen fertile-window badge's accent\n // token so the inserted PNG chip matches the screen.\n highlightToken: HIGHLIGHT_TOKEN,\n brand: insertBrand,\n fields: [\n {\n // Ovulation is a predicted date → calendar glyph (the\n // on-screen badge uses a Check tick, which ICON_GEOMETRY\n // does not carry, so the calendar reads the date meaning).\n icon: 'calendar',\n label: t('cycleCalculator.ovulation'),\n value: fmt(result.ovulation),\n },\n {\n // Fertile window → heart glyph, matching the on-screen\n // fertile-window badge's Heart icon.\n icon: 'heart',\n label: t('cycleCalculator.fertileWindow'),\n value: `${fmt(result.fertileWindow.start)} – ${fmt(\n result.fertileWindow.end,\n )}`,\n },\n {\n // Next period → droplets glyph, matching the on-screen\n // next-period badge's Droplet icon.\n icon: 'droplets',\n label: t('cycleCalculator.nextPeriod'),\n value: fmt(result.nextPeriod),\n },\n ],\n }}\n />\n ) : null}\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('cycleCalculator.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nCycleCalculator.displayName = 'CycleCalculator';\n"],"names":["__iconNode","Droplet","createLucideIcon","DEFAULT_CYCLE_LENGTH","LUTEAL_PHASE_DAYS","FERTILE_DAYS_AFTER","predictCycle","input","occurrences","cycle","lastPeriodStart","ovulation","addDays","fertileWindow","nextPeriod","upcomingPeriods","_","i","periodRange","rootVariants","cva","HIGHLIGHT_TOKEN","CycleCalculator","forwardRef","defaultCycleLength","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","i18n","useTranslation","lastPeriod","setLastPeriod","useState","cycleLength","setCycleLength","periodLength","setPeriodLength","result","useMemo","dateFormatter","useEffect","today","fmt","d","jsxs","jsx","FormField","DatePicker","NumberInput","Card","Badge","Check","Heart","InsertButton"],"mappings":"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","x_google_ignoreList":[0]}
@@ -9,7 +9,7 @@ import { D as b } from "./date-picker-BfHblqwA.js";
9
9
  import { N as S } from "./number-input-Dj5L3pXK.js";
10
10
  import { C as k } from "./card-DPmk26CL.js";
11
11
  import { B } from "./badge-zsf5i5bH.js";
12
- import { I as X } from "./insert-result-DNdi_JYW.js";
12
+ import { I as X } from "./insert-result-njqBthzT.js";
13
13
  import { D as y, r as Z, c as ee, e as ae, f as te, A as le, h as re, i as se } from "./antenatal-schedule-timeline-CdiqkF05.js";
14
14
  const de = Q("ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]", {
15
15
  variants: {
@@ -253,4 +253,4 @@ ce.displayName = "DueDateCalculator";
253
253
  export {
254
254
  ce as D
255
255
  };
256
- //# sourceMappingURL=due-date-calculator-DykajWzh.js.map
256
+ //# sourceMappingURL=due-date-calculator-CUm5KJbf.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"due-date-calculator-DykajWzh.js","sources":["../../src/components/due-date-calculator/due-date-calculator.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* DueDateCalculator — estimate the date of delivery from one of three */\n/* dating methods, with current gestational age + trimester. */\n/* */\n/* - Maths lives in `./gestation` (pure, separately tested). */\n/* - Three methods: LMP (Naegele, cycle-length adjustable), conception, */\n/* and IVF transfer (Day-3 vs Day-5 embryo). */\n/* - The reference date can't be in the future, so the DatePicker caps */\n/* at today. */\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 { DatePicker } from '../date-picker';\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 {\n type DueDateMethod,\n type EmbryoAge,\n type DueDateResult,\n computeDueDate,\n redateByCrl,\n DEFAULT_CYCLE_LENGTH,\n TRIMESTER_BADGE_VARIANT,\n trimesterHighlightToken,\n} from '../_shared/obstetrics';\nimport {\n computeAntenatalSchedule,\n type AntenatalScheduleOptions,\n} from '../_shared/antenatal-schedule';\nimport {\n AntenatalScheduleTimeline,\n buildScheduleInsertFields,\n} from '../_shared/antenatal-schedule-timeline';\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\n/** Reference-date i18n key per method. */\nconst DATE_LABEL_KEY: Record<DueDateMethod, string> = {\n lmp: 'dueDateCalculator.lmpDate',\n conception: 'dueDateCalculator.conceptionDate',\n ivf: 'dueDateCalculator.transferDate',\n};\n\nexport interface DueDateCalculatorProps extends VariantProps<\n typeof rootVariants\n> {\n /** Initial dating method. Defaults to `'lmp'`. */\n defaultMethod?: DueDateMethod;\n /** Fires whenever a due date can be computed (and with `null` when it can't). */\n onResultChange?: (result: DueDateResult | null) => void;\n /** When provided, shows an \"Insert\" button that emits the result for an editor. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Which verb the result button performs. Defaults to `'insert'`.\n * Use `'copy'` in an app-shell surface (no editor to insert into) — the\n * button writes the result to the clipboard as a multi-format `ClipboardItem`.\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 /**\n * Append the antenatal exam schedule (the time-sensitive screening windows)\n * below the result, dated from the computed gestational start.\n */\n showSchedule?: boolean;\n /**\n * Passthrough options for the appended schedule (national `schedule`,\n * `plurality`, `chorionicity`, `rhNegative`, `gdmRisk`, …). `today` is\n * supplied by the component. Ignored unless `showSchedule` is set.\n */\n scheduleOptions?: Omit<AntenatalScheduleOptions, 'today'>;\n /** Opaque instance id, emitted as `data-component-id`. */\n id?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\n/**\n * @deprecated Superseded by `PregnancyDating`, which unifies the due-date and\n * gestational-age calculators (any anchor → both EDD and current GA) and adds a\n * manual ±days dating adjustment. This component will be removed in a future\n * release; migrate to `PregnancyDating` (`defaultMethod=\"lmp\"`).\n */\nexport const DueDateCalculator = forwardRef<\n HTMLDivElement,\n DueDateCalculatorProps\n>(\n (\n {\n defaultMethod = 'lmp',\n onResultChange,\n onInsert,\n insertVariant = 'insert',\n onCopy,\n onError,\n insertBrand,\n showSchedule = false,\n scheduleOptions,\n id,\n width,\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n\n const [method, setMethod] = useState<DueDateMethod>(defaultMethod);\n const [refDate, setRefDate] = useState<Date | undefined>(undefined);\n const [cycleLength, setCycleLength] = useState<number | null>(\n DEFAULT_CYCLE_LENGTH,\n );\n const [embryoAge, setEmbryoAge] = useState<EmbryoAge>(5);\n // Optional first-trimester ultrasound (LMP method only) for ACOG CO700 redating.\n const [crlMm, setCrlMm] = useState<number | null>(null);\n const [scanDate, setScanDate] = useState<Date | undefined>(undefined);\n\n // Real today — caps the date inputs (LMP / ultrasound can't be in the future).\n const today = useMemo(() => new Date(), []);\n // The date GA, trimester and schedule status are evaluated against; defaults\n // to today but can be a future appointment date to project forward. (The EDD\n // itself is dating-anchored and does not depend on this.)\n const [asOfDate, setAsOfDate] = useState<Date>(() => new Date());\n\n // ACOG CO700 CRL redating — only when an LMP, a CRL and a scan date are set.\n const redating = useMemo(() => {\n if (method !== 'lmp' || !refDate || crlMm === null || !scanDate) {\n return null;\n }\n return redateByCrl({ lmp: refDate, scanDate, crlMm });\n }, [method, refDate, crlMm, scanDate]);\n\n const result = useMemo<DueDateResult | null>(() => {\n if (!refDate) return null;\n // A confirmed redating moves the effective LMP to the ultrasound's; the\n // CRL already reflects true gestation, so cycle adjustment no longer applies.\n const effectiveDate =\n method === 'lmp' && redating?.redated\n ? redating.gestationalStart\n : refDate;\n const effectiveCycle =\n method === 'lmp' && redating?.redated\n ? DEFAULT_CYCLE_LENGTH\n : (cycleLength ?? DEFAULT_CYCLE_LENGTH);\n return computeDueDate(\n {\n method,\n date: effectiveDate,\n cycleLength: effectiveCycle,\n embryoAge,\n },\n asOfDate,\n );\n }, [method, refDate, cycleLength, embryoAge, redating, asOfDate]);\n\n const dateFormatter = useMemo(\n () => new Intl.DateTimeFormat(i18n.language, { dateStyle: 'long' }),\n [i18n.language],\n );\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n const schedule = useMemo(() => {\n if (!showSchedule || !result) return null;\n return computeAntenatalSchedule(result.gestationalStart, {\n ...scheduleOptions,\n today: asOfDate,\n });\n }, [showSchedule, result, asOfDate, scheduleOptions]);\n\n return (\n <div\n ref={ref}\n data-component=\"due-date-calculator\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <RadioGroup\n label={t('dueDateCalculator.method.label')}\n variant=\"horizontal\"\n value={method}\n onValueChange={(next) => setMethod(next as DueDateMethod)}\n >\n <Radio label={t('dueDateCalculator.method.lmp')} value=\"lmp\" />\n <Radio\n label={t('dueDateCalculator.method.conception')}\n value=\"conception\"\n />\n <Radio label={t('dueDateCalculator.method.ivf')} value=\"ivf\" />\n </RadioGroup>\n\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <FormField label={t(DATE_LABEL_KEY[method])}>\n <DatePicker value={refDate} onChange={setRefDate} maxDate={today} />\n </FormField>\n\n {method === 'lmp' && (\n <FormField\n label={t('dueDateCalculator.cycleLength')}\n description={t('dueDateCalculator.cycleLengthHint')}\n >\n <NumberInput\n mode=\"integer\"\n min={20}\n max={45}\n value={cycleLength}\n onChange={setCycleLength}\n />\n </FormField>\n )}\n\n {method === 'ivf' && (\n <RadioGroup\n label={t('dueDateCalculator.embryoAge')}\n variant=\"horizontal\"\n value={String(embryoAge)}\n onValueChange={(next) => setEmbryoAge(Number(next) as EmbryoAge)}\n >\n <Radio label={t('dueDateCalculator.day3')} value=\"3\" />\n <Radio label={t('dueDateCalculator.day5')} value=\"5\" />\n </RadioGroup>\n )}\n </div>\n\n <FormField\n label={t('dueDateCalculator.asOfDate')}\n description={t('dueDateCalculator.asOfDateHint')}\n >\n <DatePicker value={asOfDate} onChange={(d) => d && setAsOfDate(d)} />\n </FormField>\n\n {method === 'lmp' ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <FormField\n label={t('dueDateCalculator.crl')}\n description={t('dueDateCalculator.crlHint')}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.1}\n value={crlMm}\n onChange={setCrlMm}\n />\n </FormField>\n <FormField label={t('dueDateCalculator.scanDate')}>\n <DatePicker\n value={scanDate}\n onChange={setScanDate}\n maxDate={today}\n />\n </FormField>\n </div>\n {redating ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)]\">\n <Badge variant={redating.redated ? 'warning' : 'success'}>\n {redating.redated\n ? t('dueDateCalculator.redated', {\n days: redating.discrepancyDays,\n })\n : t('dueDateCalculator.datesAgree')}\n </Badge>\n {!redating.crlInRange ? (\n <span className=\"type-meta ds:text-muted-foreground\">\n {t('dueDateCalculator.crlOutOfRange')}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n\n {/* Concise polite announcement when a due date resolves. */}\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result\n ? `${t('dueDateCalculator.result.dueDateLabel')}: ${dateFormatter.format(\n result.dueDate,\n )}. ${t('dueDateCalculator.result.trimesterLabel')}: ${t(\n `dueDateCalculator.trimester.${result.trimester}`,\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-3\">\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('dueDateCalculator.result.trimesterLabel')}\n </dt>\n <dd>\n <Badge\n variant={TRIMESTER_BADGE_VARIANT[result.trimester]}\n size=\"lg\"\n >\n {t(`dueDateCalculator.trimester.${result.trimester}`)}\n </Badge>\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('dueDateCalculator.result.gestationLabel')}\n </dt>\n <dd className=\"type-body ds:text-foreground\">\n {result.gestationalAge\n ? t('dueDateCalculator.result.gestation', {\n weeks: result.gestationalAge.weeks,\n days: result.gestationalAge.days,\n })\n : '—'}\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('dueDateCalculator.result.dueDateLabel')}\n </dt>\n <dd className=\"type-metric ds:text-foreground\">\n {dateFormatter.format(result.dueDate)}\n </dd>\n </div>\n </dl>\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('dueDateCalculator.empty')}\n </p>\n )}\n\n {schedule ? <AntenatalScheduleTimeline events={schedule} /> : null}\n\n {/* One result-action group for the whole result — sits after both the\n due-date card and the schedule, and its payload carries the schedule\n rows when shown (no per-card buttons). */}\n {result && (insertVariant === 'copy' || onInsert) ? (\n <InsertButton\n variant={insertVariant}\n onInsert={onInsert}\n onCopy={onCopy}\n onError={onError}\n card={{\n title: t('insert.title.dueDate'),\n icon: 'calendar-heart',\n highlight: dateFormatter.format(result.dueDate),\n // Chip tinted with the trimester's semantic token so the inserted\n // PNG carries the same clinical-stage colour as the on-screen\n // trimester badge — with the contrast-safe orange override for the\n // `--warning` third trimester.\n highlightToken: trimesterHighlightToken(result.trimester),\n brand: insertBrand,\n fields: [\n {\n // The estimated date of delivery → calendar glyph.\n icon: 'calendar',\n label: t('dueDateCalculator.result.dueDateLabel'),\n value: dateFormatter.format(result.dueDate),\n },\n {\n // Gestational age is an elapsed duration → clock glyph.\n icon: 'clock',\n label: t('dueDateCalculator.result.gestationLabel'),\n value: result.gestationalAge\n ? t('dueDateCalculator.result.gestation', {\n weeks: result.gestationalAge.weeks,\n days: result.gestationalAge.days,\n })\n : '—',\n },\n {\n // Trimester is a clinical-stage classification → tag.\n icon: 'tag',\n label: t('dueDateCalculator.result.trimesterLabel'),\n value: t(`dueDateCalculator.trimester.${result.trimester}`),\n },\n // When the schedule is shown, the single Insert button carries\n // the exam windows too (no second button).\n ...(schedule\n ? buildScheduleInsertFields(schedule, t, (d) =>\n dateFormatter.format(d),\n )\n : []),\n ],\n }}\n />\n ) : null}\n </div>\n );\n },\n);\n\nDueDateCalculator.displayName = 'DueDateCalculator';\n"],"names":["rootVariants","cva","DATE_LABEL_KEY","DueDateCalculator","forwardRef","defaultMethod","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","showSchedule","scheduleOptions","id","width","className","ref","t","i18n","useTranslation","method","setMethod","useState","refDate","setRefDate","cycleLength","setCycleLength","DEFAULT_CYCLE_LENGTH","embryoAge","setEmbryoAge","crlMm","setCrlMm","scanDate","setScanDate","today","useMemo","asOfDate","setAsOfDate","redating","redateByCrl","result","effectiveDate","effectiveCycle","computeDueDate","dateFormatter","useEffect","schedule","computeAntenatalSchedule","jsxs","RadioGroup","next","jsx","Radio","FormField","DatePicker","NumberInput","Badge","Card","TRIMESTER_BADGE_VARIANT","AntenatalScheduleTimeline","InsertButton","trimesterHighlightToken","buildScheduleInsertFields"],"mappings":"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"}
1
+ {"version":3,"file":"due-date-calculator-CUm5KJbf.js","sources":["../../src/components/due-date-calculator/due-date-calculator.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* DueDateCalculator — estimate the date of delivery from one of three */\n/* dating methods, with current gestational age + trimester. */\n/* */\n/* - Maths lives in `./gestation` (pure, separately tested). */\n/* - Three methods: LMP (Naegele, cycle-length adjustable), conception, */\n/* and IVF transfer (Day-3 vs Day-5 embryo). */\n/* - The reference date can't be in the future, so the DatePicker caps */\n/* at today. */\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 { DatePicker } from '../date-picker';\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 {\n type DueDateMethod,\n type EmbryoAge,\n type DueDateResult,\n computeDueDate,\n redateByCrl,\n DEFAULT_CYCLE_LENGTH,\n TRIMESTER_BADGE_VARIANT,\n trimesterHighlightToken,\n} from '../_shared/obstetrics';\nimport {\n computeAntenatalSchedule,\n type AntenatalScheduleOptions,\n} from '../_shared/antenatal-schedule';\nimport {\n AntenatalScheduleTimeline,\n buildScheduleInsertFields,\n} from '../_shared/antenatal-schedule-timeline';\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\n/** Reference-date i18n key per method. */\nconst DATE_LABEL_KEY: Record<DueDateMethod, string> = {\n lmp: 'dueDateCalculator.lmpDate',\n conception: 'dueDateCalculator.conceptionDate',\n ivf: 'dueDateCalculator.transferDate',\n};\n\nexport interface DueDateCalculatorProps extends VariantProps<\n typeof rootVariants\n> {\n /** Initial dating method. Defaults to `'lmp'`. */\n defaultMethod?: DueDateMethod;\n /** Fires whenever a due date can be computed (and with `null` when it can't). */\n onResultChange?: (result: DueDateResult | null) => void;\n /** When provided, shows an \"Insert\" button that emits the result for an editor. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Which verb the result button performs. Defaults to `'insert'`.\n * Use `'copy'` in an app-shell surface (no editor to insert into) — the\n * button writes the result to the clipboard as a multi-format `ClipboardItem`.\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 /**\n * Append the antenatal exam schedule (the time-sensitive screening windows)\n * below the result, dated from the computed gestational start.\n */\n showSchedule?: boolean;\n /**\n * Passthrough options for the appended schedule (national `schedule`,\n * `plurality`, `chorionicity`, `rhNegative`, `gdmRisk`, …). `today` is\n * supplied by the component. Ignored unless `showSchedule` is set.\n */\n scheduleOptions?: Omit<AntenatalScheduleOptions, 'today'>;\n /** Opaque instance id, emitted as `data-component-id`. */\n id?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\n/**\n * @deprecated Superseded by `PregnancyDating`, which unifies the due-date and\n * gestational-age calculators (any anchor → both EDD and current GA) and adds a\n * manual ±days dating adjustment. This component will be removed in a future\n * release; migrate to `PregnancyDating` (`defaultMethod=\"lmp\"`).\n */\nexport const DueDateCalculator = forwardRef<\n HTMLDivElement,\n DueDateCalculatorProps\n>(\n (\n {\n defaultMethod = 'lmp',\n onResultChange,\n onInsert,\n insertVariant = 'insert',\n onCopy,\n onError,\n insertBrand,\n showSchedule = false,\n scheduleOptions,\n id,\n width,\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n\n const [method, setMethod] = useState<DueDateMethod>(defaultMethod);\n const [refDate, setRefDate] = useState<Date | undefined>(undefined);\n const [cycleLength, setCycleLength] = useState<number | null>(\n DEFAULT_CYCLE_LENGTH,\n );\n const [embryoAge, setEmbryoAge] = useState<EmbryoAge>(5);\n // Optional first-trimester ultrasound (LMP method only) for ACOG CO700 redating.\n const [crlMm, setCrlMm] = useState<number | null>(null);\n const [scanDate, setScanDate] = useState<Date | undefined>(undefined);\n\n // Real today — caps the date inputs (LMP / ultrasound can't be in the future).\n const today = useMemo(() => new Date(), []);\n // The date GA, trimester and schedule status are evaluated against; defaults\n // to today but can be a future appointment date to project forward. (The EDD\n // itself is dating-anchored and does not depend on this.)\n const [asOfDate, setAsOfDate] = useState<Date>(() => new Date());\n\n // ACOG CO700 CRL redating — only when an LMP, a CRL and a scan date are set.\n const redating = useMemo(() => {\n if (method !== 'lmp' || !refDate || crlMm === null || !scanDate) {\n return null;\n }\n return redateByCrl({ lmp: refDate, scanDate, crlMm });\n }, [method, refDate, crlMm, scanDate]);\n\n const result = useMemo<DueDateResult | null>(() => {\n if (!refDate) return null;\n // A confirmed redating moves the effective LMP to the ultrasound's; the\n // CRL already reflects true gestation, so cycle adjustment no longer applies.\n const effectiveDate =\n method === 'lmp' && redating?.redated\n ? redating.gestationalStart\n : refDate;\n const effectiveCycle =\n method === 'lmp' && redating?.redated\n ? DEFAULT_CYCLE_LENGTH\n : (cycleLength ?? DEFAULT_CYCLE_LENGTH);\n return computeDueDate(\n {\n method,\n date: effectiveDate,\n cycleLength: effectiveCycle,\n embryoAge,\n },\n asOfDate,\n );\n }, [method, refDate, cycleLength, embryoAge, redating, asOfDate]);\n\n const dateFormatter = useMemo(\n () => new Intl.DateTimeFormat(i18n.language, { dateStyle: 'long' }),\n [i18n.language],\n );\n\n useEffect(() => {\n onResultChange?.(result);\n }, [result, onResultChange]);\n\n const schedule = useMemo(() => {\n if (!showSchedule || !result) return null;\n return computeAntenatalSchedule(result.gestationalStart, {\n ...scheduleOptions,\n today: asOfDate,\n });\n }, [showSchedule, result, asOfDate, scheduleOptions]);\n\n return (\n <div\n ref={ref}\n data-component=\"due-date-calculator\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <RadioGroup\n label={t('dueDateCalculator.method.label')}\n variant=\"horizontal\"\n value={method}\n onValueChange={(next) => setMethod(next as DueDateMethod)}\n >\n <Radio label={t('dueDateCalculator.method.lmp')} value=\"lmp\" />\n <Radio\n label={t('dueDateCalculator.method.conception')}\n value=\"conception\"\n />\n <Radio label={t('dueDateCalculator.method.ivf')} value=\"ivf\" />\n </RadioGroup>\n\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <FormField label={t(DATE_LABEL_KEY[method])}>\n <DatePicker value={refDate} onChange={setRefDate} maxDate={today} />\n </FormField>\n\n {method === 'lmp' && (\n <FormField\n label={t('dueDateCalculator.cycleLength')}\n description={t('dueDateCalculator.cycleLengthHint')}\n >\n <NumberInput\n mode=\"integer\"\n min={20}\n max={45}\n value={cycleLength}\n onChange={setCycleLength}\n />\n </FormField>\n )}\n\n {method === 'ivf' && (\n <RadioGroup\n label={t('dueDateCalculator.embryoAge')}\n variant=\"horizontal\"\n value={String(embryoAge)}\n onValueChange={(next) => setEmbryoAge(Number(next) as EmbryoAge)}\n >\n <Radio label={t('dueDateCalculator.day3')} value=\"3\" />\n <Radio label={t('dueDateCalculator.day5')} value=\"5\" />\n </RadioGroup>\n )}\n </div>\n\n <FormField\n label={t('dueDateCalculator.asOfDate')}\n description={t('dueDateCalculator.asOfDateHint')}\n >\n <DatePicker value={asOfDate} onChange={(d) => d && setAsOfDate(d)} />\n </FormField>\n\n {method === 'lmp' ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-2\">\n <FormField\n label={t('dueDateCalculator.crl')}\n description={t('dueDateCalculator.crlHint')}\n >\n <NumberInput\n mode=\"decimal\"\n min={0}\n step={0.1}\n value={crlMm}\n onChange={setCrlMm}\n />\n </FormField>\n <FormField label={t('dueDateCalculator.scanDate')}>\n <DatePicker\n value={scanDate}\n onChange={setScanDate}\n maxDate={today}\n />\n </FormField>\n </div>\n {redating ? (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)]\">\n <Badge variant={redating.redated ? 'warning' : 'success'}>\n {redating.redated\n ? t('dueDateCalculator.redated', {\n days: redating.discrepancyDays,\n })\n : t('dueDateCalculator.datesAgree')}\n </Badge>\n {!redating.crlInRange ? (\n <span className=\"type-meta ds:text-muted-foreground\">\n {t('dueDateCalculator.crlOutOfRange')}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n\n {/* Concise polite announcement when a due date resolves. */}\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {result\n ? `${t('dueDateCalculator.result.dueDateLabel')}: ${dateFormatter.format(\n result.dueDate,\n )}. ${t('dueDateCalculator.result.trimesterLabel')}: ${t(\n `dueDateCalculator.trimester.${result.trimester}`,\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-3\">\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('dueDateCalculator.result.trimesterLabel')}\n </dt>\n <dd>\n <Badge\n variant={TRIMESTER_BADGE_VARIANT[result.trimester]}\n size=\"lg\"\n >\n {t(`dueDateCalculator.trimester.${result.trimester}`)}\n </Badge>\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('dueDateCalculator.result.gestationLabel')}\n </dt>\n <dd className=\"type-body ds:text-foreground\">\n {result.gestationalAge\n ? t('dueDateCalculator.result.gestation', {\n weeks: result.gestationalAge.weeks,\n days: result.gestationalAge.days,\n })\n : '—'}\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('dueDateCalculator.result.dueDateLabel')}\n </dt>\n <dd className=\"type-metric ds:text-foreground\">\n {dateFormatter.format(result.dueDate)}\n </dd>\n </div>\n </dl>\n </Card.Body>\n </Card>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('dueDateCalculator.empty')}\n </p>\n )}\n\n {schedule ? <AntenatalScheduleTimeline events={schedule} /> : null}\n\n {/* One result-action group for the whole result — sits after both the\n due-date card and the schedule, and its payload carries the schedule\n rows when shown (no per-card buttons). */}\n {result && (insertVariant === 'copy' || onInsert) ? (\n <InsertButton\n variant={insertVariant}\n onInsert={onInsert}\n onCopy={onCopy}\n onError={onError}\n card={{\n title: t('insert.title.dueDate'),\n icon: 'calendar-heart',\n highlight: dateFormatter.format(result.dueDate),\n // Chip tinted with the trimester's semantic token so the inserted\n // PNG carries the same clinical-stage colour as the on-screen\n // trimester badge — with the contrast-safe orange override for the\n // `--warning` third trimester.\n highlightToken: trimesterHighlightToken(result.trimester),\n brand: insertBrand,\n fields: [\n {\n // The estimated date of delivery → calendar glyph.\n icon: 'calendar',\n label: t('dueDateCalculator.result.dueDateLabel'),\n value: dateFormatter.format(result.dueDate),\n },\n {\n // Gestational age is an elapsed duration → clock glyph.\n icon: 'clock',\n label: t('dueDateCalculator.result.gestationLabel'),\n value: result.gestationalAge\n ? t('dueDateCalculator.result.gestation', {\n weeks: result.gestationalAge.weeks,\n days: result.gestationalAge.days,\n })\n : '—',\n },\n {\n // Trimester is a clinical-stage classification → tag.\n icon: 'tag',\n label: t('dueDateCalculator.result.trimesterLabel'),\n value: t(`dueDateCalculator.trimester.${result.trimester}`),\n },\n // When the schedule is shown, the single Insert button carries\n // the exam windows too (no second button).\n ...(schedule\n ? buildScheduleInsertFields(schedule, t, (d) =>\n dateFormatter.format(d),\n )\n : []),\n ],\n }}\n />\n ) : null}\n </div>\n );\n },\n);\n\nDueDateCalculator.displayName = 'DueDateCalculator';\n"],"names":["rootVariants","cva","DATE_LABEL_KEY","DueDateCalculator","forwardRef","defaultMethod","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","showSchedule","scheduleOptions","id","width","className","ref","t","i18n","useTranslation","method","setMethod","useState","refDate","setRefDate","cycleLength","setCycleLength","DEFAULT_CYCLE_LENGTH","embryoAge","setEmbryoAge","crlMm","setCrlMm","scanDate","setScanDate","today","useMemo","asOfDate","setAsOfDate","redating","redateByCrl","result","effectiveDate","effectiveCycle","computeDueDate","dateFormatter","useEffect","schedule","computeAntenatalSchedule","jsxs","RadioGroup","next","jsx","Radio","FormField","DatePicker","NumberInput","Badge","Card","TRIMESTER_BADGE_VARIANT","AntenatalScheduleTimeline","InsertButton","trimesterHighlightToken","buildScheduleInsertFields"],"mappings":"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"}
@@ -6,7 +6,7 @@ import { F as D } from "./form-field-BOm9hK35.js";
6
6
  import { N as E } from "./number-input-Dj5L3pXK.js";
7
7
  import { C as N } from "./card-DPmk26CL.js";
8
8
  import { B as H } from "./badge-zsf5i5bH.js";
9
- import { I as S } from "./insert-result-DNdi_JYW.js";
9
+ import { I as S } from "./insert-result-njqBthzT.js";
10
10
  function h(l) {
11
11
  return typeof l == "number" && Number.isFinite(l) && l > 0 ? l / 10 : null;
12
12
  }
@@ -119,4 +119,4 @@ export {
119
119
  G as F,
120
120
  q as e
121
121
  };
122
- //# sourceMappingURL=fetal-weight-Da-B4P4k.js.map
122
+ //# sourceMappingURL=fetal-weight-Xf8-ZoDy.js.map