@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,258 @@
1
+ <script lang="ts">
2
+ import {computed, defineComponent, ref, watch} from 'vue'
3
+ import useResizeObserver from "@/composables/resizeObserver";
4
+ import { RequestAnimationFrameWrapper } from '@/utils/animation'
5
+
6
+ type ActiveTextTruncateData = { textLivePosition: number; resizeObserverKey: string | null }
7
+
8
+ export default defineComponent({
9
+ name: 'ActiveTextTruncate',
10
+ props: {
11
+ /**
12
+ * Number of Pixel Per Millisecond for the text transition.
13
+ */
14
+ ppms: {
15
+ type: Number,
16
+ default: 0.025
17
+ },
18
+ /**
19
+ * Maximum width of the fading mask.
20
+ */
21
+ fadingMaxWidth: {
22
+ type: Number,
23
+ default: 50,
24
+ validator: (value: number) => value > 0
25
+ },
26
+ /**
27
+ * Minimum width of the fading mask.
28
+ */
29
+ fadingMinWidth: {
30
+ type: Number,
31
+ default: 0.001,
32
+ validator: (value: number) => value > 0
33
+ },
34
+ /**
35
+ * Delay to start moving the text (in milliseconds).
36
+ */
37
+ delay: {
38
+ type: Number,
39
+ default: 1000
40
+ },
41
+ /**
42
+ * Direction of the truncate
43
+ */
44
+ direction: {
45
+ type: String,
46
+ default: 'ltr',
47
+ validator: (value: string) => ['ltr', 'rtl'].indexOf(value) > -1
48
+ }
49
+ },
50
+ emits:['cancel','end','start'],
51
+ setup(props,{emit}){
52
+ const el = ref(null)
53
+ const textLivePosition = ref(0)
54
+ // This will hold a key generated every time the component is resized.
55
+ const {resizeRef,resizeState} = useResizeObserver(el)
56
+ watch(resizeState,()=>{
57
+ onResized()
58
+ })
59
+
60
+ const wrapperElement = computed((): HTMLElement | null => {
61
+ const selector = '.active-text-truncate__wrapper'
62
+ return resizeRef.value?.querySelector(selector) ?? null
63
+ })
64
+ const wrapperElementWidth = computed((): number => {
65
+ return wrapperElement.value?.offsetWidth ?? 0
66
+ })
67
+ const textElement = computed((): HTMLElement | null => {
68
+ const selector = '.active-text-truncate__wrapper__text'
69
+ return resizeRef.value?.querySelector(selector) ?? null
70
+ })
71
+ const textElementWidth = computed((): number => {
72
+ return textElement.value?.offsetWidth ?? 0
73
+ })
74
+ const textOffsetTransitionDelay = computed((): string => {
75
+ return `${props.delay}ms`
76
+ })
77
+ const textOffsetTransitionDuration = computed((): string => {
78
+ const offset = Math.abs(wrapperElementWidth.value - textElementWidth.value)
79
+ const duration = offset / props.ppms
80
+ return `${duration}ms`
81
+ })
82
+ const textInitialOffset = computed((): string => {
83
+ return '0'
84
+ })
85
+ const textFinalOffset = computed((): string => {
86
+ const offset = wrapperElementWidth.value - textElementWidth.value
87
+ return `${offset}px`
88
+ })
89
+ const textOffsetValues = computed((): string [] => {
90
+ if (props.direction === 'ltr') {
91
+ return [textInitialOffset.value, textFinalOffset.value]
92
+ }
93
+ return [textFinalOffset.value, textInitialOffset.value]
94
+ })
95
+ const isFadingLeft = computed((): boolean => {
96
+ return props.direction === 'rtl' && isFading.value
97
+ })
98
+ const isFadingRight = computed((): boolean => {
99
+ return props.direction === 'ltr' && isFading.value
100
+ })
101
+ const isFading = computed((): boolean => {
102
+ return wrapperElementWidth.value < textElementWidth.value
103
+ })
104
+ const fadingLeftWidth = computed((): string => {
105
+ const offset = textLivePosition.value
106
+ const width = Math.min(Math.max(props.fadingMinWidth, Math.abs(offset)), props.fadingMaxWidth)
107
+ return `${width}px`
108
+ })
109
+ const fadingRightWidth = computed((): string => {
110
+ const offset = parseInt(textFinalOffset.value) - textLivePosition.value
111
+ const width = Math.min(Math.max(props.fadingMinWidth, Math.abs(offset)), props.fadingMaxWidth)
112
+ return `${width}px`
113
+ })
114
+ const textLivePositionRequestAnimationFrame = computed((): RequestAnimationFrameWrapper => {
115
+ return new RequestAnimationFrameWrapper()
116
+ })
117
+
118
+ function onResized() {
119
+
120
+ textLivePosition.value = parseInt(textOffsetValues.value[0])
121
+ // Track transitions to update the text position in live using Request Animation Frame
122
+ listenOnTextElement('transitionstart', startTrackingTextLivePosition)
123
+ listenOnTextElement('transitionend', endTrackingTextLivePosition)
124
+ listenOnTextElement('transitioncancel', resetTextLivePosition)
125
+ }
126
+ function listenOnTextElement(name: string, func: () => void) {
127
+ textElement.value?.removeEventListener(name, func)
128
+ textElement.value?.addEventListener(name, func)
129
+ }
130
+ function trackTextLivePosition(): void {
131
+ if (!textElement.value) return
132
+ const left = window.getComputedStyle(textElement.value, null).getPropertyValue('left')
133
+ textLivePosition.value = parseInt(left)
134
+ }
135
+ function startTrackingTextLivePosition(): void {
136
+ textLivePositionRequestAnimationFrame.value.start(trackTextLivePosition)
137
+ /**
138
+ * Emitted when the animation on the text starts.
139
+ * @event start
140
+ */
141
+ emit('start')
142
+ }
143
+ function endTrackingTextLivePosition(): void {
144
+ textLivePositionRequestAnimationFrame.value.stop()
145
+ /**
146
+ * Emitted when the animation on the text reaches the end.
147
+ * @event end
148
+ */
149
+ emit('end')
150
+ }
151
+ function resetTextLivePosition(): void {
152
+ textLivePositionRequestAnimationFrame.value.stop()
153
+ textLivePosition.value = parseInt(textOffsetValues.value[0])
154
+ /**
155
+ * Emitted when the animation on the text is cancelled.
156
+ * @event cancel
157
+ */
158
+ emit('cancel')
159
+ }
160
+ return {
161
+ el,
162
+ isFading,
163
+ fadingLeftWidth,
164
+ fadingRightWidth,
165
+ textOffsetTransitionDuration,
166
+ textOffsetTransitionDelay,
167
+ textFinalOffset,
168
+ resetTextLivePosition
169
+ }
170
+ }
171
+ })
172
+ </script>
173
+
174
+ <template>
175
+ <span
176
+ ref="el"
177
+ class="active-text-truncate"
178
+ :class="{
179
+ 'active-text-truncate--fading': isFading,
180
+ [`active-text-truncate--${direction}`]: true
181
+ }"
182
+ :style="{
183
+ '--fading-left-width': fadingLeftWidth,
184
+ '--fading-right-width': fadingRightWidth,
185
+ '--text-offset-transition-duration': textOffsetTransitionDuration,
186
+ '--text-offset-transition-delay': textOffsetTransitionDelay,
187
+ '--text-final-offset': textFinalOffset
188
+ }"
189
+ @mouseleave="resetTextLivePosition"
190
+ >
191
+ <span class="active-text-truncate__wrapper">
192
+ <span class="active-text-truncate__wrapper__text">
193
+ <slot />
194
+ </span>
195
+ </span>
196
+ </span>
197
+ </template>
198
+
199
+ <style lang="scss" scoped>
200
+ @import '../styles/lib';
201
+ @import '../styles/mixins';
202
+
203
+ .active-text-truncate {
204
+ --fading-left-width: 0;
205
+ --fading-right-width: 0;
206
+ --fading-left-gradient: linear-gradient(to left, black calc(100% - var(--fading-left-width)), transparent 100%);
207
+ --fading-right-gradient: linear-gradient(to right, black calc(100% - var(--fading-right-width)), transparent 100%);
208
+ --text-offset-transition-duration: 0ms;
209
+ --text-offset-transition-delay: 0ms;
210
+ --text-final-offset: 0;
211
+
212
+ overflow: hidden;
213
+ max-width: 100%;
214
+ display: inline-block;
215
+ position: relative;
216
+
217
+ &:after {
218
+ content: attr(data-text-live-position);
219
+ position: absolute;
220
+ left: 0;
221
+ top: 0;
222
+ }
223
+
224
+ &__wrapper {
225
+ width: 100%;
226
+ display: inline-block;
227
+
228
+ .active-text-truncate--fading & {
229
+ mask: var(--fading-right-gradient), var(--fading-left-gradient);
230
+ mask-composite: intersect;
231
+ }
232
+
233
+ .active-text-truncate--rtl.active-text-truncate--fading &:hover &__text,
234
+ .active-text-truncate--ltr.active-text-truncate--fading &:hover &__text {
235
+ transition: linear left var(--text-offset-transition-duration);
236
+ transition-delay: var(--text-offset-transition-delay);
237
+ }
238
+
239
+ .active-text-truncate--ltr.active-text-truncate--fading &:hover &__text {
240
+ left: var(--text-final-offset);
241
+ }
242
+
243
+ .active-text-truncate--rtl.active-text-truncate--fading &:hover &__text {
244
+ left: 0;
245
+ }
246
+
247
+ &__text {
248
+ white-space: nowrap;
249
+ position: relative;
250
+ left: 0;
251
+
252
+ .active-text-truncate--rtl & {
253
+ left: var(--text-final-offset);
254
+ }
255
+ }
256
+ }
257
+ }
258
+ </style>
@@ -0,0 +1,273 @@
1
+ <script lang="ts">
2
+ import {computed, defineComponent, ref, Ref, toRef} from 'vue'
3
+ import { BTabs, BTab, BInputGroup, BInputGroupAppend, BFormInput } from 'bootstrap-vue-next'
4
+
5
+ import HapticCopy from './HapticCopy.vue'
6
+
7
+ import {useI18n} from "vue-i18n";
8
+
9
+ type AdvancedLinkedFormClassName = `${'advanced-link-form--'}${string}`
10
+ interface AdvancedLinkedFormClasses {
11
+ [prop: AdvancedLinkedFormClassName]: boolean
12
+ }
13
+
14
+ interface TextRange {
15
+ moveToElementText: Function
16
+ select: Function
17
+ }
18
+
19
+ interface HTMLElementSupportingCreateRange extends HTMLElement {
20
+ createTextRange(): TextRange
21
+ }
22
+
23
+ /**
24
+ * A form with tabs to offer several copy formats to users.
25
+ */
26
+ export default defineComponent({
27
+ name: 'AdvancedLinkForm',
28
+ components: {
29
+ BTabs,
30
+ BTab,
31
+ BInputGroup,
32
+ BInputGroupAppend,
33
+ BFormInput,
34
+ HapticCopy
35
+ },
36
+ props: {
37
+ /**
38
+ * The link to copy
39
+ */
40
+ link: {
41
+ type: String,
42
+ default: null
43
+ },
44
+ /**
45
+ * Title associated with the link
46
+ */
47
+ title: {
48
+ type: String,
49
+ default: null
50
+ },
51
+ /**
52
+ * The forms to display
53
+ */
54
+ forms: {
55
+ type: Array,
56
+ default: () => ['raw', 'markdown', 'rich', 'html']
57
+ },
58
+ /**
59
+ * Index of the selected tab
60
+ */
61
+ modelValue: {
62
+ type: Number,
63
+ default: 0
64
+ },
65
+ /**
66
+ * Activate the card integration for the tabs
67
+ */
68
+ card: {
69
+ type: Boolean
70
+ },
71
+ /**
72
+ * Renders the tabs with the appearance of pill buttons
73
+ */
74
+ pills: {
75
+ type: Boolean
76
+ },
77
+ /**
78
+ * Makes the tabs and the panels smaller.
79
+ */
80
+ small: {
81
+ type: Boolean
82
+ },
83
+ /**
84
+ * Makes the tabs and the panels vertical.
85
+ */
86
+ vertical: {
87
+ type: Boolean
88
+ },
89
+ /**
90
+ * CSS class (or classes) to apply to the currently active tab.
91
+ */
92
+ activeNavItemClass: {
93
+ type: String,
94
+ default: null
95
+ },
96
+ /**
97
+ * When set to 'true', disables the fade animation on the tabs.
98
+ */
99
+ noFade: {
100
+ type: Boolean
101
+ }
102
+ },
103
+ emits:["update:modelValue"],
104
+ setup(props,{emit}) {
105
+ const { t } = useI18n()
106
+ const rawInput = ref<HTMLTextAreaElement| null>(null)
107
+ const richInput = ref<HTMLElement|null>(null)
108
+ const markdownInput = ref<HTMLTextAreaElement| null>(null)
109
+ const htmlInput = ref<HTMLTextAreaElement| null>(null)
110
+ const titleOrLink = computed(() => props.title || props.link);
111
+
112
+ const linkAsMarkdown = computed(() => `[${titleOrLink.value}](${props.link})`);
113
+
114
+ const linkAsHtml = computed(() => `<a href="${props.link}" target="_blank">${titleOrLink.value}</a>`);
115
+ const formClasses = computed(() => {
116
+ const propsToCheck = ['card', 'pills', 'small', 'vertical'];
117
+ return propsToCheck.reduce((classes, prop) => {
118
+ //@ts-ignore
119
+ classes[`advanced-link-form--${prop}`] = props[prop];
120
+ return classes;
121
+ }, {});
122
+ });
123
+
124
+ const size = computed(() => props.small ? 'sm' : 'md');
125
+
126
+ const showForm = computed(() => {
127
+ return (name:string) => props.forms.indexOf(name) > -1
128
+ })
129
+
130
+ const selectInput = (target: Ref<HTMLElement|null>) => {
131
+ // if(!target.value){
132
+ // throw new Error("no target")
133
+ // }
134
+ if (target.value instanceof HTMLTextAreaElement) {
135
+ target.value.select();
136
+ }
137
+ };
138
+
139
+ const selectRaw = () => selectInput(rawInput);
140
+
141
+ function selectRich() {
142
+ if (!richInput.value) return;
143
+
144
+ const selection = window.getSelection ? window.getSelection() : null;
145
+ if (selection) {
146
+ const range = document.createRange();
147
+ range.selectNodeContents(richInput.value);
148
+ selection.removeAllRanges();
149
+ selection.addRange(range);
150
+ } else if ((document.body as HTMLElementSupportingCreateRange).createTextRange) {
151
+ const range = (document.body as HTMLElementSupportingCreateRange).createTextRange();
152
+ range.moveToElementText(richInput.value);
153
+ range.select();
154
+ }
155
+ }
156
+ function selectMarkdown() {
157
+ selectInput(markdownInput)
158
+ }
159
+
160
+ function selectHtml() {
161
+ selectInput(htmlInput)
162
+ }
163
+
164
+ return {
165
+ t,
166
+ titleOrLink,
167
+ linkAsMarkdown,
168
+ linkAsHtml,
169
+ formClasses,
170
+ size,
171
+ showForm,
172
+ selectRaw,
173
+ selectRich,
174
+ selectMarkdown,
175
+ selectHtml
176
+ };
177
+ }
178
+
179
+ })
180
+ </script>
181
+
182
+ <template>
183
+ <b-tabs
184
+ class="advanced-link-form"
185
+ :content-class="card ? 'mt-0' : 'mt-3'"
186
+ :card="card"
187
+ :pills="pills"
188
+ :model-value="modelValue"
189
+ :small="small"
190
+ :vertical="vertical"
191
+ :active-nav-item-class="activeNavItemClass"
192
+ :no-fade="noFade"
193
+ :class="formClasses"
194
+ @update:model-value="$emit('update:modelValue')"
195
+ >
196
+ <b-tab v-if="showForm('raw')" :title="t('advanced-link-form.raw.tab')">
197
+ <div class="advanced-link-form__raw" :class="{ small }">
198
+ <b-input-group :size="size">
199
+ <b-form-input ref="rawInput" readonly :model-value="link" class="advanced-link-form__raw__input" @click="selectRaw" />
200
+ <b-input-group-append>
201
+ <haptic-copy class="btn-secondary" :text="link" @attempt="selectRaw" />
202
+ </b-input-group-append>
203
+ </b-input-group>
204
+ </div>
205
+ </b-tab>
206
+ <b-tab v-if="showForm('rich')" :title="t('advanced-link-form.rich.tab')">
207
+ <div class="advanced-link-form__rich" :class="{ small }">
208
+ <b-input-group :size="size">
209
+ <a ref="richInput" :href="link" class="form-control advanced-link-form__rich__input" @click.prevent="selectRich">{{
210
+ titleOrLink
211
+ }}</a>
212
+ <b-input-group-append>
213
+ <haptic-copy class="btn-secondary" html :text="linkAsHtml" :plain="link" @attempt="selectRich" />
214
+ </b-input-group-append>
215
+ </b-input-group>
216
+ <p class="text-muted mt-2 mb-0">
217
+ {{ t('advanced-link-form.rich.description') }}
218
+ </p>
219
+ </div>
220
+ </b-tab>
221
+ <b-tab v-if="showForm('markdown')" :title="t('advanced-link-form.markdown.tab')">
222
+ <div class="advanced-link-form__markdown" :class="{ small }">
223
+ <b-input-group :size="size">
224
+ <b-form-input
225
+ ref="markdownInput"
226
+ readonly
227
+ :model-value="linkAsMarkdown"
228
+ class="advanced-link-form__markdown__input"
229
+ @click="selectMarkdown"
230
+ />
231
+ <b-input-group-append>
232
+ <haptic-copy class="btn-secondary" :text="linkAsMarkdown" @attempt="selectMarkdown" />
233
+ </b-input-group-append>
234
+ </b-input-group>
235
+ <p class="text-muted mt-2 mb-0">
236
+ {{ t('advanced-link-form.markdown.description') }}
237
+ </p>
238
+ </div>
239
+ </b-tab>
240
+ <b-tab v-if="showForm('html')" :title="t('advanced-link-form.html.tab')">
241
+ <div class="advanced-link-form__html" :class="{ small }">
242
+ <b-input-group :size="size">
243
+ <b-form-input ref="htmlInput" readonly :modelValue="linkAsHtml" class="advanced-link-form__html__input" @click="selectHtml" />
244
+ <b-input-group-append>
245
+ <haptic-copy class="btn-secondary" :text="linkAsHtml" @attempt="selectHtml" />
246
+ </b-input-group-append>
247
+ </b-input-group>
248
+ </div>
249
+ </b-tab>
250
+ </b-tabs>
251
+ </template>
252
+
253
+ <style lang="scss" scoped>
254
+ @import '../styles/lib';
255
+
256
+ .advanced-link-form {
257
+ text-align: left;
258
+
259
+ &__raw__input[readonly],
260
+ &__markdown__input[readonly],
261
+ &__html__input[readonly] {
262
+ background: $input-bg;
263
+ }
264
+
265
+ &__rich__input {
266
+ text-align: center;
267
+ text-decoration: underline;
268
+ white-space: nowrap;
269
+ overflow: hidden;
270
+ text-overflow: ellipsis;
271
+ }
272
+ }
273
+ </style>
@@ -0,0 +1,150 @@
1
+ <template>
2
+ <span class="brand" :style="style" :class="{ 'brand--animated': animated }">
3
+ <svg :width="width" :height="height" viewBox="0 0 147.151 200" xmlns="http://www.w3.org/2000/svg">
4
+ <path class="triangle" d="M101.997 200H45.155l28.42-46.427z" />
5
+ <path
6
+ class="globe"
7
+ d="M119.771 19.138C107.432 6.793 91.028 0 73.576 0S39.719 6.793 27.38 19.138C15.035 31.477 8.242 47.882 8.242 65.334c0 17.451 6.793 33.856 19.138 46.195 12.339 12.339 28.744 19.138 46.196 19.138 17.452 0 33.856-6.793 46.195-19.138C132.11 99.19 138.91 82.785 138.91 65.334c0-17.452-6.793-33.857-19.138-46.196zm3.397 23.028a54.35 54.35 0 0 1 4.863 17.86H108.79c-.262-6.173-.968-12.175-2.094-17.86h16.478zM112.266 26.65a54.792 54.792 0 0 1 4.346 4.894H103.98c-1.254-3.939-2.733-7.634-4.431-11.024a61.475 61.475 0 0 0-2.6-4.681c5.619 2.654 10.793 6.288 15.316 10.81zm-14.11 33.376H78.89v-17.86h16.965c1.241 5.6 2.02 11.614 2.3 17.86zm-8.109-34.758a63.605 63.605 0 0 1 2.71 6.276H78.89v-19.48c4.03 2.174 7.92 6.721 11.157 13.204zm-32.943 0c3.238-6.477 7.134-11.03 11.158-13.203v19.473H54.395a62.794 62.794 0 0 1 2.709-6.276zm11.158 16.898v17.86H48.996c.286-6.246 1.065-12.26 2.3-17.86zM34.886 26.65A54.905 54.905 0 0 1 50.2 15.833a59.443 59.443 0 0 0-2.6 4.68c-1.698 3.391-3.177 7.086-4.43 11.025H30.538a55.764 55.764 0 0 1 4.347-4.894zm5.57 15.516c-1.127 5.685-1.833 11.687-2.095 17.86H19.12a54.201 54.201 0 0 1 4.863-17.86h16.478ZM23.976 88.507a54.35 54.35 0 0 1-4.863-17.86h19.241c.262 6.173.968 12.181 2.094 17.86H23.971Zm10.902 15.517a54.793 54.793 0 0 1-4.346-4.894h12.631c1.254 3.938 2.733 7.633 4.432 11.023a61.476 61.476 0 0 0 2.599 4.681 54.569 54.569 0 0 1-15.316-10.816zm14.11-33.376h19.266v17.86H51.29c-1.241-5.6-2.02-11.615-2.3-17.86Zm8.109 34.757a63.605 63.605 0 0 1-2.71-6.275h13.867v19.472c-4.03-2.173-7.919-6.72-11.157-13.203zm32.943 0c-3.238 6.477-7.134 11.03-11.157 13.203V99.136H92.75a62.793 62.793 0 0 1-2.709 6.275zM78.884 88.507v-17.86h19.265c-.286 6.246-1.065 12.26-2.3 17.86zm33.376 15.517a54.759 54.759 0 0 1-15.316 10.81 59.444 59.444 0 0 0 2.6-4.68c1.698-3.391 3.177-7.086 4.43-11.024h12.632a55.763 55.763 0 0 1-4.346 4.894zm-5.57-15.517c1.126-5.685 1.832-11.687 2.094-17.86h19.241a54.201 54.201 0 0 1-4.863 17.86h-16.478z"
8
+ style="stroke-width: 0.608717"
9
+ />
10
+ <path
11
+ class="plate"
12
+ d="M 147.151 136.809 L 147.151 147.431 L 9.02755e-15 147.431 L 8.37714e-15 136.809 Z"
13
+ style="stroke-width: 0.608717"
14
+ />
15
+ </svg>
16
+ </span>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ /**
21
+ * A component to create variations of ICIJ logo
22
+ */
23
+ import {computed, ref} from 'vue';
24
+ import isString from "lodash/isString";
25
+
26
+ interface BrandProps {
27
+ /**
28
+ * Add a balancing effect to the globe
29
+ */
30
+ animated?: boolean;
31
+ /**
32
+ * Monochromatic logo's color
33
+ */
34
+ color?: string | null;
35
+ /**
36
+ * Logo's background color
37
+ */
38
+ background?: string | null;
39
+ /**
40
+ * Logo's size
41
+ */
42
+ size?: number | string;
43
+ /**
44
+ * Force the width of the logo to be the same as the height
45
+ */
46
+ square?: boolean;
47
+ }
48
+
49
+ interface BrandStyle {
50
+ '--monochrome-color': string | null;
51
+ color: string | null;
52
+ background: string | null;
53
+ width: string | number;
54
+ }
55
+
56
+ const props = withDefaults(defineProps<BrandProps>(),{
57
+ color: null,
58
+ background:null,
59
+ size: '70px'
60
+ });
61
+ const sizeAsNumber = ref(isString(props.size) ? parseInt(props.size) : props.size);
62
+
63
+ const width = computed(() => `${(147.151 / 200) * sizeAsNumber.value}px`);
64
+ const height = computed(() => `${sizeAsNumber.value}px`);
65
+
66
+ const style = computed<BrandStyle>(() => {
67
+ const {square} = props;
68
+ const widthValue = square ? height.value : 'auto';
69
+
70
+ return {
71
+ '--monochrome-color': props.color,
72
+ color: props.color,
73
+ background: props.background,
74
+ width: widthValue,
75
+ };
76
+ });
77
+ </script>
78
+
79
+ <style scoped lang="scss">
80
+ @keyframes balancing-plate {
81
+ 0% {
82
+ transform: rotate(0deg);
83
+ }
84
+ 25% {
85
+ transform: rotate(7deg);
86
+ }
87
+ 50% {
88
+ transform: rotate(-7deg);
89
+ }
90
+ 75% {
91
+ transform: rotate(0deg);
92
+ }
93
+ 100% {
94
+ transform: rotate(0deg);
95
+ }
96
+ }
97
+
98
+ @keyframes balancing-globe {
99
+ 0% {
100
+ transform: rotate(0deg);
101
+ }
102
+ 35% {
103
+ transform: rotate(7deg);
104
+ }
105
+ 65% {
106
+ transform: rotate(-7deg);
107
+ }
108
+ 85% {
109
+ transform: rotate(3deg);
110
+ }
111
+ 100% {
112
+ transform: rotate(0deg);
113
+ }
114
+ }
115
+
116
+ .brand {
117
+ display: inline-flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+
121
+ svg {
122
+ .triangle,
123
+ .globe {
124
+ fill: var(--monochrome-color, #ff0000);
125
+ }
126
+
127
+ .plate {
128
+ fill: var(--monochrome-color, #999999);
129
+ }
130
+ }
131
+
132
+ &--animated svg {
133
+ .plate {
134
+ animation: balancing-plate 5s infinite cubic-bezier(0.37, 0, 0.63, 1);
135
+ }
136
+ .globe {
137
+ animation: balancing-globe 5s infinite cubic-bezier(0.37, 0, 0.63, 1);
138
+ }
139
+
140
+ .plate,
141
+ .globe {
142
+ //noinspection CssInvalidPropertyValue
143
+ animation-direction: repeat;
144
+ transform: rotate(0deg);
145
+ transform-origin: bottom center;
146
+ transform-box: fill-box;
147
+ }
148
+ }
149
+ }
150
+ </style>