Skip to content

Commit

Permalink
feat(highlight): migrate highlight component to composition API
Browse files Browse the repository at this point in the history
  • Loading branch information
victorcg88 committed May 29, 2024
1 parent 1bffd7c commit 0c19082
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 95 deletions.
1 change: 1 addition & 0 deletions packages/_vue3-migration-test/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { default as TestBaseEventButton } from './test-base-event-button.vue';
export { default as TestBaseVariableColumnGrid } from './test-base-variable-column-grid.vue';
export { default as TestSlidingPanel } from './test-sliding-panel.vue';
export { default as TestUseLayouts } from './test-use-layouts.vue';
export { default as TestHighlight } from './test-highlight.vue';
33 changes: 33 additions & 0 deletions packages/_vue3-migration-test/src/components/test-highlight.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<Highlight
:text="'Almendra garrapiñada'"
:highlight="'men'"
match-class="match-class"
no-match-class="no-match-class"
matching-part-class="matching-part-class"
/>
<br />
<Highlight
:text="'Almendra garrapiñada'"
:highlight="'tar'"
match-class="match-class"
no-match-class="no-match-class"
matching-part-class="matching-part-class"
/>
</template>

<script setup lang="ts">
import Highlight from '../../../x-components/src/components/highlight.vue';
</script>

<style>
.match-class {
color: red;
}
.matching-part-class {
color: greenyellow;
}
.no-match-class {
color: blue;
}
</style>
8 changes: 7 additions & 1 deletion packages/_vue3-migration-test/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
TestBaseVariableColumnGrid,
TestEmpathize,
TestUseLayouts,
TestSlidingPanel
TestSlidingPanel,
TestHighlight
} from './';

const routes = [
Expand Down Expand Up @@ -134,6 +135,11 @@ const routes = [
path: '/test-use-layouts',
name: 'TestUseLayouts',
component: TestUseLayouts
},
{
path: '/highlight',
name: 'Highlight',
component: TestHighlight
}
];

Expand Down
207 changes: 113 additions & 94 deletions packages/x-components/src/components/highlight.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,113 +27,132 @@
</template>

<script lang="ts">
import { Component, Mixins, Prop } from 'vue-property-decorator';
import { computed, defineComponent } from 'vue';
import { normalizeString } from '../utils/normalize';
import { VueCSSClasses } from '../utils/types';
import { NoElement } from './no-element';
import { dynamicPropsMixin } from './dynamic-props.mixin';
/**
* Highlights the given part of the text. The component is smart enough to do matches
* between special characters like tilde, cedilla, eñe, capital letters...
*
* @public
*/
@Component({
components: { NoElement }
})
export default class Highlight extends Mixins(
dynamicPropsMixin(['noMatchClass', 'matchingPartClass'])
) {
/**
* The text to highlight some part of it.
*
* @public
*/
@Prop({ default: '' })
public text!: string;
/**
* The part of the text to be highlighted.
*
* @public
*/
@Prop({ default: '' })
public highlight!: string;
/**
* CSS Class to add when the `text` string contains the `highlight` string.
*/
@Prop({ default: '' })
public matchClass!: string;
/**
* Checks if the normalized suggestion query matches with the module's query, so it has a
* matching part.
*
* @returns True if there is a match between the text and the highlight strings.
* @internal
*/
protected get hasMatch(): boolean {
return !!this.matchParts.match;
}
/**
* CSS classes to add depending on the component state.
*
* @remarks
* `x-highlight--has-match`: When there is a match between the text and the part to highlight.
* `[matchClass]`: When there is a match between the text and
* the part to highlight.
* `[noMatchClass]`: when there is no match between the text to highlight.
* @returns The {@link VueCSSClasses} classes.
* @internal
*/
protected get dynamicCSSClasses(): VueCSSClasses {
const classes: VueCSSClasses = {
'x-highlight--has-match': this.hasMatch,
'x-highlight-text': this.hasMatch,
[this.matchClass]: this.hasMatch
};
if (this.noMatchClass) {
classes[this.noMatchClass] = !this.hasMatch;
export default defineComponent({
name: 'Highlight',
components: { NoElement },
props: {
/**
* The text to highlight some part of it.
*
* @public
*/
text: {
type: String,
default: ''
},
/**
* The part of the text to be highlighted.
*
* @public
*/
highlight: {
type: String,
default: ''
},
/**
* CSS Class to add when the `text` string contains the `highlight` string.
*/
matchClass: {
type: String,
default: ''
},
/**
* CSS Class to add when the given `text` doesn't contain the `highlight` string.
*/
noMatchClass: {
type: String,
default: ''
},
/**
* CSS Class to add to the matching text.
*/
matchingPartClass: {
type: String,
default: ''
}
},
setup: function (props) {
/**
* Splits the text to highlight into 3 parts: a starting part, the matching part
* and the ending part. If there is no match between the text and the highlight, the `start`
* property will contain the whole text.
*
* @returns An object containing the different parts of the text.
* @internal
*/
const matchParts = computed((): HighlightMatch => {
const matcherIndex = normalizeString(props.text).indexOf(normalizeString(props.highlight));
return matcherIndex !== -1 && props.highlight
? splitAt(props.text.trim(), matcherIndex, matcherIndex + props.highlight.trim().length)
: { start: props.text, match: '', end: '' };
});
/**
* Checks if the normalized suggestion query matches with the module's query, so it has a
* matching part.
*
* @returns True if there is a match between the text and the highlight strings.
* @internal
*/
const hasMatch = computed((): boolean => {
return !!matchParts.value.match;
});
/**
* CSS classes to add depending on the component state.
*
* @remarks
* `x-highlight--has-match`: When there is a match between the text and the part to highlight.
* `[matchClass]`: When there is a match between the text and
* the part to highlight.
* `[noMatchClass]`: when there is no match between the text to highlight.
* @returns The {@link VueCSSClasses} classes.
* @internal
*/
const dynamicCSSClasses = computed((): VueCSSClasses => {
const classes: VueCSSClasses = {
'x-highlight--has-match': hasMatch.value,
'x-highlight-text': hasMatch.value,
[props.matchClass]: hasMatch.value
};
if (props.noMatchClass) {
classes[props.noMatchClass] = !hasMatch.value;
}
return classes;
});
/**
* Splits the label in three parts based on two indexes.
*
* @param label - The string that will be divided in three parts.
* @param start - The first index that the label will be divided by.
* @param end - The second index that the label will be divided by.
*
* @returns The three parts of the divided label.
* @internal
*/
function splitAt(label: string, start: number, end: number): HighlightMatch {
return {
start: label.substring(0, start),
match: label.substring(start, end),
end: label.substring(end)
};
}
return classes;
}
/**
* Splits the text to highlight into 3 parts: a starting part, the matching part
* and the ending part. If there is no match between the text and the highlight, the `start`
* property will contain the whole text.
*
* @returns An object containing the different parts of the text.
* @internal
*/
protected get matchParts(): HighlightMatch {
const matcherIndex = normalizeString(this.text).indexOf(normalizeString(this.highlight));
return matcherIndex !== -1 && this.highlight
? this.splitAt(this.text.trim(), matcherIndex, matcherIndex + this.highlight.trim().length)
: { start: this.text, match: '', end: '' };
}
/**
* Splits the label in three parts based on two indexes.
*
* @param label - The string that will be divided in three parts.
* @param start - The first index that the label will be divided by.
* @param end - The second index that the label will be divided by.
*
* @returns The three parts of the divided label.
* @internal
*/
protected splitAt(label: string, start: number, end: number): HighlightMatch {
return {
start: label.substring(0, start),
match: label.substring(start, end),
end: label.substring(end)
};
return { hasMatch, matchParts, dynamicCSSClasses };
}
}
});
/**
* Contains the different parts of a string match.
Expand Down

0 comments on commit 0c19082

Please sign in to comment.