All files / ext.wikilambda.app/components/visualeditor ExpandableDescription.vue

99.37% Statements 160/161
100% Branches 7/7
100% Functions 3/3
99.37% Lines 160/161

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 1621x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 16x 16x 16x 6x 6x 6x 6x 6x 2x 2x 6x 6x 6x 6x 6x 6x 6x 1x 6x 6x 6x 6x 6x 6x 6x 6x 6x 4x 6x 6x 6x 6x 6x 6x 6x 6x 6x   6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
<!--
	WikiLambda Vue component for Visual Editor Wikifunctions function call
	insertion and edit plugin.
 
	@copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
	@license MIT
-->
<template>
	<div class="ext-wikilambda-app-expandable-description">
		<span
			ref="descriptionComponent"
			class="ext-wikilambda-app-expandable-description__text"
			:class="{ 'ext-wikilambda-app-expandable-description__text--expanded': isExpanded }"
			:lang="description.langCode"
			:dir="description.langDir"
		>
			{{ description.label }}
		</span>
		<button
			v-if="isExpandable"
			type="button"
			class="ext-wikilambda-app-expandable-description__toggle-button"
			@click.stop="toggleExpanded"
		>
			{{ isExpanded ?
				i18n( 'wikilambda-visualeditor-wikifunctionscall-dialog-read-less-description' ) :
				i18n( 'wikilambda-visualeditor-wikifunctionscall-dialog-read-more-description' ) }}
		</button>
	</div>
</template>
 
<script>
const { defineComponent, inject, onBeforeUnmount, onMounted, ref, watch } = require( 'vue' );
const { throttle } = require( '../../utils/miscUtils.js' );
const LabelData = require( '../../store/classes/LabelData.js' );
 
module.exports = exports = defineComponent( {
	name: 'wl-expandable-description',
	props: {
		description: {
			type: LabelData,
			required: true
		}
	},
	setup( props ) {
		const i18n = inject( 'i18n' );
 
		// State
		const isExpanded = ref( false );
		const isExpandable = ref( false );
		const descriptionComponent = ref( null );
		let throttledResizeHandler = null;
 
		// Expansion actions
		/**
		 * Checks if the description is clamped and updates `isExpandable`.
		 */
		function checkClamped() {
			const element = descriptionComponent.value;
			isExpandable.value = element && element.scrollHeight > element.clientHeight;
		}
 
		/**
		 * Toggles the expanded state of the description.
		 */
		function toggleExpanded() {
			isExpanded.value = !isExpanded.value;
		}
 
		// Watch
		/**
		 * Re-checks clamping when the description changes.
		 * This is needed because on mounted the scrollHeight and clientHeight might be 0
		 */
		watch( () => props.description, () => {
			checkClamped();
		} );
 
		// Lifecycle
		/**
		 * Sets up resize handling and checks clamping on mount.
		 */
		onMounted( () => {
			// Use a small delay to ensure the DOM is fully rendered
			setTimeout( () => {
				checkClamped();
			}, 10 );
			throttledResizeHandler = throttle( checkClamped, 200 );
			window.addEventListener( 'resize', throttledResizeHandler );
		} );
 
		/**
		 * Cleans up resize handling before unmounting.
		 */
		onBeforeUnmount( () => {
			window.removeEventListener( 'resize', throttledResizeHandler );
		} );
 
		return {
			descriptionComponent,
			isExpandable,
			isExpanded,
			toggleExpanded,
			i18n
		};
	}
} );
</script>
 
<style lang="less">
@import 'mediawiki.skin.variables.less';
 
.ext-wikilambda-app-expandable-description {
	position: relative;
 
	&__toggle-button {
		.cdx-mixin-link();
		background: var( --background-color-base );
		border: 0;
		padding: 0;
		position: absolute;
		right: 0;
		bottom: 0;
		line-height: var( --line-height-current );
		font-size: inherit;
 
		&::before {
			content: '';
			position: absolute;
			top: 0;
			left: -80px;
			width: 80px;
			height: 100%;
			background: linear-gradient( to right, transparent, var( --background-color-base ) );
		}
	}
 
	&__text {
		margin: 0;
		padding: 0;
		overflow: hidden;
		-webkit-box-orient: vertical;
		-webkit-line-clamp: 2;
		line-clamp: 2;
		display: -webkit-box;
		line-height: var( --line-height-current );
 
		&--expanded {
			-webkit-line-clamp: unset;
			line-clamp: unset;
			display: block;
 
			& + .ext-wikilambda-app-expandable-description__toggle-button {
				display: flex;
				margin-left: auto;
				position: initial;
			}
		}
	}
}
</style>