@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.
- package/.github/workflows/deploy-github-pages.yaml +50 -0
- package/.storybook/app.scss +14 -0
- package/.storybook/doc_variables.scss +20 -0
- package/.storybook/main.ts +35 -0
- package/.storybook/preview-head.html +2 -0
- package/.storybook/preview.ts +32 -0
- package/README.md +71 -0
- package/deploy.js +15 -0
- package/docs/components/ApiTable.vue +171 -0
- package/docs/components/App.vue +146 -0
- package/docs/components/CollapsibleBlock.vue +122 -0
- package/docs/components/DocsHeader.vue +68 -0
- package/docs/components/DocsMenu.vue +201 -0
- package/docs/components/DocsMenuSection.vue +109 -0
- package/docs/components/EditLink.vue +49 -0
- package/docs/components/OutboundLink.vue +13 -0
- package/docs/components/PalettePresenter.vue +96 -0
- package/docs/components/RepositoryLink.vue +28 -0
- package/docs/components/SampleCard.vue +119 -0
- package/docs/main.js +42 -0
- package/docs/pages/components/accordion/doc.md +96 -0
- package/docs/pages/components/active-text-truncate/doc.md +44 -0
- package/docs/pages/components/advanced-link-form/doc.md +105 -0
- package/docs/pages/components/brand/doc.md +30 -0
- package/docs/pages/components/brand-expansion/doc.md +70 -0
- package/docs/pages/components/confirm-button/doc.md +91 -0
- package/docs/pages/components/content-placeholder/doc.md +16 -0
- package/docs/pages/components/custom-pagination/doc.md +61 -0
- package/docs/pages/components/digits-input/doc.md +28 -0
- package/docs/pages/components/donate-form/doc.md +20 -0
- package/docs/pages/components/embed-form/doc.md +22 -0
- package/docs/pages/components/embeddable-footer/doc.md +60 -0
- package/docs/pages/components/follow-us-popover/doc.md +5 -0
- package/docs/pages/components/generic-footer/doc.md +21 -0
- package/docs/pages/components/generic-header/doc.md +24 -0
- package/docs/pages/components/haptic-copy/doc.md +27 -0
- package/docs/pages/components/imddb-header/doc.md +23 -0
- package/docs/pages/components/ordinal-legend/doc.md +44 -0
- package/docs/pages/components/range-picker/doc.md +86 -0
- package/docs/pages/components/responsive-iframe/doc.md +13 -0
- package/docs/pages/components/scale-legend/doc.md +65 -0
- package/docs/pages/components/secret-input/doc.md +12 -0
- package/docs/pages/components/selectable-dropdown/doc.md +156 -0
- package/docs/pages/components/sharing-options/doc.md +13 -0
- package/docs/pages/components/sharing-options-link/doc.md +36 -0
- package/docs/pages/components/sign-up-form/doc.md +13 -0
- package/docs/pages/components/slide-up-down/doc.md +28 -0
- package/docs/pages/components/textured-deck/doc.md +78 -0
- package/docs/pages/components/tiny-pagination/doc.md +92 -0
- package/docs/pages/datavisualisation/bars/doc.md +110 -0
- package/docs/pages/datavisualisation/columns/doc.md +165 -0
- package/docs/pages/datavisualisation/lines/doc.md +139 -0
- package/docs/pages/datavisualisation/stacked-bar/doc.md +160 -0
- package/docs/pages/datavisualisation/stacked-column/doc.md +191 -0
- package/docs/pages/getting-started/about-icij/doc.md +13 -0
- package/docs/pages/getting-started/custom-bootstrap/doc.md +36 -0
- package/docs/pages/getting-started/installation-guide/doc.md +59 -0
- package/docs/pages/getting-started/internationalization/doc.md +74 -0
- package/docs/pages/maps/choropleth-map/doc.md +420 -0
- package/docs/pages/maps/choropleth-map-annotation/doc.md +373 -0
- package/docs/pages/maps/symbol-map/doc.md +203 -0
- package/docs/pages/structure/breakpoints/doc.md +3 -0
- package/docs/pages/structure/grid/doc.md +3 -0
- package/docs/pages/utilities/assets/doc.md +138 -0
- package/docs/pages/utilities/config/doc.md +52 -0
- package/docs/pages/utilities/iframes/doc.md +3 -0
- package/docs/pages/visual/colors/doc.md +31 -0
- package/docs/pages/visual/iconography/doc.md +56 -0
- package/docs/pages/visual/states/doc.md +77 -0
- package/docs/pages/visual/themes/doc.md +3 -0
- package/docs/pages/visual/typography/doc.md +71 -0
- package/docs/routes.js +25 -0
- package/docs/store/index.js +21 -0
- package/docs/styles/app.scss +36 -0
- package/docs/styles/variables.scss +20 -0
- package/lib/assets/images/icij-full-white.svg +6 -0
- package/lib/assets/images/icij-full.svg +6 -0
- package/lib/assets/images/icij.png +0 -0
- package/lib/assets/images/icij.svg +46 -0
- package/lib/assets/images/icij@2x.png +0 -0
- package/lib/assets/images/murmur-dark.png +0 -0
- package/lib/assets/images/murmur-dark.svg +79 -0
- package/lib/assets/images/murmur-white.png +0 -0
- package/lib/assets/images/murmur-white.svg +68 -0
- package/lib/components/AccordionStep.vue +128 -0
- package/lib/components/AccordionWrapper.vue +138 -0
- package/lib/components/ActiveTextTruncate.vue +258 -0
- package/lib/components/AdvancedLinkForm.vue +273 -0
- package/lib/components/Brand.vue +150 -0
- package/lib/components/BrandExpansion.vue +237 -0
- package/lib/components/ConfirmButton.vue +204 -0
- package/lib/components/ContentPlaceholder.vue +100 -0
- package/lib/components/CustomPagination.vue +225 -0
- package/lib/components/DigitsInput.vue +180 -0
- package/lib/components/DonateForm.vue +367 -0
- package/lib/components/EmbedForm.vue +173 -0
- package/lib/components/EmbeddableFooter.vue +201 -0
- package/lib/components/Fa.js +3 -0
- package/lib/components/FollowUsPopover.vue +117 -0
- package/lib/components/GenericFooter.vue +218 -0
- package/lib/components/GenericHeader.vue +259 -0
- package/lib/components/HapticCopy.vue +256 -0
- package/lib/components/ImddbHeader.vue +336 -0
- package/lib/components/OrdinalLegend.vue +164 -0
- package/lib/components/RangePicker.vue +430 -0
- package/lib/components/ResponsiveIframe.vue +48 -0
- package/lib/components/ScaleLegend.vue +230 -0
- package/lib/components/SecretInput.vue +132 -0
- package/lib/components/SelectableDropdown.vue +368 -0
- package/lib/components/SharingOptions.vue +230 -0
- package/lib/components/SharingOptionsLink.vue +259 -0
- package/lib/components/SignUpForm.vue +181 -0
- package/lib/components/SlideUpDown.vue +131 -0
- package/lib/components/TexturedDeck.vue +101 -0
- package/lib/components/TinyPagination.vue +268 -0
- package/lib/components/index.js +31 -0
- package/lib/composables/chart.ts +182 -0
- package/lib/composables/resizeObserver.ts +37 -0
- package/lib/composables/sendEmail.ts +50 -0
- package/lib/config.default.ts +33 -0
- package/lib/config.ts +70 -0
- package/lib/d3-geo-projection.d.ts +1 -0
- package/lib/datavisualisations/BarChart.vue +275 -0
- package/lib/datavisualisations/ColumnChart.vue +527 -0
- package/lib/datavisualisations/LineChart.vue +274 -0
- package/lib/datavisualisations/StackedBarChart.vue +614 -0
- package/lib/datavisualisations/StackedColumnChart.vue +640 -0
- package/lib/datavisualisations/index.js +5 -0
- package/lib/enums.ts +25 -0
- package/lib/i18n.ts +16 -0
- package/lib/keys.ts +2 -0
- package/lib/locales/en.json +140 -0
- package/lib/locales/fr.json +117 -0
- package/lib/locales/locales/en.json +140 -0
- package/lib/locales/locales/fr.json +117 -0
- package/lib/main.ts +87 -0
- package/lib/maps/ChoroplethMap.vue +825 -0
- package/lib/maps/ChoroplethMapAnnotation.vue +336 -0
- package/lib/maps/SymbolMap.vue +628 -0
- package/lib/maps/index.js +3 -0
- package/lib/querystring-es3.d.ts +1 -0
- package/lib/shims-bootstrap-vue.d.ts +5 -0
- package/lib/shims-tsx.d.ts +11 -0
- package/lib/shims-vue.d.ts +14 -0
- package/lib/styles/functions.scss +20 -0
- package/lib/styles/lib.scss +19 -0
- package/lib/styles/mixins.scss +37 -0
- package/lib/styles/utilities.scss +18 -0
- package/lib/styles/variables.scss +94 -0
- package/lib/styles/variables_dark.scss +1 -0
- package/lib/types.ts +46 -0
- package/lib/utils/animation.ts +24 -0
- package/lib/utils/assets.ts +46 -0
- package/lib/utils/clipboard.ts +41 -0
- package/lib/utils/iframe-resizer.ts +49 -0
- package/lib/utils/placeholder.ts +66 -0
- package/lib/utils/placeholderTypes.ts +21 -0
- package/lib/utils/strings.ts +8 -0
- package/loaders/highlight-loader.js +13 -0
- package/loaders/markdown-loader.js +91 -0
- package/loaders/metadata-loader.js +18 -0
- package/loaders/sass-extract-loader.js +14 -0
- package/loaders/vue-docgen-loader.js +14 -0
- package/package.json +96 -0
- package/plugins/MdPluginTypes.ts +10 -0
- package/plugins/docs.ts +50 -0
- package/plugins/front-matter.ts +36 -0
- package/plugins/highlight.ts +27 -0
- package/plugins/markdown-it/api-table.ts +25 -0
- package/plugins/markdown-it/sample-card.ts +31 -0
- package/plugins/plugin-delete.ts +47 -0
- package/plugins/plugin-docgen.ts +23 -0
- package/plugins/sass-vars.ts +25 -0
- package/plugins/vue-docgen.ts +29 -0
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/assets/img/arrow-bottom.svg +3 -0
- package/public/assets/img/texture-brick-black.jpg +0 -0
- package/public/assets/img/texture-brick.jpg +0 -0
- package/public/assets/img/texture-carbon-black.jpg +0 -0
- package/public/assets/img/texture-carbon.jpg +0 -0
- package/public/assets/img/texture-crack-black.jpg +0 -0
- package/public/assets/img/texture-crack.jpg +0 -0
- package/public/assets/img/texture-rock-black.jpg +0 -0
- package/public/assets/img/texture-rock.jpg +0 -0
- package/public/assets/img/texture-sand-black.jpg +0 -0
- package/public/assets/img/texture-sand.jpg +0 -0
- package/public/assets/img/texture-silk-black.jpg +0 -0
- package/public/assets/img/texture-silk.jpg +0 -0
- package/public/assets/topojson/france-departments.json +1 -0
- package/public/assets/topojson/paris-arrondissements.json +1 -0
- package/public/assets/topojson/world-countries-sans-antarctica.json +1 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/site.webmanifest +1 -0
- package/stories/assets/code-brackets.svg +1 -0
- package/stories/assets/colors.svg +1 -0
- package/stories/assets/comments.svg +1 -0
- package/stories/assets/direction.svg +1 -0
- package/stories/assets/flow.svg +1 -0
- package/stories/assets/plugin.svg +1 -0
- package/stories/assets/repo.svg +1 -0
- package/stories/assets/stackalt.svg +1 -0
- package/stories/getting-started/about-icij.mdx +14 -0
- package/stories/getting-started/custom-bootstrap.mdx +23 -0
- package/stories/getting-started/installation-guide.mdx +62 -0
- package/stories/getting-started/internationalization.mdx +63 -0
- package/stories/murmur/components/AccordionStep.stories.ts +33 -0
- package/stories/murmur/components/AccordionWrapper.stories.ts +69 -0
- package/stories/murmur/components/ActiveTextTruncate.stories.ts +32 -0
- package/stories/murmur/components/AdvancedLinkForm.stories.ts +77 -0
- package/stories/murmur/components/Brand.stories.ts +30 -0
- package/stories/murmur/components/BrandExpansion.stories.ts +41 -0
- package/stories/murmur/components/ConfirmButton.stories.ts +40 -0
- package/stories/murmur/components/ContentPlaceholder.stories.ts +41 -0
- package/stories/murmur/components/CustomPagination.stories.ts +42 -0
- package/stories/murmur/components/DigitsInput.stories.ts +29 -0
- package/stories/murmur/components/DonateForm.stories.ts +29 -0
- package/stories/murmur/components/EmbedForm.stories.ts +35 -0
- package/stories/murmur/components/EmbeddableFooter.stories.ts +59 -0
- package/stories/murmur/components/FollowUsPopover.stories.ts +24 -0
- package/stories/murmur/components/GenericFooter.stories.ts +27 -0
- package/stories/murmur/components/GenericHeader.stories.ts +27 -0
- package/stories/murmur/components/HapticCopy.stories.ts +40 -0
- package/stories/murmur/components/ImddbHeader.stories.ts +27 -0
- package/stories/murmur/components/OrdinalLegend.stories.ts +49 -0
- package/stories/murmur/components/RangePicker.stories.ts +98 -0
- package/stories/murmur/components/ResponsiveIframe.stories.ts +24 -0
- package/stories/murmur/components/ScaleLegend.stories.ts +65 -0
- package/stories/murmur/components/SecretInput.stories.ts +60 -0
- package/stories/murmur/components/SelectableDropdown.stories.ts +143 -0
- package/stories/murmur/components/SharingOptions.stories.ts +32 -0
- package/stories/murmur/components/SharingOptionsLink.stories.ts +53 -0
- package/stories/murmur/components/SignUpForm.stories.ts +51 -0
- package/stories/murmur/components/SlideUpDown.stories.ts +32 -0
- package/stories/murmur/components/TexturedDeck.stories.ts +83 -0
- package/stories/murmur/components/TinyPagination.stories.ts +65 -0
- package/stories/murmur/datavisualisations/BarChart.stories.ts +54 -0
- package/stories/murmur/datavisualisations/ColumnChart.stories.ts +88 -0
- package/stories/murmur/datavisualisations/LineChart.stories.ts +139 -0
- package/stories/murmur/datavisualisations/StackedBarChart.stories.ts +199 -0
- package/stories/murmur/datavisualisations/StackedColumnChart.stories.ts +136 -0
- package/stories/murmur/decorators.ts +108 -0
- package/stories/murmur/maps/ChoroplethMap.stories.ts +440 -0
- package/stories/murmur/maps/ChoroplethMapAnnotation.stories.ts +26 -0
- package/stories/murmur/maps/SymbolMap.stories.ts +24 -0
- package/stories/murmur/utils.ts +7 -0
- package/tests/unit/components/AccordionStep.spec.ts +157 -0
- package/tests/unit/components/AccordionWrapper.spec.ts +57 -0
- package/tests/unit/components/ActiveTextTruncate.spec.js +30 -0
- package/tests/unit/components/AdvancedLinkForm.spec.js +124 -0
- package/tests/unit/components/Brand.spec.js +50 -0
- package/tests/unit/components/ContentPlaceholder.spec.js +29 -0
- package/tests/unit/components/CustomPagination.spec.js +72 -0
- package/tests/unit/components/DigitsInput.spec.ts +157 -0
- package/tests/unit/components/DonateForm.spec.js +149 -0
- package/tests/unit/components/EmbedForm.spec.js +108 -0
- package/tests/unit/components/EmbeddableFooter.spec.js +11 -0
- package/tests/unit/components/Fa.spec.js +18 -0
- package/tests/unit/components/FollowUsPopover.spec.js +29 -0
- package/tests/unit/components/GenericFooter.spec.js +29 -0
- package/tests/unit/components/GenericHeader.spec.js +104 -0
- package/tests/unit/components/HapticCopy.spec.js +123 -0
- package/tests/unit/components/ImddbHeader.spec.js +96 -0
- package/tests/unit/components/OrdinalLegend.spec.js +120 -0
- package/tests/unit/components/RangePicker.spec.ts +87 -0
- package/tests/unit/components/ResponsiveIframe.spec.js +20 -0
- package/tests/unit/components/ScaleLegend.spec.js +139 -0
- package/tests/unit/components/SecretInput.spec.js +81 -0
- package/tests/unit/components/SelectableDropdown.spec.js +160 -0
- package/tests/unit/components/SharingOptions.spec.js +125 -0
- package/tests/unit/components/SharingOptionsLink.spec.js +184 -0
- package/tests/unit/components/SignUpForm.spec.js +145 -0
- package/tests/unit/components/SlideUpDown.spec.js +59 -0
- package/tests/unit/components/TinyPagination.spec.js +46 -0
- package/tests/unit/config.spec.js +136 -0
- package/tests/unit/datavisualisations/BarChart.spec.js +63 -0
- package/tests/unit/datavisualisations/ColumnChart.spec.js +344 -0
- package/tests/unit/datavisualisations/LineChart.spec.js +155 -0
- package/tests/unit/datavisualisations/StackedBarChart.spec.js +294 -0
- package/tests/unit/datavisualisations/StackedColumnChart.spec.js +443 -0
- package/tests/unit/i18n.spec.ts +19 -0
- package/tests/unit/main.spec.js +82 -0
- package/tests/unit/maps/ChoroplethMap.spec.js +214 -0
- package/tests/unit/maps/ChoroplethMapAnnotation.spec.ts +186 -0
- package/tests/unit/maps/SymbolMap.spec.js +92 -0
- package/tests/unit/require.spec.js +22 -0
- package/tests/unit/setup.js +13 -0
- package/tests/unit/utils/assets.spec.js +61 -0
- package/tests/unit/utils/clipboard.spec.js +18 -0
- package/tests/unit/utils/iframe-resizer.spec.js +71 -0
- package/tsconfig.json +35 -0
- package/vite.config.ts +79 -0
- 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>
|