Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/api-generator/src/locale/en/VHighlight.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"props": {
"query": "The search string or array of strings to highlight within the text.",
"matches": "Pre-computed match ranges as `[start, end]` pairs. Takes priority over **query** when provided and non-empty.",
"matchAll": "When enabled, all occurrences of each query term are highlighted. When disabled, only the first occurrence of each term is highlighted. Has no effect when **matches** is provided.",
"ignoreCase": "When enabled, matching is case-insensitive.",
"color": "Applies a theme or CSS color to highlighted matches. The background is derived by mixing this color with the theme's highlight opacity.",
"opacity": "Overrides the background opacity of highlighted matches. Accepts only CSS `<percentage>` - use `30%` instead of `0.3`.",
"markClass": "Additional CSS class(es) applied to each `<mark>` element wrapping a highlighted match."
}
}
4 changes: 4 additions & 0 deletions packages/docs/src/data/nav.json
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,10 @@
"title": "file-upload",
"subfolder": "components"
},
{
"title": "highlights",
"subfolder": "components"
},
{
"title": "icon-buttons",
"subfolder": "components"
Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/data/page-to-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"components/footers": ["VFooter"],
"components/forms": ["VForm"],
"components/grids": ["VCol", "VContainer", "VRow", "VSpacer"],
"components/highlights": ["VHighlight"],
"components/hotkeys": ["VHotkey"],
"components/hover": ["VHover"],
"components/icon-buttons": ["VIconBtn"],
Expand Down
53 changes: 53 additions & 0 deletions packages/docs/src/examples/v-highlight/misc-css-variables.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<template>
<v-container class="pt-8">
<v-row>
<v-col cols="12" md="6">
<v-combobox
:items="items"
:list-props="{ bgColor: 'primary', class: 'highlight-on-dark' }"
label="Custom list background"
multiple
></v-combobox>
</v-col>

<v-col cols="12" md="6">
<v-combobox
:items="items"
:list-props="{ class: 'fancy-yellow-highlight' }"
label="Custom highlight style"
multiple
></v-combobox>
</v-col>
</v-row>
</v-container>
</template>

<script setup>
const items = [
'Programming',
'Design',
'Vue',
'Vuetify',
'Gaming',
'Music',
'Cooking',
'Photography',
]
</script>

<style>
.highlight-on-dark .v-highlight__mark {
--v-highlight-opacity: 30%;
}

.fancy-yellow-highlight {
--v-highlight-background: yellow;
--v-highlight-color: #000;
--v-highlight-border-radius: 3px;

.v-highlight__mark {
box-shadow: 0 3px 0 color-mix(in srgb, yellow 70%, #444),
0 -3px 0 color-mix(in srgb, yellow 70%, #444);
}
}
</style>
63 changes: 63 additions & 0 deletions packages/docs/src/examples/v-highlight/misc-selection-match.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<template>
<div class="pa-6">
<p class="text-caption text-medium-emphasis mb-3">
Click a word to highlight all matches &mdash; or select a phrase.
</p>

<v-highlight
:query="query"
:text="text"
class="selection-target text-body-2 pa-4 rounded border overflow-x-auto"
tag="pre"
ignore-case
match-all
@mouseup="onMouseUp"
></v-highlight>
</div>
</template>

<script setup>
const query = shallowRef('')

const text = `# v-highlight

## Overview

The v-highlight component renders text with highlighted
substrings. Use the query prop to pass a search term,
or the matches prop for pre-computed match ranges.

## Props

- text: The source text string to render
- query: A string or array of strings to highlight
- matches: Pre-computed [start, end] match ranges
- markClass: Custom CSS class for each mark element
- tag: The root element tag (default: span)`

function onMouseUp () {
const selection = window.getSelection()
if (!selection) return

const selected = selection.toString().trim()

if (selected) {
query.value = selected
return
}

selection.modify('move', 'backward', 'word')
selection.modify('extend', 'forward', 'word')
const word = selection.toString().replace(/\W/g, '')
if (word) query.value = word
}
</script>

<style scoped>
:deep(.selection-target) {
&::selection,
*::selection {
background-color: rgba(255, 190, 0, 0.3);
}
}
</style>
58 changes: 58 additions & 0 deletions packages/docs/src/examples/v-highlight/prop-matches.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<div class="pa-6" style="max-width: 480px; margin: 0 auto">
<v-text-field
v-model="search"
class="mb-4"
density="compact"
label="Fuzzy search"
variant="outlined"
clearable
hide-details
></v-text-field>

<v-list lines="two" border rounded>
<template v-if="results.length">
<v-list-item v-for="item in results" :key="item.title" :subtitle="item.subtitle">
<template v-slot:title>
<v-highlight :matches="item.matches" :text="item.title"></v-highlight>
</template>
</v-list-item>
</template>

<v-list-item v-else class="text-medium-emphasis" title="No results"></v-list-item>
</v-list>
</div>
</template>

<script setup>
const search = shallowRef('scrt')

const items = [
{ title: 'JavaScript', subtitle: 'Dynamic scripting for the web' },
{ title: 'TypeScript', subtitle: 'Typed superset of JavaScript' },
{ title: 'CoffeeScript', subtitle: 'Syntactic sugar for JavaScript' },
{ title: 'Python', subtitle: 'General-purpose interpreted language' },
{ title: 'Rust', subtitle: 'Systems language without a GC' },
{ title: 'Ruby', subtitle: 'Optimised for developer happiness' },
]

function fuzzy (text, query) {
const lower = text.toLocaleLowerCase()
const spans = []
let pos = 0
for (const ch of query.toLocaleLowerCase()) {
pos = lower.indexOf(ch, pos)
if (pos < 0) return null
spans.push([pos, ++pos])
}
return spans
}

const results = computed(() => {
const query = search.value?.trim()
if (!query) return items.map(item => ({ ...item, matches: undefined }))
return items
.map(item => ({ ...item, matches: fuzzy(item.title, query) ?? undefined }))
.filter(item => item.matches)
})
</script>
22 changes: 22 additions & 0 deletions packages/docs/src/examples/v-highlight/prop-query.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<div class="pa-6" style="max-width: 600px; margin: 0 auto">
<v-combobox
v-model="terms"
class="mb-6"
label="Highlight terms"
variant="outlined"
chips
closable-chips
hide-details
multiple
></v-combobox>

<v-highlight :query="terms" :text="text" class="text-body-1" tag="p"></v-highlight>
</div>
</template>

<script setup>
const terms = shallowRef(['TypeScript', 'JavaScript'])

const text = 'TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale. JavaScript itself remains the language of the web — TypeScript simply adds a type layer on top. Any valid JavaScript program is also a valid TypeScript program, so the two languages always stay in sync.'
</script>
33 changes: 33 additions & 0 deletions packages/docs/src/examples/v-highlight/props-mark-class.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<div class="pa-6" style="max-width: 560px; margin: 0 auto">
<v-text-field
v-model="query"
class="mb-6"
density="compact"
label="Search"
variant="outlined"
clearable
hide-details
></v-text-field>

<v-highlight
:query="query"
:text="text"
class="text-body-1"
mark-class="bg-primary-tint font-weight-medium border-b border-primary border-opacity-100"
tag="p"
></v-highlight>
</div>
</template>

<script setup>
const query = shallowRef('color')

const text = 'Color theory forms the foundation of visual design. Understanding how hues, saturation, and value interact helps designers create harmony and contrast. Primary colors — red, blue, and yellow — combine to form secondary colors. Complementary colors sit opposite each other on the color wheel and create maximum visual contrast when paired.'
</script>

<style scoped>
:deep(.v-highlight) {
.bg-primary-tint { background: rgba(var(--v-theme-primary), 0.4); }
}
</style>
55 changes: 55 additions & 0 deletions packages/docs/src/examples/v-highlight/usage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<ExamplesUsageExample
v-model="model"
:code="code"
:name="name"
:options="options"
>
<v-container max-width="550">
<v-text-field
v-model="query"
class="mb-5"
density="compact"
label="Search"
variant="outlined"
clearable
hide-details
></v-text-field>

<v-highlight
v-bind="props"
class="text-body-1"
></v-highlight>
</v-container>

<template v-slot:configuration>
<v-select v-model="color" :items="colors" label="Color" clearable></v-select>
<v-checkbox v-model="matchAll" label="Match all"></v-checkbox>
<v-checkbox v-model="ignoreCase" label="Ignore case"></v-checkbox>
</template>
</ExamplesUsageExample>
</template>

<script setup>
const name = 'v-highlight'
const model = shallowRef('default')
const options = []
const query = shallowRef('framework')
const matchAll = shallowRef(false)
const ignoreCase = shallowRef(false)
const color = shallowRef()
const colors = ['primary', '#ac46ff', 'orange-darken-2']

const text = 'Vue is a progressive JavaScript framework for building user interfaces. Unlike monolithic frameworks, Vue is designed to be incrementally adoptable. The core library focuses on the view layer only, making it easy to integrate with other libraries. Thousands of companies use Vue in production today.'

const props = computed(() => ({
text,
query: query.value || undefined,
matchAll: matchAll.value || undefined,
ignoreCase: ignoreCase.value || undefined,
color: color.value || undefined,
tag: 'p',
}))

const code = computed(() => `<${name}${propsToString(props.value)} />`)
</script>
Loading
Loading