@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,230 @@
1
+ <script lang="ts">
2
+ import get from 'lodash/get'
3
+ import reduce from 'lodash/reduce'
4
+ import {faCode} from '@fortawesome/free-solid-svg-icons/faCode'
5
+ import {computed, CSSProperties, defineComponent, onBeforeMount, PropType, ref} from 'vue'
6
+
7
+ import {default as Fa, library} from './Fa'
8
+
9
+ import EmbedForm from '@/components/EmbedForm.vue'
10
+ import SharingOptionsLink from '@/components/SharingOptionsLink.vue'
11
+ import config from '@/config'
12
+ import IframeResizer from '@/utils/iframe-resizer'
13
+ import {BModal, useModal} from "bootstrap-vue-next";
14
+
15
+ type MetaValuesMap = {
16
+ url: string
17
+ title: string
18
+ description: string
19
+ facebook_title: string
20
+ facebook_description: string
21
+ facebook_media: string
22
+ twitter_media: string
23
+ twitter_user: string
24
+ }
25
+
26
+ /**
27
+ * SharingOptions
28
+ */
29
+ export default defineComponent({
30
+ name: 'SharingOptions',
31
+ components: {
32
+ BModal,
33
+ EmbedForm,
34
+ SharingOptionsLink,
35
+ Fa
36
+ },
37
+ props: {
38
+ /**
39
+ * URL to be shared.
40
+ */
41
+ url: {
42
+ type: String,
43
+ default: () => config.get('sharing-options.url', null) || IframeResizer.deletePymParams()
44
+ },
45
+ /**
46
+ * URL to use specifically with the embed form
47
+ */
48
+ embedUrl: {
49
+ type: String,
50
+ default: null
51
+ },
52
+ /**
53
+ * Direction of the sharing options. Can be: <em>row</em>, <em>row-reverse</em>,
54
+ * <em>column</em> or <em>column-reverse</em>.
55
+ */
56
+ direction: {
57
+ default: 'row',
58
+ validator(value: string) {
59
+ return ['row', 'row-reverse', 'column', 'column-reverse'].indexOf(value) !== -1
60
+ }
61
+ },
62
+ /**
63
+ * Sharing contents which can be generic (<em>title</em>, <em>description</em>, etc)
64
+ * or specific to a network (<em>twitter_title</em>, <em>facebook_description</em>, etc).
65
+ */
66
+ values: {
67
+ type: Object,
68
+ default: () => ({})
69
+ },
70
+ /**
71
+ * The list of all the keys to automatically inject in each social button.
72
+ */
73
+ valuesKeys: {
74
+ default: () => ['url', 'title', 'description', 'media', 'user'],
75
+ type: Array as PropType<string[]>
76
+ },
77
+ /**
78
+ * Disable embed button.
79
+ */
80
+ noEmbed: {
81
+ type: Boolean
82
+ },
83
+ /**
84
+ * Minimum height of the iframe in the embed form.
85
+ */
86
+ iframeMinHeight: {
87
+ type: Number,
88
+ default: 100
89
+ },
90
+ /**
91
+ * Minimum width of the iframe in the embed form.
92
+ */
93
+ iframeMinWidth: {
94
+ type: Number,
95
+ default: 100
96
+ },
97
+ /**
98
+ * Prevent from reading default value from the <code>meta</code>.
99
+ */
100
+ noMeta: {
101
+ type: Boolean
102
+ }
103
+ },
104
+ setup(props) {
105
+
106
+ onBeforeMount(() => {
107
+ library.add(faCode)
108
+ })
109
+
110
+ const embedForm = ref(null)
111
+ const {show} = useModal("embedForm")
112
+ const style = computed((): CSSProperties => {
113
+ return {
114
+ 'flex-direction': props.direction
115
+ } as CSSProperties
116
+ })
117
+
118
+ const metaValues = computed((): MetaValuesMap => {
119
+ return {
120
+ url: props.url ?? "",
121
+ title: defaultValueFor('sharing-options.title'),
122
+ description: defaultValueFor('sharing-options.description', 'meta[name="description]'),
123
+ facebook_title: defaultValueFor('sharing-options.facebook_title', 'meta[property="og:title"]'),
124
+ facebook_description: defaultValueFor('sharing-options.description', 'meta[property="og:description"]'),
125
+ facebook_media: defaultValueFor('sharing-options.media', 'meta[property="og:image"]'),
126
+ twitter_media: defaultValueFor('sharing-options.media', 'meta[name="twitter:image"]'),
127
+ twitter_user: defaultValueFor('sharing-options.twitter-user', 'meta[name="twitter:site"]')
128
+ }
129
+ })
130
+
131
+ function valuesFor(network: string): { [key: string]: string } {
132
+ const values = Object.assign(metaValues.value, props.values)
133
+ return reduce(
134
+ props.valuesKeys,
135
+ (res: { [name: string]: string }, key) => {
136
+ res[key] = get(values, `${network}_${key}`, values[key])
137
+ return res
138
+ },
139
+ {}
140
+ )
141
+ }
142
+
143
+ function defaultValueFor(key: string, metaSelector?: string): string {
144
+ if (props.noMeta || !metaSelector) {
145
+ return config.get(key)
146
+ }
147
+ return get(document.head.querySelector(metaSelector), 'content', config.get(key))
148
+ }
149
+
150
+ return {
151
+ style,
152
+ show,
153
+ embedForm,
154
+ valuesFor
155
+ }
156
+
157
+ },
158
+ })
159
+ </script>
160
+
161
+ <template>
162
+ <div :style="style" class="sharing-options">
163
+ <sharing-options-link class="sharing-options__link" network="facebook" v-bind="valuesFor('facebook')"/>
164
+ <sharing-options-link class="sharing-options__link" network="twitter" v-bind="valuesFor('twitter')"/>
165
+ <sharing-options-link class="sharing-options__link" network="linkedin" v-bind="valuesFor('linkedin')"/>
166
+ <sharing-options-link class="sharing-options__link" network="email" v-bind="valuesFor('email')"/>
167
+ <div v-show="!noEmbed" class="sharing-options__link sharing-options__link--embed">
168
+ <a @click="show">
169
+ <fa icon="code"/>
170
+ <span class="sr-only">Embed</span>
171
+ </a>
172
+ </div>
173
+ <b-modal id="embedForm" ref="embedForm" class="text-dark" hide-footer title="Embed on your website">
174
+ <embed-form
175
+ :min-height="iframeMinHeight"
176
+ :min-width="iframeMinWidth"
177
+ :url="embedUrl || url"
178
+ no-preview
179
+ no-title
180
+ />
181
+ </b-modal>
182
+ </div>
183
+ </template>
184
+
185
+ <style lang="scss">
186
+ @import '@/styles/lib';
187
+ @import '@/styles/mixins';
188
+
189
+ .sharing-options {
190
+ display: flex;
191
+
192
+ &__link {
193
+ display: block;
194
+ margin: $spacer * 0.25;
195
+ background: black;
196
+ height: 2.5em;
197
+ line-height: 2.5em;
198
+ width: 2.5em;
199
+ text-align: center;
200
+ font-size: 80%;
201
+ border-radius: 50%;
202
+ cursor: pointer;
203
+ color: white;
204
+ position: relative;
205
+
206
+ i {
207
+ position: absolute;
208
+ top: 50%;
209
+ left: 50%;
210
+ transform: translate(-50%, -50%);
211
+ }
212
+
213
+ & > a,
214
+ & > button {
215
+ padding: 0;
216
+ margin: 0;
217
+ width: 100%;
218
+ height: 100%;
219
+ display: block;
220
+ background: transparent !important;
221
+ text-decoration: none;
222
+ opacity: 1;
223
+ }
224
+
225
+ .svg-inline--fa {
226
+ color: white;
227
+ }
228
+ }
229
+ }
230
+ </style>
@@ -0,0 +1,259 @@
1
+ <script lang="ts">
2
+ import querystring from 'querystring-es3'
3
+ import reduce from 'lodash/reduce'
4
+ import noop from 'lodash/noop'
5
+ import get from 'lodash/get'
6
+ import { faEnvelope } from '@fortawesome/free-solid-svg-icons/faEnvelope'
7
+ import { faTwitter } from '@fortawesome/free-brands-svg-icons/faTwitter'
8
+ import { faFacebook } from '@fortawesome/free-brands-svg-icons/faFacebook'
9
+ import { faLinkedin } from '@fortawesome/free-brands-svg-icons/faLinkedin'
10
+ import { h, defineComponent, PropType, VNode} from 'vue'
11
+ import { IconDefinition } from '@fortawesome/fontawesome-svg-core'
12
+
13
+ import Fa from './Fa'
14
+
15
+ // Popup instance and an interval holder
16
+ type Popup = {
17
+ instance: Window | null | undefined
18
+ interval: undefined | ReturnType<typeof setTimeout>
19
+ parent: (Window & typeof globalThis) | null
20
+ }
21
+ export const $popup: Popup = {
22
+ instance: null,
23
+ interval: undefined,
24
+ parent: typeof window !== 'undefined' ? window : null
25
+ }
26
+
27
+ // Prevent propagation when an event is fired through the given callback
28
+ const preventDefault = (callback: Function) => {
29
+ return (event: Event) => {
30
+ event && event.preventDefault()
31
+ callback()
32
+ }
33
+ }
34
+
35
+ type SharingPlatform = {
36
+ base: string
37
+ icon: IconDefinition
38
+ args: {
39
+ [key: string]: string
40
+ }
41
+ }
42
+ type Platform = 'email' | 'facebook' | 'linkedin' | 'twitter'
43
+ // eslint-disable-next-line no-unused-vars
44
+ type SharingPlatforms = { [key in Platform]: SharingPlatform }
45
+ /**
46
+ * @source https://github.com/bradvin/social-share-urls
47
+ */
48
+ export const networks: SharingPlatforms = {
49
+ email: {
50
+ base: 'mailto:?',
51
+ icon: faEnvelope,
52
+ args: {
53
+ subject: 'title',
54
+ body: 'description'
55
+ }
56
+ },
57
+ facebook: {
58
+ base: 'https://www.facebook.com/sharer.php?',
59
+ icon: faFacebook,
60
+ args: {
61
+ u: 'url',
62
+ title: 'title',
63
+ description: 'description',
64
+ hashtag: 'hashtags'
65
+ }
66
+ },
67
+ linkedin: {
68
+ base: 'https://www.linkedin.com/sharing/share-offsite/?',
69
+ icon: faLinkedin,
70
+ args: {
71
+ url: 'url',
72
+ title: 'title',
73
+ summary: 'description'
74
+ }
75
+ },
76
+ twitter: {
77
+ base: 'https://twitter.com/intent/tweet?',
78
+ icon: faTwitter,
79
+ args: {
80
+ url: 'url',
81
+ text: 'title',
82
+ via: 'user',
83
+ hashtags: 'hashtags'
84
+ }
85
+ }
86
+ }
87
+
88
+ /**
89
+ * SharingOptionsLink
90
+ */
91
+ export default defineComponent({
92
+ name: 'SharingOptionsLink',
93
+ components: {
94
+ Fa
95
+ },
96
+ props: {
97
+ /**
98
+ * Root element type
99
+ */
100
+ tag: {
101
+ type: String,
102
+ default: 'a'
103
+ },
104
+ /**
105
+ * Social network to use
106
+ */
107
+ network: {
108
+ type: String as PropType<Platform>,
109
+ required: true,
110
+ validator(val: string) {
111
+ return Object.keys(networks).includes(val)
112
+ }
113
+ },
114
+ /**
115
+ * Disable icon
116
+ */
117
+ noIcon: {
118
+ type: Boolean
119
+ },
120
+ /**
121
+ * Shared URL
122
+ */
123
+ url: {
124
+ type: String,
125
+ default: null
126
+ },
127
+ /**
128
+ * Shared text
129
+ */
130
+ title: {
131
+ type: String,
132
+ default: null
133
+ },
134
+ /**
135
+ * Shared description
136
+ */
137
+ description: {
138
+ type: String,
139
+ default: null
140
+ },
141
+ /**
142
+ * Shared image
143
+ */
144
+ media: {
145
+ type: String,
146
+ default: null
147
+ },
148
+ /**
149
+ * Twitter user
150
+ */
151
+ user: {
152
+ type: String,
153
+ default: null
154
+ },
155
+ /**
156
+ * Shared hashtags
157
+ */
158
+ hashtags: {
159
+ type: String,
160
+ default: null
161
+ }
162
+ },
163
+ data() {
164
+ return {
165
+ popup: {
166
+ status: 'no',
167
+ resizable: 'yes',
168
+ toolbar: 'no',
169
+ menubar: 'no',
170
+ scrollbars: 'no',
171
+ location: 'no',
172
+ directories: 'no',
173
+ width: 626,
174
+ height: 436,
175
+ top: 0,
176
+ left: 0,
177
+ screenY: 0,
178
+ screenX: 0
179
+ }
180
+ }
181
+ },
182
+ computed: {
183
+ href(): string {
184
+ return this.base + querystring.stringify(this.query)
185
+ },
186
+ base(): string {
187
+ return get(networks, [this.network, 'base'], '')
188
+ },
189
+ args(): { [key: string]: string } {
190
+ return get(networks, [this.network, 'args'], {})
191
+ },
192
+ icon(): IconDefinition | null {
193
+ return get(networks, [this.network, 'icon'], null)
194
+ },
195
+ query(): any {
196
+ return reduce(
197
+ this.args,
198
+ (obj, prop, param) => {
199
+ // @ts-ignore
200
+ if (this.$props[prop]) {
201
+ // @ts-ignore
202
+ obj[param] = this.$props[prop]
203
+ }
204
+ return obj
205
+ },
206
+ {}
207
+ )
208
+ },
209
+ name(): string {
210
+ return get(networks, [this.network, 'name'], this.network)
211
+ },
212
+ popupParams(): string {
213
+ return querystring.stringify(this.popup).split('&').join(',')
214
+ }
215
+ },
216
+ methods: {
217
+ click(): void {
218
+ this.cleanExistingPopupInstance()
219
+ this.openPopup()
220
+ },
221
+ renderIcon(): void | VNode | null {
222
+ if (!this.noIcon) {
223
+ //@ts-ignore
224
+ return h( Fa, {icon: this.icon})
225
+ }
226
+ },
227
+ openPopup(): void {
228
+ // Create the popup
229
+ $popup.instance = $popup.parent?.open(this.href, 'sharer', this.popupParams)
230
+ $popup.instance?.focus()
231
+ // Watch for popup closing
232
+ $popup.interval = setInterval(this.cleanExistingPopupInterval, 500)
233
+ },
234
+ cleanExistingPopupInstance(): void {
235
+ if ($popup.instance && $popup.interval) {
236
+ clearInterval($popup.interval)
237
+ $popup.interval = undefined
238
+ $popup.instance.close()
239
+ }
240
+ },
241
+ cleanExistingPopupInterval() {
242
+ if ($popup.instance && $popup.instance.closed) {
243
+ clearInterval($popup.interval)
244
+ $popup.interval = undefined
245
+ $popup.instance = null
246
+ }
247
+ },
248
+ hasPopup(): boolean {
249
+ return this.network !== 'email'
250
+ }
251
+ },
252
+ render(): void | VNode | null {
253
+ const click = this.hasPopup() ? preventDefault(this.click) : noop
254
+ const href = this.href
255
+ const children = this.$slots.default? this.$slots.default() : ([this.renderIcon(), h('span', { class: 'visually-hidden' }, this.name)])
256
+ return h(this.tag, { attrs: { href }, onClick: click }, children)
257
+ }
258
+ })
259
+ </script>
@@ -0,0 +1,181 @@
1
+ <template>
2
+ <form class="sign-up-form" :class="{ 'sign-up-form--horizontal': horizontal }" @submit.prevent="subscribe">
3
+ <fieldset :disabled="frozen">
4
+ <label v-if="!noLabel" class="text-uppercase text-muted fw-bold" for="input-email">
5
+ {{ t('sign-up-form.label') }}
6
+ </label>
7
+ <div class="sign-up-form__fieldset__group " :class="{'input-group': horizontal}">
8
+ <input
9
+ id="input-email"
10
+ v-model="email"
11
+ name="EMAIL"
12
+ type="email"
13
+ class="form-control"
14
+ :placeholder="t('sign-up-form.placeholder').toString()"
15
+ />
16
+ <button class="sign-up-form__fieldset__group__addon btn text-uppercase fw-bold input-group-text" :class="{ [variantColorClass] :true }" type="submit">
17
+ {{ t('sign-up-form.submit') }}
18
+ </button>
19
+ </div>
20
+ </fieldset>
21
+ <p v-if="errorMessage" class="alert alert-danger p-2 m-0 mt-2">
22
+ {{ errorMessage }}
23
+ </p>
24
+ <p v-if="successMessage" class="alert alert-success p-2 m-0 mt-2">
25
+ {{ successMessage }}
26
+ </p>
27
+ </form>
28
+ </template>
29
+
30
+ <script lang="ts">
31
+ import jsonp from 'jsonp'
32
+ import castArray from 'lodash/castArray'
33
+ import flatten from 'lodash/flatten'
34
+ import last from 'lodash/last'
35
+ import {computed, defineComponent, PropType, ref} from 'vue'
36
+
37
+ import config from '../config'
38
+ import {useI18n} from "vue-i18n";
39
+ import {useSendEmail} from "@/composables/sendEmail";
40
+
41
+
42
+ type SignUpFormData = {
43
+ email: string
44
+ frozen: boolean
45
+ response: any
46
+ errorMessage: string | null
47
+ successMessage: string | null
48
+ }
49
+
50
+ /**
51
+ * SignUpForm
52
+ */
53
+ export default defineComponent({
54
+ name: 'SignUpForm',
55
+ props: {
56
+ /**
57
+ * Mailchimp URL to send the data to.
58
+ */
59
+ action: {
60
+ type: String,
61
+ default: () => config.get('signup-form.action')
62
+ },
63
+ /**
64
+ * Malchimp email field parameter
65
+ */
66
+ emailField: {
67
+ type: String,
68
+ default: () => config.get('signup-form.email-field')
69
+ },
70
+ /**
71
+ * Malchimp default groups. Can be an array or a commat-separated list of groups.
72
+ */
73
+ defaultGroups: {
74
+ type: [String, Array] as PropType<string | string[]>,
75
+ default: () => config.get('signup-form.default-groups')
76
+ },
77
+ /**
78
+ * Disable the main label.
79
+ */
80
+ noLabel: {
81
+ type: Boolean
82
+ },
83
+ /**
84
+ * Horizontal layout of the form.
85
+ */
86
+ horizontal: {
87
+ type: Boolean
88
+ },
89
+ /**
90
+ * Mailchimp tracker tag to identify the origin.
91
+ */
92
+ tracker: {
93
+ type: String,
94
+ default: () => config.get('signup-form.tracker')
95
+ },
96
+ /**
97
+ * Referrer URL cant be passed explicitly
98
+ */
99
+ referrer: {
100
+ type: String,
101
+ default: null
102
+ },
103
+ /**
104
+ * Color variant of the sign-up button
105
+ */
106
+ variant: {
107
+ type: String,
108
+ default: 'primary'
109
+ }
110
+ },
111
+ setup(props){
112
+ const {t} = useI18n()
113
+ const email = ref('')
114
+ const frozen = ref(false)
115
+ const response = ref({})
116
+ const errorMessage = ref(null)
117
+ const successMessage = ref(null)
118
+ const {send} = useSendEmail(email,props.action,props.emailField, props.tracker, props.referrer,props.defaultGroups)
119
+
120
+ const variantColorClass = computed(()=> {
121
+ return `btn-${props.variant}`
122
+ } )
123
+ async function subscribe() {
124
+ resetMessages()
125
+ freeze()
126
+ // Send the data, catch the result no matter what and unfreeze the form
127
+ await send().then(done,done).finally(unfreeze)
128
+
129
+ }
130
+
131
+ function done({ result, msg }: any): void {
132
+ if (result === 'success') {
133
+ email.value = ''
134
+ successMessage.value = msg
135
+ } else {
136
+ // Mailchimp formats errors in list
137
+ errorMessage.value = last((msg || "Something's wrong").split('0 -')) ?? null
138
+ }
139
+ }
140
+ function resetMessages() {
141
+ errorMessage.value = null
142
+ successMessage.value = null
143
+ }
144
+ function freeze() {
145
+ frozen.value = true
146
+ }
147
+ function unfreeze() {
148
+ frozen.value = false
149
+ }
150
+
151
+ return {
152
+ t,
153
+ email,
154
+ frozen,
155
+ response,
156
+ errorMessage,
157
+ successMessage,
158
+ variantColorClass,
159
+ subscribe,
160
+ send
161
+ }
162
+ }
163
+ })
164
+ </script>
165
+
166
+ <style lang="scss">
167
+ @import '../styles/lib.scss';
168
+
169
+ .sign-up-form {
170
+ .sign-up-form__fieldset__group__addon.btn {
171
+ font-size: 0.9em;
172
+ }
173
+
174
+ &:not(&--horizontal) {
175
+ .sign-up-form__fieldset__group__addon.btn {
176
+ display: block;
177
+ width: 100%;
178
+ }
179
+ }
180
+ }
181
+ </style>