Skip to content

A Vue 3 text input component, that let's you style text with Regex and UnoCSS in real time

License

Notifications You must be signed in to change notification settings

Rettend/vue-text-styler

Repository files navigation

vue-text-styler

Netlify Status

Demo

📦 Install

npm i vue-text-styler
yarn add vue-text-styler
pnpm i vue-text-styler
// main.ts
import TextStyler from 'vue-text-styler'

// for global registration
app.component(TextStyler)

🧰 Props

Name Type Default Description
text string A string representing the text to be displayed in the component.
special Record<string, string | string[] | RegExp | RegExp[]> An object that contains key-value pairs. The keys represent the classes that will be applied to the text that matches the value. The value can be a string or an array of strings representing the text that will be matched.
line? 'single' | 'multiple' 'multiple' By default, the component acts like a textarea (though you have to use Shift+Enter). Set this prop to 'single' to force it to display the text in a single line. You propably also want to add @keydown.enter.prevent or e.preventDefault().
track? string | string[] A string or an array of strings from the values of special that will be tracked. If set, the component will emit the strings that were matched.
readonly? boolean false A boolean value indicating whether the component is read-only or not. If set to true, the component will be read-only.

📜 Events

Name Type Description
update:text string Emitted when the text changes.
tracked string[] Emitted when the text matches any of the values of track.

🚀 Usage

Simple example:

<script setup lang="ts">
import { ref } from 'vue'

const text = ref('text1 and text2 are here')

const special = {
  'text-red-600 font-bold': ['text1', 'text2'],
  'underline': 'here',
}
</script>

<template>
  <TextStyler
    v-model:text="text"
    :special="special"
    p-3 bg-gray-100 rounded-xl
  />
</template>

A bit more complex example, where your text is an external object:

<script setup lang="ts">
import { computed, reactive, ref } from 'vue'

const text = ref('I\'m a large red circle.')

const object = reactive({
  color: 'red',
  shape: 'circle',
  size: 'large',
})

const special = computed(() => {
  return {
    'text-red-5 font-bold': object.color,
    'text-blue-5 font-bold': object.shape,
    'text-green-5 font-bold': object.size,
  }
})
</script>

<template>
  <TextStyler
    v-model:text="text"
    :special="special"
    p-3 bg-gray-100 rounded-xl
  />
</template>

A ridiculously complex example, where your text is an external array of objects for some unbeknownst reason:

<script setup lang="ts">
import { computed, reactive, ref } from 'vue'

const text = ref('I\'m John and I\'m 20 years old.\nAnd I\'m Jane and I\'m 21 years old.')

const object = reactive([
  {
    name: 'Jane',
    age: 21,
  },
  {
    name: 'John',
    age: 20,
  },
],
)

const keyNames = {
  name: 'text-green-5 font-bold',
  age: 'text-blue-5 font-bold',
}

const special = computed(() => {
  return {
    ...object.reduce((acc, prop) => {
      // loop over the properties of prop
      for (const key in prop) {
        // use the property value as the key name
        const value = prop[key as keyof typeof prop]
        // use the key name from the keyNames object
        const keyName = keyNames[key as keyof typeof keyNames]
        // check if the key name already exists in the accumulator object
        if (acc[keyName]) {
          // append the new value to the existing array
          acc[keyName].push(value.toString())
        }
        else {
          // create a new array with the first value
          acc[keyName] = [value.toString()]
        }
      }
      return acc
    }, {} as Record<string, string[]>),
  }
})
</script>

<template>
  <TextStyler
    v-model:text="text"
    :special="special"
    p-3 bg-gray-100 rounded-xl
  />
</template>

Note: find more examples in the demo playground

⚠️ Limitations

  • Don't use in Nuclear reactor code, as it uses innerHTML with user-provided content, see more: Vue Security. It might be the case that despite Vue escaping js, and we using html sanitization, it is still vulnerable to attacks. I tried most of these and they didn't work though, so idk.
  • Korean keyboard is broken. The component uses innerHTML on every character so this breaks the Korean input completely. I also tested the Chinese input and that's sometimes a little weird too, so other keyboard layouts may have issues as well.

License

MIT