114 lines
3.1 KiB
Vue
114 lines
3.1 KiB
Vue
<template>
|
|
<div class="word-counter-varchar d-flex flex-row justify-content-around align-items-center w-100">
|
|
<span>
|
|
{{ trans.methods.__("vue.WordCounterComponent.words") }} : <b>{{ wordsCount }}</b>
|
|
</span>
|
|
<span>
|
|
{{ trans.methods.__("vue.WordCounterComponent.characters") }} : <b>{{ charactersCount }}</b>
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, defineOptions, onMounted, ref, useAttrs, watch } from "vue";
|
|
import trans from "../../modules/trans";
|
|
|
|
defineOptions({
|
|
name: "WordCounterComponent"
|
|
});
|
|
|
|
// * ATTRIBUTES
|
|
const attrs = useAttrs();
|
|
|
|
// * PROPS
|
|
const props = defineProps({
|
|
id: {
|
|
type: String,
|
|
default: String(Math.pow(10, 16) / Math.random())
|
|
},
|
|
});
|
|
|
|
// * DATA
|
|
const id = ref<string>(props.id);
|
|
const wordsCount = ref<number>(0);
|
|
const charactersCount = ref<number>(0);
|
|
const input = ref<HTMLInputElement|HTMLTextAreaElement|null>(null);
|
|
|
|
// * COMPUTED
|
|
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
|
|
|
|
// * LIFECYCLE
|
|
onMounted(() => {
|
|
const json = String(attrs.json ?? "{}"),
|
|
data = JSON.parse(json);
|
|
if (!isUsedWithProps.value) {
|
|
id.value = data.id;
|
|
}
|
|
input.value = checkInput(document.getElementById(id.value));
|
|
});
|
|
|
|
// * WATCHERS
|
|
|
|
/** Update counters when the value of input change. */
|
|
watch(input, () => {
|
|
input.value?.addEventListener("input", (e) => {
|
|
updateValues(checkInput(e.target).value);
|
|
});
|
|
updateValues(input.value?.value ?? "");
|
|
});
|
|
|
|
// * METHODS
|
|
|
|
/** Check if there is an input or textearea. */
|
|
function checkInput(input: HTMLElement|EventTarget|null): HTMLInputElement|HTMLTextAreaElement {
|
|
if (
|
|
!input ||
|
|
!(
|
|
input instanceof HTMLInputElement ||
|
|
input instanceof HTMLTextAreaElement
|
|
)
|
|
) {
|
|
throw new Error(`${id.value} is not a input or textarea`);
|
|
}
|
|
return input;
|
|
}
|
|
|
|
/** Update counters values. */
|
|
function updateValues(string: string) {
|
|
let chars, words;
|
|
[words, chars] = calculateValues(string);
|
|
wordsCount.value = words;
|
|
charactersCount.value = chars;
|
|
}
|
|
|
|
/** Calculate number of words and length of the string. */
|
|
function calculateValues(string: string) {
|
|
const arr = string.split(" ");
|
|
return [arr.filter((word) => word !== "").length, string.length];
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scopped>
|
|
#word-counter + input[type="text"],
|
|
.word-counter + input[type="text"],
|
|
#word-counter + textarea,
|
|
.word-counter + textarea,
|
|
#word-counter + .input-group > input[type="password"],
|
|
.word-counter + .input-group > input[type="password"],
|
|
#word-counter-varchar + input[type="text"],
|
|
.word-counter-varchar + input[type="text"],
|
|
#word-counter-varchar + textarea,
|
|
.word-counter-varchar + textarea,
|
|
#word-counter-varchar + .input-group > input[type="password"],
|
|
.word-counter-varchar + .input-group > input[type="password"] {
|
|
border-radius: 0 0 0.375rem 0.375rem;
|
|
}
|
|
.word-counter-varchar {
|
|
color: var(--bs-body-color);
|
|
border: var(--bs-border-width) solid var(--bs-border-color);
|
|
border-bottom: 0;
|
|
background-color: rgb(var(--bs-secondary-bg-rgb));
|
|
border-top-left-radius: 0.375rem;
|
|
border-top-right-radius: 0.375rem;
|
|
}
|
|
</style>
|