Vielen Dank für Ihre Unterstützung
++ Es freut uns, dass Sie uns bereits unterstützt haben oder regelmäßig unterstützen. Ohne Sie wäre die + Finanzierung unserer Projekte nicht möglich. +
++ Falls Sie sich eine (weitere) Spende vorstellen können, dann klicken Sie "Vielleicht später". Falls + nicht, klicken Sie: "Im Moment nicht". In diesem Fall wird ein Cookie in Ihrem Browser gesetzt, + der die Anzeige der Banner vorerst verhindert. +
+
+
+ Vielleicht kommen wir gerade ungelegen, aber dennoch: Klicken Sie jetzt bitte nicht weg! Am + heutigen {{ currentDayName }}, den {{ currentDate }}, bitten wir Sie, die Unabhängigkeit + von Wikipedia zu unterstützen. +
+
+ {{ campaignDaySentence }}
+ Die meisten Menschen spenden, weil sie Wikipedia nützlich finden. Die durchschnittliche Spende + beträgt 22,25 €, doch bereits 5 € helfen uns weiter. +
++ Hat Wikipedia Ihnen in diesem Jahr Wissen im Wert einer Tasse Kaffee geschenkt? Dann entscheiden + Sie sich, eine der seltenen Ausnahmen zu sein, und geben Sie etwas zurück. Vielen Dank! +
++ Unser Spendenziel: 9,7 Millionen € +
++ Hat Wikipedia Ihnen Wissen im Wert einer Tasse Kaffee geschenkt? +
++ Dann entscheiden Sie sich, etwas zurückzugeben. Danke! +
+Jedes Jahr sind wir auf Menschen wie Sie angewiesen. Jährliche Spenden helfen uns' + + ' besonders und ermöglichen langfristige Weiterentwicklungen.
' + + 'Sie gehen kein Risiko ein: Jederzeit formlos zu sofort kündbar.
', + 'upgrade-to-yearly-no': 'Nein, ich spende einmalig {{amount}}', + 'upgrade-to-yearly-yes': 'Ja, ich spende {{amount}} jährlich' +}; + +export default messages; diff --git a/archive/desktop/C23_WMDE_Desktop_DE_15/styles/Banner.scss b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/Banner.scss new file mode 100644 index 000000000..3bd07328c --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/Banner.scss @@ -0,0 +1,23 @@ +@use 'src/themes/Treedip/variables/fonts'; +@use 'src/themes/Treedip/variables/colors'; + +.wmde-banner { + &-wrapper { + font-size: 14px; + font-family: fonts.$ui; + box-shadow: 0 3px 0.6em rgba( 60 60 60 / 40% ); + background-color: colors.$white; + } + + &--pending { + .wmde-banner-wrapper { + box-shadow: none; + } + } + + &--closed { + .wmde-banner-wrapper { + display: none; + } + } +} diff --git a/archive/desktop/C23_WMDE_Desktop_DE_15/styles/FallbackBanner.scss b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/FallbackBanner.scss new file mode 100644 index 000000000..1e6d9ae8a --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/FallbackBanner.scss @@ -0,0 +1,136 @@ +@use 'src/themes/Treedip/variables/globals'; +@use 'src/themes/Treedip/variables/colors'; +@use 'src/themes/Treedip/variables/fonts'; +@use 'sass:color'; + +$breakpoint: 800px; + +.wmde-banner { + &-fallback { + width: 100%; + height: 150px; + display: flex; + flex-direction: column; + background: colors.$white; + box-shadow: 0 3px 0.6em rgba( 60 60 60 / 40% ); + + @media ( min-width: $breakpoint ) { + height: auto; + min-height: 250px; + padding: 4px; + } + + &-small, + &-large { + display: flex; + flex-direction: column; + flex: 1 1 auto; + } + + &-small { + align-items: center; + border: 4px solid colors.$primary; + border-radius: 4px; + padding: 8px; + + .wmde-banner-progress-bar { + height: 25px; + line-height: 25px; + } + + .wmde-banner-progress-bar-text, + .wmde-banner-progress-bar-fill-wrapper { + height: 25px; + } + + .wmde-banner-progress-bar-fill { + line-height: 20px; + } + + .wmde-banner-fallback-button { + margin: 0 0 8px; + } + + .wmde-banner-selection-input-text, + .wmde-banner-selection-input-input { + font-family: fonts.$content; + font-size: 14px; + font-weight: normal; + } + } + + &-large { + align-items: stretch; + } + + &-usage-link { + color: colors.$gray; + } + + &-message { + flex: 1 1 auto; + display: flex; + flex-direction: column; + border: 4px solid colors.$primary; + border-radius: 4px; + padding: 8px 0; + } + + &-bank-item { + display: block; + + &-label { + font-weight: bold; + } + } + + .wmde-banner-close { + height: 16px; + width: 16px; + top: 8px; + right: 8px; + + @media ( min-width: $breakpoint ) { + height: 30px; + width: 30px; + top: 12px; + right: 12px; + } + } + + .wmde-banner-slider-container { + padding: 0 0 8px; + } + + .wmde-banner-slide-content { + font-size: 14px; + p { + margin-bottom: 8px; + } + } + + .wmde-banner-slider-navigation-previous, + .wmde-banner-slider-navigation-next { + align-items: end; + } + + .wmde-banner-slider-pagination-dot { + cursor: default; + } + + .wmde-banner-message { + flex: 1 1 auto; + margin-bottom: 15px; + + p { + margin-bottom: 0; + } + } + } + + &--pending { + .wmde-banner-fallback { + box-shadow: none; + } + } +} diff --git a/archive/desktop/C23_WMDE_Desktop_DE_15/styles/MainBanner.scss b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/MainBanner.scss new file mode 100644 index 000000000..c328cbca9 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/MainBanner.scss @@ -0,0 +1,45 @@ +@use 'src/themes/Treedip/variables/colors'; + +$banner-height: 357px !default; +$form-width: 300px !default; + +.wmde-banner { + &-main { + min-height: $banner-height; + display: flex; + flex-direction: column; + padding: 12px 24px 0; + } + + &-content { + display: flex; + flex-direction: row; + flex-grow: 1; + } + + &-message { + padding: 0 15px; + } + + &-column-left { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1 1 auto; + margin-bottom: 0; + overflow-y: hidden; + margin-right: 30px; + padding: 0 0 10px; + border: 5px solid colors.$primary; + border-radius: 9px; + } + + &-column-right { + order: 2; + flex: 0 0 $form-width; + display: flex; + flex-direction: column; + width: $form-width; + padding: 10px 0; + } +} diff --git a/archive/desktop/C23_WMDE_Desktop_DE_15/styles/styles.scss b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/styles.scss new file mode 100644 index 000000000..05c1818cf --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/styles.scss @@ -0,0 +1,35 @@ +// This is the file where we import the theme-specific component styles + +@use 'src/components/BannerConductor/banner-transition'; +@use 'Banner'; +@use 'MainBanner' with ( + $banner-height: 357px, + $form-width: 300px +); +@use 'src/themes/UseOfFunds/UseOfFunds'; +@use 'src/themes/Treedip/defaults'; +@use 'src/themes/Treedip/ButtonClose/ButtonClose'; +@use 'src/themes/Treedip/ProgressBar/ProgressBar' with ( + $progress-bar-margin: 0 15px +); +@use 'src/themes/Treedip/DonationForm/DonationForm'; +@use 'src/themes/Treedip/DonationForm/MultiStepDonation'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectGroup'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectGroupRadios'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectCustomAmountRadio'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SmsBox'; +@use 'src/themes/Treedip/DonationForm/Forms/MainDonationForm'; +@use 'src/themes/Treedip/DonationForm/Forms/UpgradeToYearlyForm'; +@use 'src/themes/Treedip/DonationForm/Forms/CustomAmountForm'; +@use 'src/themes/Treedip/Footer/FooterAlreadyDonated' with ( + $right-column-width: 300px +); +@use 'src/themes/Treedip/Footer/SelectionInput'; +@use 'src/themes/Treedip/Message/Message'; +@use 'src/themes/Treedip/Slider/KeenSlider'; +@use 'src/themes/Treedip/SoftClose/SoftClose'; +@use 'src/themes/Treedip/AlreadyDonatedModal/AlreadyDonatedModal'; +@use 'FallbackBanner'; +@use 'src/themes/Treedip/FallbackBanner/FallbackButton'; +@use 'src/themes/Treedip/FallbackBanner/LargeFooter'; +@use 'src/themes/Treedip/FallbackBanner/SmallFooter'; diff --git a/archive/desktop/C23_WMDE_Desktop_DE_15/styles/styles_var.scss b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/styles_var.scss new file mode 100644 index 000000000..2be1475c3 --- /dev/null +++ b/archive/desktop/C23_WMDE_Desktop_DE_15/styles/styles_var.scss @@ -0,0 +1,37 @@ +// This is the file where we import the theme-specific component styles + +@use 'src/components/BannerConductor/banner-transition'; +@use 'Banner'; +@use 'MainBanner' with ( + $banner-height: 357px, + $form-width: 300px +); +@use 'src/themes/UseOfFunds/UseOfFunds'; +@use 'src/themes/Treedip/defaults'; +@use 'src/themes/Treedip/ButtonClose/ButtonClose'; +@use 'src/themes/Treedip/ProgressBar/ProgressBar' with ( + $progress-bar-margin: 0 15px +); +@use 'src/themes/Treedip/DonationForm/DonationForm'; +@use 'src/themes/Treedip/DonationForm/MultiStepDonation'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectGroup'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectGroupRadios'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SelectCustomAmountRadio'; +@use 'src/themes/Treedip/DonationForm/SubComponents/SmsBox'; +@use 'src/themes/Treedip/DonationForm/Forms/MainDonationForm'; +@use 'src/themes/Treedip/DonationForm/Forms/UpgradeToYearlyButtonForm' with ( + $font-size: 14px +); +@use 'src/themes/Treedip/DonationForm/Forms/CustomAmountForm'; +@use 'src/themes/Treedip/Footer/FooterAlreadyDonated' with ( + $right-column-width: 300px +); +@use 'src/themes/Treedip/Footer/SelectionInput'; +@use 'src/themes/Treedip/Message/Message'; +@use 'src/themes/Treedip/Slider/KeenSlider'; +@use 'src/themes/Treedip/SoftClose/SoftClose'; +@use 'src/themes/Treedip/AlreadyDonatedModal/AlreadyDonatedModal'; +@use 'FallbackBanner'; +@use 'src/themes/Treedip/FallbackBanner/FallbackButton'; +@use 'src/themes/Treedip/FallbackBanner/LargeFooter'; +@use 'src/themes/Treedip/FallbackBanner/SmallFooter'; diff --git a/banners/desktop/banner_var.ts b/banners/desktop/banner_var.ts index 0f55d41d2..d22c4c932 100644 --- a/banners/desktop/banner_var.ts +++ b/banners/desktop/banner_var.ts @@ -26,7 +26,11 @@ import { LocaleFactoryDe } from '@src/utils/LocaleFactory/LocaleFactoryDe'; import { createFormItems } from './form_items'; import { createFormActions } from '@src/createFormActions'; import { createFallbackDonationLink } from '@src/createFallbackDonationLink'; +import { LinearDailyDonorAverage } from '@src/utils/DynamicContent/LinearDailyDonorAverage'; +import { IntegerDe } from '@src/utils/DynamicContent/formatters/IntegerDe'; +import { visitorsVsDailyDonorsSentence } from './visitorsVsDailyDonorsSentence'; +const date = new Date(); const localeFactory = new LocaleFactoryDe(); const translator = new Translator( messages ); const mediaWiki = new WindowMediaWiki(); @@ -34,6 +38,8 @@ const page = new PageWPORG( mediaWiki, ( new SkinFactory( mediaWiki ) ).getSkin( const runtimeEnvironment = new UrlRuntimeEnvironment( window.location ); const impressionCount = new LocalImpressionCount( page.getTracking().keyword, runtimeEnvironment ); const tracker = new LegacyTrackerWPORG( mediaWiki, page.getTracking().keyword, eventMappings, runtimeEnvironment ); +const campaignParameters = page.getCampaignParameters(); +const dailyDonorAverage = new LinearDailyDonorAverage( 6260, new IntegerDe() ).getDonorStatsForTime( date ); const app = createVueApp( BannerConductor, { page, @@ -55,8 +61,8 @@ const app = createVueApp( BannerConductor, { app.use( TranslationPlugin, translator ); app.use( DynamicTextPlugin, { - campaignParameters: page.getCampaignParameters(), - date: new Date(), + campaignParameters, + date, formatters: localeFactory.getFormatters(), impressionCount, translator @@ -68,5 +74,7 @@ app.provide( 'currencyFormatter', currencyFormatter ); app.provide( 'formItems', createFormItems( translator, currencyFormatter.euroAmount.bind( currencyFormatter ) ) ); app.provide( 'formActions', createFormActions( page.getTracking(), impressionCount, { des: '1' } ) ); app.provide( 'tracker', tracker ); +app.provide( 'dailyDonorAverage', dailyDonorAverage ); +app.provide( 'visitorsVsDailyDonorsSentence', visitorsVsDailyDonorsSentence( campaignParameters.millionImpressionsPerDay, dailyDonorAverage.currentDonorsSoFar ) ); app.mount( page.getBannerContainer() ); diff --git a/banners/desktop/components/BannerVar.vue b/banners/desktop/components/BannerVar.vue index 7725ab495..e63ab01d0 100644 --- a/banners/desktop/components/BannerVar.vue +++ b/banners/desktop/components/BannerVar.vue @@ -35,7 +35,7 @@ -+
diff --git a/banners/desktop/content/BannerSlidesVar.vue b/banners/desktop/content/BannerSlidesVar.vue
new file mode 100644
index 000000000..d17af1850
--- /dev/null
+++ b/banners/desktop/content/BannerSlidesVar.vue
@@ -0,0 +1,60 @@
+
+
+
+ Vielleicht kommen wir gerade ungelegen, aber dennoch: Klicken Sie jetzt bitte nicht weg! Am
+ heutigen {{ currentDayName }}, den {{ currentDate }}, bitten wir Sie, die Unabhängigkeit
+ von Wikipedia zu unterstützen.
+
+ {{ campaignDaySentence }}
+ Die meisten Menschen spenden, weil sie Wikipedia nützlich finden. Die durchschnittliche Spende
+ beträgt 22,25 €, doch bereits 5 € helfen uns weiter.
+
+ Hat Wikipedia Ihnen in diesem Jahr Wissen im Wert einer Tasse Kaffee geschenkt? Dann entscheiden
+ Sie sich, eine der seltenen Ausnahmen zu sein, und geben Sie etwas zurück. Vielen Dank!
+
Jedes Jahr sind wir auf Menschen wie Sie angewiesen. Jährliche Spenden helfen uns' + - ' besonders und ermöglichen langfristige Weiterentwicklungen.
' + - 'Sie gehen kein Risiko ein: Jederzeit formlos zu sofort kündbar.
', - 'upgrade-to-yearly-no': 'Nein, ich spende einmalig {{amount}}', - 'upgrade-to-yearly-yes': 'Ja, ich spende {{amount}} jährlich' + 'soft-close-prompt': 'Dürfen wir später nochmal fragen?' }; export default messages; diff --git a/banners/desktop/styles/MainBanner.scss b/banners/desktop/styles/MainBanner.scss index c328cbca9..a948df468 100644 --- a/banners/desktop/styles/MainBanner.scss +++ b/banners/desktop/styles/MainBanner.scss @@ -22,6 +22,7 @@ $form-width: 300px !default; } &-column-left { + position: relative; display: flex; flex-direction: column; justify-content: center; diff --git a/banners/desktop/styles/styles_var.scss b/banners/desktop/styles/styles_var.scss index 2be1475c3..86d05d589 100644 --- a/banners/desktop/styles/styles_var.scss +++ b/banners/desktop/styles/styles_var.scss @@ -19,19 +19,20 @@ @use 'src/themes/Treedip/DonationForm/SubComponents/SelectCustomAmountRadio'; @use 'src/themes/Treedip/DonationForm/SubComponents/SmsBox'; @use 'src/themes/Treedip/DonationForm/Forms/MainDonationForm'; -@use 'src/themes/Treedip/DonationForm/Forms/UpgradeToYearlyButtonForm' with ( - $font-size: 14px -); +@use 'src/themes/Treedip/DonationForm/Forms/UpgradeToYearlyForm'; @use 'src/themes/Treedip/DonationForm/Forms/CustomAmountForm'; @use 'src/themes/Treedip/Footer/FooterAlreadyDonated' with ( $right-column-width: 300px ); @use 'src/themes/Treedip/Footer/SelectionInput'; @use 'src/themes/Treedip/Message/Message'; -@use 'src/themes/Treedip/Slider/KeenSlider'; +@use '../../../src/themes/Treedip/Slider/KeenSlider' with ( + $slider-padding: 60px 0 16px +); @use 'src/themes/Treedip/SoftClose/SoftClose'; @use 'src/themes/Treedip/AlreadyDonatedModal/AlreadyDonatedModal'; @use 'FallbackBanner'; @use 'src/themes/Treedip/FallbackBanner/FallbackButton'; @use 'src/themes/Treedip/FallbackBanner/LargeFooter'; @use 'src/themes/Treedip/FallbackBanner/SmallFooter'; +@use 'src/themes/Treedip/DonorHeart/DonorHeart'; diff --git a/banners/desktop/visitorsVsDailyDonorsSentence.ts b/banners/desktop/visitorsVsDailyDonorsSentence.ts new file mode 100644 index 000000000..7e5747fce --- /dev/null +++ b/banners/desktop/visitorsVsDailyDonorsSentence.ts @@ -0,0 +1,7 @@ +const text = `Über {{millionImpressionsPerDay}} Millionen Mal wird unser Spendenaufruf täglich + angezeigt, aber nur {{currentDonorsSoFar}} Menschen haben heute gespendet.`; + +export function visitorsVsDailyDonorsSentence( millionImpressionsPerDay: number, currentDonorsSoFar: string ): string { + return text.replace( '{{millionImpressionsPerDay}}', millionImpressionsPerDay.toString() ) + .replace( '{{currentDonorsSoFar}}', currentDonorsSoFar ); +} diff --git a/campaign_info.toml b/campaign_info.toml index 6b7a50288..2eff4e4b1 100644 --- a/campaign_info.toml +++ b/campaign_info.toml @@ -6,9 +6,9 @@ [desktop] name = "Desktop" icon = "desktop" -campaign = "C23_WMDE_Desktop_DE_15" -description = "based on ctrl of test 14, variant has a button yearly upgrade form" -campaign_tracking = "15-ba-231124" +campaign = "C23_WMDE_Desktop_DE_16" +description = "based on ctrl of test 15, variant has a donor heart" +campaign_tracking = "16-ba-231129" preview_link = "/wiki/Wikipedia:Hauptseite?devbanner={{banner}}&banner=B22_WMDE_local_prototype" preview_url = 'https://de.wikipedia.org/wiki/Wikipedia:Hauptseite?banner={{banner}}&devMode' wrapper_template = "wikipedia_org" @@ -17,13 +17,13 @@ use_of_funds_source = "MediaWiki:WMDE_Fundraising/UseOfFunds_2023_DE" # Banners of the campaign, key after "banners" can be anything [desktop.banners.ctrl] filename = "./banners/desktop/banner_ctrl.ts" -pagename = "B23_WMDE_Desktop_DE_15_ctrl" -tracking = "org-15-231124-ctrl" +pagename = "B23_WMDE_Desktop_DE_16_ctrl" +tracking = "org-16-231129-ctrl" [desktop.banners.var] filename = "./banners/desktop/banner_var.ts" -pagename = "B23_WMDE_Desktop_DE_15_var" -tracking = "org-15-231124-var" +pagename = "B23_WMDE_Desktop_DE_16_var" +tracking = "org-16-231129-var" [desktop.test_matrix] platform = ["edge", "firefox_win10", "chrome_win10", "safari", "firefox_macos", "chrome_macos", "firefox_linux", "chrome_linux"] diff --git a/src/themes/Treedip/Slider/KeenSlider.scss b/src/themes/Treedip/Slider/KeenSlider.scss index 8b19764f2..3c489912a 100644 --- a/src/themes/Treedip/Slider/KeenSlider.scss +++ b/src/themes/Treedip/Slider/KeenSlider.scss @@ -1,8 +1,11 @@ @use '../variables/colors'; @use '../variables/fonts'; +@use '../variables/breakpoints'; @use 'src/components/Slider/KeenSlider'; $font: fonts.$content !default; +$font-size: 1.3em !default; +$line-height: 1.5 !default; $slider-padding: 16px 0 !default; .wmde-banner { @@ -10,8 +13,14 @@ $slider-padding: 16px 0 !default; &-slider-container { font-family: $font; - font-size: 1.3em; + font-size: 1.8vw; padding: $slider-padding; + line-height: 1.2; + + @include breakpoints.small-up { + font-size: $font-size; + line-height: $line-height; + } } &-slide { diff --git a/src/tracking/LegacyEventTracking/mapShownEvent.ts b/src/tracking/LegacyEventTracking/mapShownEvent.ts index 507b1e67f..811a5acb9 100644 --- a/src/tracking/LegacyEventTracking/mapShownEvent.ts +++ b/src/tracking/LegacyEventTracking/mapShownEvent.ts @@ -8,6 +8,6 @@ export function mapShownEvent( shownEvent: ShownEvent ): WMDESizeIssueEvent|WMDE return new WMDESizeIssueEvent( `fallback-banner-shown`, createViewportInfo(), 1 ); } - // We don't track other "not shown" events, hence the trackingRate of 0 + // We don't track other "shown" events, hence the trackingRate of 0 return new WMDELegacyBannerEvent( 'untracked-shown-event', 0 ); } diff --git a/test/banners/desktop/components/BannerVar.spec.ts b/test/banners/desktop/components/BannerVar.spec.ts index 31377ce39..2c07f9286 100644 --- a/test/banners/desktop/components/BannerVar.spec.ts +++ b/test/banners/desktop/components/BannerVar.spec.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, test, vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; import { mount, VueWrapper } from '@vue/test-utils'; import Banner from '../../../../banners/desktop/components/BannerVar.vue'; import { BannerStates } from '@src/components/BannerConductor/StateMachine/BannerStates'; @@ -10,15 +10,16 @@ import { TrackerStub } from '@test/fixtures/TrackerStub'; import { softCloseFeatures } from '@test/features/SoftCloseDesktop'; import { useOfFundsFeatures } from '@test/features/UseOfFunds'; import { - bannerContentAnimatedTextFeatures, bannerContentDateAndTimeFeatures, + bannerContentDateAndTimeFeatures, bannerContentDisplaySwitchFeatures, bannerContentFeatures } from '@test/features/BannerContent'; -import { donationFormFeatures } from '@test/features/forms/MainDonation_UpgradeToYearlyButton'; +import { donationFormFeatures } from '@test/features/forms/MainDonation_UpgradeToYearlyLink'; import { useFormModel } from '@src/components/composables/useFormModel'; import { resetFormModel } from '@test/resetFormModel'; import { DynamicContent } from '@src/utils/DynamicContent/DynamicContent'; import { bannerMainFeatures } from '@test/features/MainBanner'; +import { donorHeartFeatures, testDonorHeartValues } from '@test/features/DonorHeart'; const formModel = useFormModel(); const translator = ( key: string ): string => key; @@ -37,7 +38,6 @@ describe( 'BannerVar.vue', () => { const getWrapper = ( dynamicContent: DynamicContent = null ): VueWrapper