@tekkare/romulus 0.1.250 → 0.1.252

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/BarChart.vue +1 -0
  3. package/dist/runtime/components/ChoroplethMap.vue +4 -1
  4. package/dist/runtime/internal/geo/fr-regions.json +1 -1
  5. package/dist/runtime/utils/chart.d.ts +6 -3
  6. package/dist/runtime/utils/chart.js +15 -2
  7. package/layer/app/app.config.ts +6 -0
  8. package/layer/app/components/OipPubmedSummary.vue +223 -0
  9. package/layer/app/components/OipResourceDrawer.vue +331 -0
  10. package/layer/app/components/OipResourceDrawerLink.vue +40 -0
  11. package/layer/app/components/OipResourceField.vue +41 -0
  12. package/layer/app/components/OipResourceGenericCard.vue +54 -0
  13. package/layer/app/components/OipResourceSource.vue +50 -0
  14. package/layer/app/components/OipResourceTags.vue +65 -0
  15. package/layer/app/components/OipResourceView.vue +107 -0
  16. package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
  17. package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
  18. package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
  19. package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
  20. package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
  21. package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
  22. package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
  23. package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
  24. package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
  25. package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
  26. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
  27. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
  28. package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
  29. package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
  30. package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
  31. package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
  32. package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
  33. package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
  34. package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
  35. package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
  36. package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
  37. package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
  38. package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
  39. package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
  40. package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
  41. package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
  42. package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
  43. package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
  44. package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
  45. package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
  46. package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
  47. package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
  48. package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
  49. package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
  50. package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
  51. package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
  52. package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
  53. package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
  54. package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
  55. package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
  56. package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
  57. package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
  58. package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
  59. package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
  60. package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
  61. package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
  62. package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
  63. package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
  64. package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
  65. package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
  66. package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
  67. package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
  68. package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
  69. package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
  70. package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
  71. package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
  72. package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
  73. package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
  74. package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
  75. package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
  76. package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
  77. package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
  78. package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
  79. package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
  80. package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
  81. package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
  82. package/layer/app/components/oip-science/common/Form.vue +73 -0
  83. package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
  84. package/layer/app/components/oip-science/common/NoData.vue +16 -0
  85. package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
  86. package/layer/app/components/oip-science/common/Source.vue +31 -0
  87. package/layer/app/components/oip-science/common/TableTags.vue +279 -0
  88. package/layer/app/components/oip-science/common/Timeline.vue +104 -0
  89. package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
  90. package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
  91. package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
  92. package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
  93. package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
  94. package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
  95. package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
  96. package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
  97. package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
  98. package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
  99. package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
  100. package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
  101. package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
  102. package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
  103. package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
  104. package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
  105. package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
  106. package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
  107. package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
  108. package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
  109. package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
  110. package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
  111. package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
  112. package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
  113. package/layer/app/components/oip-science/euct/Locations.vue +102 -0
  114. package/layer/app/components/oip-science/euct/Products.vue +101 -0
  115. package/layer/app/components/oip-science/euct/Publications.vue +89 -0
  116. package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
  117. package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
  118. package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
  119. package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
  120. package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
  121. package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
  122. package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
  123. package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
  124. package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
  125. package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
  126. package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
  127. package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
  128. package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
  129. package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
  130. package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
  131. package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
  132. package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
  133. package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
  134. package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
  135. package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
  136. package/layer/app/composables/useOipChapterScroll.ts +43 -0
  137. package/layer/app/composables/useOipResourceHooks.ts +27 -0
  138. package/layer/app/composables/useOipResourceNav.ts +66 -0
  139. package/layer/app/composables/useOipResourceViews.ts +117 -0
  140. package/layer/app/composables/useOipResources.ts +83 -0
  141. package/layer/app/oip-drugs/PORTING.md +120 -0
  142. package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
  143. package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
  144. package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
  145. package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
  146. package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
  147. package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
  148. package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
  149. package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
  150. package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
  151. package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
  152. package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
  153. package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
  154. package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
  155. package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
  156. package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
  157. package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
  158. package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
  159. package/layer/app/oip-drugs/drugs.css +500 -0
  160. package/layer/app/oip-drugs/esIndices.ts +322 -0
  161. package/layer/app/oip-drugs/kinds.ts +264 -0
  162. package/layer/app/oip-drugs/locales/en.json +1571 -0
  163. package/layer/app/oip-drugs/locales/fr.json +1571 -0
  164. package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
  165. package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
  166. package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
  167. package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
  168. package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
  169. package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
  170. package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
  171. package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
  172. package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
  173. package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
  174. package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
  175. package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
  176. package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
  177. package/layer/app/oip-drugs/types/elastic.ts +113 -0
  178. package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
  179. package/layer/app/oip-drugs/types/es/directory.ts +54 -0
  180. package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
  181. package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
  182. package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
  183. package/layer/app/oip-drugs/types/es/portal.ts +130 -0
  184. package/layer/app/oip-drugs/types/es/retail.ts +107 -0
  185. package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
  186. package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
  187. package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
  188. package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
  189. package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
  190. package/layer/app/oip-drugs/utils/contributions.ts +36 -0
  191. package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
  192. package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
  193. package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
  194. package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
  195. package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
  196. package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
  197. package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
  198. package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
  199. package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
  200. package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
  201. package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
  202. package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
  203. package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
  204. package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
  205. package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
  206. package/layer/app/oip-science/PORTING.md +93 -0
  207. package/layer/app/oip-science/assets/smartSelection.json +428 -0
  208. package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
  209. package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
  210. package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
  211. package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
  212. package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
  213. package/layer/app/oip-science/composables/useEuct.ts +418 -0
  214. package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
  215. package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
  216. package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
  217. package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
  218. package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
  219. package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
  220. package/layer/app/oip-science/composables/usePubmed.ts +487 -0
  221. package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
  222. package/layer/app/oip-science/indexConfig.json +794 -0
  223. package/layer/app/oip-science/locales/en.json +483 -0
  224. package/layer/app/oip-science/locales/fr.json +483 -0
  225. package/layer/app/oip-science/science.css +1182 -0
  226. package/layer/app/oip-science/utils/a-openCard.ts +23 -0
  227. package/layer/app/oip-science/utils/b-openCard.ts +35 -0
  228. package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
  229. package/layer/app/oip-science/utils/f-types.ts +241 -0
  230. package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
  231. package/layer/app/oip-science/utils/matchMethod.ts +53 -0
  232. package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
  233. package/layer/app/oip-science/utils/sources.ts +15 -0
  234. package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
  235. package/layer/app/oip-science/utils/toPresets.ts +7 -0
  236. package/layer/app/oip-science/utils/valueTable.ts +126 -0
  237. package/layer/app/plugins/oip-science-i18n.ts +17 -0
  238. package/layer/app/utils/oipFicheRoutes.ts +118 -0
  239. package/layer/app/utils/oipResources.ts +440 -0
  240. package/layer/nuxt.config.ts +22 -2
  241. package/layer/public/reports/blincyto-has.html +292 -0
  242. package/layer/public/reports/blincyto-has.pdf +0 -0
  243. package/layer/public/reports/darzalex-has.html +279 -0
  244. package/layer/public/reports/darzalex-has.pdf +0 -0
  245. package/layer/public/reports/dupixent-has.html +217 -0
  246. package/layer/public/reports/dupixent-has.pdf +0 -0
  247. package/layer/public/reports/enhertu-has.html +283 -0
  248. package/layer/public/reports/enhertu-has.pdf +0 -0
  249. package/layer/public/reports/has-sein-2023.html +1639 -0
  250. package/layer/public/reports/ibrance-has.html +280 -0
  251. package/layer/public/reports/ibrance-has.pdf +0 -0
  252. package/layer/public/reports/imfinzi-has.html +297 -0
  253. package/layer/public/reports/imfinzi-has.pdf +0 -0
  254. package/layer/public/reports/keytruda-has.html +337 -0
  255. package/layer/public/reports/keytruda-has.pdf +0 -0
  256. package/layer/public/reports/kisqali-has.html +202 -0
  257. package/layer/public/reports/kisqali-has.pdf +0 -0
  258. package/layer/public/reports/opdivo-has.html +232 -0
  259. package/layer/public/reports/opdivo-has.pdf +0 -0
  260. package/layer/public/reports/ozempic-has.html +188 -0
  261. package/layer/public/reports/ozempic-has.pdf +0 -0
  262. package/layer/public/reports/tecentriq-has.html +315 -0
  263. package/layer/public/reports/tecentriq-has.pdf +0 -0
  264. package/layer/public/reports/yescarta-has.html +275 -0
  265. package/layer/public/reports/yescarta-has.pdf +0 -0
  266. package/layer/server/api/oip/es.post.ts +99 -0
  267. package/layer/server/api/oip/pdf.get.ts +70 -0
  268. package/layer/server/api/oip/reports.get.ts +98 -0
  269. package/layer/server/api/oip/resource-types.get.ts +22 -0
  270. package/package.json +1 -1
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.16.0"
6
6
  },
7
- "version": "0.1.249",
7
+ "version": "0.1.251",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
@@ -12,6 +12,7 @@ const props = withDefaults(defineProps<{
12
12
  /** Categories de l'axe : annees, libelles d'indicateur. */
13
13
  categories: (string | number)[]
14
14
  series: RmSeriesInput[]
15
+ /** Barres couchees, la premiere categorie en haut, comme dans un classement. */
15
16
  horizontal?: boolean
16
17
  height?: keyof typeof RM_CHART_HEIGHTS | number
17
18
  description?: string
@@ -133,10 +133,13 @@ const ramp = computed(() => {
133
133
  return props.reverseColors ? [...seq].reverse() : seq
134
134
  })
135
135
 
136
+ // Les noms sont compares en NFC des deux cotes : un meme « ô » s'ecrit en un
137
+ // caractere ou en « o » suivi d'un accent combinant, et la region restait
138
+ // grise quand l'app et le trace ne l'ecrivaient pas pareil.
136
139
  const byName = computed(() => {
137
140
  const out = new Map<string, number | null>()
138
141
  for (const entry of props.data) {
139
- out.set(entry.name, entry.value === null || entry.value === undefined ? null : Number(entry.value))
142
+ out.set(entry.name.normalize('NFC'), entry.value === null || entry.value === undefined ? null : Number(entry.value))
140
143
  }
141
144
  return out
142
145
  })
@@ -1,5 +1,5 @@
1
1
  {
2
- "Auvergne-Rhône-Alpes": {
2
+ "Auvergne-Rhône-Alpes": {
3
3
  "path": "M1594.02 905.625L1592.46 900.11L1587.06 899.714L1587.64 896.318L1581.84 894.741L1581.35 884.209L1579.37 882.144L1581.8 874.258L1578.2 872.067L1572.78 872.447L1561.24 861.188L1562.65 844.212L1575.52 842.67L1581.44 838.84L1582.11 834.563L1586.54 830.972L1585.84 826.218L1582.86 823.834L1584.5 822.596L1575.11 809.523L1570.15 812.68L1568.32 811.301L1571.03 800.142L1560.23 798.149L1560.96 788.003L1567.44 776.23L1557.43 764.763L1561.16 761.018L1562.7 753.713L1547.99 749.472L1530.59 749.102L1502.15 764.039L1498.72 771.46L1501.65 772.752L1500.55 776.782L1503.01 780.764L1508.21 780.042L1506.7 784.792L1499.96 787.756L1488.89 797.902L1480.74 796.045L1479.07 798.637L1474.54 797.134L1470.66 799.636L1474.62 791.488L1471.42 789.171L1473.07 786.084L1488.46 780.797L1486.08 775.552L1493.31 762.945L1482.08 755.362L1461.26 778.642L1445.88 779.241L1444.65 771.796L1437.48 769.503L1437.09 766.346L1432.06 773.857L1428.87 774.088L1426 778.056L1419.05 778.701L1417.26 777.147L1418.29 769.549L1415.13 770.841L1413.77 765.837L1411.6 771.596L1411.5 767.27L1407.06 762.39L1408.75 760.54L1400.44 755.793L1403.08 753.435L1402.09 750.674L1393.71 748.883L1390.12 741.056L1386.49 739.451L1386.36 741.783L1383.03 741.072L1374.64 744.856L1366.9 740.114L1363.32 742.277L1362.39 739.51L1345.1 792.238L1339.33 791.671L1340.34 786.695L1338.64 784.376L1340.43 783.484L1334.4 779.549L1337.72 776.52L1334.44 772.29L1329.9 773.073L1327.87 778.534L1324.16 777.949L1322.55 774.165L1319.37 777.751L1315.68 778.211L1310.85 773.659L1305.19 773.872L1303.29 786.569L1300.55 788.688L1301.67 790.761L1296.88 790.885L1291.91 795.182L1288.57 794.951L1289.06 790.499L1286.02 792.002L1284.09 789.439L1280.94 792.601L1276.11 792.079L1272.91 788.778L1265.07 793.292L1260.82 793.307L1259.04 788.177L1250.26 786.379L1252.29 781.447L1250.34 775.495L1255.99 773.127L1256.23 769.88L1260.6 770.941L1262.44 768.986L1260.32 768.77L1259.4 758.033L1261.91 748.269L1256.92 745.847L1256.62 743.468L1247.99 744.224L1244.78 737.535L1241.2 739.482L1234.44 736.422L1233.84 726.602L1231.39 725.567L1232.28 722.749L1229.69 717.794L1223.35 710.324L1221.91 702.911L1218.05 701.84L1216.67 703.794L1218.92 707.624L1213.65 707.874L1215.06 713.114L1213.44 714.742L1211.82 713.006L1207.12 718.798L1203.97 718.922L1203.51 712.973L1200.1 709.238L1195.68 709.422L1192.24 713.887L1188.67 713.034L1187.31 708.927L1183.96 708.77L1176.56 715.063L1169 707.157L1161.44 703.481L1158.6 696.697L1151.38 695.238L1146.82 697.008L1139.23 704.334L1138.87 707.419L1133.78 706.024L1129.77 708.783L1126.84 704.069L1124.94 704.395L1123.43 708.521L1115.19 712.924L1115.58 717.82L1113.14 715.852L1109.41 717.789L1112.16 720.841L1109.02 725.577L1112.99 727.774L1114.16 734.144L1105.82 739.443L1100.53 737.357L1087.09 739.816L1083.08 746.934L1080.43 746.949L1078.52 754.755L1079.27 760.658L1084.24 762.076L1080.86 766.543L1087.55 771.593L1089.98 768.347L1093.59 775.793L1094.67 773.792L1100.08 776.238L1104.73 790.499L1108.99 795.395L1107.32 806.022L1109.61 811.966L1112.82 813.849L1111.9 820.554L1113.87 823.492L1108.86 825.832L1106.72 829.562L1108.02 832.403L1101.99 836.541L1101.18 839.991L1097.81 839.534L1097.33 839.642L1094.54 844.479L1091.58 843.852L1090.08 845.881L1095.94 855.989L1100.37 857.559L1104.5 863.436L1104.29 873.78L1100.49 873.993L1097.84 880.788L1100.83 886.133L1103.86 887.15L1100.64 907.634L1104.97 911.736L1099.87 915.08L1089.87 908.769L1085.79 908.738L1087.94 912.463L1087 920.13L1082.34 922.414L1079.46 927.446L1077.33 927.007L1074.29 933.442L1069.3 937.546L1070.04 943.624L1073.39 946.021L1067.77 950.303L1067.06 958.587L1063.87 957.94L1059.13 961.929L1063.86 970.727L1060.31 973.688L1059.43 973.462L1055.17 974.559L1056.82 980.776L1059.6 982.861L1057.68 985.368L1058.34 989.9L1066.92 1002.59L1062.44 1016.36L1065.57 1016.49L1066.93 1025.28L1070.78 1028.26L1072.6 1027.28L1071.58 1023.44L1080.09 1020.57L1083.47 1020.27L1086.2 1024.74L1100.23 1023.21L1101.9 1017.5L1108.37 1012.85L1107.28 1007.87L1112.67 1002.08L1113.12 994.033L1127.24 979.692L1131.23 984.076L1131.05 992.53L1134.46 989.74L1140.29 989.756L1143.35 1002.51L1146.78 1001.85L1148.56 1004.79L1145.83 1006.69L1148.34 1009.25L1148.52 1018.87L1153.31 1024.17L1157.1 1013.84L1159.42 1013.53L1158.08 1011.63L1160.17 1000.88L1162.98 995.248L1165.81 995.846L1166.29 991.032H1166.9L1166.27 987.989L1169.27 985.363L1175.7 991.528L1180.43 987.75L1179.07 984.508L1181.86 983.442L1181.03 980.575L1183.71 979.525L1185.72 981.883L1194.82 974.629L1194.88 976.912L1199.53 979.63L1199.71 986.175L1207.12 999.936L1209.2 997.269L1216.06 995.53L1218.03 997.015L1218.56 989.566L1225.01 989.535L1227.2 992.802L1226.36 996.609L1234.55 995.217L1241.6 1006.23L1244.25 1004.44L1244.4 1008.74L1248.65 1009.88L1247.09 1014.2L1249.55 1016.37L1248 1019.12L1251.06 1024.08L1251.59 1031.66L1254.37 1035.39L1256.88 1035.17L1260.68 1049.85L1269.29 1059.79L1266.25 1061.88L1268.11 1066L1265.83 1070.99L1267.06 1072.63L1275.78 1070.01L1277.16 1073.49L1280.96 1072.77L1289.36 1080.64L1292.71 1076.59L1292.62 1073.21L1296.73 1069.76L1303.74 1068.54L1304.12 1076.86L1308.63 1077.92L1310.25 1069.73L1315.81 1068.94L1316.52 1071.18L1320.44 1071.67L1321.48 1075.1L1331.16 1079.81L1331.29 1070.98L1346.01 1073.46L1349.25 1086.19L1355.74 1081.24L1360.26 1081.39L1374.94 1074.29L1376.7 1077.81L1380.12 1078.76L1384.65 1074.01L1386.35 1073.2L1384.8 1085.52L1393.06 1088.61L1395.13 1085.64L1400.35 1089.24L1406.44 1088.17L1409.6 1090.25L1409.41 1096.94L1415 1097.45L1417.09 1102.27L1422.25 1102.8L1426.31 1100.3L1430.72 1096.61L1428.75 1094.65L1429.84 1092.31L1432.73 1091.6L1436.76 1095.08L1435.86 1097.64L1440.91 1098.25L1441.35 1095.72L1437.84 1094.1L1438.06 1091.88L1441.76 1090.87L1441.78 1080.37H1437.57L1436.45 1075.37L1433.39 1074.54L1435.66 1070.48L1426.97 1070.96L1424.15 1067.91L1421.15 1069.86L1414.45 1064.34L1415.82 1061.91L1413.25 1056.86L1415.3 1055.66L1419.4 1057.73L1421.64 1055.08L1418.06 1053.52L1417.36 1045.86L1434 1049.99L1435.55 1045.73L1439.32 1045.4L1432.19 1039.2L1437.62 1029.08L1437.1 1023.13L1446.28 1025.04L1448.44 1021.6L1452.72 1022.89L1457.09 1017.36L1453.73 1014.97L1456.73 1007.02L1462.67 1008.96L1469.96 1006.87L1473.07 1003.71L1469.79 1000.22L1477.06 995.61L1481.2 998.546L1489.46 991.307L1501.56 993.167L1506.26 989.869L1511.03 993.737L1513.11 992.718L1513.38 980.038L1509.59 977.86L1509.71 971.115L1502 971.461L1496.97 968.5L1498.62 961.233L1501.23 960.3L1499.76 955.589L1501.36 952.774L1503.24 951.87L1506.52 954.641L1510.72 952.365L1514.01 955.468L1514.07 960.344L1523.26 963.082L1527.06 962.615L1526.39 956.909L1529.6 954.441L1532.94 955.887L1536.81 952.257L1542.43 954.487L1547.67 949.876L1554.28 950.254L1557.01 947.015L1565.67 951.895L1570.38 950.449L1570.4 945.671L1576 945.461L1581.98 937.844L1590.55 938.358L1596.23 932.427L1593.57 921.625L1599.22 916.603L1600.48 909.188L1594.02 905.625Z",
4
4
  "departments": [
5
5
  "Ain",
@@ -418,15 +418,18 @@ export declare function rmBarHorizontalOption(labels: string[], values: (number
418
418
  type: string;
419
419
  };
420
420
  yAxis: {
421
- splitLine: {
422
- show: boolean;
423
- };
421
+ inverse: boolean;
424
422
  axisLabel: {
423
+ width: number;
424
+ overflow: string;
425
425
  formatter?: ((value: number) => string) | undefined;
426
426
  fontSize: number;
427
427
  color: string;
428
428
  fontFamily: string;
429
429
  };
430
+ splitLine: {
431
+ show: boolean;
432
+ };
430
433
  axisLine: {
431
434
  lineStyle: {
432
435
  color: string;
@@ -193,9 +193,22 @@ export function rmBarHorizontalOption(labels, values, theme, palette, decal = fa
193
193
  return {
194
194
  ...rmBaseOption(theme, decal),
195
195
  tooltip: { ...rmBaseOption(theme, decal).tooltip, ...rmTooltipValue(valueFormatter) },
196
- grid: { left: 140, right: 60, top: 16, bottom: 24, containLabel: true },
196
+ // `containLabel` reserve deja la place des libelles : une marge gauche
197
+ // fixe s'y ajoutait, et un libelle long ecrasait le trace au point de
198
+ // faire se chevaucher les graduations de l'axe des valeurs.
199
+ grid: { left: 8, right: 60, top: 16, bottom: 24, containLabel: true },
197
200
  xAxis: { type: "value", ...axisCommon(theme, valueFormatter) },
198
- yAxis: { type: "category", data: labels, ...axisCommon(theme), splitLine: { show: false } },
201
+ yAxis: {
202
+ type: "category",
203
+ data: labels,
204
+ ...axisCommon(theme),
205
+ // ECharts empile les categories de bas en haut : la premiere valeur se
206
+ // lisait en bas, a l'inverse d'un classement ou d'un tableau.
207
+ inverse: true,
208
+ // Au-dela, le libelle est coupe ; l'infobulle le donne en entier.
209
+ axisLabel: { ...axisCommon(theme).axisLabel, width: 180, overflow: "truncate" },
210
+ splitLine: { show: false }
211
+ },
199
212
  series: [{
200
213
  type: "bar",
201
214
  data: values,
@@ -39,6 +39,12 @@ export default defineAppConfig({
39
39
  * pointe la route qui nourrit le panneau : une app peut la detourner vers
40
40
  * la sienne (ou une autre API) sans toucher au panneau.
41
41
  */
42
+ /**
43
+ * L'app, pour les vues de ressources (cf. utils/oipResources) : une fiche
44
+ * dont c'est l'app d'origine s'ouvre sur place, les autres dans leur app.
45
+ * « france_augmented_science_app », « fr_drugs_app »... Vide : aucune.
46
+ */
47
+ resourceApp: "",
42
48
  /**
43
49
  * Registre des index Elasticsearch (collection Directus ElasticIndices).
44
50
  * `preload` : charge les noms au demarrage, pour les apps qui designent
@@ -0,0 +1,223 @@
1
+ <!--
2
+ Resume d'une publication PubMed : l'onglet « Summary » de la fiche de France
3
+ Augmented Science, son app d'origine.
4
+
5
+ Une seule vue pour toutes les apps : la page /pubmed/[id]/summary de France
6
+ Augmented Science la monte, et le tiroir d'un projet la monte dans
7
+ n'importe quelle app du portail. Elle ne lit ni route ni store : l'id et
8
+ les termes a surligner lui sont donnes.
9
+ -->
10
+ <template>
11
+ <div ref="root" class="oip-pm">
12
+ <RmLoadingState v-if="pending" :lines="3" :chart-height="240" />
13
+ <RmEmptyState
14
+ v-else-if="!doc"
15
+ icon="FileX"
16
+ :title="en ? 'Record unavailable' : 'Fiche indisponible'"
17
+ :text="en ? 'The record is no longer in the data.' : 'La fiche n\'est plus dans la donnée.'"
18
+ />
19
+ <div v-else class="oip-pm__layout">
20
+ <div class="oip-pm__nav">
21
+ <RmChaptersNav
22
+ class="oip-pm__chapters"
23
+ :chapters="chapters"
24
+ :title="en ? 'Chapters' : 'Chapitres'"
25
+ :offset="chaptersOffset"
26
+ :style="{ top: `${stickyTop}px` }"
27
+ @select="scrollToChapter"
28
+ />
29
+ </div>
30
+
31
+ <div class="oip-pm__content">
32
+ <section id="introduction" class="oip-pm__card">
33
+ <div class="oip-pm__head">
34
+ <RmTag v-if="doc.delete" variant="rose">
35
+ {{ en ? "Article has been deleted" : "L'article a été supprimé" }}
36
+ </RmTag>
37
+ <h2 class="oip-pm__title">Introduction</h2>
38
+ <OipResourceSource :uuid="PUBMED_SOURCE_UUID" :lang="lang" />
39
+ </div>
40
+
41
+ <OipResourceField :label="en ? 'Full Title' : 'Titre complet'" :text="orNA(doc.title)" :highlight="highlight" :lang="lang" />
42
+ <OipResourceField
43
+ :label="en ? 'First Published' : 'Première publication'"
44
+ :text="doc.article_date ? longDate(doc.article_date) : 'N/A'"
45
+ :lang="lang"
46
+ />
47
+ <OipResourceTags v-for="group in tagGroups" :key="group.label" v-bind="group" :highlight="highlight" :lang="lang" />
48
+ </section>
49
+
50
+ <section v-for="section in sections" :id="anchor(section.label)" :key="section.label" class="oip-pm__card">
51
+ <h2 class="oip-pm__title">{{ titleCase(section.label) }}</h2>
52
+ <RmTextHighlight class="oip-pm__body" :queries="highlight" :text="section.text" :lang="lang" />
53
+ </section>
54
+
55
+ <section v-if="hasLinks" id="links" class="oip-pm__card">
56
+ <h2 class="oip-pm__title">{{ en ? "Links" : "Liens" }}</h2>
57
+ <div v-if="!doc.delete" class="oip-pm__link">
58
+ <p class="oip-pm__link-label">
59
+ <RmIcon name="Link" :size="12" color="var(--text-muted)" />
60
+ {{ en ? "Pubmed article" : "Article PubMed" }}
61
+ </p>
62
+ <a :href="`https://pubmed.ncbi.nlm.nih.gov/${id}/`" target="_blank" rel="noopener">
63
+ https://pubmed.ncbi.nlm.nih.gov/<b>{{ id }}</b>/
64
+ </a>
65
+ </div>
66
+ <div v-if="doc.doi" class="oip-pm__link">
67
+ <p class="oip-pm__link-label">
68
+ <RmIcon name="Link" :size="12" color="var(--text-muted)" />
69
+ {{ en ? "Complete article" : "Article complet" }}
70
+ </p>
71
+ <a :href="`https://www.doi.org/${doc.doi}`" target="_blank" rel="noopener">
72
+ https://www.doi.org/<b>{{ doc.doi }}</b>
73
+ </a>
74
+ </div>
75
+ </section>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </template>
80
+
81
+ <script setup lang="ts">
82
+ import { oipResourceSpec } from "../utils/oipResources";
83
+ import { oipEsGet } from "../composables/useOipResources";
84
+
85
+ const props = withDefaults(
86
+ defineProps<{
87
+ /** `_id` de la publication (son PMID). */
88
+ id: string;
89
+ /** Termes de la recherche en cours, surlignes dans la fiche. */
90
+ highlight?: string[];
91
+ lang?: "fr" | "en";
92
+ }>(),
93
+ { highlight: () => [], lang: "fr" },
94
+ );
95
+
96
+ /** `source` : l'uuid Directus d'une source citee, pour ouvrir le panneau Sources. */
97
+ const emit = defineEmits<{ source: [uuid: string] }>();
98
+
99
+ /** La fiche Directus « PubMed » (cf. useSources de France Augmented Science). */
100
+ const PUBMED_SOURCE_UUID = "63805e97-48ac-4c04-afd6-8a15055dec97";
101
+
102
+ const en = computed(() => props.lang === "en");
103
+ const root = ref<HTMLElement | null>(null);
104
+ const { chaptersOffset, stickyTop, scrollToChapter } = useOipChapterScroll(root);
105
+
106
+ const spec = oipResourceSpec("PubMed")!;
107
+ const { data: doc, pending } = useAsyncData(
108
+ () => `oip-pubmed-${props.id}`,
109
+ async () => (await oipEsGet(spec.cluster, spec.kind.index, props.id))?._source ?? null,
110
+ { server: false, watch: [() => props.id] },
111
+ );
112
+
113
+ const orNA = (value: unknown) => (value == null || value === "" ? "N/A" : String(value));
114
+ const unique = (values: unknown[]) => [...new Set(values.filter((v): v is string => typeof v === "string" && v !== ""))];
115
+ const titleCase = (value: string) => (value || "").toLowerCase().replace(/(^|\s)\S/g, (c) => c.toUpperCase());
116
+ const anchor = (label: string) => (label || "").toLowerCase();
117
+ const longDate = (value: string) =>
118
+ new Date(value).toLocaleDateString(en.value ? "en-US" : "fr-FR", { year: "numeric", month: "long", day: "numeric" });
119
+
120
+ const tagGroups = computed(() => {
121
+ const d = doc.value ?? {};
122
+ const diseases: any[] = d.diseases ?? [];
123
+ return [
124
+ { icon: "Article", label: en.value ? "Article type" : "Type d'article", values: unique(d.publication_types ?? []) },
125
+ { icon: "BookOpen", label: en.value ? "Journal name" : "Nom de la revue", values: [orNA(d.journal)] },
126
+ { icon: "Molecule", label: en.value ? "Molecules" : "Molécules", values: unique(d.chemical_list ?? []) },
127
+ { icon: "TextAa", label: en.value ? "Keywords" : "Mots-clés", values: unique(d.keywords ?? []) },
128
+ { icon: "Tag", label: en.value ? "Mesh Terms" : "Termes MeSH", values: unique(d.mesh_terms ?? []) },
129
+ {
130
+ icon: "Bandaids",
131
+ label: en.value ? "Rare diseases" : "Maladies rares",
132
+ values: unique((d.rare_diseases ?? []).map((r: any) => r.concat_disease_name_orpha_code)),
133
+ },
134
+ {
135
+ icon: "FaceMask",
136
+ label: "ICD 10",
137
+ values: unique(diseases.flatMap((x) => x.icd10?.map((i: any) => i.combined_label) ?? [])),
138
+ },
139
+ {
140
+ icon: "ListMagnifyingGlass",
141
+ label: en.value ? "Oncology Terms" : "Termes oncologiques",
142
+ values: unique(diseases.flatMap((x) => x.section?.map((s: any) => s.label) ?? [])),
143
+ },
144
+ ];
145
+ });
146
+
147
+ const sections = computed<{ label: string; text: string; order: number }[]>(() =>
148
+ [...(doc.value?.sections ?? [])].sort((a, b) => a?.order - b?.order),
149
+ );
150
+
151
+ const hasLinks = computed(() => !doc.value?.delete || !!doc.value?.doi);
152
+
153
+ const chapters = computed(() => [
154
+ { label: "Introduction", value: "introduction" },
155
+ ...sections.value.map((s) => ({ label: titleCase(s.label), value: anchor(s.label) })),
156
+ ...(hasLinks.value ? [{ label: en.value ? "Links" : "Liens", value: "links" }] : []),
157
+ ]);
158
+ </script>
159
+
160
+ <style scoped>
161
+ .oip-pm__layout {
162
+ display: flex;
163
+ gap: var(--space-16);
164
+ align-items: flex-start;
165
+ }
166
+ .oip-pm__nav {
167
+ flex: 0 0 auto;
168
+ align-self: stretch;
169
+ }
170
+ .oip-pm__chapters {
171
+ position: sticky;
172
+ }
173
+ .oip-pm__content {
174
+ flex: 1 1 auto;
175
+ min-width: 0;
176
+ display: grid;
177
+ gap: var(--space-16);
178
+ }
179
+ .oip-pm__card {
180
+ display: grid;
181
+ gap: var(--space-24);
182
+ padding: var(--space-24);
183
+ border: 1px solid var(--border-default);
184
+ border-radius: 12px;
185
+ background: var(--bg-surface);
186
+ box-shadow: var(--shadow-sm);
187
+ }
188
+ .oip-pm__head {
189
+ display: grid;
190
+ gap: var(--space-4);
191
+ justify-items: start;
192
+ }
193
+ .oip-pm__title {
194
+ margin: 0;
195
+ color: var(--text-primary);
196
+ font-size: var(--font-18);
197
+ font-weight: 800;
198
+ line-height: normal;
199
+ }
200
+ .oip-pm__body {
201
+ color: var(--text-primary);
202
+ font-size: var(--font-14);
203
+ line-height: 160%;
204
+ }
205
+ .oip-pm__link {
206
+ display: grid;
207
+ gap: var(--space-4);
208
+ }
209
+ .oip-pm__link-label {
210
+ display: flex;
211
+ align-items: center;
212
+ gap: var(--space-4);
213
+ margin: 0;
214
+ color: var(--text-primary);
215
+ font-size: var(--font-12);
216
+ }
217
+ .oip-pm__link a {
218
+ color: var(--color-primary);
219
+ font-size: var(--font-14);
220
+ text-decoration: underline;
221
+ word-break: break-all;
222
+ }
223
+ </style>
@@ -0,0 +1,331 @@
1
+ <!--
2
+ Le detail d'une ressource de projet, ouvert sans quitter la page, dans
3
+ n'importe quelle app du portail.
4
+
5
+ - Un type que le layer sait montrer (cf. OIP_RESOURCE_SPECS) s'affiche avec
6
+ la fiche de son app d'origine, ses onglets compris : une publication comme
7
+ dans France Augmented Science, une marque comme dans fr_drugs_app, dans
8
+ l'une comme dans l'autre.
9
+ - Les autres passent par la carte generique, decrite dans le registre des
10
+ types de l'API : un type ajoute la-bas s'affiche ici sans deploiement.
11
+
12
+ Sous le titre, un lien mene a la fiche dans son app d'origine. Dans l'app
13
+ d'origine elle-meme, il navigue sur place ; ailleurs, il ouvre l'autre app
14
+ dans un onglet.
15
+
16
+ Une fiche ouverte depuis la fiche du tiroir (l'auteur d'une publication, la
17
+ marque d'une contribution...) s'empile dans un tiroir de plus ; celui de
18
+ dessous reste ou il etait. Un lien vers une page qui n'est pas une fiche
19
+ quitte le tiroir.
20
+ -->
21
+ <template>
22
+ <RmDrawer
23
+ v-for="(entry, index) in entries"
24
+ :key="`${index}-${entry.type ?? entry.fiche?.view}-${entry.id ?? entry.fiche?.path}`"
25
+ :model-value="index === 0 ? open : true"
26
+ :size="index === 0 ? size : `min(${1180 - index * 40}px, calc(100vw - ${96 + index * 24}px))`"
27
+ :z-index="index === 0 ? undefined : 100 + index * 2"
28
+ :label="entry.title || entry.id || ''"
29
+ @update:model-value="(value: boolean) => !value && close(index)"
30
+ >
31
+ <!-- Carte generique : l'en-tete du tiroir porte le titre et le lien. -->
32
+ <template v-if="!isPage(entry)" #header>
33
+ <div class="oip-rd__head">
34
+ <p class="oip-rd__title">{{ entry.title || entry.id }}</p>
35
+ <OipResourceDrawerLink :origin="originOf(entry)" :lang="lang" @leave="close(0)" />
36
+ </div>
37
+ </template>
38
+
39
+ <div v-if="index > 0 || open" class="oip-rd__body" :class="{ 'oip-rd__body--page': isPage(entry) }">
40
+ <!-- Une fiche porte son bandeau (titre, lien dessous, onglets) : l'en-tete
41
+ du tiroir s'efface, sa croix se pose au bout du bandeau. -->
42
+ <RmButton
43
+ v-if="isPage(entry)"
44
+ class="oip-rd__close"
45
+ variant="tertiary"
46
+ size="md"
47
+ icon-only
48
+ ghost
49
+ prefix-icon="X"
50
+ :aria-label="en ? 'Close' : 'Fermer'"
51
+ @click="close(index)"
52
+ />
53
+ <!-- Une fiche visee par un lien, montee par sa vue. -->
54
+ <div v-if="entry.fiche && !entry.fiche.view.startsWith('resource:')" class="oip-rv oip-rv--whole">
55
+ <component :is="oipResourceViewComponent(entry.fiche.view)" v-bind="entry.fiche.props" :lang="lang">
56
+ <template #title-link>
57
+ <OipResourceDrawerLink :origin="originOf(entry)" :lang="lang" @leave="close(0)" />
58
+ </template>
59
+ </component>
60
+ </div>
61
+ <OipResourceView
62
+ v-else-if="entry.type && oipResourceViewSpec(entry.type)"
63
+ :type="entry.type"
64
+ :id="entry.id ?? ''"
65
+ :title="entry.title"
66
+ :tab="entry.tab"
67
+ :highlight="index === 0 ? highlight : []"
68
+ :lang="lang"
69
+ @source="emit('source', $event)"
70
+ >
71
+ <template v-if="isPage(entry)" #title-link>
72
+ <OipResourceDrawerLink :origin="originOf(entry)" :lang="lang" @leave="close(0)" />
73
+ </template>
74
+ </OipResourceView>
75
+ <OipResourceGenericCard
76
+ v-else
77
+ :type="entry.type ?? ''"
78
+ :id="entry.id ?? ''"
79
+ :title="entry.title"
80
+ :source="source"
81
+ :lang="lang"
82
+ />
83
+ </div>
84
+ </RmDrawer>
85
+ </template>
86
+
87
+ <script setup lang="ts">
88
+ import {
89
+ OIP_RESOURCE_APPS,
90
+ oipFillTemplate,
91
+ oipResourceAppUrl,
92
+ oipResourceViewSpec,
93
+ type OipResourceApp,
94
+ } from "../utils/oipResources";
95
+ import { oipFicheOf, type OipFicheTarget } from "../utils/oipFicheRoutes";
96
+ import { oipHydrate, useOipResourceTypes } from "../composables/useOipResources";
97
+ import { OIP_RESOURCE_NAV, useOipResourceApp } from "../composables/useOipResourceNav";
98
+ import { oipResourceViewComponent } from "../composables/useOipResourceViews";
99
+
100
+ const props = withDefaults(
101
+ defineProps<{
102
+ modelValue: boolean;
103
+ /** `resourceType` de l'API. */
104
+ type: string;
105
+ /** `resourceId` de l'API : l'`_id` du document. */
106
+ id: string;
107
+ title?: string;
108
+ /** L'app qui ouvre le tiroir. Absente : `oip.resourceApp` de l'app.config. */
109
+ app?: OipResourceApp | "";
110
+ /** Le document deja relu par l'app, pour la carte generique. */
111
+ source?: Record<string, unknown> | null;
112
+ highlight?: string[];
113
+ lang?: "fr" | "en";
114
+ size?: string;
115
+ }>(),
116
+ {
117
+ title: "",
118
+ app: "",
119
+ source: null,
120
+ highlight: () => [],
121
+ lang: "fr",
122
+ size: "min(1280px, calc(100vw - 48px))",
123
+ },
124
+ );
125
+
126
+ const emit = defineEmits<{
127
+ "update:modelValue": [value: boolean];
128
+ /** Une source citee dans la fiche : l'app ouvre son panneau Sources. */
129
+ source: [uuid: string];
130
+ }>();
131
+
132
+ const open = computed({
133
+ get: () => props.modelValue,
134
+ set: (value: boolean) => emit("update:modelValue", value),
135
+ });
136
+
137
+ const en = computed(() => props.lang === "en");
138
+ const configuredApp = useOipResourceApp();
139
+ const app = computed(() => props.app || configuredApp);
140
+ const localePath = useLocalePath();
141
+
142
+ /**
143
+ * Une fiche ouverte : une ressource (`type`, `id`, l'onglet et le document
144
+ * relu pour son lien) ou une fiche visee par un lien (`fiche`).
145
+ */
146
+ interface Entry {
147
+ type?: string;
148
+ id?: string;
149
+ tab?: string;
150
+ title: string;
151
+ doc?: Record<string, any> | null;
152
+ fiche?: OipFicheTarget & { app: OipResourceApp; path: string };
153
+ }
154
+
155
+ /** Les fiches ouvertes au-dessus de celle du projet, de bas en haut. */
156
+ const stacked = ref<Entry[]>([]);
157
+ /** Le document de la ressource du projet : certaines fiches s'adressent par un de ses champs. */
158
+ const rootDoc = ref<Record<string, any> | null>(null);
159
+ const entries = computed<Entry[]>(() => [
160
+ { type: props.type, id: props.id, title: props.title, doc: rootDoc.value },
161
+ ...stacked.value,
162
+ ]);
163
+
164
+ watch(
165
+ () => [props.modelValue, props.type, props.id] as const,
166
+ async ([isOpen, type, id]) => {
167
+ rootDoc.value = null;
168
+ const spec = oipResourceViewSpec(type);
169
+ if (!isOpen || !spec) return;
170
+ rootDoc.value = (await oipHydrate(spec.type, [id]).catch(() => ({})))[id] ?? null;
171
+ },
172
+ { immediate: true },
173
+ );
174
+
175
+ /** Fermer un tiroir ferme aussi ceux qui sont au-dessus de lui. */
176
+ function close(index: number) {
177
+ if (index === 0) open.value = false;
178
+ stacked.value = stacked.value.slice(0, Math.max(0, index - 1));
179
+ }
180
+
181
+ watch(open, (isOpen) => {
182
+ if (!isOpen) stacked.value = [];
183
+ });
184
+
185
+ /** Empile une ressource, titree par la description de son type des que son document arrive. */
186
+ async function pushResource(type: string, id: string, tab?: string) {
187
+ const spec = oipResourceViewSpec(type);
188
+ if (!spec) return;
189
+ const entry = reactive<Entry>({ type, id, tab, title: "", doc: null });
190
+ stacked.value = [...stacked.value, entry];
191
+ const doc = (await oipHydrate(spec.type, [id]).catch(() => ({})))[id];
192
+ if (doc) {
193
+ entry.doc = doc;
194
+ entry.title = spec.kind.title(doc, props.lang);
195
+ }
196
+ }
197
+
198
+ const appHref = (target: OipResourceApp, path: string) =>
199
+ target === app.value ? localePath(path) : oipResourceAppUrl(target, path, props.lang);
200
+
201
+ // Les liens des fiches du tiroir empilent un tiroir de plus ; un lien vers une
202
+ // page qui n'est pas une fiche quitte le tiroir (sur place dans l'app, dans un
203
+ // onglet ailleurs).
204
+ provide(OIP_RESOURCE_NAV, {
205
+ href: (type, id, tab) => urlOf(type, id, tab)?.url ?? "",
206
+ open: (type, id, tab) => pushResource(type, id, tab),
207
+ appHref,
208
+ openApp(target, path) {
209
+ const fiche = oipFicheOf(target, path);
210
+ if (fiche?.view.startsWith("resource:")) {
211
+ pushResource(fiche.props.type!, fiche.props.id!, fiche.props.tab || undefined);
212
+ return;
213
+ }
214
+ if (fiche) {
215
+ stacked.value = [...stacked.value, { title: fiche.title, fiche: { ...fiche, app: target, path } }];
216
+ return;
217
+ }
218
+ const url = appHref(target, path);
219
+ if (target === app.value) {
220
+ close(0);
221
+ navigateTo(url);
222
+ } else window.open(url, "_blank", "noopener");
223
+ },
224
+ });
225
+
226
+ /** L'URL de la fiche : locale dans son app d'origine, absolue ailleurs. */
227
+ function urlOf(type: string, id: string, tab?: string, doc?: Record<string, any> | null) {
228
+ const spec = oipResourceViewSpec(type);
229
+ if (!spec) return null;
230
+ const path = spec.detailPath(id, tab, doc);
231
+ if (!path) return null;
232
+ if (spec.app === app.value) return { local: true, url: localePath(path), name: "" };
233
+ return { local: false, url: oipResourceAppUrl(spec.app, path, props.lang), name: OIP_RESOURCE_APPS[spec.app].name };
234
+ }
235
+
236
+ /**
237
+ * Les vues sans bandeau a elles (une carte de resultat) : le tiroir garde son
238
+ * en-tete, titre et lien.
239
+ */
240
+ const CARD_VIEWS = new Set(["drugs-national-assembly", "science-phrc"]);
241
+
242
+ /** Une fiche du layer : elle porte son bandeau, le tiroir efface le sien. */
243
+ function isPage(entry: Entry) {
244
+ if (entry.fiche) return true;
245
+ const spec = entry.type ? oipResourceViewSpec(entry.type) : undefined;
246
+ return !!spec && !CARD_VIEWS.has(spec.view ?? "");
247
+ }
248
+
249
+ function originOf(entry: Entry) {
250
+ if (entry.fiche) {
251
+ const local = entry.fiche.app === app.value;
252
+ return { local, url: appHref(entry.fiche.app, entry.fiche.path), name: OIP_RESOURCE_APPS[entry.fiche.app].name };
253
+ }
254
+ if (entry.type && oipResourceViewSpec(entry.type)) return urlOf(entry.type, entry.id ?? "", entry.tab, entry.doc);
255
+ return registryUrl.value ? { local: false, url: registryUrl.value, name: registryName.value } : null;
256
+ }
257
+
258
+ // Un type sans vue : son lien vient du registre de l'API.
259
+ const registryUrl = ref("");
260
+ const registryName = ref("");
261
+ watch(
262
+ () => [props.modelValue, props.type, props.id] as const,
263
+ async ([isOpen, type, id]) => {
264
+ registryUrl.value = "";
265
+ registryName.value = "";
266
+ if (!isOpen || oipResourceViewSpec(type)) return;
267
+ const entry = (await useOipResourceTypes()).find((e) => e.key === type);
268
+ registryUrl.value = entry?.display?.detailUrl ? oipFillTemplate(entry.display.detailUrl, props.source ?? {}, id) : "";
269
+ const owner = entry?.ownerApp ?? "";
270
+ registryName.value = OIP_RESOURCE_APPS[owner as OipResourceApp]?.name ?? owner;
271
+ },
272
+ { immediate: true },
273
+ );
274
+ </script>
275
+
276
+ <style scoped>
277
+ .oip-rd__head {
278
+ display: grid;
279
+ gap: var(--space-4);
280
+ min-width: 0;
281
+ }
282
+ .oip-rd__title {
283
+ margin: 0;
284
+ color: var(--text-primary);
285
+ font-size: var(--font-18);
286
+ font-weight: 700;
287
+ line-height: 1.3;
288
+ }
289
+ .oip-rd__body {
290
+ padding: var(--space-16) 0;
291
+ }
292
+ .oip-rd__body.oip-rd__body--page {
293
+ padding: 0 0 var(--space-32);
294
+ }
295
+ .oip-rd__close {
296
+ position: absolute;
297
+ top: var(--space-16);
298
+ right: var(--space-24);
299
+ z-index: 96;
300
+ }
301
+ /* La place de la croix, au bout de la rangee titre + actions du bandeau. */
302
+ .oip-rd__body--page :deep(.rm-page-header__row) {
303
+ padding-right: 72px;
304
+ }
305
+ /* La fleche de retour des pages remonterait l'historique de l'ecran dessous. */
306
+ .oip-rd__body--page :deep(.rm-page-header__back) {
307
+ display: none;
308
+ }
309
+ /* Le contenu d'une fiche d'un seul tenant recoit les gouttieres de la colonne
310
+ de contenu ; son bandeau reste bord a bord. On vise l'element qui porte le
311
+ bandeau, a quelque profondeur qu'il soit : une vue de ressource enveloppe
312
+ souvent sa fiche, et la marge posee sur l'enveloppe decalerait le bandeau. */
313
+ .oip-rd__body--page :deep(.oip-rv--whole :has(> .rm-page-header) > :not(.rm-page-header)) {
314
+ box-sizing: border-box;
315
+ padding-inline: var(--space-24);
316
+ }
317
+ .oip-rd__body--page :deep(.oip-rv--whole :has(> .rm-page-header) > .rm-page-header + *) {
318
+ margin-top: var(--space-24);
319
+ }
320
+ </style>
321
+
322
+ <style>
323
+ /* Hors portee : l'en-tete et le corps appartiennent au tiroir (teleporte).
324
+ Sans marge interne, le bandeau colle de la fiche tient le haut du tiroir. */
325
+ .rm-drawer:has(> .rm-drawer__body .oip-rd__body--page) > .rm-drawer__header {
326
+ display: none;
327
+ }
328
+ .rm-drawer:has(> .rm-drawer__body .oip-rd__body--page) > .rm-drawer__body {
329
+ padding: 0;
330
+ }
331
+ </style>