@alfadocs/ui-kit-debug 1.4.11 → 1.4.13
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.
- package/dist/_chunks/actions-cell-renderer-BD-jruqA.js +1784 -0
- package/dist/_chunks/actions-cell-renderer-BD-jruqA.js.map +1 -0
- package/dist/_chunks/{address-fields-pHsfkXGk.js → address-fields-D-8akw2m.js} +2 -2
- package/dist/_chunks/{address-fields-pHsfkXGk.js.map → address-fields-D-8akw2m.js.map} +1 -1
- package/dist/_chunks/{agenda-card-BCGaQazD.js → agenda-card-BQDs2ZMp.js} +2 -2
- package/dist/_chunks/{agenda-card-BCGaQazD.js.map → agenda-card-BQDs2ZMp.js.map} +1 -1
- package/dist/_chunks/{agenda-tray-CG6FuccE.js → agenda-tray-BTyWsVwT.js} +2 -2
- package/dist/_chunks/{agenda-tray-CG6FuccE.js.map → agenda-tray-BTyWsVwT.js.map} +1 -1
- package/dist/_chunks/{ai-consent-banner-CpF_rbG9.js → ai-consent-banner-gVcoiXxv.js} +2 -2
- package/dist/_chunks/{ai-consent-banner-CpF_rbG9.js.map → ai-consent-banner-gVcoiXxv.js.map} +1 -1
- package/dist/_chunks/{anamnesis-card-C4FVp9wO.js → anamnesis-card-Bt0twrML.js} +2 -2
- package/dist/_chunks/{anamnesis-card-C4FVp9wO.js.map → anamnesis-card-Bt0twrML.js.map} +1 -1
- package/dist/_chunks/anamnesis-summary-card-BKGVcwny.js +350 -0
- package/dist/_chunks/anamnesis-summary-card-BKGVcwny.js.map +1 -0
- package/dist/_chunks/{anamnesis-summary-rows-Dw3l6nOD.js → anamnesis-summary-rows-B5MAYkJ-.js} +2768 -2951
- package/dist/_chunks/anamnesis-summary-rows-B5MAYkJ-.js.map +1 -0
- package/dist/_chunks/{antenatal-schedule-timeline-u8TsS2QJ.js → antenatal-schedule-timeline-BSLrOQ3S.js} +2 -2
- package/dist/_chunks/{antenatal-schedule-timeline-u8TsS2QJ.js.map → antenatal-schedule-timeline-BSLrOQ3S.js.map} +1 -1
- package/dist/_chunks/{appointment-card-DgKI5QKD.js → appointment-card-kcUC7vWR.js} +2 -2
- package/dist/_chunks/{appointment-card-DgKI5QKD.js.map → appointment-card-kcUC7vWR.js.map} +1 -1
- package/dist/_chunks/{appointment-timeline-CUijZCxZ.js → appointment-timeline-BC3dlZu5.js} +2 -2
- package/dist/_chunks/{appointment-timeline-CUijZCxZ.js.map → appointment-timeline-BC3dlZu5.js.map} +1 -1
- package/dist/_chunks/{appointment-tray-B1aUk46V.js → appointment-tray-BPY5rATW.js} +2 -2
- package/dist/_chunks/{appointment-tray-B1aUk46V.js.map → appointment-tray-BPY5rATW.js.map} +1 -1
- package/dist/_chunks/{badge-DCOqV9mI.js → badge-CIt-UFEx.js} +28 -28
- package/dist/_chunks/{badge-DCOqV9mI.js.map → badge-CIt-UFEx.js.map} +1 -1
- package/dist/_chunks/{bishop-score-Bpe20Ctl.js → bishop-score-B6iYYO0c.js} +3 -3
- package/dist/_chunks/{bishop-score-Bpe20Ctl.js.map → bishop-score-B6iYYO0c.js.map} +1 -1
- package/dist/_chunks/{bmi-calculator-DF5bNsDD.js → bmi-calculator-DC5SaqXT.js} +5 -5
- package/dist/_chunks/{bmi-calculator-DF5bNsDD.js.map → bmi-calculator-DC5SaqXT.js.map} +1 -1
- package/dist/_chunks/{booking-DuSkvE5x.js → booking-CmHX15dh.js} +2 -2
- package/dist/_chunks/{booking-DuSkvE5x.js.map → booking-CmHX15dh.js.map} +1 -1
- package/dist/_chunks/{care-plan-card-DiyK3Eys.js → care-plan-card-9tLgsDYY.js} +3 -3
- package/dist/_chunks/{care-plan-card-DiyK3Eys.js.map → care-plan-card-9tLgsDYY.js.map} +1 -1
- package/dist/_chunks/{care-plan-entry-card-6du8PYVk.js → care-plan-entry-card-CSan4h7A.js} +2 -2
- package/dist/_chunks/{care-plan-entry-card-6du8PYVk.js.map → care-plan-entry-card-CSan4h7A.js.map} +1 -1
- package/dist/_chunks/{care-plan-header-BWvDvly-.js → care-plan-header-D_BTi77b.js} +2 -2
- package/dist/_chunks/{care-plan-header-BWvDvly-.js.map → care-plan-header-D_BTi77b.js.map} +1 -1
- package/dist/_chunks/{clinical-note-card-D63x_AkK.js → clinical-note-card-ByzQ86uz.js} +2 -2
- package/dist/_chunks/{clinical-note-card-D63x_AkK.js.map → clinical-note-card-ByzQ86uz.js.map} +1 -1
- package/dist/_chunks/{connection-status-Bi4Hwpd1.js → connection-status-D8mZ4klk.js} +2 -2
- package/dist/_chunks/{connection-status-Bi4Hwpd1.js.map → connection-status-D8mZ4klk.js.map} +1 -1
- package/dist/_chunks/{contact-profile-card-CGS_NABR.js → contact-profile-card-DuW6VpzY.js} +2 -2
- package/dist/_chunks/{contact-profile-card-CGS_NABR.js.map → contact-profile-card-DuW6VpzY.js.map} +1 -1
- package/dist/_chunks/{cycle-calculator-Bd4_fJhq.js → cycle-calculator-B_NlctlQ.js} +4 -4
- package/dist/_chunks/{cycle-calculator-Bd4_fJhq.js.map → cycle-calculator-B_NlctlQ.js.map} +1 -1
- package/dist/_chunks/{date-time-picker-DafkGDGO.js → date-time-picker-DVETwpzc.js} +2 -2
- package/dist/_chunks/{date-time-picker-DafkGDGO.js.map → date-time-picker-DVETwpzc.js.map} +1 -1
- package/dist/_chunks/{dependent-selector-DyFpdzZV.js → dependent-selector-BUvrZRi0.js} +2 -2
- package/dist/_chunks/{dependent-selector-DyFpdzZV.js.map → dependent-selector-BUvrZRi0.js.map} +1 -1
- package/dist/_chunks/{document-preview-DuMzmupZ.js → document-preview-Di15VrpQ.js} +2 -2
- package/dist/_chunks/{document-preview-DuMzmupZ.js.map → document-preview-Di15VrpQ.js.map} +1 -1
- package/dist/_chunks/{due-date-calculator-Do67C-Z-.js → due-date-calculator-le1z3KpW.js} +6 -6
- package/dist/_chunks/{due-date-calculator-Do67C-Z-.js.map → due-date-calculator-le1z3KpW.js.map} +1 -1
- package/dist/_chunks/{fetal-weight-DHxXx7Uz.js → fetal-weight-DXA6ZpMq.js} +4 -4
- package/dist/_chunks/{fetal-weight-DHxXx7Uz.js.map → fetal-weight-DXA6ZpMq.js.map} +1 -1
- package/dist/_chunks/{field-value-C5A4t4OE.js → field-value-BCFLVBmu.js} +2 -2
- package/dist/_chunks/{field-value-C5A4t4OE.js.map → field-value-BCFLVBmu.js.map} +1 -1
- package/dist/_chunks/{file-manager-BY6FdHAt.js → file-manager-BjCM2fZB.js} +3 -3
- package/dist/_chunks/{file-manager-BY6FdHAt.js.map → file-manager-BjCM2fZB.js.map} +1 -1
- package/dist/_chunks/form-field-D1oqYisL.js +127 -0
- package/dist/_chunks/form-field-D1oqYisL.js.map +1 -0
- package/dist/_chunks/{gestational-age-calculator-CMhOtFE_.js → gestational-age-calculator-Ch8vmJAl.js} +5 -5
- package/dist/_chunks/{gestational-age-calculator-CMhOtFE_.js.map → gestational-age-calculator-Ch8vmJAl.js.map} +1 -1
- package/dist/_chunks/{hcg-doubling-DNG9Q2go.js → hcg-doubling-BT6lki_i.js} +5 -5
- package/dist/_chunks/{hcg-doubling-DNG9Q2go.js.map → hcg-doubling-BT6lki_i.js.map} +1 -1
- package/dist/_chunks/{link-cell-renderer-C8npOgmM.js → link-cell-renderer-C7GvyK66.js} +3 -3
- package/dist/_chunks/{link-cell-renderer-C8npOgmM.js.map → link-cell-renderer-C7GvyK66.js.map} +1 -1
- package/dist/_chunks/{marketplace-app-shell-BxOiy25s.js → marketplace-app-shell-BhRH7Zi4.js} +2 -2
- package/dist/_chunks/{marketplace-app-shell-BxOiy25s.js.map → marketplace-app-shell-BhRH7Zi4.js.map} +1 -1
- package/dist/_chunks/{notes-panel-BoyzMHI_.js → notes-panel-Bz83SO4-.js} +2 -2
- package/dist/_chunks/{notes-panel-BoyzMHI_.js.map → notes-panel-Bz83SO4-.js.map} +1 -1
- package/dist/_chunks/number-input-CM1q9zvH.js +414 -0
- package/dist/_chunks/number-input-CM1q9zvH.js.map +1 -0
- package/dist/_chunks/{operator-hero-DVy0RGoD.js → operator-hero-CpJkDNAM.js} +2 -2
- package/dist/_chunks/{operator-hero-DVy0RGoD.js.map → operator-hero-CpJkDNAM.js.map} +1 -1
- package/dist/_chunks/{optical-prescription-Brs0CeP3.js → optical-prescription-CQR_WJiS.js} +2 -2
- package/dist/_chunks/{optical-prescription-Brs0CeP3.js.map → optical-prescription-CQR_WJiS.js.map} +1 -1
- package/dist/_chunks/{patient-details-Bf4iTAPp.js → patient-details-CnFgF3MU.js} +3 -3
- package/dist/_chunks/{patient-details-Bf4iTAPp.js.map → patient-details-CnFgF3MU.js.map} +1 -1
- package/dist/_chunks/{patient-summary-card-dkV6a53T.js → patient-summary-card-DfbOtOV6.js} +3 -3
- package/dist/_chunks/{patient-summary-card-dkV6a53T.js.map → patient-summary-card-DfbOtOV6.js.map} +1 -1
- package/dist/_chunks/{patient-table-zifAcsjV.js → patient-table-BXzcrC79.js} +4 -4
- package/dist/_chunks/{patient-table-zifAcsjV.js.map → patient-table-BXzcrC79.js.map} +1 -1
- package/dist/_chunks/{payment-automation-upsell-CZZPGfs0.js → payment-automation-upsell-Ckh3b7Xx.js} +2 -2
- package/dist/_chunks/{payment-automation-upsell-CZZPGfs0.js.map → payment-automation-upsell-Ckh3b7Xx.js.map} +1 -1
- package/dist/_chunks/{payment-request-card-Dy7_DA-2.js → payment-request-card-Dn6-tH-D.js} +2 -2
- package/dist/_chunks/{payment-request-card-Dy7_DA-2.js.map → payment-request-card-Dn6-tH-D.js.map} +1 -1
- package/dist/_chunks/{periodontal-chart-CfFgKXWa.js → periodontal-chart-B0yko9Xm.js} +2 -2
- package/dist/_chunks/{periodontal-chart-CfFgKXWa.js.map → periodontal-chart-B0yko9Xm.js.map} +1 -1
- package/dist/_chunks/{periodontal-chart-card--6yTUsrQ.js → periodontal-chart-card-HbYTmWlo.js} +2 -2
- package/dist/_chunks/{periodontal-chart-card--6yTUsrQ.js.map → periodontal-chart-card-HbYTmWlo.js.map} +1 -1
- package/dist/_chunks/{periodontal-diagnosis-cco40xTa.js → periodontal-diagnosis-Qk4F3Wc_.js} +3 -3
- package/dist/_chunks/{periodontal-diagnosis-cco40xTa.js.map → periodontal-diagnosis-Qk4F3Wc_.js.map} +1 -1
- package/dist/_chunks/{periodontal-risk-chart-bUGkMwDg.js → periodontal-risk-chart-ByKuM4cF.js} +2 -2
- package/dist/_chunks/{periodontal-risk-chart-bUGkMwDg.js.map → periodontal-risk-chart-ByKuM4cF.js.map} +1 -1
- package/dist/_chunks/{practice-results-Mys11x9p.js → practice-results-C5exrEn0.js} +2 -2
- package/dist/_chunks/{practice-results-Mys11x9p.js.map → practice-results-C5exrEn0.js.map} +1 -1
- package/dist/_chunks/{pregnancy-dating-t3oM4ZzC.js → pregnancy-dating-4tPV_Yko.js} +6 -6
- package/dist/_chunks/{pregnancy-dating-t3oM4ZzC.js.map → pregnancy-dating-4tPV_Yko.js.map} +1 -1
- package/dist/_chunks/{pregnancy-weight-gain-Bfz7SbIw.js → pregnancy-weight-gain-BBgbWBGe.js} +5 -5
- package/dist/_chunks/{pregnancy-weight-gain-Bfz7SbIw.js.map → pregnancy-weight-gain-BBgbWBGe.js.map} +1 -1
- package/dist/_chunks/radio-group-BmIqhIT2.js +171 -0
- package/dist/_chunks/radio-group-BmIqhIT2.js.map +1 -0
- package/dist/_chunks/{radiograph-panel.agent-DyA4wsZA.js → radiograph-panel.agent-Drj0hYVV.js} +3 -3
- package/dist/_chunks/{radiograph-panel.agent-DyA4wsZA.js.map → radiograph-panel.agent-Drj0hYVV.js.map} +1 -1
- package/dist/_chunks/signature-status-chip-DlLJk1VA.js +97 -0
- package/dist/_chunks/signature-status-chip-DlLJk1VA.js.map +1 -0
- package/dist/_chunks/{tab-bar-BYESZINM.js → tab-bar-Ddp_ZbnL.js} +2 -2
- package/dist/_chunks/{tab-bar-BYESZINM.js.map → tab-bar-Ddp_ZbnL.js.map} +1 -1
- package/dist/_chunks/{tasks-panel-CoESdfZI.js → tasks-panel-T7vaKJcA.js} +2 -2
- package/dist/_chunks/{tasks-panel-CoESdfZI.js.map → tasks-panel-T7vaKJcA.js.map} +1 -1
- package/dist/_chunks/{time-picker-BPlJWm8w.js → time-picker-D0RS391l.js} +66 -63
- package/dist/_chunks/{time-picker-BPlJWm8w.js.map → time-picker-D0RS391l.js.map} +1 -1
- package/dist/_chunks/{unit-converter-lUW9Q888.js → unit-converter-Dsk__pjA.js} +3 -3
- package/dist/_chunks/{unit-converter-lUW9Q888.js.map → unit-converter-Dsk__pjA.js.map} +1 -1
- package/dist/_chunks/{widget-picker-DEmTVuFL.js → widget-picker-DqMbjQy_.js} +2 -2
- package/dist/_chunks/{widget-picker-DEmTVuFL.js.map → widget-picker-DqMbjQy_.js.map} +1 -1
- package/dist/_chunks/{workflow-map-C5PGP6cD.js → workflow-map-BNBeG2zn.js} +2 -2
- package/dist/_chunks/{workflow-map-C5PGP6cD.js.map → workflow-map-BNBeG2zn.js.map} +1 -1
- package/dist/agent-catalog.json +34 -34
- package/dist/agent-i18n/en.json +4 -4
- package/dist/components/address-fields/index.js +1 -1
- package/dist/components/agenda-card/index.js +1 -1
- package/dist/components/agenda-tray/index.js +1 -1
- package/dist/components/ai-consent-banner/index.js +1 -1
- package/dist/components/anamnesis-card/index.js +1 -1
- package/dist/components/anamnesis-summary-card/anamnesis-summary-card.d.ts.map +1 -1
- package/dist/components/anamnesis-summary-card/index.js +1 -1
- package/dist/components/appointment-card/index.js +2 -2
- package/dist/components/appointment-timeline/index.js +1 -1
- package/dist/components/badge/badge.d.ts +1 -0
- package/dist/components/badge/badge.d.ts.map +1 -1
- package/dist/components/badge/index.js +1 -1
- package/dist/components/bishop-score/index.js +1 -1
- package/dist/components/bmi-calculator/index.js +1 -1
- package/dist/components/booking/index.js +1 -1
- package/dist/components/care-plan-card/index.js +1 -1
- package/dist/components/care-plan-entry-card/index.js +1 -1
- package/dist/components/care-plan-header/index.js +1 -1
- package/dist/components/clinical-note-card/index.js +1 -1
- package/dist/components/connection-status/index.js +1 -1
- package/dist/components/contact-profile-card/index.js +1 -1
- package/dist/components/cycle-calculator/index.js +1 -1
- package/dist/components/data-table/data-table.agent.d.ts.map +1 -1
- package/dist/components/data-table/data-table.d.ts +19 -0
- package/dist/components/data-table/data-table.d.ts.map +1 -1
- package/dist/components/data-table/expanded-row-renderer.d.ts +47 -0
- package/dist/components/data-table/expanded-row-renderer.d.ts.map +1 -0
- package/dist/components/data-table/index.d.ts +2 -0
- package/dist/components/data-table/index.d.ts.map +1 -1
- package/dist/components/data-table/index.js +31 -27
- package/dist/components/date-time-picker/index.js +1 -1
- package/dist/components/dependent-selector/index.js +1 -1
- package/dist/components/document-preview/index.js +1 -1
- package/dist/components/due-date-calculator/index.js +2 -2
- package/dist/components/fetal-weight/index.js +1 -1
- package/dist/components/file-manager/index.js +1 -1
- package/dist/components/form-field/form-field.d.ts +6 -0
- package/dist/components/form-field/form-field.d.ts.map +1 -1
- package/dist/components/form-field/index.js +1 -1
- package/dist/components/gestational-age-calculator/index.js +1 -1
- package/dist/components/hcg-doubling/index.js +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/notes-panel/index.js +1 -1
- package/dist/components/number-input/index.js +1 -1
- package/dist/components/number-input/number-input.d.ts +11 -0
- package/dist/components/number-input/number-input.d.ts.map +1 -1
- package/dist/components/operator-hero/index.js +1 -1
- package/dist/components/optical-prescription/index.js +1 -1
- package/dist/components/patient-details/index.js +1 -1
- package/dist/components/patient-summary-card/index.js +1 -1
- package/dist/components/patient-table/index.js +1 -1
- package/dist/components/payment-card/index.js +1 -1
- package/dist/components/periodontal-chart/index.js +1 -1
- package/dist/components/periodontal-chart-card/index.js +1 -1
- package/dist/components/periodontal-diagnosis/index.js +1 -1
- package/dist/components/periodontal-risk-chart/index.js +1 -1
- package/dist/components/practice-results/index.js +1 -1
- package/dist/components/pregnancy-dating/index.js +1 -1
- package/dist/components/pregnancy-weight-gain/index.js +1 -1
- package/dist/components/radio-group/index.js +1 -1
- package/dist/components/radio-group/radio-group.d.ts +3 -0
- package/dist/components/radio-group/radio-group.d.ts.map +1 -1
- package/dist/components/radiograph-panel/index.js +1 -1
- package/dist/components/signature-status-chip/index.d.ts +3 -0
- package/dist/components/signature-status-chip/index.d.ts.map +1 -0
- package/dist/components/signature-status-chip/index.js +6 -0
- package/dist/components/signature-status-chip/index.js.map +1 -0
- package/dist/components/signature-status-chip/signature-status-chip.d.ts +29 -0
- package/dist/components/signature-status-chip/signature-status-chip.d.ts.map +1 -0
- package/dist/components/tab-bar/index.js +1 -1
- package/dist/components/tasks-panel/index.js +1 -1
- package/dist/components/time-picker/index.js +1 -1
- package/dist/components/time-picker/time-picker.d.ts.map +1 -1
- package/dist/components/unit-converter/index.js +1 -1
- package/dist/components/widget-picker/index.js +1 -1
- package/dist/components/workflow/index.js +1 -1
- package/dist/host-compat.css +4 -1
- package/dist/i18n/locales/ar.d.ts +17 -0
- package/dist/i18n/locales/ar.d.ts.map +1 -1
- package/dist/i18n/locales/ar.js +11 -0
- package/dist/i18n/locales/ar.js.map +1 -1
- package/dist/i18n/locales/de.d.ts +17 -0
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/de.js +11 -0
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/el.d.ts +17 -0
- package/dist/i18n/locales/el.d.ts.map +1 -1
- package/dist/i18n/locales/el.js +11 -0
- package/dist/i18n/locales/el.js.map +1 -1
- package/dist/i18n/locales/en.d.ts +17 -0
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +11 -0
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +17 -0
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/es.js +11 -0
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +17 -0
- package/dist/i18n/locales/fr.d.ts.map +1 -1
- package/dist/i18n/locales/fr.js +11 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hi.d.ts +17 -0
- package/dist/i18n/locales/hi.d.ts.map +1 -1
- package/dist/i18n/locales/hi.js +11 -0
- package/dist/i18n/locales/hi.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +17 -0
- package/dist/i18n/locales/it.d.ts.map +1 -1
- package/dist/i18n/locales/it.js +11 -0
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/ja.d.ts +17 -0
- package/dist/i18n/locales/ja.d.ts.map +1 -1
- package/dist/i18n/locales/ja.js +11 -0
- package/dist/i18n/locales/ja.js.map +1 -1
- package/dist/i18n/locales/nl.d.ts +17 -0
- package/dist/i18n/locales/nl.d.ts.map +1 -1
- package/dist/i18n/locales/nl.js +11 -0
- package/dist/i18n/locales/nl.js.map +1 -1
- package/dist/i18n/locales/pl.d.ts +17 -0
- package/dist/i18n/locales/pl.d.ts.map +1 -1
- package/dist/i18n/locales/pl.js +11 -0
- package/dist/i18n/locales/pl.js.map +1 -1
- package/dist/i18n/locales/pt.d.ts +17 -0
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/pt.js +11 -0
- package/dist/i18n/locales/pt.js.map +1 -1
- package/dist/i18n/locales/ro.d.ts +17 -0
- package/dist/i18n/locales/ro.d.ts.map +1 -1
- package/dist/i18n/locales/ro.js +11 -0
- package/dist/i18n/locales/ro.js.map +1 -1
- package/dist/i18n/locales/ru.d.ts +17 -0
- package/dist/i18n/locales/ru.d.ts.map +1 -1
- package/dist/i18n/locales/ru.js +11 -0
- package/dist/i18n/locales/ru.js.map +1 -1
- package/dist/i18n/locales/sq.d.ts +17 -0
- package/dist/i18n/locales/sq.d.ts.map +1 -1
- package/dist/i18n/locales/sq.js +11 -0
- package/dist/i18n/locales/sq.js.map +1 -1
- package/dist/i18n/locales/sv.d.ts +17 -0
- package/dist/i18n/locales/sv.d.ts.map +1 -1
- package/dist/i18n/locales/sv.js +11 -0
- package/dist/i18n/locales/sv.js.map +1 -1
- package/dist/i18n/locales/tr.d.ts +17 -0
- package/dist/i18n/locales/tr.d.ts.map +1 -1
- package/dist/i18n/locales/tr.js +11 -0
- package/dist/i18n/locales/tr.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +17 -0
- package/dist/i18n/locales/zh.d.ts.map +1 -1
- package/dist/i18n/locales/zh.js +11 -0
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.js +799 -792
- package/dist/index.js.map +1 -1
- package/dist/locales/ar.json +17 -0
- package/dist/locales/de.json +17 -0
- package/dist/locales/el.json +17 -0
- package/dist/locales/en.json +17 -0
- package/dist/locales/es.json +17 -0
- package/dist/locales/fr.json +17 -0
- package/dist/locales/hi.json +17 -0
- package/dist/locales/it.json +17 -0
- package/dist/locales/ja.json +17 -0
- package/dist/locales/nl.json +17 -0
- package/dist/locales/pl.json +17 -0
- package/dist/locales/pt.json +17 -0
- package/dist/locales/ro.json +17 -0
- package/dist/locales/ru.json +17 -0
- package/dist/locales/sq.json +17 -0
- package/dist/locales/sv.json +17 -0
- package/dist/locales/tr.json +17 -0
- package/dist/locales/zh.json +17 -0
- package/dist/patterns/anamnesis/anamnesis-baseline.d.ts +6 -0
- package/dist/patterns/anamnesis/anamnesis-baseline.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-compile.d.ts +4 -1
- package/dist/patterns/anamnesis/anamnesis-compile.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-evolution-grid.d.ts +8 -9
- package/dist/patterns/anamnesis/anamnesis-evolution-grid.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-evolution.d.ts +24 -12
- package/dist/patterns/anamnesis/anamnesis-evolution.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-field.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-i18n.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-intake.d.ts +5 -1
- package/dist/patterns/anamnesis/anamnesis-intake.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-questionnaire.d.ts +4 -1
- package/dist/patterns/anamnesis/anamnesis-questionnaire.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-readonly.d.ts +4 -1
- package/dist/patterns/anamnesis/anamnesis-readonly.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis-source-blocks.d.ts +29 -0
- package/dist/patterns/anamnesis/anamnesis-source-blocks.d.ts.map +1 -0
- package/dist/patterns/anamnesis/anamnesis-window.d.ts +3 -5
- package/dist/patterns/anamnesis/anamnesis-window.d.ts.map +1 -1
- package/dist/patterns/anamnesis/anamnesis.d.ts +4 -1
- package/dist/patterns/anamnesis/anamnesis.d.ts.map +1 -1
- package/dist/patterns/anamnesis/index.js +1 -1
- package/dist/patterns/marketplace-app-shell/index.js +1 -1
- package/dist/patterns/payment-automation-upsell/index.js +1 -1
- package/dist/tokens.css +5 -2
- package/package.json +5 -1
- package/dist/_chunks/actions-cell-renderer-C67oj7mR.js +0 -1581
- package/dist/_chunks/actions-cell-renderer-C67oj7mR.js.map +0 -1
- package/dist/_chunks/anamnesis-summary-card-CpQViN5l.js +0 -392
- package/dist/_chunks/anamnesis-summary-card-CpQViN5l.js.map +0 -1
- package/dist/_chunks/anamnesis-summary-rows-Dw3l6nOD.js.map +0 -1
- package/dist/_chunks/form-field-M5UpX65Z.js +0 -123
- package/dist/_chunks/form-field-M5UpX65Z.js.map +0 -1
- package/dist/_chunks/number-input-DOe5VYei.js +0 -370
- package/dist/_chunks/number-input-DOe5VYei.js.map +0 -1
- package/dist/_chunks/radio-group-CI-6SmSw.js +0 -167
- package/dist/_chunks/radio-group-CI-6SmSw.js.map +0 -1
- package/dist/patterns/anamnesis/anamnesis-compare-panel.d.ts +0 -89
- package/dist/patterns/anamnesis/anamnesis-compare-panel.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radiograph-panel.agent-DyA4wsZA.js","sources":["../../node_modules/lucide-react/dist/esm/icons/git-compare-arrows.mjs","../../node_modules/lucide-react/dist/esm/icons/images.mjs","../../src/components/radiograph-panel/radiograph-panel.tsx","../../src/components/radiograph-panel/radiograph-panel.agent.ts"],"sourcesContent":["/**\n * @license lucide-react v1.22.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.mjs';\n\nconst __iconNode = [\n [\"circle\", { cx: \"5\", cy: \"6\", r: \"3\", key: \"1qnov2\" }],\n [\"path\", { d: \"M12 6h5a2 2 0 0 1 2 2v7\", key: \"1yj91y\" }],\n [\"path\", { d: \"m15 9-3-3 3-3\", key: \"1lwv8l\" }],\n [\"circle\", { cx: \"19\", cy: \"18\", r: \"3\", key: \"1qljk2\" }],\n [\"path\", { d: \"M12 18H7a2 2 0 0 1-2-2V9\", key: \"16sdep\" }],\n [\"path\", { d: \"m9 15 3 3-3 3\", key: \"1m3kbl\" }]\n];\nconst GitCompareArrows = createLucideIcon(\"git-compare-arrows\", __iconNode);\n\nexport { __iconNode, GitCompareArrows as default };\n//# sourceMappingURL=git-compare-arrows.mjs.map\n","/**\n * @license lucide-react v1.22.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.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"m22 11-1.296-1.296a2.4 2.4 0 0 0-3.408 0L11 16\", key: \"9kzy35\" }],\n [\"path\", { d: \"M4 8a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2\", key: \"1t0f0t\" }],\n [\"circle\", { cx: \"13\", cy: \"7\", r: \"1\", fill: \"currentColor\", key: \"1obus6\" }],\n [\"rect\", { x: \"8\", y: \"2\", width: \"14\", height: \"14\", rx: \"2\", key: \"1gvhby\" }]\n];\nconst Images = createLucideIcon(\"images\", __iconNode);\n\nexport { __iconNode, Images as default };\n//# sourceMappingURL=images.mjs.map\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactElement,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport {\n Upload,\n ZoomIn,\n ZoomOut,\n ChevronLeft,\n ChevronRight,\n Trash2,\n Eye,\n GitCompareArrows,\n Images,\n} from 'lucide-react';\nimport type { FdiId, Radiograph, RadiographKind } from '../tooth-scheme';\nimport { Dialog } from '../dialog';\nimport { Button } from '../button';\nimport { IconButton } from '../icon-button';\nimport { Badge } from '../badge';\nimport { FormField } from '../form-field';\nimport { TextArea } from '../text-area';\nimport { FileUpload } from '../file-upload';\nimport { DatePicker } from '../date-picker';\nimport { DateRangePicker, type DateRangeValue } from '../date-range-picker';\nimport { EmptyState } from '../empty-state';\nimport { PanelHeader } from '../panel-header';\n\n/* ------------------------------------------------------------------ */\n/* RadiographPanel — the Imaging tool for the Control Room rail. */\n/* */\n/* Shows one tooth's radiographs (x-rays / endorali): an upload */\n/* composer, a sortable + date-filtered history, and a fullscreen */\n/* viewer that zooms / steps / compares films. Drops into a */\n/* `ControlRoomRail` tool `panel`, the patient profile, or a care-plan */\n/* editor — anywhere a selected tooth's imaging belongs. The host owns */\n/* the films (`radiographs`) and persistence (`onAddRadiograph` / */\n/* `onDeleteRadiograph`); the panel owns only view state. The chart */\n/* drives selection via `ToothScheme.onOpenRadiographs(toothId)`. */\n/* */\n/* Image urls are opaque / consumer-hosted — never bundled. Real */\n/* radiographs are PHI; stories use synthetic placeholders only. */\n/* ------------------------------------------------------------------ */\n\nconst ZOOM_CLASS = ['ds:scale-100', 'ds:scale-150', 'ds:scale-200'] as const;\n\n/** Sort films newest-first (or oldest-first); undated films sink to the end in\n * BOTH directions — mirrors `ToothScheme.sortByDateDesc`. */\nfunction sortByDate(films: Radiograph[], desc: boolean): Radiograph[] {\n return [...films].sort((a, b) => {\n if (a.date === b.date) return 0;\n if (!a.date) return 1;\n if (!b.date) return -1;\n return (a.date < b.date ? -1 : 1) * (desc ? -1 : 1);\n });\n}\n\n/** Date-range membership. ISO `YYYY-MM-DD` is parsed at LOCAL midnight (the\n * `+'T00:00:00'`) so the filter agrees with the local-midnight `Date` objects\n * the DateRangePicker emits — without it, films are off-by-one west of UTC. */\nfunction inRange(film: Radiograph, range: DateRangeValue): boolean {\n if (!film.date) return !range.from && !range.to;\n const d = new Date(`${film.date}T00:00:00`);\n if (range.from && d < range.from) return false;\n if (range.to && d > range.to) return false;\n return true;\n}\n\n/** Local `YYYY-MM-DD` — avoids the UTC shift `Date.toISOString()` introduces\n * west of UTC for the local-midnight Date the DatePicker emits. */\nfunction toLocalIsoDate(d: Date): string {\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\nexport interface RadiographPanelHandle {\n /** Read the films currently shown (after the host's `radiographs` prop). */\n getRadiographs: () => Radiograph[];\n /** Open the fullscreen viewer on a film by id. */\n openViewer: (id: string) => void;\n /** Close the fullscreen viewer. */\n closeViewer: () => void;\n}\n\nexport interface RadiographPanelProps extends Omit<\n HTMLAttributes<HTMLElement>,\n 'title' | 'children'\n> {\n /** Instance id — emitted as `data-component-id` for the agent registry. */\n id?: string;\n /** The tooth whose films are shown (FDI id). Null → a \"select a tooth\" prompt. */\n toothId?: FdiId | null;\n /** Display label for the selected tooth. Defaults to the localised \"Tooth {{id}}\". */\n toothLabel?: string;\n /** The selected tooth's films. The panel sorts + filters; order in is irrelevant. */\n radiographs?: Radiograph[];\n /** Commit a new film (the host persists it). Omit to hide the upload composer. */\n onAddRadiograph?: (draft: {\n fileName: string;\n date?: string;\n note?: string;\n }) => void;\n /** Remove a film by id. Omit to hide the delete affordance. */\n onDeleteRadiograph?: (id: string) => void;\n /** Panel heading. Defaults to the localised \"Imaging\". */\n title?: string;\n /** Accessible name for the panel region (defaults to `title`). */\n 'aria-label'?: string;\n}\n\nfunction KindBadge({ kind }: { kind: RadiographKind }): ReactElement {\n const { t } = useTranslation();\n const label = t(`radiographPanel.kind.${kind}`, {\n defaultValue: {\n periapical: 'Periapical',\n bitewing: 'Bitewing',\n panoramic: 'Panoramic',\n other: 'Other',\n }[kind],\n });\n return (\n <Badge variant=\"neutral\" size=\"sm\">\n {label}\n </Badge>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Fullscreen viewer — lightbox + compare */\n/* ------------------------------------------------------------------ */\n\ninterface ViewerState {\n films: Radiograph[];\n index: number;\n compare: boolean;\n}\n\nfunction RadiographViewer({\n state,\n toothLabel,\n onClose,\n onIndexChange,\n}: {\n state: ViewerState | null;\n toothLabel: string;\n onClose: () => void;\n onIndexChange: (index: number) => void;\n}): ReactElement {\n const { t } = useTranslation();\n const [zoom, setZoom] = useState(0);\n // Reset zoom when the viewed film changes (prev/next) or the mode flips, so a\n // user doesn't carry one film's zoom into the next.\n useEffect(() => {\n setZoom(0);\n }, [state?.index, state?.compare]);\n const films = state?.films ?? [];\n const current = state ? films[state.index] : undefined;\n\n return (\n <Dialog.Root\n open={state !== null}\n onOpenChange={(next) => {\n if (!next) {\n setZoom(0);\n onClose();\n }\n }}\n >\n <Dialog.Content size=\"fullscreen\" aria-describedby={undefined}>\n <Dialog.Header>\n <Dialog.Title>\n {state?.compare\n ? t('radiographPanel.viewer.compareTitle', {\n defaultValue: '{{tooth}} — comparison',\n tooth: toothLabel,\n })\n : t('radiographPanel.viewer.title', {\n defaultValue: '{{tooth}} — radiograph',\n tooth: toothLabel,\n })}\n </Dialog.Title>\n </Dialog.Header>\n <Dialog.Body>\n {state?.compare ? (\n <div className=\"ds:grid ds:grid-cols-2 ds:gap-[var(--spacing-md)]\">\n {films.slice(0, 2).map((film) => (\n <figure\n key={film.id}\n className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n <div className=\"ds:flex ds:items-center ds:justify-center ds:overflow-hidden ds:rounded-[var(--radius-md)] ds:bg-[var(--muted)] ds:p-[var(--spacing-xs)]\">\n <img\n src={film.thumbnailUrl ?? film.url}\n alt={film.alt ?? toothLabel}\n className=\"ds:max-h-full ds:max-w-full ds:object-contain\"\n />\n </div>\n <figcaption className=\"type-meta ds:text-[color:var(--muted-foreground)]\">\n {film.date}\n </figcaption>\n </figure>\n ))}\n </div>\n ) : (\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:flex ds:max-h-full ds:items-center ds:justify-center ds:overflow-hidden ds:rounded-[var(--radius-md)] ds:bg-[var(--muted)] ds:p-[var(--spacing-sm)]\">\n <img\n src={current?.thumbnailUrl ?? current?.url}\n alt={current?.alt ?? toothLabel}\n className={[\n 'ds:max-h-full ds:max-w-full ds:object-contain',\n 'ds:origin-center ds:transition-transform ds:motion-reduce:transition-none',\n ZOOM_CLASS[zoom],\n ].join(' ')}\n />\n </div>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <IconButton\n icon={<ChevronLeft aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.viewer.prev', {\n defaultValue: 'Previous radiograph',\n })}\n size=\"sm\"\n flipIconInRtl\n disabled={!state || state.index === 0}\n onClick={() =>\n state && onIndexChange(Math.max(0, state.index - 1))\n }\n />\n <span\n aria-live=\"polite\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)]\"\n >\n {t('radiographPanel.viewer.position', {\n defaultValue: '{{current}} / {{total}}',\n current: (state?.index ?? 0) + 1,\n total: films.length,\n })}\n </span>\n <IconButton\n icon={<ChevronRight aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.viewer.next', {\n defaultValue: 'Next radiograph',\n })}\n size=\"sm\"\n flipIconInRtl\n disabled={!state || state.index >= films.length - 1}\n onClick={() =>\n state &&\n onIndexChange(Math.min(films.length - 1, state.index + 1))\n }\n />\n <span\n aria-hidden=\"true\"\n className=\"ds:ms-[var(--spacing-xs)] ds:me-[var(--spacing-xs)] ds:h-[var(--spacing-md)] ds:w-px ds:bg-[var(--border)]\"\n />\n <IconButton\n icon={<ZoomOut aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.viewer.zoomOut', {\n defaultValue: 'Zoom out',\n })}\n size=\"sm\"\n disabled={zoom === 0}\n onClick={() => setZoom((z) => Math.max(0, z - 1))}\n />\n <IconButton\n icon={<ZoomIn aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.viewer.zoomIn', {\n defaultValue: 'Zoom in',\n })}\n size=\"sm\"\n disabled={zoom === ZOOM_CLASS.length - 1}\n onClick={() =>\n setZoom((z) => Math.min(ZOOM_CLASS.length - 1, z + 1))\n }\n />\n </div>\n {current?.note ? (\n <p className=\"type-body-sm ds:text-[color:var(--muted-foreground)]\">\n {current.note}\n </p>\n ) : null}\n </div>\n )}\n </Dialog.Body>\n </Dialog.Content>\n </Dialog.Root>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* RadiographPanel */\n/* ------------------------------------------------------------------ */\n\nexport const RadiographPanel = forwardRef<\n RadiographPanelHandle,\n RadiographPanelProps\n>(function RadiographPanel(\n {\n id,\n toothId,\n toothLabel: toothLabelProp,\n radiographs = [],\n onAddRadiograph,\n onDeleteRadiograph,\n title,\n 'aria-label': ariaLabel,\n className,\n ...rest\n },\n ref,\n) {\n const { t } = useTranslation();\n\n const [viewer, setViewer] = useState<ViewerState | null>(null);\n const [compareIds, setCompareIds] = useState<string[]>([]);\n const [sortDesc, setSortDesc] = useState(true);\n const [range, setRange] = useState<DateRangeValue>({});\n const [pendingFile, setPendingFile] = useState<string | null>(null);\n const [date, setDate] = useState<Date | undefined>(undefined);\n const [note, setNote] = useState('');\n\n // Stable, current snapshot for the imperative handle.\n const filmsRef = useRef<Radiograph[]>(radiographs);\n filmsRef.current = radiographs;\n\n useImperativeHandle(\n ref,\n () => ({\n getRadiographs: () => filmsRef.current,\n openViewer: (filmId) => {\n const index = filmsRef.current.findIndex((f) => f.id === filmId);\n if (index >= 0) {\n setViewer({ films: filmsRef.current, index, compare: false });\n }\n },\n closeViewer: () => setViewer(null),\n }),\n [],\n );\n\n const heading =\n title ?? t('radiographPanel.title', { defaultValue: 'Imaging' });\n const toothLabel =\n toothLabelProp ??\n (toothId\n ? t('radiographPanel.toothLabel', {\n defaultValue: 'Tooth {{id}}',\n id: toothId,\n })\n : '');\n\n const visible = useMemo(\n () =>\n sortByDate(\n radiographs.filter((f) => inRange(f, range)),\n sortDesc,\n ),\n [radiographs, range, sortDesc],\n );\n\n const toggleCompare = useCallback((filmId: string) => {\n setCompareIds((ids) =>\n ids.includes(filmId)\n ? ids.filter((x) => x !== filmId)\n : [...ids, filmId].slice(-2),\n );\n }, []);\n\n const handleUpload = useCallback(() => {\n if (!pendingFile || !onAddRadiograph) return;\n onAddRadiograph({\n fileName: pendingFile,\n date: date ? toLocalIsoDate(date) : undefined,\n note: note || undefined,\n });\n setPendingFile(null);\n setNote('');\n setDate(undefined);\n }, [pendingFile, date, note, onAddRadiograph]);\n\n // History filter controls — pinned in the PanelHeader's filters row (only\n // meaningful once a tooth is selected) so they stay put as the list scrolls.\n const historyFilters = toothId ? (\n <>\n <FormField\n label={t('radiographPanel.history.range', {\n defaultValue: 'Date range',\n })}\n >\n <DateRangePicker value={range} onChange={setRange} />\n </FormField>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <Button intent=\"ghost\" size=\"sm\" onClick={() => setSortDesc((s) => !s)}>\n {sortDesc\n ? t('radiographPanel.history.sortNewest', {\n defaultValue: 'Newest first',\n })\n : t('radiographPanel.history.sortOldest', {\n defaultValue: 'Oldest first',\n })}\n </Button>\n <Button\n intent=\"ghost\"\n size=\"sm\"\n startIcon={<GitCompareArrows aria-hidden=\"true\" />}\n disabled={compareIds.length !== 2}\n onClick={() => {\n const films = radiographs.filter((f) => compareIds.includes(f.id));\n if (films.length === 2) {\n setViewer({ films, index: 0, compare: true });\n }\n }}\n >\n {t('radiographPanel.history.compare', {\n defaultValue: 'Compare',\n })}\n </Button>\n </div>\n </>\n ) : undefined;\n\n return (\n <section\n data-component=\"radiograph-panel\"\n data-component-id={id}\n data-tooth={toothId ?? undefined}\n aria-label={ariaLabel ?? heading}\n className={['ds:flex ds:flex-col', className].filter(Boolean).join(' ')}\n {...rest}\n >\n {/* Shared rail-panel header: title + film-count badge, history filters\n pinned beneath, divider, sticky so it stays visible as the list\n scrolls. The rail injects favourite/close into the action cluster. */}\n <PanelHeader\n sticky\n icon={<Images aria-hidden=\"true\" />}\n title={\n <>\n {toothId ? toothLabel : heading}\n {toothId ? (\n <Badge variant=\"info\" size=\"sm\">\n {radiographs.length}\n </Badge>\n ) : null}\n </>\n }\n filters={historyFilters}\n />\n\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:p-[var(--spacing-md)]\">\n {!toothId ? (\n <EmptyState\n variant=\"first-use\"\n title={t('radiographPanel.noTooth.title', {\n defaultValue: 'No tooth selected',\n })}\n description={t('radiographPanel.noTooth.body', {\n defaultValue:\n 'Select a tooth on the chart to view its radiographs.',\n })}\n />\n ) : (\n <>\n {/* Upload */}\n {onAddRadiograph ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <FormField\n label={t('radiographPanel.upload.file', {\n defaultValue: 'Image file',\n })}\n >\n <FileUpload\n accept={{ 'image/*': ['.png', '.jpg', '.jpeg', '.webp'] }}\n variant=\"both\"\n showPreview\n onDrop={(accepted) =>\n setPendingFile(accepted[0]?.name ?? null)\n }\n />\n </FormField>\n <FormField\n label={t('radiographPanel.upload.date', {\n defaultValue: 'Acquisition date',\n })}\n >\n <DatePicker value={date} onChange={(d) => setDate(d)} />\n </FormField>\n <FormField\n label={t('radiographPanel.upload.note', {\n defaultValue: 'Note (optional)',\n })}\n >\n <TextArea\n rows={2}\n value={note}\n onChange={(e) => setNote(e.target.value)}\n placeholder={t('radiographPanel.upload.notePlaceholder', {\n defaultValue: 'E.g. routine check, apical lesion…',\n })}\n />\n </FormField>\n <Button\n intent=\"primary\"\n size=\"sm\"\n startIcon={<Upload aria-hidden=\"true\" />}\n disabled={!pendingFile}\n onClick={handleUpload}\n className=\"ds:self-start\"\n >\n {t('radiographPanel.upload.submit', {\n defaultValue: 'Upload',\n })}\n </Button>\n </div>\n ) : null}\n\n {/* History */}\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {visible.length === 0 ? (\n <EmptyState\n variant=\"first-use\"\n title={t('radiographPanel.history.emptyTitle', {\n defaultValue: 'No radiographs yet',\n })}\n description={t('radiographPanel.history.emptyBody', {\n defaultValue: 'Upload the first film for this tooth.',\n })}\n />\n ) : (\n <ul className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-xs)] ds:p-0\">\n {visible.map((film, visibleIndex) => {\n const selected = compareIds.includes(film.id);\n // Disambiguate the repeated row actions for screen readers by\n // the film's date (falls back to its 1-based position).\n const filmLabel = film.date ?? String(visibleIndex + 1);\n return (\n <li\n key={film.id}\n data-radiograph-id={film.id}\n data-selected={selected}\n className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--card-border)] ds:bg-[var(--card)] ds:p-[var(--spacing-xs)] ds:data-[selected=true]:border-[color:var(--info-foreground)]\"\n >\n <img\n src={film.thumbnailUrl ?? film.url}\n alt={film.alt ?? ''}\n className=\"ds:h-16 ds:w-12 ds:shrink-0 ds:rounded-[var(--radius-sm)] ds:object-cover\"\n />\n <div className=\"ds:flex ds:min-w-0 ds:flex-1 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n <span className=\"type-body-sm ds:text-[color:var(--foreground)]\">\n {film.date}\n </span>\n <KindBadge kind={film.kind ?? 'periapical'} />\n </div>\n <IconButton\n icon={<Eye aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.history.view', {\n defaultValue: 'View radiograph {{film}}',\n film: filmLabel,\n })}\n size=\"sm\"\n intent=\"ghost\"\n // Step the viewer through the FILTERED + sorted list the\n // user is looking at, not every film on the tooth.\n onClick={() =>\n setViewer({\n films: visible,\n index: visibleIndex,\n compare: false,\n })\n }\n />\n <IconButton\n icon={<GitCompareArrows aria-hidden=\"true\" />}\n aria-label={\n selected\n ? t('radiographPanel.history.compareRemove', {\n defaultValue:\n 'Remove {{film}} from comparison',\n film: filmLabel,\n })\n : t('radiographPanel.history.compareAdd', {\n defaultValue:\n 'Select {{film}} for comparison',\n film: filmLabel,\n })\n }\n aria-pressed={selected}\n size=\"sm\"\n intent={selected ? 'secondary' : 'ghost'}\n onClick={() => toggleCompare(film.id)}\n />\n {onDeleteRadiograph ? (\n <IconButton\n icon={<Trash2 aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.history.delete', {\n defaultValue: 'Delete radiograph {{film}}',\n film: filmLabel,\n })}\n size=\"sm\"\n intent=\"ghost\"\n onClick={() => onDeleteRadiograph(film.id)}\n />\n ) : null}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n </>\n )}\n </div>\n\n <RadiographViewer\n state={viewer}\n toothLabel={toothLabel}\n onClose={() => setViewer(null)}\n onIndexChange={(index) => setViewer((v) => (v ? { ...v, index } : v))}\n />\n </section>\n );\n});\n\n// `Images` is the panel's imaging glyph (a films archive, not a radiation\n// hazard) — re-exported so a host registering the Control Room tool can use\n// the same icon as the panel.\nexport { Images as RadiographPanelIcon };\n","import type { AgentAdapter } from '../../agent/types';\nimport type { RadiographPanelHandle } from './radiograph-panel';\nimport type { Radiograph } from '../tooth-scheme';\n\n/**\n * The Imaging panel for the Control Room rail. It DISPLAYS the selected tooth's\n * radiographs and drives the fullscreen viewer; uploads and deletes are the\n * host's responsibility (the `onAddRadiograph` / `onDeleteRadiograph` props),\n * so they are not exposed here — the consuming pattern/app owns those `write` /\n * `destructive` workflows and their `radiograph.*` API contracts.\n */\nexport const radiographPanelAgent: AgentAdapter<RadiographPanelHandle> = {\n id: 'radiograph-panel',\n // pick — open a specific film in the viewer; view_change — the viewer is a\n // view surface over the host-owned films.\n capabilities: ['pick', 'view_change'],\n state: {\n radiographs: {\n // A9: structural refs only (id + kind) — never the free-text `note`,\n // service dates, or consumer-hosted image URLs in the always-on snapshot.\n type: 'Array<{ id: string; kind?: string }>',\n descriptionKey: 'ui.agent.radiographPanel.state.radiographs',\n description:\n 'Structural references (id + kind) to the films shown for the selected tooth.',\n read: (handle) =>\n handle\n .getRadiographs()\n .map((film) => ({ id: film.id, kind: film.kind ?? 'periapical' })),\n },\n },\n actions: {\n view_radiograph: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.radiographPanel.actions.viewRadiograph',\n description: 'Open a film in the fullscreen viewer by id.',\n invoke: (handle, args: { id: string }) => {\n handle.openViewer(args.id);\n },\n },\n close_viewer: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.radiographPanel.actions.closeViewer',\n description: 'Close the fullscreen radiograph viewer.',\n invoke: (handle) => {\n handle.closeViewer();\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'radiograph-panel' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-radiograph-id',\n description:\n \"Each film row (`<li>`) carries its radiograph id as `data-radiograph-id`. The panel root additionally carries the selected tooth's FDI id as `data-tooth`.\",\n },\n },\n};\n\n// Re-exported for callers building typed reads against the adapter.\nexport type { Radiograph };\n"],"names":["__iconNode","GitCompareArrows","createLucideIcon","Images","ZOOM_CLASS","sortByDate","films","desc","a","b","inRange","film","range","d","toLocalIsoDate","pad","n","KindBadge","kind","t","useTranslation","label","Badge","RadiographViewer","state","toothLabel","onClose","onIndexChange","zoom","setZoom","useState","useEffect","current","jsx","Dialog","next","jsxs","IconButton","ChevronLeft","ChevronRight","ZoomOut","z","ZoomIn","RadiographPanel","forwardRef","id","toothId","toothLabelProp","radiographs","onAddRadiograph","onDeleteRadiograph","title","ariaLabel","className","rest","ref","viewer","setViewer","compareIds","setCompareIds","sortDesc","setSortDesc","setRange","pendingFile","setPendingFile","date","setDate","note","setNote","filmsRef","useRef","useImperativeHandle","filmId","index","f","heading","visible","useMemo","toggleCompare","useCallback","ids","x","handleUpload","historyFilters","Fragment","FormField","DateRangePicker","Button","s","PanelHeader","FileUpload","accepted","_a","DatePicker","TextArea","e","Upload","EmptyState","visibleIndex","selected","filmLabel","Eye","Trash2","v","radiographPanelAgent","handle","args"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,MAAMA,KAAa;AAAA,EACjB,CAAC,UAAU,EAAE,IAAI,KAAK,IAAI,KAAK,GAAG,KAAK,KAAK,UAAU;AAAA,EACtD,CAAC,QAAQ,EAAE,GAAG,2BAA2B,KAAK,SAAQ,CAAE;AAAA,EACxD,CAAC,QAAQ,EAAE,GAAG,iBAAiB,KAAK,SAAQ,CAAE;AAAA,EAC9C,CAAC,UAAU,EAAE,IAAI,MAAM,IAAI,MAAM,GAAG,KAAK,KAAK,UAAU;AAAA,EACxD,CAAC,QAAQ,EAAE,GAAG,4BAA4B,KAAK,SAAQ,CAAE;AAAA,EACzD,CAAC,QAAQ,EAAE,GAAG,iBAAiB,KAAK,SAAQ,CAAE;AAChD,GACMC,IAAmBC,EAAiB,sBAAsBF,EAAU;ACjB1E;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,MAAMA,KAAa;AAAA,EACjB,CAAC,QAAQ,EAAE,GAAG,kDAAkD,KAAK,SAAQ,CAAE;AAAA,EAC/E,CAAC,QAAQ,EAAE,GAAG,wDAAwD,KAAK,SAAQ,CAAE;AAAA,EACrF,CAAC,UAAU,EAAE,IAAI,MAAM,IAAI,KAAK,GAAG,KAAK,MAAM,gBAAgB,KAAK,SAAQ,CAAE;AAAA,EAC7E,CAAC,QAAQ,EAAE,GAAG,KAAK,GAAG,KAAK,OAAO,MAAM,QAAQ,MAAM,IAAI,KAAK,KAAK,SAAQ,CAAE;AAChF,GACMG,KAASD,EAAiB,UAAUF,EAAU,GCqC9CI,IAAa,CAAC,gBAAgB,gBAAgB,cAAc;AAIlE,SAASC,GAAWC,GAAqBC,GAA6B;AACpE,SAAO,CAAC,GAAGD,CAAK,EAAE,KAAK,CAACE,GAAGC,MACrBD,EAAE,SAASC,EAAE,OAAa,IACzBD,EAAE,OACFC,EAAE,QACCD,EAAE,OAAOC,EAAE,OAAO,KAAK,MAAMF,IAAO,KAAK,KAD7B,KADA,CAGrB;AACH;AAKA,SAASG,GAAQC,GAAkBC,GAAgC;AACjE,MAAI,CAACD,EAAK,KAAM,QAAO,CAACC,EAAM,QAAQ,CAACA,EAAM;AAC7C,QAAMC,IAAI,oBAAI,KAAK,GAAGF,EAAK,IAAI,WAAW;AAE1C,SADI,EAAAC,EAAM,QAAQC,IAAID,EAAM,QACxBA,EAAM,MAAMC,IAAID,EAAM;AAE5B;AAIA,SAASE,GAAeD,GAAiB;AACvC,QAAME,IAAM,CAACC,MAAc,OAAOA,CAAC,EAAE,SAAS,GAAG,GAAG;AACpD,SAAO,GAAGH,EAAE,YAAA,CAAa,IAAIE,EAAIF,EAAE,SAAA,IAAa,CAAC,CAAC,IAAIE,EAAIF,EAAE,QAAA,CAAS,CAAC;AACxE;AAqCA,SAASI,GAAU,EAAE,MAAAC,KAAgD;AACnE,QAAM,EAAE,GAAAC,EAAA,IAAMC,EAAA,GACRC,IAAQF,EAAE,wBAAwBD,CAAI,IAAI;AAAA,IAC9C,cAAc;AAAA,MACZ,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,EACPA,CAAI;AAAA,EAAA,CACP;AACD,2BACGI,GAAA,EAAM,SAAQ,WAAU,MAAK,MAC3B,UAAAD,GACH;AAEJ;AAYA,SAASE,GAAiB;AAAA,EACxB,OAAAC;AAAA,EACA,YAAAC;AAAA,EACA,SAAAC;AAAA,EACA,eAAAC;AACF,GAKiB;AACf,QAAM,EAAE,GAAAR,EAAA,IAAMC,EAAA,GACR,CAACQ,GAAMC,CAAO,IAAIC,EAAS,CAAC;AAGlC,EAAAC,GAAU,MAAM;AACd,IAAAF,EAAQ,CAAC;AAAA,EACX,GAAG,CAACL,KAAA,gBAAAA,EAAO,OAAOA,KAAA,gBAAAA,EAAO,OAAO,CAAC;AACjC,QAAMlB,KAAQkB,KAAA,gBAAAA,EAAO,UAAS,CAAA,GACxBQ,IAAUR,IAAQlB,EAAMkB,EAAM,KAAK,IAAI;AAE7C,SACE,gBAAAS;AAAA,IAACC,EAAO;AAAA,IAAP;AAAA,MACC,MAAMV,MAAU;AAAA,MAChB,cAAc,CAACW,MAAS;AACtB,QAAKA,MACHN,EAAQ,CAAC,GACTH,EAAA;AAAA,MAEJ;AAAA,MAEA,4BAACQ,EAAO,SAAP,EAAe,MAAK,cAAa,oBAAkB,QAClD,UAAA;AAAA,QAAA,gBAAAD,EAACC,EAAO,QAAP,EACC,UAAA,gBAAAD,EAACC,EAAO,OAAP,EACE,UAAAV,KAAA,QAAAA,EAAO,UACJL,EAAE,uCAAuC;AAAA,UACvC,cAAc;AAAA,UACd,OAAOM;AAAA,QAAA,CACR,IACDN,EAAE,gCAAgC;AAAA,UAChC,cAAc;AAAA,UACd,OAAOM;AAAA,QAAA,CACR,GACP,EAAA,CACF;AAAA,0BACCS,EAAO,MAAP,EACE,UAAAV,KAAA,QAAAA,EAAO,UACN,gBAAAS,EAAC,OAAA,EAAI,WAAU,qDACZ,YAAM,MAAM,GAAG,CAAC,EAAE,IAAI,CAACtB,MACtB,gBAAAyB;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,WAAU;AAAA,YAEV,UAAA;AAAA,cAAA,gBAAAH,EAAC,OAAA,EAAI,WAAU,4IACb,UAAA,gBAAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,KAAKtB,EAAK,gBAAgBA,EAAK;AAAA,kBAC/B,KAAKA,EAAK,OAAOc;AAAA,kBACjB,WAAU;AAAA,gBAAA;AAAA,cAAA,GAEd;AAAA,cACA,gBAAAQ,EAAC,cAAA,EAAW,WAAU,qDACnB,YAAK,KAAA,CACR;AAAA,YAAA;AAAA,UAAA;AAAA,UAZKtB,EAAK;AAAA,QAAA,CAcb,EAAA,CACH,IAEA,gBAAAyB,EAAC,OAAA,EAAI,WAAU,kEACb,UAAA;AAAA,UAAA,gBAAAH,EAAC,OAAA,EAAI,WAAU,0JACb,UAAA,gBAAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAKD,KAAA,gBAAAA,EAAS,kBAAgBA,KAAA,gBAAAA,EAAS;AAAA,cACvC,MAAKA,KAAA,gBAAAA,EAAS,QAAOP;AAAA,cACrB,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACArB,EAAWwB,CAAI;AAAA,cAAA,EACf,KAAK,GAAG;AAAA,YAAA;AAAA,UAAA,GAEd;AAAA,UACA,gBAAAQ,EAAC,OAAA,EAAI,WAAU,sDACb,UAAA;AAAA,YAAA,gBAAAH;AAAA,cAACI;AAAA,cAAA;AAAA,gBACC,MAAM,gBAAAJ,EAACK,IAAA,EAAY,eAAY,OAAA,CAAO;AAAA,gBACtC,cAAYnB,EAAE,+BAA+B;AAAA,kBAC3C,cAAc;AAAA,gBAAA,CACf;AAAA,gBACD,MAAK;AAAA,gBACL,eAAa;AAAA,gBACb,UAAU,CAACK,KAASA,EAAM,UAAU;AAAA,gBACpC,SAAS,MACPA,KAASG,EAAc,KAAK,IAAI,GAAGH,EAAM,QAAQ,CAAC,CAAC;AAAA,cAAA;AAAA,YAAA;AAAA,YAGvD,gBAAAS;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,aAAU;AAAA,gBACV,WAAU;AAAA,gBAET,YAAE,mCAAmC;AAAA,kBACpC,cAAc;AAAA,kBACd,WAAUT,KAAA,gBAAAA,EAAO,UAAS,KAAK;AAAA,kBAC/B,OAAOlB,EAAM;AAAA,gBAAA,CACd;AAAA,cAAA;AAAA,YAAA;AAAA,YAEH,gBAAA2B;AAAA,cAACI;AAAA,cAAA;AAAA,gBACC,MAAM,gBAAAJ,EAACM,IAAA,EAAa,eAAY,OAAA,CAAO;AAAA,gBACvC,cAAYpB,EAAE,+BAA+B;AAAA,kBAC3C,cAAc;AAAA,gBAAA,CACf;AAAA,gBACD,MAAK;AAAA,gBACL,eAAa;AAAA,gBACb,UAAU,CAACK,KAASA,EAAM,SAASlB,EAAM,SAAS;AAAA,gBAClD,SAAS,MACPkB,KACAG,EAAc,KAAK,IAAIrB,EAAM,SAAS,GAAGkB,EAAM,QAAQ,CAAC,CAAC;AAAA,cAAA;AAAA,YAAA;AAAA,YAG7D,gBAAAS;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,eAAY;AAAA,gBACZ,WAAU;AAAA,cAAA;AAAA,YAAA;AAAA,YAEZ,gBAAAA;AAAA,cAACI;AAAA,cAAA;AAAA,gBACC,MAAM,gBAAAJ,EAACO,IAAA,EAAQ,eAAY,OAAA,CAAO;AAAA,gBAClC,cAAYrB,EAAE,kCAAkC;AAAA,kBAC9C,cAAc;AAAA,gBAAA,CACf;AAAA,gBACD,MAAK;AAAA,gBACL,UAAUS,MAAS;AAAA,gBACnB,SAAS,MAAMC,EAAQ,CAACY,MAAM,KAAK,IAAI,GAAGA,IAAI,CAAC,CAAC;AAAA,cAAA;AAAA,YAAA;AAAA,YAElD,gBAAAR;AAAA,cAACI;AAAA,cAAA;AAAA,gBACC,MAAM,gBAAAJ,EAACS,IAAA,EAAO,eAAY,OAAA,CAAO;AAAA,gBACjC,cAAYvB,EAAE,iCAAiC;AAAA,kBAC7C,cAAc;AAAA,gBAAA,CACf;AAAA,gBACD,MAAK;AAAA,gBACL,UAAUS,MAASxB,EAAW,SAAS;AAAA,gBACvC,SAAS,MACPyB,EAAQ,CAACY,MAAM,KAAK,IAAIrC,EAAW,SAAS,GAAGqC,IAAI,CAAC,CAAC;AAAA,cAAA;AAAA,YAAA;AAAA,UAEzD,GACF;AAAA,UACCT,KAAA,QAAAA,EAAS,OACR,gBAAAC,EAAC,KAAA,EAAE,WAAU,wDACV,UAAAD,EAAQ,MACX,IACE;AAAA,QAAA,EAAA,CACN,EAAA,CAEJ;AAAA,MAAA,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAGN;AAMO,MAAMW,KAAkBC,EAG7B,SACA;AAAA,EACE,IAAAC;AAAA,EACA,SAAAC;AAAA,EACA,YAAYC;AAAA,EACZ,aAAAC,IAAc,CAAA;AAAA,EACd,iBAAAC;AAAA,EACA,oBAAAC;AAAA,EACA,OAAAC;AAAA,EACA,cAAcC;AAAA,EACd,WAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAM,EAAE,GAAApC,EAAA,IAAMC,EAAA,GAER,CAACoC,GAAQC,CAAS,IAAI3B,EAA6B,IAAI,GACvD,CAAC4B,GAAYC,CAAa,IAAI7B,EAAmB,CAAA,CAAE,GACnD,CAAC8B,GAAUC,CAAW,IAAI/B,EAAS,EAAI,GACvC,CAAClB,GAAOkD,CAAQ,IAAIhC,EAAyB,CAAA,CAAE,GAC/C,CAACiC,GAAaC,CAAc,IAAIlC,EAAwB,IAAI,GAC5D,CAACmC,GAAMC,CAAO,IAAIpC,EAA2B,MAAS,GACtD,CAACqC,GAAMC,CAAO,IAAItC,EAAS,EAAE,GAG7BuC,IAAWC,GAAqBtB,CAAW;AACjD,EAAAqB,EAAS,UAAUrB,GAEnBuB;AAAA,IACEhB;AAAA,IACA,OAAO;AAAA,MACL,gBAAgB,MAAMc,EAAS;AAAA,MAC/B,YAAY,CAACG,MAAW;AACtB,cAAMC,IAAQJ,EAAS,QAAQ,UAAU,CAACK,MAAMA,EAAE,OAAOF,CAAM;AAC/D,QAAIC,KAAS,KACXhB,EAAU,EAAE,OAAOY,EAAS,SAAS,OAAAI,GAAO,SAAS,IAAO;AAAA,MAEhE;AAAA,MACA,aAAa,MAAMhB,EAAU,IAAI;AAAA,IAAA;AAAA,IAEnC,CAAA;AAAA,EAAC;AAGH,QAAMkB,IACJxB,KAAShC,EAAE,yBAAyB,EAAE,cAAc,WAAW,GAC3DM,IACJsB,MACCD,IACG3B,EAAE,8BAA8B;AAAA,IAC9B,cAAc;AAAA,IACd,IAAI2B;AAAA,EAAA,CACL,IACD,KAEA8B,IAAUC;AAAA,IACd,MACExE;AAAA,MACE2C,EAAY,OAAO,CAAC0B,MAAMhE,GAAQgE,GAAG9D,CAAK,CAAC;AAAA,MAC3CgD;AAAA,IAAA;AAAA,IAEJ,CAACZ,GAAapC,GAAOgD,CAAQ;AAAA,EAAA,GAGzBkB,IAAgBC,EAAY,CAACP,MAAmB;AACpD,IAAAb;AAAA,MAAc,CAACqB,MACbA,EAAI,SAASR,CAAM,IACfQ,EAAI,OAAO,CAACC,MAAMA,MAAMT,CAAM,IAC9B,CAAC,GAAGQ,GAAKR,CAAM,EAAE,MAAM,EAAE;AAAA,IAAA;AAAA,EAEjC,GAAG,CAAA,CAAE,GAECU,IAAeH,EAAY,MAAM;AACrC,IAAI,CAAChB,KAAe,CAACd,MACrBA,EAAgB;AAAA,MACd,UAAUc;AAAA,MACV,MAAME,IAAOnD,GAAemD,CAAI,IAAI;AAAA,MACpC,MAAME,KAAQ;AAAA,IAAA,CACf,GACDH,EAAe,IAAI,GACnBI,EAAQ,EAAE,GACVF,EAAQ,MAAS;AAAA,EACnB,GAAG,CAACH,GAAaE,GAAME,GAAMlB,CAAe,CAAC,GAIvCkC,IAAiBrC,IACrB,gBAAAV,EAAAgD,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAnD;AAAA,MAACoD;AAAA,MAAA;AAAA,QACC,OAAOlE,EAAE,iCAAiC;AAAA,UACxC,cAAc;AAAA,QAAA,CACf;AAAA,QAED,UAAA,gBAAAc,EAACqD,IAAA,EAAgB,OAAO1E,GAAO,UAAUkD,EAAA,CAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAErD,gBAAA1B,EAAC,OAAA,EAAI,WAAU,sDACb,UAAA;AAAA,MAAA,gBAAAH,EAACsD,GAAA,EAAO,QAAO,SAAQ,MAAK,MAAK,SAAS,MAAM1B,EAAY,CAAC2B,MAAM,CAACA,CAAC,GAClE,UAAA5B,IACGzC,EAAE,sCAAsC;AAAA,QACtC,cAAc;AAAA,MAAA,CACf,IACDA,EAAE,sCAAsC;AAAA,QACtC,cAAc;AAAA,MAAA,CACf,GACP;AAAA,MACA,gBAAAc;AAAA,QAACsD;AAAA,QAAA;AAAA,UACC,QAAO;AAAA,UACP,MAAK;AAAA,UACL,WAAW,gBAAAtD,EAAChC,GAAA,EAAiB,eAAY,OAAA,CAAO;AAAA,UAChD,UAAUyD,EAAW,WAAW;AAAA,UAChC,SAAS,MAAM;AACb,kBAAMpD,IAAQ0C,EAAY,OAAO,CAAC0B,MAAMhB,EAAW,SAASgB,EAAE,EAAE,CAAC;AACjE,YAAIpE,EAAM,WAAW,KACnBmD,EAAU,EAAE,OAAAnD,GAAO,OAAO,GAAG,SAAS,IAAM;AAAA,UAEhD;AAAA,UAEC,YAAE,mCAAmC;AAAA,YACpC,cAAc;AAAA,UAAA,CACf;AAAA,QAAA;AAAA,MAAA;AAAA,IACH,EAAA,CACF;AAAA,EAAA,EAAA,CACF,IACE;AAEJ,SACE,gBAAA8B;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,kBAAe;AAAA,MACf,qBAAmBS;AAAA,MACnB,cAAYC,KAAW;AAAA,MACvB,cAAYM,KAAauB;AAAA,MACzB,WAAW,CAAC,uBAAuBtB,CAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MACrE,GAAGC;AAAA,MAKJ,UAAA;AAAA,QAAA,gBAAArB;AAAA,UAACwD;AAAA,UAAA;AAAA,YACC,QAAM;AAAA,YACN,MAAM,gBAAAxD,EAAC9B,IAAA,EAAO,eAAY,OAAA,CAAO;AAAA,YACjC,OACE,gBAAAiC,EAAAgD,GAAA,EACG,UAAA;AAAA,cAAAtC,IAAUrB,IAAakD;AAAA,cACvB7B,sBACExB,GAAA,EAAM,SAAQ,QAAO,MAAK,MACxB,UAAA0B,EAAY,OAAA,CACf,IACE;AAAA,YAAA,GACN;AAAA,YAEF,SAASmC;AAAA,UAAA;AAAA,QAAA;AAAA,QAGX,gBAAAlD,EAAC,OAAA,EAAI,WAAU,2EACZ,UAACa,IAYA,gBAAAV,EAAAgD,GAAA,EAEG,UAAA;AAAA,UAAAnC,IACC,gBAAAb,EAAC,OAAA,EAAI,WAAU,kDACb,UAAA;AAAA,YAAA,gBAAAH;AAAA,cAACoD;AAAA,cAAA;AAAA,gBACC,OAAOlE,EAAE,+BAA+B;AAAA,kBACtC,cAAc;AAAA,gBAAA,CACf;AAAA,gBAED,UAAA,gBAAAc;AAAA,kBAACyD;AAAA,kBAAA;AAAA,oBACC,QAAQ,EAAE,WAAW,CAAC,QAAQ,QAAQ,SAAS,OAAO,EAAA;AAAA,oBACtD,SAAQ;AAAA,oBACR,aAAW;AAAA,oBACX,QAAQ,CAACC;;AACP,6BAAA3B,IAAe4B,IAAAD,EAAS,CAAC,MAAV,gBAAAC,EAAa,SAAQ,IAAI;AAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAE5C;AAAA,YAAA;AAAA,YAEF,gBAAA3D;AAAA,cAACoD;AAAA,cAAA;AAAA,gBACC,OAAOlE,EAAE,+BAA+B;AAAA,kBACtC,cAAc;AAAA,gBAAA,CACf;AAAA,gBAED,UAAA,gBAAAc,EAAC4D,MAAW,OAAO5B,GAAM,UAAU,CAACpD,MAAMqD,EAAQrD,CAAC,EAAA,CAAG;AAAA,cAAA;AAAA,YAAA;AAAA,YAExD,gBAAAoB;AAAA,cAACoD;AAAA,cAAA;AAAA,gBACC,OAAOlE,EAAE,+BAA+B;AAAA,kBACtC,cAAc;AAAA,gBAAA,CACf;AAAA,gBAED,UAAA,gBAAAc;AAAA,kBAAC6D;AAAA,kBAAA;AAAA,oBACC,MAAM;AAAA,oBACN,OAAO3B;AAAA,oBACP,UAAU,CAAC4B,MAAM3B,EAAQ2B,EAAE,OAAO,KAAK;AAAA,oBACvC,aAAa5E,EAAE,0CAA0C;AAAA,sBACvD,cAAc;AAAA,oBAAA,CACf;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACH;AAAA,YAAA;AAAA,YAEF,gBAAAc;AAAA,cAACsD;AAAA,cAAA;AAAA,gBACC,QAAO;AAAA,gBACP,MAAK;AAAA,gBACL,WAAW,gBAAAtD,EAAC+D,IAAA,EAAO,eAAY,OAAA,CAAO;AAAA,gBACtC,UAAU,CAACjC;AAAA,gBACX,SAASmB;AAAA,gBACT,WAAU;AAAA,gBAET,YAAE,iCAAiC;AAAA,kBAClC,cAAc;AAAA,gBAAA,CACf;AAAA,cAAA;AAAA,YAAA;AAAA,UACH,EAAA,CACF,IACE;AAAA,4BAGH,OAAA,EAAI,WAAU,kDACZ,UAAAN,EAAQ,WAAW,IAClB,gBAAA3C;AAAA,YAACgE;AAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,OAAO9E,EAAE,sCAAsC;AAAA,gBAC7C,cAAc;AAAA,cAAA,CACf;AAAA,cACD,aAAaA,EAAE,qCAAqC;AAAA,gBAClD,cAAc;AAAA,cAAA,CACf;AAAA,YAAA;AAAA,UAAA,sBAGF,MAAA,EAAG,WAAU,6EACX,UAAAyD,EAAQ,IAAI,CAACjE,GAAMuF,MAAiB;AACnC,kBAAMC,IAAWzC,EAAW,SAAS/C,EAAK,EAAE,GAGtCyF,IAAYzF,EAAK,QAAQ,OAAOuF,IAAe,CAAC;AACtD,mBACE,gBAAA9D;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,sBAAoBzB,EAAK;AAAA,gBACzB,iBAAewF;AAAA,gBACf,WAAU;AAAA,gBAEV,UAAA;AAAA,kBAAA,gBAAAlE;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBACC,KAAKtB,EAAK,gBAAgBA,EAAK;AAAA,sBAC/B,KAAKA,EAAK,OAAO;AAAA,sBACjB,WAAU;AAAA,oBAAA;AAAA,kBAAA;AAAA,kBAEZ,gBAAAyB,EAAC,OAAA,EAAI,WAAU,wEACb,UAAA;AAAA,oBAAA,gBAAAH,EAAC,QAAA,EAAK,WAAU,kDACb,UAAAtB,EAAK,MACR;AAAA,oBACA,gBAAAsB,EAAChB,IAAA,EAAU,MAAMN,EAAK,QAAQ,aAAA,CAAc;AAAA,kBAAA,GAC9C;AAAA,kBACA,gBAAAsB;AAAA,oBAACI;AAAA,oBAAA;AAAA,sBACC,MAAM,gBAAAJ,EAACoE,IAAA,EAAI,eAAY,OAAA,CAAO;AAAA,sBAC9B,cAAYlF,EAAE,gCAAgC;AAAA,wBAC5C,cAAc;AAAA,wBACd,MAAMiF;AAAA,sBAAA,CACP;AAAA,sBACD,MAAK;AAAA,sBACL,QAAO;AAAA,sBAGP,SAAS,MACP3C,EAAU;AAAA,wBACR,OAAOmB;AAAA,wBACP,OAAOsB;AAAA,wBACP,SAAS;AAAA,sBAAA,CACV;AAAA,oBAAA;AAAA,kBAAA;AAAA,kBAGL,gBAAAjE;AAAA,oBAACI;AAAA,oBAAA;AAAA,sBACC,MAAM,gBAAAJ,EAAChC,GAAA,EAAiB,eAAY,OAAA,CAAO;AAAA,sBAC3C,cACEkG,IACIhF,EAAE,yCAAyC;AAAA,wBACzC,cACE;AAAA,wBACF,MAAMiF;AAAA,sBAAA,CACP,IACDjF,EAAE,sCAAsC;AAAA,wBACtC,cACE;AAAA,wBACF,MAAMiF;AAAA,sBAAA,CACP;AAAA,sBAEP,gBAAcD;AAAA,sBACd,MAAK;AAAA,sBACL,QAAQA,IAAW,cAAc;AAAA,sBACjC,SAAS,MAAMrB,EAAcnE,EAAK,EAAE;AAAA,oBAAA;AAAA,kBAAA;AAAA,kBAErCuC,IACC,gBAAAjB;AAAA,oBAACI;AAAA,oBAAA;AAAA,sBACC,MAAM,gBAAAJ,EAACqE,IAAA,EAAO,eAAY,OAAA,CAAO;AAAA,sBACjC,cAAYnF,EAAE,kCAAkC;AAAA,wBAC9C,cAAc;AAAA,wBACd,MAAMiF;AAAA,sBAAA,CACP;AAAA,sBACD,MAAK;AAAA,sBACL,QAAO;AAAA,sBACP,SAAS,MAAMlD,EAAmBvC,EAAK,EAAE;AAAA,oBAAA;AAAA,kBAAA,IAEzC;AAAA,gBAAA;AAAA,cAAA;AAAA,cAjECA,EAAK;AAAA,YAAA;AAAA,UAoEhB,CAAC,GACH,EAAA,CAEJ;AAAA,QAAA,EAAA,CACF,IA9JA,gBAAAsB;AAAA,UAACgE;AAAA,UAAA;AAAA,YACC,SAAQ;AAAA,YACR,OAAO9E,EAAE,iCAAiC;AAAA,cACxC,cAAc;AAAA,YAAA,CACf;AAAA,YACD,aAAaA,EAAE,gCAAgC;AAAA,cAC7C,cACE;AAAA,YAAA,CACH;AAAA,UAAA;AAAA,QAAA,EAsJH,CAEJ;AAAA,QAEA,gBAAAc;AAAA,UAACV;AAAA,UAAA;AAAA,YACC,OAAOiC;AAAA,YACP,YAAA/B;AAAA,YACA,SAAS,MAAMgC,EAAU,IAAI;AAAA,YAC7B,eAAe,CAACgB,MAAUhB,EAAU,CAAC8C,MAAOA,KAAI,EAAE,GAAGA,GAAG,OAAA9B,EAAA,CAAY;AAAA,UAAA;AAAA,QAAA;AAAA,MACtE;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC,GC3mBY+B,KAA4D;AAAA,EACvE,IAAI;AAAA;AAAA;AAAA,EAGJ,cAAc,CAAC,QAAQ,aAAa;AAAA,EACpC,OAAO;AAAA,IACL,aAAa;AAAA;AAAA;AAAA,MAGX,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,MAAM,CAACC,MACLA,EACG,eAAA,EACA,IAAI,CAAC9F,OAAU,EAAE,IAAIA,EAAK,IAAI,MAAMA,EAAK,QAAQ,eAAe;AAAA,IAAA;AAAA,EACvE;AAAA,EAEF,SAAS;AAAA,IACP,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY,EAAE,IAAI,EAAE,MAAM,WAAS;AAAA,QACnC,UAAU,CAAC,IAAI;AAAA,MAAA;AAAA,MAEjB,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ,CAAC8F,GAAQC,MAAyB;AACxC,QAAAD,EAAO,WAAWC,EAAK,EAAE;AAAA,MAC3B;AAAA,IAAA;AAAA,IAEF,cAAc;AAAA,MACZ,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ,CAACD,MAAW;AAClB,QAAAA,EAAO,YAAA;AAAA,MACT;AAAA,IAAA;AAAA,EACF;AAAA,EAEF,UAAU;AAAA,IACR,MAAM,EAAE,MAAM,kBAAkB,OAAO,mBAAA;AAAA,IACvC,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,aAAa;AAAA,IAAA;AAAA,IAEf,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,IAAA;AAAA,EACJ;AAEJ;","x_google_ignoreList":[0,1]}
|
|
1
|
+
{"version":3,"file":"radiograph-panel.agent-Drj0hYVV.js","sources":["../../node_modules/lucide-react/dist/esm/icons/git-compare-arrows.mjs","../../node_modules/lucide-react/dist/esm/icons/images.mjs","../../src/components/radiograph-panel/radiograph-panel.tsx","../../src/components/radiograph-panel/radiograph-panel.agent.ts"],"sourcesContent":["/**\n * @license lucide-react v1.22.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.mjs';\n\nconst __iconNode = [\n [\"circle\", { cx: \"5\", cy: \"6\", r: \"3\", key: \"1qnov2\" }],\n [\"path\", { d: \"M12 6h5a2 2 0 0 1 2 2v7\", key: \"1yj91y\" }],\n [\"path\", { d: \"m15 9-3-3 3-3\", key: \"1lwv8l\" }],\n [\"circle\", { cx: \"19\", cy: \"18\", r: \"3\", key: \"1qljk2\" }],\n [\"path\", { d: \"M12 18H7a2 2 0 0 1-2-2V9\", key: \"16sdep\" }],\n [\"path\", { d: \"m9 15 3 3-3 3\", key: \"1m3kbl\" }]\n];\nconst GitCompareArrows = createLucideIcon(\"git-compare-arrows\", __iconNode);\n\nexport { __iconNode, GitCompareArrows as default };\n//# sourceMappingURL=git-compare-arrows.mjs.map\n","/**\n * @license lucide-react v1.22.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.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"m22 11-1.296-1.296a2.4 2.4 0 0 0-3.408 0L11 16\", key: \"9kzy35\" }],\n [\"path\", { d: \"M4 8a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2\", key: \"1t0f0t\" }],\n [\"circle\", { cx: \"13\", cy: \"7\", r: \"1\", fill: \"currentColor\", key: \"1obus6\" }],\n [\"rect\", { x: \"8\", y: \"2\", width: \"14\", height: \"14\", rx: \"2\", key: \"1gvhby\" }]\n];\nconst Images = createLucideIcon(\"images\", __iconNode);\n\nexport { __iconNode, Images as default };\n//# sourceMappingURL=images.mjs.map\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactElement,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport {\n Upload,\n ZoomIn,\n ZoomOut,\n ChevronLeft,\n ChevronRight,\n Trash2,\n Eye,\n GitCompareArrows,\n Images,\n} from 'lucide-react';\nimport type { FdiId, Radiograph, RadiographKind } from '../tooth-scheme';\nimport { Dialog } from '../dialog';\nimport { Button } from '../button';\nimport { IconButton } from '../icon-button';\nimport { Badge } from '../badge';\nimport { FormField } from '../form-field';\nimport { TextArea } from '../text-area';\nimport { FileUpload } from '../file-upload';\nimport { DatePicker } from '../date-picker';\nimport { DateRangePicker, type DateRangeValue } from '../date-range-picker';\nimport { EmptyState } from '../empty-state';\nimport { PanelHeader } from '../panel-header';\n\n/* ------------------------------------------------------------------ */\n/* RadiographPanel — the Imaging tool for the Control Room rail. */\n/* */\n/* Shows one tooth's radiographs (x-rays / endorali): an upload */\n/* composer, a sortable + date-filtered history, and a fullscreen */\n/* viewer that zooms / steps / compares films. Drops into a */\n/* `ControlRoomRail` tool `panel`, the patient profile, or a care-plan */\n/* editor — anywhere a selected tooth's imaging belongs. The host owns */\n/* the films (`radiographs`) and persistence (`onAddRadiograph` / */\n/* `onDeleteRadiograph`); the panel owns only view state. The chart */\n/* drives selection via `ToothScheme.onOpenRadiographs(toothId)`. */\n/* */\n/* Image urls are opaque / consumer-hosted — never bundled. Real */\n/* radiographs are PHI; stories use synthetic placeholders only. */\n/* ------------------------------------------------------------------ */\n\nconst ZOOM_CLASS = ['ds:scale-100', 'ds:scale-150', 'ds:scale-200'] as const;\n\n/** Sort films newest-first (or oldest-first); undated films sink to the end in\n * BOTH directions — mirrors `ToothScheme.sortByDateDesc`. */\nfunction sortByDate(films: Radiograph[], desc: boolean): Radiograph[] {\n return [...films].sort((a, b) => {\n if (a.date === b.date) return 0;\n if (!a.date) return 1;\n if (!b.date) return -1;\n return (a.date < b.date ? -1 : 1) * (desc ? -1 : 1);\n });\n}\n\n/** Date-range membership. ISO `YYYY-MM-DD` is parsed at LOCAL midnight (the\n * `+'T00:00:00'`) so the filter agrees with the local-midnight `Date` objects\n * the DateRangePicker emits — without it, films are off-by-one west of UTC. */\nfunction inRange(film: Radiograph, range: DateRangeValue): boolean {\n if (!film.date) return !range.from && !range.to;\n const d = new Date(`${film.date}T00:00:00`);\n if (range.from && d < range.from) return false;\n if (range.to && d > range.to) return false;\n return true;\n}\n\n/** Local `YYYY-MM-DD` — avoids the UTC shift `Date.toISOString()` introduces\n * west of UTC for the local-midnight Date the DatePicker emits. */\nfunction toLocalIsoDate(d: Date): string {\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\nexport interface RadiographPanelHandle {\n /** Read the films currently shown (after the host's `radiographs` prop). */\n getRadiographs: () => Radiograph[];\n /** Open the fullscreen viewer on a film by id. */\n openViewer: (id: string) => void;\n /** Close the fullscreen viewer. */\n closeViewer: () => void;\n}\n\nexport interface RadiographPanelProps extends Omit<\n HTMLAttributes<HTMLElement>,\n 'title' | 'children'\n> {\n /** Instance id — emitted as `data-component-id` for the agent registry. */\n id?: string;\n /** The tooth whose films are shown (FDI id). Null → a \"select a tooth\" prompt. */\n toothId?: FdiId | null;\n /** Display label for the selected tooth. Defaults to the localised \"Tooth {{id}}\". */\n toothLabel?: string;\n /** The selected tooth's films. The panel sorts + filters; order in is irrelevant. */\n radiographs?: Radiograph[];\n /** Commit a new film (the host persists it). Omit to hide the upload composer. */\n onAddRadiograph?: (draft: {\n fileName: string;\n date?: string;\n note?: string;\n }) => void;\n /** Remove a film by id. Omit to hide the delete affordance. */\n onDeleteRadiograph?: (id: string) => void;\n /** Panel heading. Defaults to the localised \"Imaging\". */\n title?: string;\n /** Accessible name for the panel region (defaults to `title`). */\n 'aria-label'?: string;\n}\n\nfunction KindBadge({ kind }: { kind: RadiographKind }): ReactElement {\n const { t } = useTranslation();\n const label = t(`radiographPanel.kind.${kind}`, {\n defaultValue: {\n periapical: 'Periapical',\n bitewing: 'Bitewing',\n panoramic: 'Panoramic',\n other: 'Other',\n }[kind],\n });\n return (\n <Badge variant=\"neutral\" size=\"sm\">\n {label}\n </Badge>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Fullscreen viewer — lightbox + compare */\n/* ------------------------------------------------------------------ */\n\ninterface ViewerState {\n films: Radiograph[];\n index: number;\n compare: boolean;\n}\n\nfunction RadiographViewer({\n state,\n toothLabel,\n onClose,\n onIndexChange,\n}: {\n state: ViewerState | null;\n toothLabel: string;\n onClose: () => void;\n onIndexChange: (index: number) => void;\n}): ReactElement {\n const { t } = useTranslation();\n const [zoom, setZoom] = useState(0);\n // Reset zoom when the viewed film changes (prev/next) or the mode flips, so a\n // user doesn't carry one film's zoom into the next.\n useEffect(() => {\n setZoom(0);\n }, [state?.index, state?.compare]);\n const films = state?.films ?? [];\n const current = state ? films[state.index] : undefined;\n\n return (\n <Dialog.Root\n open={state !== null}\n onOpenChange={(next) => {\n if (!next) {\n setZoom(0);\n onClose();\n }\n }}\n >\n <Dialog.Content size=\"fullscreen\" aria-describedby={undefined}>\n <Dialog.Header>\n <Dialog.Title>\n {state?.compare\n ? t('radiographPanel.viewer.compareTitle', {\n defaultValue: '{{tooth}} — comparison',\n tooth: toothLabel,\n })\n : t('radiographPanel.viewer.title', {\n defaultValue: '{{tooth}} — radiograph',\n tooth: toothLabel,\n })}\n </Dialog.Title>\n </Dialog.Header>\n <Dialog.Body>\n {state?.compare ? (\n <div className=\"ds:grid ds:grid-cols-2 ds:gap-[var(--spacing-md)]\">\n {films.slice(0, 2).map((film) => (\n <figure\n key={film.id}\n className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n <div className=\"ds:flex ds:items-center ds:justify-center ds:overflow-hidden ds:rounded-[var(--radius-md)] ds:bg-[var(--muted)] ds:p-[var(--spacing-xs)]\">\n <img\n src={film.thumbnailUrl ?? film.url}\n alt={film.alt ?? toothLabel}\n className=\"ds:max-h-full ds:max-w-full ds:object-contain\"\n />\n </div>\n <figcaption className=\"type-meta ds:text-[color:var(--muted-foreground)]\">\n {film.date}\n </figcaption>\n </figure>\n ))}\n </div>\n ) : (\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)]\">\n <div className=\"ds:flex ds:max-h-full ds:items-center ds:justify-center ds:overflow-hidden ds:rounded-[var(--radius-md)] ds:bg-[var(--muted)] ds:p-[var(--spacing-sm)]\">\n <img\n src={current?.thumbnailUrl ?? current?.url}\n alt={current?.alt ?? toothLabel}\n className={[\n 'ds:max-h-full ds:max-w-full ds:object-contain',\n 'ds:origin-center ds:transition-transform ds:motion-reduce:transition-none',\n ZOOM_CLASS[zoom],\n ].join(' ')}\n />\n </div>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <IconButton\n icon={<ChevronLeft aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.viewer.prev', {\n defaultValue: 'Previous radiograph',\n })}\n size=\"sm\"\n flipIconInRtl\n disabled={!state || state.index === 0}\n onClick={() =>\n state && onIndexChange(Math.max(0, state.index - 1))\n }\n />\n <span\n aria-live=\"polite\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)]\"\n >\n {t('radiographPanel.viewer.position', {\n defaultValue: '{{current}} / {{total}}',\n current: (state?.index ?? 0) + 1,\n total: films.length,\n })}\n </span>\n <IconButton\n icon={<ChevronRight aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.viewer.next', {\n defaultValue: 'Next radiograph',\n })}\n size=\"sm\"\n flipIconInRtl\n disabled={!state || state.index >= films.length - 1}\n onClick={() =>\n state &&\n onIndexChange(Math.min(films.length - 1, state.index + 1))\n }\n />\n <span\n aria-hidden=\"true\"\n className=\"ds:ms-[var(--spacing-xs)] ds:me-[var(--spacing-xs)] ds:h-[var(--spacing-md)] ds:w-px ds:bg-[var(--border)]\"\n />\n <IconButton\n icon={<ZoomOut aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.viewer.zoomOut', {\n defaultValue: 'Zoom out',\n })}\n size=\"sm\"\n disabled={zoom === 0}\n onClick={() => setZoom((z) => Math.max(0, z - 1))}\n />\n <IconButton\n icon={<ZoomIn aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.viewer.zoomIn', {\n defaultValue: 'Zoom in',\n })}\n size=\"sm\"\n disabled={zoom === ZOOM_CLASS.length - 1}\n onClick={() =>\n setZoom((z) => Math.min(ZOOM_CLASS.length - 1, z + 1))\n }\n />\n </div>\n {current?.note ? (\n <p className=\"type-body-sm ds:text-[color:var(--muted-foreground)]\">\n {current.note}\n </p>\n ) : null}\n </div>\n )}\n </Dialog.Body>\n </Dialog.Content>\n </Dialog.Root>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* RadiographPanel */\n/* ------------------------------------------------------------------ */\n\nexport const RadiographPanel = forwardRef<\n RadiographPanelHandle,\n RadiographPanelProps\n>(function RadiographPanel(\n {\n id,\n toothId,\n toothLabel: toothLabelProp,\n radiographs = [],\n onAddRadiograph,\n onDeleteRadiograph,\n title,\n 'aria-label': ariaLabel,\n className,\n ...rest\n },\n ref,\n) {\n const { t } = useTranslation();\n\n const [viewer, setViewer] = useState<ViewerState | null>(null);\n const [compareIds, setCompareIds] = useState<string[]>([]);\n const [sortDesc, setSortDesc] = useState(true);\n const [range, setRange] = useState<DateRangeValue>({});\n const [pendingFile, setPendingFile] = useState<string | null>(null);\n const [date, setDate] = useState<Date | undefined>(undefined);\n const [note, setNote] = useState('');\n\n // Stable, current snapshot for the imperative handle.\n const filmsRef = useRef<Radiograph[]>(radiographs);\n filmsRef.current = radiographs;\n\n useImperativeHandle(\n ref,\n () => ({\n getRadiographs: () => filmsRef.current,\n openViewer: (filmId) => {\n const index = filmsRef.current.findIndex((f) => f.id === filmId);\n if (index >= 0) {\n setViewer({ films: filmsRef.current, index, compare: false });\n }\n },\n closeViewer: () => setViewer(null),\n }),\n [],\n );\n\n const heading =\n title ?? t('radiographPanel.title', { defaultValue: 'Imaging' });\n const toothLabel =\n toothLabelProp ??\n (toothId\n ? t('radiographPanel.toothLabel', {\n defaultValue: 'Tooth {{id}}',\n id: toothId,\n })\n : '');\n\n const visible = useMemo(\n () =>\n sortByDate(\n radiographs.filter((f) => inRange(f, range)),\n sortDesc,\n ),\n [radiographs, range, sortDesc],\n );\n\n const toggleCompare = useCallback((filmId: string) => {\n setCompareIds((ids) =>\n ids.includes(filmId)\n ? ids.filter((x) => x !== filmId)\n : [...ids, filmId].slice(-2),\n );\n }, []);\n\n const handleUpload = useCallback(() => {\n if (!pendingFile || !onAddRadiograph) return;\n onAddRadiograph({\n fileName: pendingFile,\n date: date ? toLocalIsoDate(date) : undefined,\n note: note || undefined,\n });\n setPendingFile(null);\n setNote('');\n setDate(undefined);\n }, [pendingFile, date, note, onAddRadiograph]);\n\n // History filter controls — pinned in the PanelHeader's filters row (only\n // meaningful once a tooth is selected) so they stay put as the list scrolls.\n const historyFilters = toothId ? (\n <>\n <FormField\n label={t('radiographPanel.history.range', {\n defaultValue: 'Date range',\n })}\n >\n <DateRangePicker value={range} onChange={setRange} />\n </FormField>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <Button intent=\"ghost\" size=\"sm\" onClick={() => setSortDesc((s) => !s)}>\n {sortDesc\n ? t('radiographPanel.history.sortNewest', {\n defaultValue: 'Newest first',\n })\n : t('radiographPanel.history.sortOldest', {\n defaultValue: 'Oldest first',\n })}\n </Button>\n <Button\n intent=\"ghost\"\n size=\"sm\"\n startIcon={<GitCompareArrows aria-hidden=\"true\" />}\n disabled={compareIds.length !== 2}\n onClick={() => {\n const films = radiographs.filter((f) => compareIds.includes(f.id));\n if (films.length === 2) {\n setViewer({ films, index: 0, compare: true });\n }\n }}\n >\n {t('radiographPanel.history.compare', {\n defaultValue: 'Compare',\n })}\n </Button>\n </div>\n </>\n ) : undefined;\n\n return (\n <section\n data-component=\"radiograph-panel\"\n data-component-id={id}\n data-tooth={toothId ?? undefined}\n aria-label={ariaLabel ?? heading}\n className={['ds:flex ds:flex-col', className].filter(Boolean).join(' ')}\n {...rest}\n >\n {/* Shared rail-panel header: title + film-count badge, history filters\n pinned beneath, divider, sticky so it stays visible as the list\n scrolls. The rail injects favourite/close into the action cluster. */}\n <PanelHeader\n sticky\n icon={<Images aria-hidden=\"true\" />}\n title={\n <>\n {toothId ? toothLabel : heading}\n {toothId ? (\n <Badge variant=\"info\" size=\"sm\">\n {radiographs.length}\n </Badge>\n ) : null}\n </>\n }\n filters={historyFilters}\n />\n\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:p-[var(--spacing-md)]\">\n {!toothId ? (\n <EmptyState\n variant=\"first-use\"\n title={t('radiographPanel.noTooth.title', {\n defaultValue: 'No tooth selected',\n })}\n description={t('radiographPanel.noTooth.body', {\n defaultValue:\n 'Select a tooth on the chart to view its radiographs.',\n })}\n />\n ) : (\n <>\n {/* Upload */}\n {onAddRadiograph ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <FormField\n label={t('radiographPanel.upload.file', {\n defaultValue: 'Image file',\n })}\n >\n <FileUpload\n accept={{ 'image/*': ['.png', '.jpg', '.jpeg', '.webp'] }}\n variant=\"both\"\n showPreview\n onDrop={(accepted) =>\n setPendingFile(accepted[0]?.name ?? null)\n }\n />\n </FormField>\n <FormField\n label={t('radiographPanel.upload.date', {\n defaultValue: 'Acquisition date',\n })}\n >\n <DatePicker value={date} onChange={(d) => setDate(d)} />\n </FormField>\n <FormField\n label={t('radiographPanel.upload.note', {\n defaultValue: 'Note (optional)',\n })}\n >\n <TextArea\n rows={2}\n value={note}\n onChange={(e) => setNote(e.target.value)}\n placeholder={t('radiographPanel.upload.notePlaceholder', {\n defaultValue: 'E.g. routine check, apical lesion…',\n })}\n />\n </FormField>\n <Button\n intent=\"primary\"\n size=\"sm\"\n startIcon={<Upload aria-hidden=\"true\" />}\n disabled={!pendingFile}\n onClick={handleUpload}\n className=\"ds:self-start\"\n >\n {t('radiographPanel.upload.submit', {\n defaultValue: 'Upload',\n })}\n </Button>\n </div>\n ) : null}\n\n {/* History */}\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {visible.length === 0 ? (\n <EmptyState\n variant=\"first-use\"\n title={t('radiographPanel.history.emptyTitle', {\n defaultValue: 'No radiographs yet',\n })}\n description={t('radiographPanel.history.emptyBody', {\n defaultValue: 'Upload the first film for this tooth.',\n })}\n />\n ) : (\n <ul className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-xs)] ds:p-0\">\n {visible.map((film, visibleIndex) => {\n const selected = compareIds.includes(film.id);\n // Disambiguate the repeated row actions for screen readers by\n // the film's date (falls back to its 1-based position).\n const filmLabel = film.date ?? String(visibleIndex + 1);\n return (\n <li\n key={film.id}\n data-radiograph-id={film.id}\n data-selected={selected}\n className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--card-border)] ds:bg-[var(--card)] ds:p-[var(--spacing-xs)] ds:data-[selected=true]:border-[color:var(--info-foreground)]\"\n >\n <img\n src={film.thumbnailUrl ?? film.url}\n alt={film.alt ?? ''}\n className=\"ds:h-16 ds:w-12 ds:shrink-0 ds:rounded-[var(--radius-sm)] ds:object-cover\"\n />\n <div className=\"ds:flex ds:min-w-0 ds:flex-1 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n <span className=\"type-body-sm ds:text-[color:var(--foreground)]\">\n {film.date}\n </span>\n <KindBadge kind={film.kind ?? 'periapical'} />\n </div>\n <IconButton\n icon={<Eye aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.history.view', {\n defaultValue: 'View radiograph {{film}}',\n film: filmLabel,\n })}\n size=\"sm\"\n intent=\"ghost\"\n // Step the viewer through the FILTERED + sorted list the\n // user is looking at, not every film on the tooth.\n onClick={() =>\n setViewer({\n films: visible,\n index: visibleIndex,\n compare: false,\n })\n }\n />\n <IconButton\n icon={<GitCompareArrows aria-hidden=\"true\" />}\n aria-label={\n selected\n ? t('radiographPanel.history.compareRemove', {\n defaultValue:\n 'Remove {{film}} from comparison',\n film: filmLabel,\n })\n : t('radiographPanel.history.compareAdd', {\n defaultValue:\n 'Select {{film}} for comparison',\n film: filmLabel,\n })\n }\n aria-pressed={selected}\n size=\"sm\"\n intent={selected ? 'secondary' : 'ghost'}\n onClick={() => toggleCompare(film.id)}\n />\n {onDeleteRadiograph ? (\n <IconButton\n icon={<Trash2 aria-hidden=\"true\" />}\n aria-label={t('radiographPanel.history.delete', {\n defaultValue: 'Delete radiograph {{film}}',\n film: filmLabel,\n })}\n size=\"sm\"\n intent=\"ghost\"\n onClick={() => onDeleteRadiograph(film.id)}\n />\n ) : null}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n </>\n )}\n </div>\n\n <RadiographViewer\n state={viewer}\n toothLabel={toothLabel}\n onClose={() => setViewer(null)}\n onIndexChange={(index) => setViewer((v) => (v ? { ...v, index } : v))}\n />\n </section>\n );\n});\n\n// `Images` is the panel's imaging glyph (a films archive, not a radiation\n// hazard) — re-exported so a host registering the Control Room tool can use\n// the same icon as the panel.\nexport { Images as RadiographPanelIcon };\n","import type { AgentAdapter } from '../../agent/types';\nimport type { RadiographPanelHandle } from './radiograph-panel';\nimport type { Radiograph } from '../tooth-scheme';\n\n/**\n * The Imaging panel for the Control Room rail. It DISPLAYS the selected tooth's\n * radiographs and drives the fullscreen viewer; uploads and deletes are the\n * host's responsibility (the `onAddRadiograph` / `onDeleteRadiograph` props),\n * so they are not exposed here — the consuming pattern/app owns those `write` /\n * `destructive` workflows and their `radiograph.*` API contracts.\n */\nexport const radiographPanelAgent: AgentAdapter<RadiographPanelHandle> = {\n id: 'radiograph-panel',\n // pick — open a specific film in the viewer; view_change — the viewer is a\n // view surface over the host-owned films.\n capabilities: ['pick', 'view_change'],\n state: {\n radiographs: {\n // A9: structural refs only (id + kind) — never the free-text `note`,\n // service dates, or consumer-hosted image URLs in the always-on snapshot.\n type: 'Array<{ id: string; kind?: string }>',\n descriptionKey: 'ui.agent.radiographPanel.state.radiographs',\n description:\n 'Structural references (id + kind) to the films shown for the selected tooth.',\n read: (handle) =>\n handle\n .getRadiographs()\n .map((film) => ({ id: film.id, kind: film.kind ?? 'periapical' })),\n },\n },\n actions: {\n view_radiograph: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.radiographPanel.actions.viewRadiograph',\n description: 'Open a film in the fullscreen viewer by id.',\n invoke: (handle, args: { id: string }) => {\n handle.openViewer(args.id);\n },\n },\n close_viewer: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.radiographPanel.actions.closeViewer',\n description: 'Close the fullscreen radiograph viewer.',\n invoke: (handle) => {\n handle.closeViewer();\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'radiograph-panel' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-radiograph-id',\n description:\n \"Each film row (`<li>`) carries its radiograph id as `data-radiograph-id`. The panel root additionally carries the selected tooth's FDI id as `data-tooth`.\",\n },\n },\n};\n\n// Re-exported for callers building typed reads against the adapter.\nexport type { Radiograph };\n"],"names":["__iconNode","GitCompareArrows","createLucideIcon","Images","ZOOM_CLASS","sortByDate","films","desc","a","b","inRange","film","range","d","toLocalIsoDate","pad","n","KindBadge","kind","t","useTranslation","label","Badge","RadiographViewer","state","toothLabel","onClose","onIndexChange","zoom","setZoom","useState","useEffect","current","jsx","Dialog","next","jsxs","IconButton","ChevronLeft","ChevronRight","ZoomOut","z","ZoomIn","RadiographPanel","forwardRef","id","toothId","toothLabelProp","radiographs","onAddRadiograph","onDeleteRadiograph","title","ariaLabel","className","rest","ref","viewer","setViewer","compareIds","setCompareIds","sortDesc","setSortDesc","setRange","pendingFile","setPendingFile","date","setDate","note","setNote","filmsRef","useRef","useImperativeHandle","filmId","index","f","heading","visible","useMemo","toggleCompare","useCallback","ids","x","handleUpload","historyFilters","Fragment","FormField","DateRangePicker","Button","s","PanelHeader","FileUpload","accepted","_a","DatePicker","TextArea","e","Upload","EmptyState","visibleIndex","selected","filmLabel","Eye","Trash2","v","radiographPanelAgent","handle","args"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as q } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as C } from "react";
|
|
3
|
+
import { useTranslation as R } from "react-i18next";
|
|
4
|
+
import { B as V } from "./badge-CIt-UFEx.js";
|
|
5
|
+
function T(e, n) {
|
|
6
|
+
const t = [e, n].filter((i) => i !== "notRequired");
|
|
7
|
+
if (t.length === 0) return "notRequired";
|
|
8
|
+
const s = t.filter((i) => i === "signed").length;
|
|
9
|
+
return s === 0 ? "unsigned" : s === t.length ? "signed" : "partial";
|
|
10
|
+
}
|
|
11
|
+
const A = {
|
|
12
|
+
// Traffic light: green when everyone required has signed, amber when one
|
|
13
|
+
// party is still missing, red when nobody has.
|
|
14
|
+
signed: "success",
|
|
15
|
+
partial: "warning",
|
|
16
|
+
unsigned: "error",
|
|
17
|
+
notRequired: "neutral"
|
|
18
|
+
}, b = {
|
|
19
|
+
operator: "Operator",
|
|
20
|
+
patient: "Patient"
|
|
21
|
+
}, w = {
|
|
22
|
+
signed: "signed",
|
|
23
|
+
pending: "pending"
|
|
24
|
+
};
|
|
25
|
+
function y(e, n) {
|
|
26
|
+
const t = [];
|
|
27
|
+
return e !== "notRequired" && t.push({ party: "operator", standing: e }), n !== "notRequired" && t.push({ party: "patient", standing: n }), t;
|
|
28
|
+
}
|
|
29
|
+
const N = C(
|
|
30
|
+
({
|
|
31
|
+
operator: e,
|
|
32
|
+
patient: n,
|
|
33
|
+
size: t = "md",
|
|
34
|
+
detail: s = !1,
|
|
35
|
+
id: i,
|
|
36
|
+
className: p,
|
|
37
|
+
"aria-label": c,
|
|
38
|
+
title: f,
|
|
39
|
+
...m
|
|
40
|
+
}, h) => {
|
|
41
|
+
const { t: a } = R(), o = T(e, n), r = (() => {
|
|
42
|
+
switch (o) {
|
|
43
|
+
case "signed":
|
|
44
|
+
return a("signatureStatusChip.signed", { defaultValue: "Signed" });
|
|
45
|
+
case "unsigned":
|
|
46
|
+
return a("signatureStatusChip.unsigned", {
|
|
47
|
+
defaultValue: "Not signed"
|
|
48
|
+
});
|
|
49
|
+
case "notRequired":
|
|
50
|
+
return a("signatureStatusChip.notRequired", {
|
|
51
|
+
defaultValue: "No signature required"
|
|
52
|
+
});
|
|
53
|
+
case "partial":
|
|
54
|
+
return e === "pending" ? a("signatureStatusChip.missingOperator", {
|
|
55
|
+
defaultValue: "Operator signature missing"
|
|
56
|
+
}) : a("signatureStatusChip.missingPatient", {
|
|
57
|
+
defaultValue: "Patient signature missing"
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
})(), u = y(e, n).map(
|
|
61
|
+
({ party: g, standing: l }) => a("signatureStatusChip.breakdownItem", {
|
|
62
|
+
defaultValue: "{{party}} {{state}}",
|
|
63
|
+
party: a(`signatureStatusChip.party.${g}`, {
|
|
64
|
+
defaultValue: b[g]
|
|
65
|
+
}),
|
|
66
|
+
state: a(`signatureStatusChip.state.${l}`, {
|
|
67
|
+
defaultValue: w[l]
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
).join(" · "), d = u ? `${r}, ${u}` : r, S = s && u ? `${r} · ${u}` : r;
|
|
71
|
+
return /* @__PURE__ */ q(
|
|
72
|
+
V,
|
|
73
|
+
{
|
|
74
|
+
ref: h,
|
|
75
|
+
variant: A[o],
|
|
76
|
+
size: t,
|
|
77
|
+
withDot: !0,
|
|
78
|
+
role: "img",
|
|
79
|
+
"aria-label": c ?? d,
|
|
80
|
+
title: f ?? d,
|
|
81
|
+
"data-component": "signature-status-chip",
|
|
82
|
+
"data-status": o,
|
|
83
|
+
"data-component-id": i,
|
|
84
|
+
id: i,
|
|
85
|
+
className: p,
|
|
86
|
+
...m,
|
|
87
|
+
children: S
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
N.displayName = "SignatureStatusChip";
|
|
93
|
+
export {
|
|
94
|
+
N as S,
|
|
95
|
+
T as s
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=signature-status-chip-DlLJk1VA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature-status-chip-DlLJk1VA.js","sources":["../../src/components/signature-status-chip/signature-status-chip.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Badge, type BadgeProps } from '../badge';\n\n/* ------------------------------------------------------------------ */\n/* State vocabulary */\n/* ------------------------------------------------------------------ */\n\n/** One party's standing on the document. */\nexport type SignatureParty = 'signed' | 'pending' | 'notRequired';\n\n/** The chip's derived state. */\nexport type SignatureStatus = 'signed' | 'partial' | 'unsigned' | 'notRequired';\n\ntype PartyName = 'operator' | 'patient';\ntype RequiredStanding = Exclude<SignatureParty, 'notRequired'>;\n\n/**\n * The rule, exported so hosts can sort/filter by the same state the chip\n * shows: required = parties not `notRequired`; all signed → `signed`, some\n * signed → `partial`, none signed → `unsigned`, nobody required → `notRequired`.\n */\nexport function signatureStatusOf(\n operator: SignatureParty,\n patient: SignatureParty,\n): SignatureStatus {\n const required = [operator, patient].filter((p) => p !== 'notRequired');\n if (required.length === 0) return 'notRequired';\n const signed = required.filter((p) => p === 'signed').length;\n if (signed === 0) return 'unsigned';\n return signed === required.length ? 'signed' : 'partial';\n}\n\n// `partial` shares the warning tone with `unsigned` so a half-signed document\n// never reads as done at a glance — only a fully signed one goes green.\nconst STATUS_VARIANT: Record<\n SignatureStatus,\n NonNullable<BadgeProps['variant']>\n> = {\n // Traffic light: green when everyone required has signed, amber when one\n // party is still missing, red when nobody has.\n signed: 'success',\n partial: 'warning',\n unsigned: 'error',\n notRequired: 'neutral',\n};\n\nconst PARTY_DEFAULT: Record<PartyName, string> = {\n operator: 'Operator',\n patient: 'Patient',\n};\n\nconst STANDING_DEFAULT: Record<RequiredStanding, string> = {\n signed: 'signed',\n pending: 'pending',\n};\n\nfunction requiredParties(\n operator: SignatureParty,\n patient: SignatureParty,\n): Array<{ party: PartyName; standing: RequiredStanding }> {\n const out: Array<{ party: PartyName; standing: RequiredStanding }> = [];\n if (operator !== 'notRequired')\n out.push({ party: 'operator', standing: operator });\n if (patient !== 'notRequired')\n out.push({ party: 'patient', standing: patient });\n return out;\n}\n\n/* ------------------------------------------------------------------ */\n/* Props */\n/* ------------------------------------------------------------------ */\n\nexport interface SignatureStatusChipProps extends Omit<\n HTMLAttributes<HTMLSpanElement>,\n 'children'\n> {\n /** The operator's standing. */\n operator: SignatureParty;\n /** The patient's standing. */\n patient: SignatureParty;\n /** Chip height — maps onto the Badge `sm` / `md` sizes. */\n size?: 'sm' | 'md';\n /**\n * Spell the breakdown in the chip text (\"Signed · Operator signed · Patient\n * signed\") instead of the summary word. Default false — the breakdown is\n * always in the accessible name / title.\n */\n detail?: boolean;\n /** Agent-readiness consumer id, surfaced as `data-component-id`. */\n id?: string;\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport const SignatureStatusChip = forwardRef<\n HTMLSpanElement,\n SignatureStatusChipProps\n>(\n (\n {\n operator,\n patient,\n size = 'md',\n detail = false,\n id,\n className,\n 'aria-label': ariaLabelProp,\n title: titleProp,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const status = signatureStatusOf(operator, patient);\n\n // In `partial` exactly one required party is pending, so the summary\n // names the missing signature rather than a vague \"partially signed\".\n const summary = (() => {\n switch (status) {\n case 'signed':\n return t('signatureStatusChip.signed', { defaultValue: 'Signed' });\n case 'unsigned':\n return t('signatureStatusChip.unsigned', {\n defaultValue: 'Not signed',\n });\n case 'notRequired':\n return t('signatureStatusChip.notRequired', {\n defaultValue: 'No signature required',\n });\n case 'partial':\n return operator === 'pending'\n ? t('signatureStatusChip.missingOperator', {\n defaultValue: 'Operator signature missing',\n })\n : t('signatureStatusChip.missingPatient', {\n defaultValue: 'Patient signature missing',\n });\n }\n })();\n\n const breakdown = requiredParties(operator, patient)\n .map(({ party, standing }) =>\n t('signatureStatusChip.breakdownItem', {\n defaultValue: '{{party}} {{state}}',\n party: t(`signatureStatusChip.party.${party}`, {\n defaultValue: PARTY_DEFAULT[party],\n }),\n state: t(`signatureStatusChip.state.${standing}`, {\n defaultValue: STANDING_DEFAULT[standing],\n }),\n }),\n )\n .join(' · ');\n\n // The full picture always reaches AT and hover, even in the compact form.\n const label = breakdown ? `${summary}, ${breakdown}` : summary;\n const visible = detail && breakdown ? `${summary} · ${breakdown}` : summary;\n\n return (\n <Badge\n ref={ref}\n variant={STATUS_VARIANT[status]}\n size={size}\n withDot\n role=\"img\"\n aria-label={ariaLabelProp ?? label}\n title={titleProp ?? label}\n data-component=\"signature-status-chip\"\n data-status={status}\n data-component-id={id}\n id={id}\n className={className}\n {...rest}\n >\n {visible}\n </Badge>\n );\n },\n);\n\nSignatureStatusChip.displayName = 'SignatureStatusChip';\n"],"names":["signatureStatusOf","operator","patient","required","p","signed","STATUS_VARIANT","PARTY_DEFAULT","STANDING_DEFAULT","requiredParties","out","SignatureStatusChip","forwardRef","size","detail","id","className","ariaLabelProp","titleProp","rest","ref","t","useTranslation","status","summary","breakdown","party","standing","label","visible","jsx","Badge"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { jsx as a, jsxs as v, Fragment as S } from "react/jsx-runtime";
|
|
|
2
2
|
import { useRef as V, useState as F, useCallback as O, forwardRef as D, Fragment as M } from "react";
|
|
3
3
|
import { c as C } from "./index-D2ZczOXr.js";
|
|
4
4
|
import { useTranslation as L } from "react-i18next";
|
|
5
|
-
import { B as W } from "./badge-
|
|
5
|
+
import { B as W } from "./badge-CIt-UFEx.js";
|
|
6
6
|
import { D as b } from "./dropdown-menu-DU-Hrf33.js";
|
|
7
7
|
import { u as P } from "./use-media-query-CcAx5SMM.js";
|
|
8
8
|
import { u as I } from "./use-isomorphic-layout-effect-BGfaCOP1.js";
|
|
@@ -265,4 +265,4 @@ ts.displayName = "TabBar";
|
|
|
265
265
|
export {
|
|
266
266
|
ts as T
|
|
267
267
|
};
|
|
268
|
-
//# sourceMappingURL=tab-bar-
|
|
268
|
+
//# sourceMappingURL=tab-bar-Ddp_ZbnL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-bar-BYESZINM.js","sources":["../../src/hooks/use-scroll-overflow.ts","../../src/components/tab-bar/tab-bar.tsx"],"sourcesContent":["import { useCallback, useRef, useState, type RefObject } from 'react';\nimport { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';\n\nexport interface UseScrollOverflowReturn<T extends HTMLElement> {\n /** Attach to the horizontally-scrollable element (the `overflow-x-auto` box). */\n ref: RefObject<T | null>;\n /** Content is hidden past the inline-START edge (scrolled away from the start). */\n canScrollStart: boolean;\n /** Content is hidden past the inline-END edge (more to reveal toward the end). */\n canScrollEnd: boolean;\n /**\n * Scroll by ~one viewport toward the inline start (`-1`) or end (`+1`),\n * smoothly unless the user prefers reduced motion. Direction-agnostic: works\n * in both LTR and RTL.\n */\n scrollByPage: (direction: -1 | 1) => void;\n}\n\n/**\n * Reports whether a horizontally-scrollable element has hidden content at its\n * inline-start / inline-end edges, and exposes a paged scroll action — the\n * plumbing behind a scroll strip's edge affordances (fade + chevron buttons).\n *\n * Direction-agnostic: distance-from-start is read as `Math.abs(scrollLeft)`,\n * which is correct in both LTR (`0…max`) and modern RTL (`0…-max`), so the two\n * edges are reported on the right logical side without branching. `scrollByPage`\n * flips its physical sign from the element's computed `direction`.\n *\n * Re-measures on element resize AND on content-size change (a tab added past the\n * fold widens the content without changing the container) by observing both the\n * scroll box and its first child. `enabled` switches measurement off — e.g. when\n * the host collapses the strip into a menu and there is nothing to scroll.\n *\n * Because the scrolled content stays in the DOM and is reachable by keyboard\n * (focus scrolls it into view), the affordance this drives is a sighted-pointer\n * convenience; assistive tech is unaffected.\n */\nexport function useScrollOverflow<T extends HTMLElement>(\n enabled = true,\n): UseScrollOverflowReturn<T> {\n const ref = useRef<T>(null);\n const [edges, setEdges] = useState({ start: false, end: false });\n\n useIsomorphicLayoutEffect(() => {\n const el = ref.current;\n if (!el || !enabled) {\n setEdges((prev) =>\n prev.start || prev.end ? { start: false, end: false } : prev,\n );\n return;\n }\n const measure = () => {\n // Math.abs() makes this direction-agnostic: modern RTL reports scrollLeft\n // as 0 at the start and negative toward the end, so |scrollLeft| is the\n // distance scrolled from the start in both writing directions.\n const distanceFromStart = Math.abs(el.scrollLeft);\n const maxScroll = el.scrollWidth - el.clientWidth;\n // +1 tolerates sub-pixel rounding between scroll/client widths.\n const start = distanceFromStart > 1;\n const end = maxScroll - distanceFromStart > 1;\n // Bail to the same reference when nothing changed, so neither the initial\n // no-overflow measure nor an idle scroll event triggers a re-render.\n setEdges((prev) =>\n prev.start === start && prev.end === end ? prev : { start, end },\n );\n };\n measure();\n el.addEventListener('scroll', measure, { passive: true });\n let observer: ResizeObserver | undefined;\n if (typeof ResizeObserver !== 'undefined') {\n observer = new ResizeObserver(measure);\n // The container catches viewport-width changes; its first child (the\n // content row) catches a tab added/removed past the fold, which grows the\n // scroll width without resizing the container.\n observer.observe(el);\n if (el.firstElementChild) observer.observe(el.firstElementChild);\n }\n return () => {\n el.removeEventListener('scroll', measure);\n observer?.disconnect();\n };\n }, [enabled]);\n\n const scrollByPage = useCallback((direction: -1 | 1) => {\n const el = ref.current;\n if (!el || typeof window === 'undefined') return;\n const isRtl = window.getComputedStyle(el).direction === 'rtl';\n const reduceMotion = window.matchMedia?.(\n '(prefers-reduced-motion: reduce)',\n ).matches;\n // ~85% of the viewport so a sliver of context carries across pages. The\n // physical sign flips in RTL (scrollLeft runs toward negative).\n const amount = el.clientWidth * 0.85 * direction * (isRtl ? -1 : 1);\n el.scrollBy({ left: amount, behavior: reduceMotion ? 'auto' : 'smooth' });\n }, []);\n\n return {\n ref,\n canScrollStart: edges.start,\n canScrollEnd: edges.end,\n scrollByPage,\n };\n}\n","/**\n * TabBar — a floating-pill bar of page-level navigation tabs.\n *\n * Each tab is an icon + label (+ optional count badge); the active tab is\n * marked with `aria-current=\"page\"` and an `--accent` underline. The bar is a\n * glassy `--card` pill that floats on whatever canvas the consumer provides\n * (the strip area itself is transparent, so it blends with the surrounding\n * surface).\n *\n * Two layouts:\n * - default (`fill` off): content-width tabs that **centre** when they fit and\n * scroll from the inline-start (`safe center`) when they overflow, with a\n * gutter preserved at both ends. The label stacks under the icon below `sm`.\n * Use this when the tab set is open-ended (e.g. a marketplace app's routes).\n * - `fill`: equal-width tabs that fill the bar and always stack the label under\n * the icon — a fixed, always-visible set that fits any column width without\n * scrolling (e.g. a patient portal's handful of sections).\n *\n * Tabs render as links when given an `href` (real navigation) or as buttons\n * otherwise (in-app view switching via `onSelect`). Use it as a header (top,\n * default) for section navigation; `position=\"bottom\"` floats it from the\n * bottom edge for a footer.\n *\n * With `collapseToMenu`, the strip folds into a single DropdownMenu whenever the\n * tabs don't fit — below the `sm` breakpoint OR when they overflow their\n * container at any width (a narrow column on a wide screen, e.g. a page squeezed\n * between two sidebars, clips the tabs just the same as a small viewport).\n *\n * SPA / consumer-router links: pass `renderLink` and TabBar hands each `href`\n * tab off to your router's `<Link>` instead of rendering its own `<a>` — the\n * embedded MarketplaceAppShell strip uses this so a tab click is a client-side\n * route change, not a full page navigation. The kit forwards the same\n * `className` + `aria-current` so the accent-underline active styling and the\n * `aria-current=\"page\"` marker survive in both modes. With `renderLink`\n * omitted the output is the plain `<a href>` exactly as before.\n */\nimport { forwardRef, Fragment, type MouseEvent, type ReactNode } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { Check, ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\n\nimport { Badge } from '../badge';\nimport { DropdownMenu } from '../dropdown-menu';\nimport { useMediaQuery } from '../../hooks/use-media-query';\nimport { useScrollOverflow } from '../../hooks/use-scroll-overflow';\nimport { useOverflowCollapse } from '../../hooks/use-overflow-collapse';\n\nexport interface TabBarItem {\n /** Stable id; also the value handed to `onSelect` for button tabs. */\n id: string;\n /** Pre-translated label — consumers localise their own nav copy. */\n label: string;\n /** Optional leading icon (a lucide-react element or equivalent), tinted with `--accent`. */\n icon?: ReactNode;\n /** Optional unseen-item count rendered as a pill badge. */\n badgeCount?: number;\n /** Mark the tab as the current page (`aria-current='page'` + accent underline). */\n isActive?: boolean;\n /** When set, the tab renders as a link to this href; otherwise it's a button. */\n href?: string;\n /**\n * Optional click handler threaded onto the link tab's `<a>` (or, when\n * `renderLink` is supplied, available to the consumer's router element). Use\n * it to intercept a same-origin click and route client-side without a full\n * navigation. Only applies to `href` (link) tabs — button tabs use `onSelect`.\n */\n onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;\n}\n\n/**\n * Props the kit hands to `renderLink` for each `href` tab. The consumer returns\n * their router's `<Link to={href}>` carrying the same `className` (the active\n * accent-underline styling) and `aria-current` (`'page'` on the active tab).\n */\nexport interface TabBarRenderLinkProps {\n /** The tab's destination — feed it to your router's `to` / `href`. */\n href: string;\n /** The kit's tab classes (CVA output) — apply verbatim to the link element. */\n className: string;\n /** Icon + label + badge — render as the link's children. */\n children: ReactNode;\n /** `'page'` on the active tab, otherwise omitted — forward to the link. */\n 'aria-current'?: 'page';\n /**\n * The tab's stable `TabBarItem.id`, as the agent / automation DOM hook.\n * Spread it onto your router link (`{...rest}`) so the tab stays addressable\n * by `[data-tab-id]` the same way the kit's own `<a>` / `<button>` tabs are —\n * see PRS §26 §6.\n */\n 'data-tab-id'?: string;\n /** Optional per-item click handler (the item's `onClick`), if supplied. */\n onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;\n}\n\nexport interface TabBarProps {\n /** The tabs, in order. */\n items: TabBarItem[];\n /**\n * Opaque instance id, emitted on the strip as `data-component-id`. Pass it\n * when more than one TabBar can be on screen so an agent / automation tool\n * can scope its `[data-tab-id]` lookup to one bar (PRS §26 §6).\n */\n id?: string;\n /** Accessible name for the `<nav>` landmark. */\n ariaLabel: string;\n /** Fires with the tab `id` when a button tab (one without `href`) is chosen. */\n onSelect?: (id: string) => void;\n /**\n * Render an `href` tab via the consumer's router instead of the kit's own\n * `<a>`. TabBar calls it for each link tab and renders the returned node; the\n * consumer returns their `<Link to={href}>` carrying the supplied `className`\n * + `aria-current` so the active accent underline and the `aria-current=\"page\"`\n * marker are preserved. Use it for SPA navigation (e.g. the MarketplaceAppShell\n * strip) so a tab click is a client-side route change, not a full page load.\n * Omit it to render the plain `<a href>` exactly as before. Button tabs (no\n * `href`) ignore `renderLink` and continue to use `onSelect`.\n */\n renderLink?: (props: TabBarRenderLinkProps) => ReactNode;\n /**\n * Equal-width tabs that fill the bar and always stack the label under the\n * icon (for a fixed, always-visible set). Off (default) = content-width tabs\n * that centre when they fit and scroll when they overflow.\n */\n fill?: boolean;\n /**\n * Float side. `'top'` (default) gutters from the top edge for a header;\n * `'bottom'` gutters from the bottom edge for a footer.\n */\n position?: 'top' | 'bottom';\n /**\n * Collapse the tab strip into a single DropdownMenu whenever the tabs don't\n * fit — below the `sm` breakpoint (≤640px) OR when they overflow their\n * container at any viewport width (a narrow column between two sidebars clips\n * the tabs the same as a small screen). The trigger shows the active tab; the\n * menu lists them all. Off (default) keeps the content-width / scroll\n * behaviour at every width, so existing consumers are unaffected. Designed for\n * button (`onSelect`) tabs; in the collapsed menu an `href` tab navigates with\n * a full page load, so `renderLink` (SPA routing) and a per-item `onClick`\n * apply only to the expanded strip.\n */\n collapseToMenu?: boolean;\n /** Extra classes merged onto the outer strip (e.g. sticky/fixed positioning). */\n className?: string;\n}\n\nfunction formatBadgeCount(count: number): string {\n return count > 99 ? '99+' : String(count);\n}\n\n/* Transparent strip area — provides the floating gutter on the active side. The\n default layout also insets the pill horizontally so it reads as a floating\n strip; `fill` drops that inset so the pill spans the full width and aligns\n with the consumer's content (e.g. a portal's cards). */\nconst stripVariants = cva('ds:shrink-0', {\n variants: {\n position: {\n top: 'ds:pt-[var(--spacing-md)]',\n bottom: 'ds:pb-[var(--spacing-md)]',\n },\n fill: {\n false: 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n true: '',\n },\n },\n defaultVariants: { position: 'top', fill: false },\n});\n\n/* The pill is the glass chrome — the bordered, blurred, rounded surface. The\n scrollable tab row is a separate inner element (`pillScrollClasses`) so the\n edge-fade mask touches only the tabs, never the pill's border. It is also the\n overflow-collapse MEASUREMENT container: it stays mounted in both the scroll\n and the dropdown render, and (carrying no padding) its `clientWidth` is\n exactly the width the tab row has to fit. */\nconst pillClasses =\n 'ds:rounded-[var(--radius-lg)] ds:border ds:border-[color:var(--card-border)] ds:bg-[var(--card)]/80 ds:backdrop-blur-md ds:shadow-[var(--shadow-md)]';\n\n/* The inner scroll container. `safe center` centres the tabs when they fit but\n falls back to start-alignment the moment they overflow, so the row stays fully\n scrollable end to end. Inline-edge MASK: the leading / trailing edge fades only\n when tabs are hidden past it (`data-fade-start` / `data-fade-end`), so a clipped\n tab dissolves into the glass under the accent chevron — alpha-based, so no seam\n with the translucent pill. Logical via `--tabbar-mask-*` (RTL-mapped); `black`\n is the mask-opaque sentinel, not a colour. Disabled under reduced motion, the\n accessible theme, and forced-colors so content is never hidden from those\n users. `--tabbar-fade-distance` (default `--spacing-lg`) is per-instance. */\nconst pillScrollClasses = [\n 'ds:flex ds:[justify-content:safe_center] ds:overflow-x-auto ds:py-[var(--spacing-xs)]',\n 'ds:[--tabbar-mask-start:black] ds:[--tabbar-mask-end:black]',\n 'ds:ltr:data-[fade-start=true]:[--tabbar-mask-start:transparent]',\n 'ds:ltr:data-[fade-end=true]:[--tabbar-mask-end:transparent]',\n 'ds:rtl:data-[fade-start=true]:[--tabbar-mask-end:transparent]',\n 'ds:rtl:data-[fade-end=true]:[--tabbar-mask-start:transparent]',\n 'ds:[mask-image:linear-gradient(to_right,var(--tabbar-mask-start)_0,black_var(--tabbar-fade-distance,var(--spacing-lg)),black_calc(100%-var(--tabbar-fade-distance,var(--spacing-lg))),var(--tabbar-mask-end)_100%)]',\n 'ds:motion-reduce:[mask-image:none]',\n 'ds:[.theme-accessible_&]:[mask-image:none]',\n 'ds:forced-colors:[mask-image:none]',\n].join(' ');\n\n/* The nav's inline padding is the start/end gutter — it scrolls WITH the tabs,\n so the gutter survives a full scroll. Content width (`shrink-0`) by default so\n it overflows and scrolls; `w-full` in fill mode so the tabs spread to fit. */\nconst navVariants = cva(\n 'ds:flex ds:items-stretch ds:gap-[var(--spacing-xs)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n {\n variants: {\n fill: { false: 'ds:shrink-0', true: 'ds:w-full' },\n },\n defaultVariants: { fill: false },\n },\n);\n\n/* Each tab is a centred flex box so the icon + label sit centred on both axes.\n Regular weight; the active state is carried by the accent underline + accent\n icon, not a heavier weight. */\nconst tabVariants = cva(\n 'ds:group ds:flex ds:items-center ds:justify-center ds:gap-[var(--spacing-xs)] ds:min-h-[var(--min-target-size)] ds:rounded-[var(--radius-sm)] ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-xs)] ds:whitespace-nowrap ds:cursor-pointer ds:appearance-none ds:border-0 ds:bg-transparent ds:text-[length:var(--font-size-sm)] ds:font-[var(--font-weight-regular)] ds:text-[color:var(--foreground)] ds:no-underline ds:hover:bg-[var(--muted)]/20 ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n {\n variants: {\n fill: {\n // Responsive: label under the icon below `sm`, beside it at `sm`+.\n false: 'ds:flex-col ds:sm:flex-row ds:sm:gap-[var(--spacing-sm)]',\n // Fill the bar, always icon-over-label. No `min-w-0`, so a tab never\n // shrinks below its label (labels never overlap); if the full set can't\n // fit, the pill scrolls instead.\n true: 'ds:flex-1 ds:basis-0 ds:flex-col',\n },\n },\n defaultVariants: { fill: false },\n },\n);\n\nconst iconClasses =\n 'ds:inline-flex ds:items-center ds:text-[color:var(--accent)] ds:[&>svg]:size-4';\n\n/* The accent underline rides the label text itself (not the whole tab), so it\n never reaches the icon or badge and never has to propagate through flex. */\nconst labelClasses =\n 'ds:group-aria-[current=page]:underline ds:group-aria-[current=page]:decoration-2 ds:group-aria-[current=page]:decoration-[color:var(--accent)] ds:group-aria-[current=page]:underline-offset-[var(--spacing-xs)]';\n\n/* The mobile collapse trigger — a full-width button styled like a tab, showing\n the active tab's icon + label with a trailing chevron. */\nconst menuTriggerClasses =\n 'ds:flex ds:w-full ds:items-center ds:gap-[var(--spacing-sm)] ds:min-h-[var(--min-target-size)] ds:rounded-[var(--radius-sm)] ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-xs)] ds:cursor-pointer ds:appearance-none ds:border-0 ds:bg-transparent ds:text-[length:var(--font-size-sm)] ds:font-[var(--font-weight-regular)] ds:text-[color:var(--foreground)] ds:hover:bg-[var(--muted)]/20 ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]';\n\n/* Scroll affordance — an accent chevron pinned to a scrollable edge of the pill,\n shown only when there are tabs hidden past that edge. No button chrome: the\n edge tab fades into the glass via the inner scroll's mask (above); the chevron\n just points the way and pages the strip on click. A `--min-target-size` hit\n area keeps it tappable. Block axis spans the pill (`top`/`bottom` px-inset past\n the border); the inline edge (`start`/`end`) and the chevron flip mirror RTL. */\nconst scrollAffordanceClasses =\n 'ds:absolute ds:top-px ds:bottom-px ds:z-10 ds:flex ds:min-w-[var(--min-target-size)] ds:items-center ds:cursor-pointer ds:appearance-none ds:border-0 ds:bg-transparent ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]';\n\n/* Pin the chevron to its inline edge, over the faded tab. */\nconst scrollAffordanceStartClasses =\n 'ds:start-px ds:justify-start ds:ps-[var(--spacing-xs)]';\n\nconst scrollAffordanceEndClasses =\n 'ds:end-px ds:justify-end ds:pe-[var(--spacing-xs)]';\n\n/* The chevron is the only visible mark — accent-tinted (matching the tab icons),\n mirrored under RTL. Decorative (`aria-hidden`); the button carries the label. */\nconst scrollChevronClasses =\n 'ds:size-4 ds:shrink-0 ds:text-[color:var(--accent)] ds:rtl:-scale-x-100';\n\nexport const TabBar = forwardRef<HTMLDivElement, TabBarProps>(\n (\n {\n items,\n id,\n ariaLabel,\n onSelect,\n renderLink,\n fill = false,\n position = 'top',\n collapseToMenu = false,\n className,\n },\n ref,\n ) => {\n const { t } = useTranslation();\n\n // Collapse is twofold, OR'd: the documented mobile floor (≤sm) AND actual\n // container overflow (a narrow column on a wide screen clips the tabs just\n // the same). Both hooks run every render (hook order); only consulted when\n // collapseToMenu is on. The pill (containerRef) stays mounted across the\n // collapse so the overflow hook can still tell when the column has grown\n // back; the tab row (contentRef) carries the natural width.\n const isNarrow = useMediaQuery('(max-width: 640px)');\n const { containerRef, contentRef, isOverflowing } = useOverflowCollapse<\n HTMLDivElement,\n HTMLElement\n >(collapseToMenu);\n const isDropdown = collapseToMenu && (isNarrow || isOverflowing);\n\n // Edge scroll affordances for the scroll layout. Disabled in dropdown mode\n // (no scroll box), so the observer tears down / re-attaches across the flip.\n const {\n ref: pillRef,\n canScrollStart,\n canScrollEnd,\n scrollByPage,\n } = useScrollOverflow<HTMLDivElement>(!isDropdown);\n\n const activeItem = items.find((item) => item.isActive) ?? items[0];\n\n return (\n <div\n ref={ref}\n data-component=\"tab-bar\"\n data-component-id={id}\n className={`${stripVariants({ position, fill })}${className ? ` ${className}` : ''}`}\n >\n {/* Relative wrapper: the scroll affordances are absolutely positioned\n against it, NOT inside the scroll box (absolute children of a scroller\n scroll away with the content). */}\n <div className=\"ds:relative\">\n {/* The pill doubles as the overflow-collapse measurement container —\n rendered in BOTH branches (same element, same width) so the hook\n can keep measuring after the tab row is swapped for the menu. */}\n <div ref={containerRef} className={pillClasses}>\n {isDropdown ? (\n <nav aria-label={ariaLabel} className=\"ds:w-full\">\n <DropdownMenu.Root>\n <DropdownMenu.Trigger className={menuTriggerClasses}>\n {activeItem?.icon ? (\n <span aria-hidden=\"true\" className={iconClasses}>\n {activeItem.icon}\n </span>\n ) : null}\n <span className=\"ds:flex-1 ds:truncate ds:text-start\">\n {activeItem?.label}\n </span>\n <ChevronDown\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:text-[color:var(--muted-foreground)]\"\n />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content\n align=\"start\"\n className=\"ds:min-w-[12rem]\"\n >\n {items.map((item) => (\n <DropdownMenu.Item\n key={item.id}\n data-tab-id={item.id}\n startIcon={item.icon}\n endIcon={\n item.isActive ? (\n <Check\n aria-hidden=\"true\"\n className=\"ds:text-[color:var(--accent)]\"\n />\n ) : undefined\n }\n aria-current={item.isActive ? 'page' : undefined}\n onSelect={() => {\n if (item.href) {\n window.location.assign(item.href);\n } else {\n onSelect?.(item.id);\n }\n }}\n >\n {item.label}\n </DropdownMenu.Item>\n ))}\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n </nav>\n ) : (\n <div\n ref={pillRef}\n data-fade-start={canScrollStart ? 'true' : undefined}\n data-fade-end={canScrollEnd ? 'true' : undefined}\n className={pillScrollClasses}\n >\n <nav\n ref={contentRef}\n aria-label={ariaLabel}\n className={navVariants({ fill })}\n >\n {items.map((item) => {\n const badge =\n typeof item.badgeCount === 'number' && item.badgeCount > 0\n ? item.badgeCount\n : undefined;\n const ariaCurrent = item.isActive ? 'page' : undefined;\n const inner = (\n <>\n {item.icon ? (\n <span aria-hidden=\"true\" className={iconClasses}>\n {item.icon}\n </span>\n ) : null}\n {/* Label + badge stay paired; they sit under the icon when\n the tab stacks to a column. */}\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <span className={labelClasses}>{item.label}</span>\n {badge !== undefined ? (\n <Badge variant=\"neutral\">\n {formatBadgeCount(badge)}\n </Badge>\n ) : null}\n </span>\n </>\n );\n if (item.href !== undefined) {\n const tabClassName = tabVariants({ fill });\n // Consumer-router mode: hand the tab to the consumer's <Link>,\n // forwarding the same className + aria-current (+ the item's\n // onClick) so the active styling and `aria-current=\"page\"`\n // marker carry over and clicking routes client-side.\n if (renderLink) {\n return (\n <Fragment key={item.id}>\n {renderLink({\n href: item.href,\n className: tabClassName,\n children: inner,\n 'aria-current': ariaCurrent,\n 'data-tab-id': item.id,\n onClick: item.onClick,\n })}\n </Fragment>\n );\n }\n // Default: plain anchor — byte-identical to before when no\n // per-item onClick is supplied (onClick is omitted entirely,\n // not passed as undefined, so the rendered <a> is unchanged).\n return (\n <a\n key={item.id}\n href={item.href}\n data-tab-id={item.id}\n aria-current={ariaCurrent}\n className={tabClassName}\n {...(item.onClick ? { onClick: item.onClick } : {})}\n >\n {inner}\n </a>\n );\n }\n return (\n <button\n key={item.id}\n type=\"button\"\n data-tab-id={item.id}\n aria-current={ariaCurrent}\n className={tabVariants({ fill })}\n onClick={() => onSelect?.(item.id)}\n >\n {inner}\n </button>\n );\n })}\n </nav>\n </div>\n )}\n </div>\n {canScrollStart ? (\n <button\n type=\"button\"\n aria-label={t('navigation.tabBar.scrollBack')}\n onClick={() => scrollByPage(-1)}\n className={`${scrollAffordanceClasses} ${scrollAffordanceStartClasses}`}\n >\n <ChevronLeft\n aria-hidden=\"true\"\n className={scrollChevronClasses}\n />\n </button>\n ) : null}\n {canScrollEnd ? (\n <button\n type=\"button\"\n aria-label={t('navigation.tabBar.scrollForward')}\n onClick={() => scrollByPage(1)}\n className={`${scrollAffordanceClasses} ${scrollAffordanceEndClasses}`}\n >\n <ChevronRight\n aria-hidden=\"true\"\n className={scrollChevronClasses}\n />\n </button>\n ) : null}\n </div>\n </div>\n );\n },\n);\nTabBar.displayName = 'TabBar';\n"],"names":["useScrollOverflow","enabled","ref","useRef","edges","setEdges","useState","useIsomorphicLayoutEffect","el","prev","measure","distanceFromStart","maxScroll","start","end","observer","scrollByPage","useCallback","direction","isRtl","reduceMotion","_a","amount","formatBadgeCount","count","stripVariants","cva","pillClasses","pillScrollClasses","navVariants","tabVariants","iconClasses","labelClasses","menuTriggerClasses","scrollAffordanceClasses","scrollAffordanceStartClasses","scrollAffordanceEndClasses","scrollChevronClasses","TabBar","forwardRef","items","id","ariaLabel","onSelect","renderLink","fill","position","collapseToMenu","className","t","useTranslation","isNarrow","useMediaQuery","containerRef","contentRef","isOverflowing","useOverflowCollapse","isDropdown","pillRef","canScrollStart","canScrollEnd","activeItem","item","jsx","jsxs","DropdownMenu","ChevronDown","Check","badge","ariaCurrent","inner","Fragment","Badge","tabClassName","ChevronLeft","ChevronRight"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tab-bar-Ddp_ZbnL.js","sources":["../../src/hooks/use-scroll-overflow.ts","../../src/components/tab-bar/tab-bar.tsx"],"sourcesContent":["import { useCallback, useRef, useState, type RefObject } from 'react';\nimport { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';\n\nexport interface UseScrollOverflowReturn<T extends HTMLElement> {\n /** Attach to the horizontally-scrollable element (the `overflow-x-auto` box). */\n ref: RefObject<T | null>;\n /** Content is hidden past the inline-START edge (scrolled away from the start). */\n canScrollStart: boolean;\n /** Content is hidden past the inline-END edge (more to reveal toward the end). */\n canScrollEnd: boolean;\n /**\n * Scroll by ~one viewport toward the inline start (`-1`) or end (`+1`),\n * smoothly unless the user prefers reduced motion. Direction-agnostic: works\n * in both LTR and RTL.\n */\n scrollByPage: (direction: -1 | 1) => void;\n}\n\n/**\n * Reports whether a horizontally-scrollable element has hidden content at its\n * inline-start / inline-end edges, and exposes a paged scroll action — the\n * plumbing behind a scroll strip's edge affordances (fade + chevron buttons).\n *\n * Direction-agnostic: distance-from-start is read as `Math.abs(scrollLeft)`,\n * which is correct in both LTR (`0…max`) and modern RTL (`0…-max`), so the two\n * edges are reported on the right logical side without branching. `scrollByPage`\n * flips its physical sign from the element's computed `direction`.\n *\n * Re-measures on element resize AND on content-size change (a tab added past the\n * fold widens the content without changing the container) by observing both the\n * scroll box and its first child. `enabled` switches measurement off — e.g. when\n * the host collapses the strip into a menu and there is nothing to scroll.\n *\n * Because the scrolled content stays in the DOM and is reachable by keyboard\n * (focus scrolls it into view), the affordance this drives is a sighted-pointer\n * convenience; assistive tech is unaffected.\n */\nexport function useScrollOverflow<T extends HTMLElement>(\n enabled = true,\n): UseScrollOverflowReturn<T> {\n const ref = useRef<T>(null);\n const [edges, setEdges] = useState({ start: false, end: false });\n\n useIsomorphicLayoutEffect(() => {\n const el = ref.current;\n if (!el || !enabled) {\n setEdges((prev) =>\n prev.start || prev.end ? { start: false, end: false } : prev,\n );\n return;\n }\n const measure = () => {\n // Math.abs() makes this direction-agnostic: modern RTL reports scrollLeft\n // as 0 at the start and negative toward the end, so |scrollLeft| is the\n // distance scrolled from the start in both writing directions.\n const distanceFromStart = Math.abs(el.scrollLeft);\n const maxScroll = el.scrollWidth - el.clientWidth;\n // +1 tolerates sub-pixel rounding between scroll/client widths.\n const start = distanceFromStart > 1;\n const end = maxScroll - distanceFromStart > 1;\n // Bail to the same reference when nothing changed, so neither the initial\n // no-overflow measure nor an idle scroll event triggers a re-render.\n setEdges((prev) =>\n prev.start === start && prev.end === end ? prev : { start, end },\n );\n };\n measure();\n el.addEventListener('scroll', measure, { passive: true });\n let observer: ResizeObserver | undefined;\n if (typeof ResizeObserver !== 'undefined') {\n observer = new ResizeObserver(measure);\n // The container catches viewport-width changes; its first child (the\n // content row) catches a tab added/removed past the fold, which grows the\n // scroll width without resizing the container.\n observer.observe(el);\n if (el.firstElementChild) observer.observe(el.firstElementChild);\n }\n return () => {\n el.removeEventListener('scroll', measure);\n observer?.disconnect();\n };\n }, [enabled]);\n\n const scrollByPage = useCallback((direction: -1 | 1) => {\n const el = ref.current;\n if (!el || typeof window === 'undefined') return;\n const isRtl = window.getComputedStyle(el).direction === 'rtl';\n const reduceMotion = window.matchMedia?.(\n '(prefers-reduced-motion: reduce)',\n ).matches;\n // ~85% of the viewport so a sliver of context carries across pages. The\n // physical sign flips in RTL (scrollLeft runs toward negative).\n const amount = el.clientWidth * 0.85 * direction * (isRtl ? -1 : 1);\n el.scrollBy({ left: amount, behavior: reduceMotion ? 'auto' : 'smooth' });\n }, []);\n\n return {\n ref,\n canScrollStart: edges.start,\n canScrollEnd: edges.end,\n scrollByPage,\n };\n}\n","/**\n * TabBar — a floating-pill bar of page-level navigation tabs.\n *\n * Each tab is an icon + label (+ optional count badge); the active tab is\n * marked with `aria-current=\"page\"` and an `--accent` underline. The bar is a\n * glassy `--card` pill that floats on whatever canvas the consumer provides\n * (the strip area itself is transparent, so it blends with the surrounding\n * surface).\n *\n * Two layouts:\n * - default (`fill` off): content-width tabs that **centre** when they fit and\n * scroll from the inline-start (`safe center`) when they overflow, with a\n * gutter preserved at both ends. The label stacks under the icon below `sm`.\n * Use this when the tab set is open-ended (e.g. a marketplace app's routes).\n * - `fill`: equal-width tabs that fill the bar and always stack the label under\n * the icon — a fixed, always-visible set that fits any column width without\n * scrolling (e.g. a patient portal's handful of sections).\n *\n * Tabs render as links when given an `href` (real navigation) or as buttons\n * otherwise (in-app view switching via `onSelect`). Use it as a header (top,\n * default) for section navigation; `position=\"bottom\"` floats it from the\n * bottom edge for a footer.\n *\n * With `collapseToMenu`, the strip folds into a single DropdownMenu whenever the\n * tabs don't fit — below the `sm` breakpoint OR when they overflow their\n * container at any width (a narrow column on a wide screen, e.g. a page squeezed\n * between two sidebars, clips the tabs just the same as a small viewport).\n *\n * SPA / consumer-router links: pass `renderLink` and TabBar hands each `href`\n * tab off to your router's `<Link>` instead of rendering its own `<a>` — the\n * embedded MarketplaceAppShell strip uses this so a tab click is a client-side\n * route change, not a full page navigation. The kit forwards the same\n * `className` + `aria-current` so the accent-underline active styling and the\n * `aria-current=\"page\"` marker survive in both modes. With `renderLink`\n * omitted the output is the plain `<a href>` exactly as before.\n */\nimport { forwardRef, Fragment, type MouseEvent, type ReactNode } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { Check, ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\n\nimport { Badge } from '../badge';\nimport { DropdownMenu } from '../dropdown-menu';\nimport { useMediaQuery } from '../../hooks/use-media-query';\nimport { useScrollOverflow } from '../../hooks/use-scroll-overflow';\nimport { useOverflowCollapse } from '../../hooks/use-overflow-collapse';\n\nexport interface TabBarItem {\n /** Stable id; also the value handed to `onSelect` for button tabs. */\n id: string;\n /** Pre-translated label — consumers localise their own nav copy. */\n label: string;\n /** Optional leading icon (a lucide-react element or equivalent), tinted with `--accent`. */\n icon?: ReactNode;\n /** Optional unseen-item count rendered as a pill badge. */\n badgeCount?: number;\n /** Mark the tab as the current page (`aria-current='page'` + accent underline). */\n isActive?: boolean;\n /** When set, the tab renders as a link to this href; otherwise it's a button. */\n href?: string;\n /**\n * Optional click handler threaded onto the link tab's `<a>` (or, when\n * `renderLink` is supplied, available to the consumer's router element). Use\n * it to intercept a same-origin click and route client-side without a full\n * navigation. Only applies to `href` (link) tabs — button tabs use `onSelect`.\n */\n onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;\n}\n\n/**\n * Props the kit hands to `renderLink` for each `href` tab. The consumer returns\n * their router's `<Link to={href}>` carrying the same `className` (the active\n * accent-underline styling) and `aria-current` (`'page'` on the active tab).\n */\nexport interface TabBarRenderLinkProps {\n /** The tab's destination — feed it to your router's `to` / `href`. */\n href: string;\n /** The kit's tab classes (CVA output) — apply verbatim to the link element. */\n className: string;\n /** Icon + label + badge — render as the link's children. */\n children: ReactNode;\n /** `'page'` on the active tab, otherwise omitted — forward to the link. */\n 'aria-current'?: 'page';\n /**\n * The tab's stable `TabBarItem.id`, as the agent / automation DOM hook.\n * Spread it onto your router link (`{...rest}`) so the tab stays addressable\n * by `[data-tab-id]` the same way the kit's own `<a>` / `<button>` tabs are —\n * see PRS §26 §6.\n */\n 'data-tab-id'?: string;\n /** Optional per-item click handler (the item's `onClick`), if supplied. */\n onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;\n}\n\nexport interface TabBarProps {\n /** The tabs, in order. */\n items: TabBarItem[];\n /**\n * Opaque instance id, emitted on the strip as `data-component-id`. Pass it\n * when more than one TabBar can be on screen so an agent / automation tool\n * can scope its `[data-tab-id]` lookup to one bar (PRS §26 §6).\n */\n id?: string;\n /** Accessible name for the `<nav>` landmark. */\n ariaLabel: string;\n /** Fires with the tab `id` when a button tab (one without `href`) is chosen. */\n onSelect?: (id: string) => void;\n /**\n * Render an `href` tab via the consumer's router instead of the kit's own\n * `<a>`. TabBar calls it for each link tab and renders the returned node; the\n * consumer returns their `<Link to={href}>` carrying the supplied `className`\n * + `aria-current` so the active accent underline and the `aria-current=\"page\"`\n * marker are preserved. Use it for SPA navigation (e.g. the MarketplaceAppShell\n * strip) so a tab click is a client-side route change, not a full page load.\n * Omit it to render the plain `<a href>` exactly as before. Button tabs (no\n * `href`) ignore `renderLink` and continue to use `onSelect`.\n */\n renderLink?: (props: TabBarRenderLinkProps) => ReactNode;\n /**\n * Equal-width tabs that fill the bar and always stack the label under the\n * icon (for a fixed, always-visible set). Off (default) = content-width tabs\n * that centre when they fit and scroll when they overflow.\n */\n fill?: boolean;\n /**\n * Float side. `'top'` (default) gutters from the top edge for a header;\n * `'bottom'` gutters from the bottom edge for a footer.\n */\n position?: 'top' | 'bottom';\n /**\n * Collapse the tab strip into a single DropdownMenu whenever the tabs don't\n * fit — below the `sm` breakpoint (≤640px) OR when they overflow their\n * container at any viewport width (a narrow column between two sidebars clips\n * the tabs the same as a small screen). The trigger shows the active tab; the\n * menu lists them all. Off (default) keeps the content-width / scroll\n * behaviour at every width, so existing consumers are unaffected. Designed for\n * button (`onSelect`) tabs; in the collapsed menu an `href` tab navigates with\n * a full page load, so `renderLink` (SPA routing) and a per-item `onClick`\n * apply only to the expanded strip.\n */\n collapseToMenu?: boolean;\n /** Extra classes merged onto the outer strip (e.g. sticky/fixed positioning). */\n className?: string;\n}\n\nfunction formatBadgeCount(count: number): string {\n return count > 99 ? '99+' : String(count);\n}\n\n/* Transparent strip area — provides the floating gutter on the active side. The\n default layout also insets the pill horizontally so it reads as a floating\n strip; `fill` drops that inset so the pill spans the full width and aligns\n with the consumer's content (e.g. a portal's cards). */\nconst stripVariants = cva('ds:shrink-0', {\n variants: {\n position: {\n top: 'ds:pt-[var(--spacing-md)]',\n bottom: 'ds:pb-[var(--spacing-md)]',\n },\n fill: {\n false: 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n true: '',\n },\n },\n defaultVariants: { position: 'top', fill: false },\n});\n\n/* The pill is the glass chrome — the bordered, blurred, rounded surface. The\n scrollable tab row is a separate inner element (`pillScrollClasses`) so the\n edge-fade mask touches only the tabs, never the pill's border. It is also the\n overflow-collapse MEASUREMENT container: it stays mounted in both the scroll\n and the dropdown render, and (carrying no padding) its `clientWidth` is\n exactly the width the tab row has to fit. */\nconst pillClasses =\n 'ds:rounded-[var(--radius-lg)] ds:border ds:border-[color:var(--card-border)] ds:bg-[var(--card)]/80 ds:backdrop-blur-md ds:shadow-[var(--shadow-md)]';\n\n/* The inner scroll container. `safe center` centres the tabs when they fit but\n falls back to start-alignment the moment they overflow, so the row stays fully\n scrollable end to end. Inline-edge MASK: the leading / trailing edge fades only\n when tabs are hidden past it (`data-fade-start` / `data-fade-end`), so a clipped\n tab dissolves into the glass under the accent chevron — alpha-based, so no seam\n with the translucent pill. Logical via `--tabbar-mask-*` (RTL-mapped); `black`\n is the mask-opaque sentinel, not a colour. Disabled under reduced motion, the\n accessible theme, and forced-colors so content is never hidden from those\n users. `--tabbar-fade-distance` (default `--spacing-lg`) is per-instance. */\nconst pillScrollClasses = [\n 'ds:flex ds:[justify-content:safe_center] ds:overflow-x-auto ds:py-[var(--spacing-xs)]',\n 'ds:[--tabbar-mask-start:black] ds:[--tabbar-mask-end:black]',\n 'ds:ltr:data-[fade-start=true]:[--tabbar-mask-start:transparent]',\n 'ds:ltr:data-[fade-end=true]:[--tabbar-mask-end:transparent]',\n 'ds:rtl:data-[fade-start=true]:[--tabbar-mask-end:transparent]',\n 'ds:rtl:data-[fade-end=true]:[--tabbar-mask-start:transparent]',\n 'ds:[mask-image:linear-gradient(to_right,var(--tabbar-mask-start)_0,black_var(--tabbar-fade-distance,var(--spacing-lg)),black_calc(100%-var(--tabbar-fade-distance,var(--spacing-lg))),var(--tabbar-mask-end)_100%)]',\n 'ds:motion-reduce:[mask-image:none]',\n 'ds:[.theme-accessible_&]:[mask-image:none]',\n 'ds:forced-colors:[mask-image:none]',\n].join(' ');\n\n/* The nav's inline padding is the start/end gutter — it scrolls WITH the tabs,\n so the gutter survives a full scroll. Content width (`shrink-0`) by default so\n it overflows and scrolls; `w-full` in fill mode so the tabs spread to fit. */\nconst navVariants = cva(\n 'ds:flex ds:items-stretch ds:gap-[var(--spacing-xs)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n {\n variants: {\n fill: { false: 'ds:shrink-0', true: 'ds:w-full' },\n },\n defaultVariants: { fill: false },\n },\n);\n\n/* Each tab is a centred flex box so the icon + label sit centred on both axes.\n Regular weight; the active state is carried by the accent underline + accent\n icon, not a heavier weight. */\nconst tabVariants = cva(\n 'ds:group ds:flex ds:items-center ds:justify-center ds:gap-[var(--spacing-xs)] ds:min-h-[var(--min-target-size)] ds:rounded-[var(--radius-sm)] ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-xs)] ds:whitespace-nowrap ds:cursor-pointer ds:appearance-none ds:border-0 ds:bg-transparent ds:text-[length:var(--font-size-sm)] ds:font-[var(--font-weight-regular)] ds:text-[color:var(--foreground)] ds:no-underline ds:hover:bg-[var(--muted)]/20 ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n {\n variants: {\n fill: {\n // Responsive: label under the icon below `sm`, beside it at `sm`+.\n false: 'ds:flex-col ds:sm:flex-row ds:sm:gap-[var(--spacing-sm)]',\n // Fill the bar, always icon-over-label. No `min-w-0`, so a tab never\n // shrinks below its label (labels never overlap); if the full set can't\n // fit, the pill scrolls instead.\n true: 'ds:flex-1 ds:basis-0 ds:flex-col',\n },\n },\n defaultVariants: { fill: false },\n },\n);\n\nconst iconClasses =\n 'ds:inline-flex ds:items-center ds:text-[color:var(--accent)] ds:[&>svg]:size-4';\n\n/* The accent underline rides the label text itself (not the whole tab), so it\n never reaches the icon or badge and never has to propagate through flex. */\nconst labelClasses =\n 'ds:group-aria-[current=page]:underline ds:group-aria-[current=page]:decoration-2 ds:group-aria-[current=page]:decoration-[color:var(--accent)] ds:group-aria-[current=page]:underline-offset-[var(--spacing-xs)]';\n\n/* The mobile collapse trigger — a full-width button styled like a tab, showing\n the active tab's icon + label with a trailing chevron. */\nconst menuTriggerClasses =\n 'ds:flex ds:w-full ds:items-center ds:gap-[var(--spacing-sm)] ds:min-h-[var(--min-target-size)] ds:rounded-[var(--radius-sm)] ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-xs)] ds:cursor-pointer ds:appearance-none ds:border-0 ds:bg-transparent ds:text-[length:var(--font-size-sm)] ds:font-[var(--font-weight-regular)] ds:text-[color:var(--foreground)] ds:hover:bg-[var(--muted)]/20 ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]';\n\n/* Scroll affordance — an accent chevron pinned to a scrollable edge of the pill,\n shown only when there are tabs hidden past that edge. No button chrome: the\n edge tab fades into the glass via the inner scroll's mask (above); the chevron\n just points the way and pages the strip on click. A `--min-target-size` hit\n area keeps it tappable. Block axis spans the pill (`top`/`bottom` px-inset past\n the border); the inline edge (`start`/`end`) and the chevron flip mirror RTL. */\nconst scrollAffordanceClasses =\n 'ds:absolute ds:top-px ds:bottom-px ds:z-10 ds:flex ds:min-w-[var(--min-target-size)] ds:items-center ds:cursor-pointer ds:appearance-none ds:border-0 ds:bg-transparent ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]';\n\n/* Pin the chevron to its inline edge, over the faded tab. */\nconst scrollAffordanceStartClasses =\n 'ds:start-px ds:justify-start ds:ps-[var(--spacing-xs)]';\n\nconst scrollAffordanceEndClasses =\n 'ds:end-px ds:justify-end ds:pe-[var(--spacing-xs)]';\n\n/* The chevron is the only visible mark — accent-tinted (matching the tab icons),\n mirrored under RTL. Decorative (`aria-hidden`); the button carries the label. */\nconst scrollChevronClasses =\n 'ds:size-4 ds:shrink-0 ds:text-[color:var(--accent)] ds:rtl:-scale-x-100';\n\nexport const TabBar = forwardRef<HTMLDivElement, TabBarProps>(\n (\n {\n items,\n id,\n ariaLabel,\n onSelect,\n renderLink,\n fill = false,\n position = 'top',\n collapseToMenu = false,\n className,\n },\n ref,\n ) => {\n const { t } = useTranslation();\n\n // Collapse is twofold, OR'd: the documented mobile floor (≤sm) AND actual\n // container overflow (a narrow column on a wide screen clips the tabs just\n // the same). Both hooks run every render (hook order); only consulted when\n // collapseToMenu is on. The pill (containerRef) stays mounted across the\n // collapse so the overflow hook can still tell when the column has grown\n // back; the tab row (contentRef) carries the natural width.\n const isNarrow = useMediaQuery('(max-width: 640px)');\n const { containerRef, contentRef, isOverflowing } = useOverflowCollapse<\n HTMLDivElement,\n HTMLElement\n >(collapseToMenu);\n const isDropdown = collapseToMenu && (isNarrow || isOverflowing);\n\n // Edge scroll affordances for the scroll layout. Disabled in dropdown mode\n // (no scroll box), so the observer tears down / re-attaches across the flip.\n const {\n ref: pillRef,\n canScrollStart,\n canScrollEnd,\n scrollByPage,\n } = useScrollOverflow<HTMLDivElement>(!isDropdown);\n\n const activeItem = items.find((item) => item.isActive) ?? items[0];\n\n return (\n <div\n ref={ref}\n data-component=\"tab-bar\"\n data-component-id={id}\n className={`${stripVariants({ position, fill })}${className ? ` ${className}` : ''}`}\n >\n {/* Relative wrapper: the scroll affordances are absolutely positioned\n against it, NOT inside the scroll box (absolute children of a scroller\n scroll away with the content). */}\n <div className=\"ds:relative\">\n {/* The pill doubles as the overflow-collapse measurement container —\n rendered in BOTH branches (same element, same width) so the hook\n can keep measuring after the tab row is swapped for the menu. */}\n <div ref={containerRef} className={pillClasses}>\n {isDropdown ? (\n <nav aria-label={ariaLabel} className=\"ds:w-full\">\n <DropdownMenu.Root>\n <DropdownMenu.Trigger className={menuTriggerClasses}>\n {activeItem?.icon ? (\n <span aria-hidden=\"true\" className={iconClasses}>\n {activeItem.icon}\n </span>\n ) : null}\n <span className=\"ds:flex-1 ds:truncate ds:text-start\">\n {activeItem?.label}\n </span>\n <ChevronDown\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:text-[color:var(--muted-foreground)]\"\n />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content\n align=\"start\"\n className=\"ds:min-w-[12rem]\"\n >\n {items.map((item) => (\n <DropdownMenu.Item\n key={item.id}\n data-tab-id={item.id}\n startIcon={item.icon}\n endIcon={\n item.isActive ? (\n <Check\n aria-hidden=\"true\"\n className=\"ds:text-[color:var(--accent)]\"\n />\n ) : undefined\n }\n aria-current={item.isActive ? 'page' : undefined}\n onSelect={() => {\n if (item.href) {\n window.location.assign(item.href);\n } else {\n onSelect?.(item.id);\n }\n }}\n >\n {item.label}\n </DropdownMenu.Item>\n ))}\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n </nav>\n ) : (\n <div\n ref={pillRef}\n data-fade-start={canScrollStart ? 'true' : undefined}\n data-fade-end={canScrollEnd ? 'true' : undefined}\n className={pillScrollClasses}\n >\n <nav\n ref={contentRef}\n aria-label={ariaLabel}\n className={navVariants({ fill })}\n >\n {items.map((item) => {\n const badge =\n typeof item.badgeCount === 'number' && item.badgeCount > 0\n ? item.badgeCount\n : undefined;\n const ariaCurrent = item.isActive ? 'page' : undefined;\n const inner = (\n <>\n {item.icon ? (\n <span aria-hidden=\"true\" className={iconClasses}>\n {item.icon}\n </span>\n ) : null}\n {/* Label + badge stay paired; they sit under the icon when\n the tab stacks to a column. */}\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <span className={labelClasses}>{item.label}</span>\n {badge !== undefined ? (\n <Badge variant=\"neutral\">\n {formatBadgeCount(badge)}\n </Badge>\n ) : null}\n </span>\n </>\n );\n if (item.href !== undefined) {\n const tabClassName = tabVariants({ fill });\n // Consumer-router mode: hand the tab to the consumer's <Link>,\n // forwarding the same className + aria-current (+ the item's\n // onClick) so the active styling and `aria-current=\"page\"`\n // marker carry over and clicking routes client-side.\n if (renderLink) {\n return (\n <Fragment key={item.id}>\n {renderLink({\n href: item.href,\n className: tabClassName,\n children: inner,\n 'aria-current': ariaCurrent,\n 'data-tab-id': item.id,\n onClick: item.onClick,\n })}\n </Fragment>\n );\n }\n // Default: plain anchor — byte-identical to before when no\n // per-item onClick is supplied (onClick is omitted entirely,\n // not passed as undefined, so the rendered <a> is unchanged).\n return (\n <a\n key={item.id}\n href={item.href}\n data-tab-id={item.id}\n aria-current={ariaCurrent}\n className={tabClassName}\n {...(item.onClick ? { onClick: item.onClick } : {})}\n >\n {inner}\n </a>\n );\n }\n return (\n <button\n key={item.id}\n type=\"button\"\n data-tab-id={item.id}\n aria-current={ariaCurrent}\n className={tabVariants({ fill })}\n onClick={() => onSelect?.(item.id)}\n >\n {inner}\n </button>\n );\n })}\n </nav>\n </div>\n )}\n </div>\n {canScrollStart ? (\n <button\n type=\"button\"\n aria-label={t('navigation.tabBar.scrollBack')}\n onClick={() => scrollByPage(-1)}\n className={`${scrollAffordanceClasses} ${scrollAffordanceStartClasses}`}\n >\n <ChevronLeft\n aria-hidden=\"true\"\n className={scrollChevronClasses}\n />\n </button>\n ) : null}\n {canScrollEnd ? (\n <button\n type=\"button\"\n aria-label={t('navigation.tabBar.scrollForward')}\n onClick={() => scrollByPage(1)}\n className={`${scrollAffordanceClasses} ${scrollAffordanceEndClasses}`}\n >\n <ChevronRight\n aria-hidden=\"true\"\n className={scrollChevronClasses}\n />\n </button>\n ) : null}\n </div>\n </div>\n );\n },\n);\nTabBar.displayName = 'TabBar';\n"],"names":["useScrollOverflow","enabled","ref","useRef","edges","setEdges","useState","useIsomorphicLayoutEffect","el","prev","measure","distanceFromStart","maxScroll","start","end","observer","scrollByPage","useCallback","direction","isRtl","reduceMotion","_a","amount","formatBadgeCount","count","stripVariants","cva","pillClasses","pillScrollClasses","navVariants","tabVariants","iconClasses","labelClasses","menuTriggerClasses","scrollAffordanceClasses","scrollAffordanceStartClasses","scrollAffordanceEndClasses","scrollChevronClasses","TabBar","forwardRef","items","id","ariaLabel","onSelect","renderLink","fill","position","collapseToMenu","className","t","useTranslation","isNarrow","useMediaQuery","containerRef","contentRef","isOverflowing","useOverflowCollapse","isDropdown","pillRef","canScrollStart","canScrollEnd","activeItem","item","jsx","jsxs","DropdownMenu","ChevronDown","Check","badge","ariaCurrent","inner","Fragment","Badge","tabClassName","ChevronLeft","ChevronRight"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { jsx as a, jsxs as k, Fragment as ke } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef as pt, useId as ge, useMemo as V, useState as h, useCallback as f, useRef as O, useImperativeHandle as ut } from "react";
|
|
3
3
|
import { c as ft } from "./index-D2ZczOXr.js";
|
|
4
4
|
import { useTranslation as mt } from "react-i18next";
|
|
5
|
-
import { B as kt } from "./badge-
|
|
5
|
+
import { B as kt } from "./badge-CIt-UFEx.js";
|
|
6
6
|
import { B as ae } from "./button-BvNtqHAS.js";
|
|
7
7
|
import { I as $ } from "./icon-button-9c9Gp41m.js";
|
|
8
8
|
import { B as he } from "./button-group-Be_In68J.js";
|
|
@@ -754,4 +754,4 @@ export {
|
|
|
754
754
|
Gt as T,
|
|
755
755
|
qt as t
|
|
756
756
|
};
|
|
757
|
-
//# sourceMappingURL=tasks-panel-
|
|
757
|
+
//# sourceMappingURL=tasks-panel-T7vaKJcA.js.map
|