@icij/murmur-next 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/.github/workflows/deploy-github-pages.yaml +50 -0
  2. package/.storybook/app.scss +14 -0
  3. package/.storybook/doc_variables.scss +20 -0
  4. package/.storybook/main.ts +35 -0
  5. package/.storybook/preview-head.html +2 -0
  6. package/.storybook/preview.ts +32 -0
  7. package/README.md +71 -0
  8. package/deploy.js +15 -0
  9. package/docs/components/ApiTable.vue +171 -0
  10. package/docs/components/App.vue +146 -0
  11. package/docs/components/CollapsibleBlock.vue +122 -0
  12. package/docs/components/DocsHeader.vue +68 -0
  13. package/docs/components/DocsMenu.vue +201 -0
  14. package/docs/components/DocsMenuSection.vue +109 -0
  15. package/docs/components/EditLink.vue +49 -0
  16. package/docs/components/OutboundLink.vue +13 -0
  17. package/docs/components/PalettePresenter.vue +96 -0
  18. package/docs/components/RepositoryLink.vue +28 -0
  19. package/docs/components/SampleCard.vue +119 -0
  20. package/docs/main.js +42 -0
  21. package/docs/pages/components/accordion/doc.md +96 -0
  22. package/docs/pages/components/active-text-truncate/doc.md +44 -0
  23. package/docs/pages/components/advanced-link-form/doc.md +105 -0
  24. package/docs/pages/components/brand/doc.md +30 -0
  25. package/docs/pages/components/brand-expansion/doc.md +70 -0
  26. package/docs/pages/components/confirm-button/doc.md +91 -0
  27. package/docs/pages/components/content-placeholder/doc.md +16 -0
  28. package/docs/pages/components/custom-pagination/doc.md +61 -0
  29. package/docs/pages/components/digits-input/doc.md +28 -0
  30. package/docs/pages/components/donate-form/doc.md +20 -0
  31. package/docs/pages/components/embed-form/doc.md +22 -0
  32. package/docs/pages/components/embeddable-footer/doc.md +60 -0
  33. package/docs/pages/components/follow-us-popover/doc.md +5 -0
  34. package/docs/pages/components/generic-footer/doc.md +21 -0
  35. package/docs/pages/components/generic-header/doc.md +24 -0
  36. package/docs/pages/components/haptic-copy/doc.md +27 -0
  37. package/docs/pages/components/imddb-header/doc.md +23 -0
  38. package/docs/pages/components/ordinal-legend/doc.md +44 -0
  39. package/docs/pages/components/range-picker/doc.md +86 -0
  40. package/docs/pages/components/responsive-iframe/doc.md +13 -0
  41. package/docs/pages/components/scale-legend/doc.md +65 -0
  42. package/docs/pages/components/secret-input/doc.md +12 -0
  43. package/docs/pages/components/selectable-dropdown/doc.md +156 -0
  44. package/docs/pages/components/sharing-options/doc.md +13 -0
  45. package/docs/pages/components/sharing-options-link/doc.md +36 -0
  46. package/docs/pages/components/sign-up-form/doc.md +13 -0
  47. package/docs/pages/components/slide-up-down/doc.md +28 -0
  48. package/docs/pages/components/textured-deck/doc.md +78 -0
  49. package/docs/pages/components/tiny-pagination/doc.md +92 -0
  50. package/docs/pages/datavisualisation/bars/doc.md +110 -0
  51. package/docs/pages/datavisualisation/columns/doc.md +165 -0
  52. package/docs/pages/datavisualisation/lines/doc.md +139 -0
  53. package/docs/pages/datavisualisation/stacked-bar/doc.md +160 -0
  54. package/docs/pages/datavisualisation/stacked-column/doc.md +191 -0
  55. package/docs/pages/getting-started/about-icij/doc.md +13 -0
  56. package/docs/pages/getting-started/custom-bootstrap/doc.md +36 -0
  57. package/docs/pages/getting-started/installation-guide/doc.md +59 -0
  58. package/docs/pages/getting-started/internationalization/doc.md +74 -0
  59. package/docs/pages/maps/choropleth-map/doc.md +420 -0
  60. package/docs/pages/maps/choropleth-map-annotation/doc.md +373 -0
  61. package/docs/pages/maps/symbol-map/doc.md +203 -0
  62. package/docs/pages/structure/breakpoints/doc.md +3 -0
  63. package/docs/pages/structure/grid/doc.md +3 -0
  64. package/docs/pages/utilities/assets/doc.md +138 -0
  65. package/docs/pages/utilities/config/doc.md +52 -0
  66. package/docs/pages/utilities/iframes/doc.md +3 -0
  67. package/docs/pages/visual/colors/doc.md +31 -0
  68. package/docs/pages/visual/iconography/doc.md +56 -0
  69. package/docs/pages/visual/states/doc.md +77 -0
  70. package/docs/pages/visual/themes/doc.md +3 -0
  71. package/docs/pages/visual/typography/doc.md +71 -0
  72. package/docs/routes.js +25 -0
  73. package/docs/store/index.js +21 -0
  74. package/docs/styles/app.scss +36 -0
  75. package/docs/styles/variables.scss +20 -0
  76. package/lib/assets/images/icij-full-white.svg +6 -0
  77. package/lib/assets/images/icij-full.svg +6 -0
  78. package/lib/assets/images/icij.png +0 -0
  79. package/lib/assets/images/icij.svg +46 -0
  80. package/lib/assets/images/icij@2x.png +0 -0
  81. package/lib/assets/images/murmur-dark.png +0 -0
  82. package/lib/assets/images/murmur-dark.svg +79 -0
  83. package/lib/assets/images/murmur-white.png +0 -0
  84. package/lib/assets/images/murmur-white.svg +68 -0
  85. package/lib/components/AccordionStep.vue +128 -0
  86. package/lib/components/AccordionWrapper.vue +138 -0
  87. package/lib/components/ActiveTextTruncate.vue +258 -0
  88. package/lib/components/AdvancedLinkForm.vue +273 -0
  89. package/lib/components/Brand.vue +150 -0
  90. package/lib/components/BrandExpansion.vue +237 -0
  91. package/lib/components/ConfirmButton.vue +204 -0
  92. package/lib/components/ContentPlaceholder.vue +100 -0
  93. package/lib/components/CustomPagination.vue +225 -0
  94. package/lib/components/DigitsInput.vue +180 -0
  95. package/lib/components/DonateForm.vue +367 -0
  96. package/lib/components/EmbedForm.vue +173 -0
  97. package/lib/components/EmbeddableFooter.vue +201 -0
  98. package/lib/components/Fa.js +3 -0
  99. package/lib/components/FollowUsPopover.vue +117 -0
  100. package/lib/components/GenericFooter.vue +218 -0
  101. package/lib/components/GenericHeader.vue +259 -0
  102. package/lib/components/HapticCopy.vue +256 -0
  103. package/lib/components/ImddbHeader.vue +336 -0
  104. package/lib/components/OrdinalLegend.vue +164 -0
  105. package/lib/components/RangePicker.vue +430 -0
  106. package/lib/components/ResponsiveIframe.vue +48 -0
  107. package/lib/components/ScaleLegend.vue +230 -0
  108. package/lib/components/SecretInput.vue +132 -0
  109. package/lib/components/SelectableDropdown.vue +368 -0
  110. package/lib/components/SharingOptions.vue +230 -0
  111. package/lib/components/SharingOptionsLink.vue +259 -0
  112. package/lib/components/SignUpForm.vue +181 -0
  113. package/lib/components/SlideUpDown.vue +131 -0
  114. package/lib/components/TexturedDeck.vue +101 -0
  115. package/lib/components/TinyPagination.vue +268 -0
  116. package/lib/components/index.js +31 -0
  117. package/lib/composables/chart.ts +182 -0
  118. package/lib/composables/resizeObserver.ts +37 -0
  119. package/lib/composables/sendEmail.ts +50 -0
  120. package/lib/config.default.ts +33 -0
  121. package/lib/config.ts +70 -0
  122. package/lib/d3-geo-projection.d.ts +1 -0
  123. package/lib/datavisualisations/BarChart.vue +275 -0
  124. package/lib/datavisualisations/ColumnChart.vue +527 -0
  125. package/lib/datavisualisations/LineChart.vue +274 -0
  126. package/lib/datavisualisations/StackedBarChart.vue +614 -0
  127. package/lib/datavisualisations/StackedColumnChart.vue +640 -0
  128. package/lib/datavisualisations/index.js +5 -0
  129. package/lib/enums.ts +25 -0
  130. package/lib/i18n.ts +16 -0
  131. package/lib/keys.ts +2 -0
  132. package/lib/locales/en.json +140 -0
  133. package/lib/locales/fr.json +117 -0
  134. package/lib/locales/locales/en.json +140 -0
  135. package/lib/locales/locales/fr.json +117 -0
  136. package/lib/main.ts +87 -0
  137. package/lib/maps/ChoroplethMap.vue +825 -0
  138. package/lib/maps/ChoroplethMapAnnotation.vue +336 -0
  139. package/lib/maps/SymbolMap.vue +628 -0
  140. package/lib/maps/index.js +3 -0
  141. package/lib/querystring-es3.d.ts +1 -0
  142. package/lib/shims-bootstrap-vue.d.ts +5 -0
  143. package/lib/shims-tsx.d.ts +11 -0
  144. package/lib/shims-vue.d.ts +14 -0
  145. package/lib/styles/functions.scss +20 -0
  146. package/lib/styles/lib.scss +19 -0
  147. package/lib/styles/mixins.scss +37 -0
  148. package/lib/styles/utilities.scss +18 -0
  149. package/lib/styles/variables.scss +94 -0
  150. package/lib/styles/variables_dark.scss +1 -0
  151. package/lib/types.ts +46 -0
  152. package/lib/utils/animation.ts +24 -0
  153. package/lib/utils/assets.ts +46 -0
  154. package/lib/utils/clipboard.ts +41 -0
  155. package/lib/utils/iframe-resizer.ts +49 -0
  156. package/lib/utils/placeholder.ts +66 -0
  157. package/lib/utils/placeholderTypes.ts +21 -0
  158. package/lib/utils/strings.ts +8 -0
  159. package/loaders/highlight-loader.js +13 -0
  160. package/loaders/markdown-loader.js +91 -0
  161. package/loaders/metadata-loader.js +18 -0
  162. package/loaders/sass-extract-loader.js +14 -0
  163. package/loaders/vue-docgen-loader.js +14 -0
  164. package/package.json +96 -0
  165. package/plugins/MdPluginTypes.ts +10 -0
  166. package/plugins/docs.ts +50 -0
  167. package/plugins/front-matter.ts +36 -0
  168. package/plugins/highlight.ts +27 -0
  169. package/plugins/markdown-it/api-table.ts +25 -0
  170. package/plugins/markdown-it/sample-card.ts +31 -0
  171. package/plugins/plugin-delete.ts +47 -0
  172. package/plugins/plugin-docgen.ts +23 -0
  173. package/plugins/sass-vars.ts +25 -0
  174. package/plugins/vue-docgen.ts +29 -0
  175. package/public/android-chrome-192x192.png +0 -0
  176. package/public/android-chrome-512x512.png +0 -0
  177. package/public/apple-touch-icon.png +0 -0
  178. package/public/assets/img/arrow-bottom.svg +3 -0
  179. package/public/assets/img/texture-brick-black.jpg +0 -0
  180. package/public/assets/img/texture-brick.jpg +0 -0
  181. package/public/assets/img/texture-carbon-black.jpg +0 -0
  182. package/public/assets/img/texture-carbon.jpg +0 -0
  183. package/public/assets/img/texture-crack-black.jpg +0 -0
  184. package/public/assets/img/texture-crack.jpg +0 -0
  185. package/public/assets/img/texture-rock-black.jpg +0 -0
  186. package/public/assets/img/texture-rock.jpg +0 -0
  187. package/public/assets/img/texture-sand-black.jpg +0 -0
  188. package/public/assets/img/texture-sand.jpg +0 -0
  189. package/public/assets/img/texture-silk-black.jpg +0 -0
  190. package/public/assets/img/texture-silk.jpg +0 -0
  191. package/public/assets/topojson/france-departments.json +1 -0
  192. package/public/assets/topojson/paris-arrondissements.json +1 -0
  193. package/public/assets/topojson/world-countries-sans-antarctica.json +1 -0
  194. package/public/favicon-16x16.png +0 -0
  195. package/public/favicon-32x32.png +0 -0
  196. package/public/favicon.ico +0 -0
  197. package/public/site.webmanifest +1 -0
  198. package/stories/assets/code-brackets.svg +1 -0
  199. package/stories/assets/colors.svg +1 -0
  200. package/stories/assets/comments.svg +1 -0
  201. package/stories/assets/direction.svg +1 -0
  202. package/stories/assets/flow.svg +1 -0
  203. package/stories/assets/plugin.svg +1 -0
  204. package/stories/assets/repo.svg +1 -0
  205. package/stories/assets/stackalt.svg +1 -0
  206. package/stories/getting-started/about-icij.mdx +14 -0
  207. package/stories/getting-started/custom-bootstrap.mdx +23 -0
  208. package/stories/getting-started/installation-guide.mdx +62 -0
  209. package/stories/getting-started/internationalization.mdx +63 -0
  210. package/stories/murmur/components/AccordionStep.stories.ts +33 -0
  211. package/stories/murmur/components/AccordionWrapper.stories.ts +69 -0
  212. package/stories/murmur/components/ActiveTextTruncate.stories.ts +32 -0
  213. package/stories/murmur/components/AdvancedLinkForm.stories.ts +77 -0
  214. package/stories/murmur/components/Brand.stories.ts +30 -0
  215. package/stories/murmur/components/BrandExpansion.stories.ts +41 -0
  216. package/stories/murmur/components/ConfirmButton.stories.ts +40 -0
  217. package/stories/murmur/components/ContentPlaceholder.stories.ts +41 -0
  218. package/stories/murmur/components/CustomPagination.stories.ts +42 -0
  219. package/stories/murmur/components/DigitsInput.stories.ts +29 -0
  220. package/stories/murmur/components/DonateForm.stories.ts +29 -0
  221. package/stories/murmur/components/EmbedForm.stories.ts +35 -0
  222. package/stories/murmur/components/EmbeddableFooter.stories.ts +59 -0
  223. package/stories/murmur/components/FollowUsPopover.stories.ts +24 -0
  224. package/stories/murmur/components/GenericFooter.stories.ts +27 -0
  225. package/stories/murmur/components/GenericHeader.stories.ts +27 -0
  226. package/stories/murmur/components/HapticCopy.stories.ts +40 -0
  227. package/stories/murmur/components/ImddbHeader.stories.ts +27 -0
  228. package/stories/murmur/components/OrdinalLegend.stories.ts +49 -0
  229. package/stories/murmur/components/RangePicker.stories.ts +98 -0
  230. package/stories/murmur/components/ResponsiveIframe.stories.ts +24 -0
  231. package/stories/murmur/components/ScaleLegend.stories.ts +65 -0
  232. package/stories/murmur/components/SecretInput.stories.ts +60 -0
  233. package/stories/murmur/components/SelectableDropdown.stories.ts +143 -0
  234. package/stories/murmur/components/SharingOptions.stories.ts +32 -0
  235. package/stories/murmur/components/SharingOptionsLink.stories.ts +53 -0
  236. package/stories/murmur/components/SignUpForm.stories.ts +51 -0
  237. package/stories/murmur/components/SlideUpDown.stories.ts +32 -0
  238. package/stories/murmur/components/TexturedDeck.stories.ts +83 -0
  239. package/stories/murmur/components/TinyPagination.stories.ts +65 -0
  240. package/stories/murmur/datavisualisations/BarChart.stories.ts +54 -0
  241. package/stories/murmur/datavisualisations/ColumnChart.stories.ts +88 -0
  242. package/stories/murmur/datavisualisations/LineChart.stories.ts +139 -0
  243. package/stories/murmur/datavisualisations/StackedBarChart.stories.ts +199 -0
  244. package/stories/murmur/datavisualisations/StackedColumnChart.stories.ts +136 -0
  245. package/stories/murmur/decorators.ts +108 -0
  246. package/stories/murmur/maps/ChoroplethMap.stories.ts +440 -0
  247. package/stories/murmur/maps/ChoroplethMapAnnotation.stories.ts +26 -0
  248. package/stories/murmur/maps/SymbolMap.stories.ts +24 -0
  249. package/stories/murmur/utils.ts +7 -0
  250. package/tests/unit/components/AccordionStep.spec.ts +157 -0
  251. package/tests/unit/components/AccordionWrapper.spec.ts +57 -0
  252. package/tests/unit/components/ActiveTextTruncate.spec.js +30 -0
  253. package/tests/unit/components/AdvancedLinkForm.spec.js +124 -0
  254. package/tests/unit/components/Brand.spec.js +50 -0
  255. package/tests/unit/components/ContentPlaceholder.spec.js +29 -0
  256. package/tests/unit/components/CustomPagination.spec.js +72 -0
  257. package/tests/unit/components/DigitsInput.spec.ts +157 -0
  258. package/tests/unit/components/DonateForm.spec.js +149 -0
  259. package/tests/unit/components/EmbedForm.spec.js +108 -0
  260. package/tests/unit/components/EmbeddableFooter.spec.js +11 -0
  261. package/tests/unit/components/Fa.spec.js +18 -0
  262. package/tests/unit/components/FollowUsPopover.spec.js +29 -0
  263. package/tests/unit/components/GenericFooter.spec.js +29 -0
  264. package/tests/unit/components/GenericHeader.spec.js +104 -0
  265. package/tests/unit/components/HapticCopy.spec.js +123 -0
  266. package/tests/unit/components/ImddbHeader.spec.js +96 -0
  267. package/tests/unit/components/OrdinalLegend.spec.js +120 -0
  268. package/tests/unit/components/RangePicker.spec.ts +87 -0
  269. package/tests/unit/components/ResponsiveIframe.spec.js +20 -0
  270. package/tests/unit/components/ScaleLegend.spec.js +139 -0
  271. package/tests/unit/components/SecretInput.spec.js +81 -0
  272. package/tests/unit/components/SelectableDropdown.spec.js +160 -0
  273. package/tests/unit/components/SharingOptions.spec.js +125 -0
  274. package/tests/unit/components/SharingOptionsLink.spec.js +184 -0
  275. package/tests/unit/components/SignUpForm.spec.js +145 -0
  276. package/tests/unit/components/SlideUpDown.spec.js +59 -0
  277. package/tests/unit/components/TinyPagination.spec.js +46 -0
  278. package/tests/unit/config.spec.js +136 -0
  279. package/tests/unit/datavisualisations/BarChart.spec.js +63 -0
  280. package/tests/unit/datavisualisations/ColumnChart.spec.js +344 -0
  281. package/tests/unit/datavisualisations/LineChart.spec.js +155 -0
  282. package/tests/unit/datavisualisations/StackedBarChart.spec.js +294 -0
  283. package/tests/unit/datavisualisations/StackedColumnChart.spec.js +443 -0
  284. package/tests/unit/i18n.spec.ts +19 -0
  285. package/tests/unit/main.spec.js +82 -0
  286. package/tests/unit/maps/ChoroplethMap.spec.js +214 -0
  287. package/tests/unit/maps/ChoroplethMapAnnotation.spec.ts +186 -0
  288. package/tests/unit/maps/SymbolMap.spec.js +92 -0
  289. package/tests/unit/require.spec.js +22 -0
  290. package/tests/unit/setup.js +13 -0
  291. package/tests/unit/utils/assets.spec.js +61 -0
  292. package/tests/unit/utils/clipboard.spec.js +18 -0
  293. package/tests/unit/utils/iframe-resizer.spec.js +71 -0
  294. package/tsconfig.json +35 -0
  295. package/vite.config.ts +79 -0
  296. package/vitest.config.ts +19 -0
@@ -0,0 +1,628 @@
1
+ <script lang="ts">
2
+ import * as d3 from 'd3'
3
+ import {GeoPermissibleObjects} from 'd3'
4
+ import {geoRobinson} from 'd3-geo-projection'
5
+ import {feature} from 'topojson'
6
+ import {debounce, find, get, groupBy, isFunction, kebabCase, keys, pickBy, set, uniq, uniqueId} from 'lodash'
7
+
8
+ import config from '../config'
9
+ import OrdinalLegend from '../components/OrdinalLegend.vue'
10
+ import {chartEmits, chartProps, getChartProps, useChart} from "@/composables/chart.js";
11
+ import {ComponentPublicInstance, computed, defineComponent, PropType, ref, watch} from "vue";
12
+ import {GeometryCollection} from "topojson-specification"
13
+
14
+ export default defineComponent({
15
+ components: {
16
+ OrdinalLegend
17
+ },
18
+ name: 'SymbolMap',
19
+ props: {
20
+ categoryObjectsPath: {
21
+ type: [String, Array],
22
+ default: 'category'
23
+ },
24
+ clickable: {
25
+ type: Boolean
26
+ },
27
+ hideLegend: {
28
+ type: Boolean
29
+ },
30
+ hideTooltip: {
31
+ type: Boolean
32
+ },
33
+ horizontalLegend: {
34
+ type: Boolean
35
+ },
36
+ featureColor: {
37
+ type: [String, Function],
38
+ default: 'currentColor'
39
+ },
40
+ fitToMarkers: {
41
+ type: Boolean
42
+ },
43
+ labelObjectsPath: {
44
+ type: [String, Array],
45
+ default: 'label'
46
+ },
47
+ mapPadding: {
48
+ type: Number,
49
+ default: 15
50
+ },
51
+ markerObjectsPath: {
52
+ type: [String, Array],
53
+ default: 'id'
54
+ },
55
+ markerPath: {
56
+ type: [String, Function],
57
+ default:
58
+ 'M512 256C512 397.4 397.4 512 256 512C114.6 512 0 397.4 0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256z'
59
+ },
60
+ markerColor: {
61
+ type: String,
62
+ default: null
63
+ },
64
+ markerWidth: {
65
+ type: [Number, Function],
66
+ default: 10
67
+ },
68
+ noMarkersScale: {
69
+ type: Boolean
70
+ },
71
+ tooltipCustomClass: {
72
+ type: String,
73
+ default: null
74
+ },
75
+ tooltipPlacement: {
76
+ type: String,
77
+ default: 'top'
78
+ },
79
+ tooltipFallbackPlacement: {
80
+ type: [Array, String],
81
+ default: 'flip'
82
+ },
83
+ topojsonObjects: {
84
+ type: String,
85
+ default: 'countries1'
86
+ },
87
+ topojsonObjectsPath: {
88
+ type: [String, Array] as PropType<string | string[]>,
89
+ default: 'id'
90
+ },
91
+ topojsonUrl: {
92
+ type: String,
93
+ default: () => config.get('map.topojson.world-countries-sans-antarctica')
94
+ },
95
+ transitionDuration: {
96
+ type: Number,
97
+ default: 750
98
+ },
99
+ zoomable: {
100
+ type: Boolean
101
+ },
102
+ zoomMin: {
103
+ type: Number,
104
+ default: 1
105
+ },
106
+ zoomMax: {
107
+ type: Number,
108
+ default: 8
109
+ },
110
+ ...chartProps()
111
+ },
112
+ emits: ["click", 'reset', 'zoomed', ...chartEmits],
113
+ setup(props, {emit}) {
114
+ const el = ref<ComponentPublicInstance<HTMLElement> | null>(null)
115
+ const topojson = ref<any>(null)
116
+ const topojsonPromise = ref<Promise<void> | null>(null)
117
+ const mapRect = ref<DOMRect>(new DOMRect(0, 0, 0, 0))
118
+ const markerCursor = ref<{ [cursor: string]: string } | null>(null)
119
+ const categoryHighlight = ref<any | null>(null)
120
+ const featureZoom = ref<string | null>(null)
121
+
122
+ const isLoaded = ref<boolean>(false)
123
+ const debouncedDraw = debounce(function () {
124
+ draw()
125
+ }, 10)
126
+
127
+ const {loadedData} = useChart(el, getChartProps(props), {emit}, isLoaded, debouncedDraw, afterLoaded)
128
+
129
+ function afterLoaded() {
130
+ return new Promise<void>(async (resolve) => {
131
+ await loadTopojson()
132
+ draw()
133
+ resolve()
134
+ })
135
+ }
136
+
137
+ //computed
138
+ const featurePath = computed(() => {
139
+ return d3.geoPath().projection(mapProjection.value)
140
+ })
141
+ const hasCursor = computed(() => {
142
+ return !!markerCursor.value
143
+ })
144
+ const hasHighlight = computed(() => {
145
+ return !!categoryHighlight.value
146
+ })
147
+ const hasZoom = computed(() => {
148
+ return !!featureZoom.value
149
+ })
150
+ const geojson = computed(() => {
151
+ return props.fitToMarkers ? markersGeojson.value : featuresGeojson.value
152
+ })
153
+ const featuresGeojson = computed(() => {
154
+ const object = get(topojson.value, ['objects', props.topojsonObjects], null)
155
+ return feature(topojson.value, object as GeometryCollection)
156
+ })
157
+ const markersGeojson = computed(() => {
158
+ return {
159
+ type: 'Feature',
160
+ geometry: {
161
+ type: 'Polygon',
162
+ coordinates: [coordinates.value]
163
+ }
164
+ }
165
+ })
166
+ const coordinates = computed(() => {
167
+ return (loadedData.value || []).map(({longitude, latitude}) => {
168
+ return [longitude, latitude]
169
+ })
170
+ })
171
+ const mapId = computed(() => {
172
+ return uniqueId('symbol-map-')
173
+ })
174
+ const mapClass = computed(() => {
175
+ return {
176
+ 'symbol-map--has-cursor': hasCursor.value,
177
+ 'symbol-map--has-highlight': hasHighlight.value,
178
+ 'symbol-map--has-zoom': hasZoom.value,
179
+ 'symbol-map--has-markers-scale': !props.noMarkersScale
180
+ }
181
+ })
182
+ const mapProjection = computed(() => {
183
+ const {height, width} = mapRect.value
184
+ const padding = props.mapPadding
185
+ return geoRobinson().fitExtent(
186
+ [
187
+ [padding, padding],
188
+ [width - padding, height - padding]
189
+ ],
190
+ geojson.value
191
+ )
192
+ })
193
+ const mapZoom = computed(() => {
194
+ return d3
195
+ .zoom()
196
+ .scaleExtent([props.zoomMin, props.zoomMax])
197
+ .translateExtent([
198
+ [0, 0],
199
+ [mapWidth.value, mapHeight.value]
200
+ ])
201
+ .on('zoom', mapZoomed)
202
+ })
203
+ const mapHeight = computed(() => {
204
+ return mapRect.value.height
205
+ })
206
+ const mapWidth = computed(() => {
207
+ return mapRect.value.width
208
+ })
209
+ const map = computed(() => {
210
+ const selection = d3.select(el.value).select<SVGElement>('.symbol-map__main');
211
+ if (!selection) {
212
+ throw new Error("Empty SVG selection")
213
+ }
214
+ return selection
215
+ })
216
+ const markerCursorValue = computed(() => {
217
+ return find(loadedDataWithIds.value, (d) => {
218
+ return get(d, props.markerObjectsPath) === markerCursor.value
219
+ })
220
+ })
221
+ const loadedDataWithIds = computed(() => {
222
+ return loadedData.value?.map((d) => {
223
+ return {
224
+ ...set({}, props.markerObjectsPath, uniqueId()),
225
+ ...d
226
+ }
227
+ })
228
+ })
229
+ const categories = computed(() => {
230
+ const categories = (loadedData.value || []).map((d) => {
231
+ return get(d, props.categoryObjectsPath)
232
+ })
233
+ return uniq(categories).map(String)
234
+ })
235
+ const legendData = computed(() => {
236
+ const categories = groupBy(loadedData.value || [], (d) => {
237
+ return get(d, props.categoryObjectsPath)
238
+ })
239
+ return Object.entries(categories).map((entry) => {
240
+ const [label, [{color: firstColor}]] = entry
241
+ const color = firstColor || categoryColor(label)
242
+ return {label, color}
243
+ })
244
+ })
245
+ const hasTooltip = computed(() => {
246
+ return !props.hideTooltip && loadedData.value && markerCursor.value
247
+ })
248
+ const tooltipTarget = computed(() => {
249
+ if (hasTooltip.value) {
250
+ return markerId(markerCursor.value)
251
+ }
252
+ return null
253
+ }
254
+ )
255
+
256
+
257
+ //methods
258
+
259
+
260
+ function prepare() {
261
+ if (!map.value) {
262
+ throw new Error("Map is null")
263
+ }
264
+ // Set the map sizes
265
+ mapRect.value = map.value.node()?.getBoundingClientRect() as DOMRect
266
+ // Remove any existing country
267
+ map.value.selectAll('g').remove()
268
+ // Return the map to allow chaining
269
+ return map.value
270
+ }
271
+
272
+ function prepareZoom() {
273
+ if (props.zoomable) {
274
+ map.value?.call(mapZoom.value)
275
+ }
276
+ }
277
+
278
+ function categoryColor(category: string) {
279
+ if (el.value && loadedData.value) {
280
+ const index = categories.value.indexOf(category)
281
+ const style = window.getComputedStyle(el.value)
282
+ return style.getPropertyValue(`--category-color-${index}n`) || '#000'
283
+ }
284
+ return null
285
+ }
286
+
287
+ function draw() {
288
+ prepare()
289
+ if (!map.value) {
290
+ throw new Error("map is not defined")
291
+ }
292
+ update()
293
+ // Bind a group for marker paths
294
+ map.value?.append('g')
295
+ .attr('class', 'symbol-map__main__markers')
296
+ .selectAll('.symbol-map__main__markers__item')
297
+ .data(loadedDataWithIds.value)
298
+ .enter()
299
+ .append('g')
300
+ .attr('id', markerId)
301
+ .attr('class', markerClass)
302
+ .attr('transform', markerTransform)
303
+ .append('path')
304
+ .on('mouseover', markerMouseOver)
305
+ .on('mouseleave', markerMouseLeave)
306
+ .attr('d', markerPathFunction)
307
+ .attr('fill', markerColorFunction)
308
+ prepareZoom()
309
+ }
310
+
311
+ function featureClass(d) {
312
+ return keys(pickBy(featureClassObject(d), (value) => value)).join(' ')
313
+ }
314
+
315
+ function featureClassObject(d) {
316
+ const pathClass = 'symbol-map__main__features__item'
317
+ const id = get(d, props.topojsonObjectsPath, null)
318
+ return {
319
+ [pathClass]: true,
320
+ [`${pathClass}--identifier-${kebabCase(id)}`]: id !== null,
321
+ [`${pathClass}--zoomed`]: featureZoom.value === id
322
+ }
323
+ }
324
+
325
+ async function loadTopojson() {
326
+ if (!topojsonPromise.value) {
327
+ if (!props.topojsonUrl?.length) {
328
+ throw new Error("Empty topojsonUrl")
329
+ }
330
+ topojsonPromise.value = d3.json(props.topojsonUrl)
331
+ topojson.value = await topojsonPromise.value
332
+ }
333
+ return topojsonPromise.value
334
+ }
335
+
336
+ function mapZoomed({transform}) {
337
+ markerCursor.value = null
338
+ map.value?.style('--map-scale', transform.k)
339
+ .selectAll('.symbol-map__main__features, .symbol-map__main__markers')
340
+ .attr('transform', transform)
341
+ }
342
+
343
+ function markerBoundingClientRect(d) {
344
+ const marker = map.value?.append('path').attr('d', markerPathFunction(d))
345
+ const rect = marker?.node()?.getBoundingClientRect()
346
+ marker?.remove()
347
+ return rect as DOMRect
348
+ }
349
+
350
+ function markerMouseLeave() {
351
+ markerCursor.value = null
352
+ }
353
+
354
+ function markerMouseOver(_, d) {
355
+ markerCursor.value = get(d, props.markerObjectsPath)
356
+ }
357
+
358
+ function markerClass(d) {
359
+ return keys(pickBy(markerClassObject(d), (value) => value)).join(' ')
360
+ }
361
+
362
+ function markerId(d) {
363
+ const id = get(d, props.markerObjectsPath)
364
+ return `${mapId.value}-marker-${id}`
365
+ }
366
+
367
+ function markerClassObject(d) {
368
+ const category = String(get(d, props.categoryObjectsPath))
369
+ const categoryIndex = categories.value.indexOf(category)
370
+ const id = get(d, props.markerObjectsPath)
371
+ const pathClass = 'symbol-map__main__markers__item'
372
+ return {
373
+ [pathClass]: true,
374
+ [`${pathClass}--category-${kebabCase(category)}`]: category !== null,
375
+ [`${pathClass}--category-${categoryIndex}n`]: category !== null,
376
+ [`${pathClass}--cursored`]: markerCursor.value === id,
377
+ [`${pathClass}--identifier-${kebabCase(id)}`]: id !== null,
378
+ [`${pathClass}--highlighted`]: categoryHighlight.value === category
379
+ }
380
+ }
381
+
382
+ function markerPathFunction(d) {
383
+ return isFunction(props.markerPath) ? props.markerPath(d) : props.markerPath
384
+ }
385
+
386
+ function markerColorFunction({color, ...d}) {
387
+ return color || (isFunction(props.markerColor) ? props.markerColor(d) : props.markerColor)
388
+ }
389
+
390
+ function markerWidthFunction(d) {
391
+ return isFunction(props.markerWidth) ? props.markerWidth(d) : props.markerWidth
392
+ }
393
+
394
+ function markerLabel(d) {
395
+ return get(d, props.labelObjectsPath)
396
+ }
397
+
398
+ function markerTransform(d) {
399
+ const {latitude, longitude} = d
400
+ const {height, width} = markerBoundingClientRect(d)
401
+ const [x, y] = mapProjection.value([longitude, latitude])
402
+ const scale = markerWidthFunction(d) / Math.max(1, width)
403
+ const cx = x - (width / 2) * scale
404
+ const cy = y - (height / 2) * scale
405
+ return `translate(${cx}, ${cy}) scale(${scale})`
406
+ }
407
+
408
+ async function featureClicked(event: MouseEvent, d: d3.GeoPermissibleObjects) {
409
+ /**
410
+ * A click on a feature
411
+ * @event click
412
+ * @param Clicked feature
413
+ */
414
+ emit('click', d)
415
+ // Don't zoom on the map feature
416
+ if (!props.clickable) {
417
+ return
418
+ }
419
+ if (featureZoom.value === get(d, props.topojsonObjectsPath)) {
420
+ return resetZoom(event, d)
421
+ }
422
+ setFeatureZoom(d, d3.pointer(event, map.value?.node()))
423
+ /**
424
+ * A zoom on a feature ended
425
+ * @event zoomed
426
+ * @param Zoomed feature
427
+ */
428
+ emit('zoomed', d)
429
+ }
430
+
431
+ function resetZoom(_event: MouseEvent, _d: number) {
432
+ map.value?.style('--map-scale', 1)
433
+ .transition()
434
+ .duration(props.transitionDuration)
435
+ .call(mapZoom.value.transform, d3.zoomIdentity)
436
+ featureZoom.value = null
437
+
438
+ /**
439
+ * The zomm on the map was reset to its initial <slot ate></slot>
440
+ * @event reset
441
+ */
442
+ emit('reset')
443
+ }
444
+
445
+ function setMarkersClasses() {
446
+ map.value?.selectAll('.symbol-map__main__markers__item').attr('class', markerClass)
447
+ }
448
+
449
+ function setFeatureZoom(d: GeoPermissibleObjects, pointer = [0, 0]) {
450
+ if (!loadedData.value) {
451
+ return
452
+ }
453
+ featureZoom.value = get(d, props.topojsonObjectsPath)
454
+
455
+ const {height, width} = mapRect.value
456
+ const [[x0, y0], [x1, y1]] = featurePath.value.bounds(d)
457
+ const scale = Math.min(8, 0.9 / Math.max((x1 - x0) / width, (y1 - y0) / height))
458
+ const x = -(x0 + x1) / 2
459
+ const y = -(y0 + y1) / 2
460
+ const zoomIdentity = d3.zoomIdentity
461
+ .translate(width / 2, height / 2)
462
+ .scale(scale)
463
+ .translate(x, y)
464
+ return map.value?.style('--map-scale', scale)
465
+ .transition()
466
+ .duration(props.transitionDuration)
467
+ .call(mapZoom.value?.transform, zoomIdentity, pointer)
468
+ .end()
469
+ }
470
+
471
+ function update() {
472
+ // Bind geojson features to path
473
+ if (!map.value) {
474
+ return
475
+ }
476
+ // Bind a group for geojson features to path
477
+ map.value?.append('g')
478
+ .attr('class', 'symbol-map__main__features')
479
+ .selectAll('.symbol-map__main__features__item')
480
+ .data(featuresGeojson.value.features)
481
+ // Add the path with the correct class
482
+ .enter()
483
+ .append('path')
484
+ .attr('class', featureClass)
485
+ .attr('d', featurePath.value)
486
+ .on('click', featureClicked)
487
+ .style('color', props.featureColor)
488
+ }
489
+
490
+ //watch
491
+ watch(() => props.data, () => {
492
+ //draw()
493
+ update()
494
+ })
495
+ watch(() => props.socialMode, () => {
496
+ draw()
497
+ })
498
+ watch(() => markerCursor.value, () => {
499
+ setMarkersClasses()
500
+ })
501
+
502
+ watch(() => categoryHighlight.value, () => {
503
+ setMarkersClasses()
504
+ })
505
+
506
+ return {
507
+ el,
508
+ categoryHighlight,
509
+ legendData,
510
+ mapClass,
511
+ markerCursor,
512
+ markerCursorValue,
513
+ markerLabel,
514
+ tooltipTarget,
515
+ loadTopojson
516
+ }
517
+ }
518
+ })
519
+
520
+ </script>
521
+
522
+ <template>
523
+ <div ref="el" :class="mapClass" class="symbol-map">
524
+ <slot name="legend" v-bind="{ legendData }">
525
+ <ordinal-legend
526
+ v-if="!hideLegend && legendData"
527
+ v-model:highlight="categoryHighlight"
528
+ :data="legendData"
529
+ :horizontal="horizontalLegend"
530
+ :marker-path="markerPath"
531
+ category-objects-path="label"
532
+ >
533
+ <template #marker="d">
534
+ <slot name="legend-marker" v-bind="d"/>
535
+ </template>
536
+ <template #label="d">
537
+ <slot name="legend-label" v-bind="d"/>
538
+ </template>
539
+ </ordinal-legend>
540
+ </slot>
541
+ <svg class="symbol-map__main"/>
542
+ <b-tooltip
543
+ v-if="tooltipTarget"
544
+ ref="marker-tooltip"
545
+ :custom-class="tooltipCustomClass"
546
+ :fallback-placement="tooltipFallbackPlacement"
547
+ :placement="tooltipPlacement"
548
+ :target="tooltipTarget"
549
+ >
550
+ <slot name="tooltip" v-bind="{ markerCursor, ...markerCursorValue }">
551
+ {{ markerLabel(markerCursorValue) }}
552
+ </slot>
553
+ </b-tooltip>
554
+ </div>
555
+ </template>
556
+
557
+ <style lang="scss" scoped>
558
+ @use 'sass:math';
559
+ @import '../styles/lib';
560
+
561
+ .symbol-map {
562
+ $muted-item-opacity: 0.2;
563
+ $muted-item-filter: grayscale(30%) brightness(10%);
564
+ $muted-item-transition: opacity 0.2s, filter 0.2s;
565
+
566
+ $colors: $primary, $info, $warning, $danger;
567
+ $quantile: 2;
568
+
569
+ @each $start-color in $colors {
570
+ $i: index($colors, $start-color) - 1;
571
+ $end-color: mix($start-color, text-contrast($start-color), 20%);
572
+
573
+ @for $j from ($quantile * $i) through ($quantile * $i + $quantile - 1) {
574
+ $amount: ($j % $quantile) * math.div(100%, $quantile);
575
+ --category-color-#{$j}n: #{mix($end-color, $start-color, $amount)};
576
+ }
577
+ }
578
+
579
+ &__main {
580
+ color: #ebebeb;
581
+ min-height: 300px;
582
+ height: 100%;
583
+ width: 100%;
584
+
585
+ .chart--social-mode & {
586
+ color: $dark;
587
+ }
588
+
589
+ &:deep(.symbol-map__main__features__item) {
590
+ stroke: currentColor;
591
+ stroke-width: calc(1px / var(--map-scale, 1));
592
+ fill: currentColor;
593
+ transition: opacity 750ms, filter 750ms;
594
+ }
595
+
596
+ &:deep(.symbol-map__main__markers) {
597
+ shape-rendering: geometricPrecision;
598
+
599
+ .symbol-map__main__markers__item {
600
+ opacity: 1;
601
+ filter: grayscale(0%) brightness(100%);
602
+ transition: $muted-item-transition;
603
+
604
+ .symbol-map--has-highlight & {
605
+ opacity: $muted-item-opacity;
606
+ filter: $muted-item-filter;
607
+ }
608
+
609
+ .symbol-map--has-highlight &--highlighted {
610
+ opacity: 1;
611
+ filter: grayscale(0%) brightness(100%);
612
+ }
613
+
614
+ @for $i from 0 through ($quantile * length($colors)) {
615
+ &--category-#{$i}n path:not([fill]) {
616
+ fill: var(--category-color-#{$i}n);
617
+ }
618
+ }
619
+
620
+ .symbol-map--has-markers-scale & path {
621
+ transform: scale(calc(1 / var(--map-scale)));
622
+ transform-origin: center center;
623
+ }
624
+ }
625
+ }
626
+ }
627
+ }
628
+ </style>
@@ -0,0 +1,3 @@
1
+ export { default as ChoroplethMap } from './ChoroplethMap.vue'
2
+ export { default as ChoroplethMapAnnotation } from './ChoroplethMapAnnotation.vue'
3
+ export { default as SymbolMap } from './SymbolMap.vue'
@@ -0,0 +1 @@
1
+ declare module 'querystring-es3'
@@ -0,0 +1,5 @@
1
+ declare module 'bootstrap-vue/esm/components/dropdown/dropdown-item'
2
+ declare module 'bootstrap-vue/esm/components/modal/modal'
3
+ declare module 'bootstrap-vue/esm/components/nav/nav-item-dropdown'
4
+ declare module 'bootstrap-vue/esm/components/popover/popover'
5
+ declare module 'bootstrap-vue/esm/components/tooltip/tooltip'
@@ -0,0 +1,11 @@
1
+ import Vue, { VNode } from 'vue'
2
+
3
+ declare global {
4
+ namespace JSX {
5
+ interface Element extends VNode {}
6
+ interface ElementClass extends Vue {}
7
+ interface IntrinsicElements {
8
+ [elem: string]: any
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,14 @@
1
+ declare module 'vue' {
2
+ import { CompatVue } from '@vue/runtime-dom'
3
+ const Vue: CompatVue
4
+ export default Vue
5
+ export * from '@vue/runtime-dom'
6
+ const { configureCompat } = Vue
7
+ export { configureCompat }
8
+ }
9
+
10
+ declare module '*.vue' {
11
+ import type {DefineComponent} from 'vue';
12
+ const component: DefineComponent<{}, {}, any>;
13
+ export default component;
14
+ }
@@ -0,0 +1,20 @@
1
+ @use "sass:math";
2
+ /*------------------------
3
+ mixin that calculates if text needs to be light or dark
4
+ depending on the background color passed.
5
+ From this W3C document: http://www.webmasterworld.com/r.cgi?f=88&d=9769&url=http://www.w3.org/TR/AERT#color-contrast
6
+ Color brightness is determined by the following formula:
7
+ ((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000
8
+ ------------------------*/
9
+
10
+ @function text-contrast($n) {
11
+ $color-brightness: round((red($n) * 299) + (green($n) * 587) + math.div(blue($n) * 114, 1000));
12
+ $light-color: round((red(#ffffff) * 299) + (green(#ffffff) * 587) + math.div(blue(#ffffff) * 114, 1000));
13
+
14
+ @if abs($color-brightness) < ($light-color * 0.5){
15
+ @return white;
16
+ }
17
+ @else {
18
+ @return black;
19
+ }
20
+ }
@@ -0,0 +1,19 @@
1
+ @import './variables';
2
+ @import './variables_dark';
3
+ @import './mixins';
4
+ @import './functions';
5
+ @import './utilities';
6
+ @import 'bootstrap/scss/_mixins';
7
+ :root,
8
+ [data-bs-theme="light"] {
9
+
10
+ --input-bg: #{$bs-input-bg};
11
+
12
+ }
13
+ @if $enable-dark-mode {
14
+ @include color-mode(dark, true) {
15
+ color-scheme: dark;
16
+ --input-bg: #{$bs-input-bg-dark};
17
+ }
18
+ }
19
+ $input-bg:var(--input-bg);