Skip to content

Commit 7e31d69

Browse files
committed
Parameterise annotate and add highlight animator
1 parent 5a387f2 commit 7e31d69

22 files changed

+1291
-290
lines changed

annotate0.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Annotate</title>
8+
<style>
9+
.highlight {
10+
background-color: yellow;
11+
}
12+
</style>
13+
</head>
14+
<body>
15+
<h1>Hello World</h1>
16+
<p>
17+
arbitrary text selection works on simple text
18+
</p>
19+
<h2>posted text</h2>
20+
<p id="text">
21+
This
22+
is
23+
a
24+
simple
25+
text
26+
to
27+
experiment
28+
with
29+
text
30+
range
31+
selection
32+
and
33+
highlighting.
34+
</p>
35+
<button onclick="highlightSelection()">Highlight Selection</button>
36+
37+
<script>
38+
function highlightSelection() {
39+
const selection = window.getSelection();
40+
if (selection.rangeCount > 0) {
41+
const range = selection.getRangeAt(0);
42+
const span = document.createElement('span');
43+
span.className = 'highlight';
44+
range.surroundContents(span);
45+
}
46+
}
47+
</script>
48+
</body>
49+
</html>

annotate1.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Annotate</title>
8+
<style>
9+
.highlight {
10+
background-color: yellow;
11+
}
12+
</style>
13+
</head>
14+
<body>
15+
<h1>Hello World</h1>
16+
<p>
17+
arbitrary text selection breaks on text with spans, but still works for word selections but not cross word selections
18+
</p>
19+
<h2>posted text</h2>
20+
<p id="text">
21+
<span>This </span>
22+
<span>is </span>
23+
<span>a </span>
24+
<span>simple </span>
25+
<span>text </span>
26+
<span>to </span>
27+
<span>experiment </span>
28+
<span>with </span>
29+
<span>text </span>
30+
<span>range </span>
31+
<span>selection </span>
32+
<span>and </span>
33+
<span>highlighting.</span>
34+
</p>
35+
<button onclick="highlightSelection()">Highlight Selection</button>
36+
37+
38+
<script>
39+
function highlightSelection() {
40+
const selection = window.getSelection();
41+
if (selection.rangeCount > 0) {
42+
const range = selection.getRangeAt(0);
43+
const span = document.createElement('span');
44+
span.className = 'highlight';
45+
range.surroundContents(span);
46+
}
47+
}
48+
</script>
49+
</body>
50+
</html>

annotate2.html

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Annotate</title>
8+
<style>
9+
.highlight {
10+
background-color: yellow;
11+
}
12+
</style>
13+
</head>
14+
<body>
15+
<h1>Hello World</h1>
16+
<p>
17+
arbitrary text selection breaks on text with spans, but can be restored by using the cloneContents method
18+
</p>
19+
<h2>posted text</h2>
20+
<p id="text">
21+
<span id="word1">This </span>
22+
<span id="word2">is </span>
23+
<span id="word3">a </span>
24+
<span id="word4">simple </span>
25+
<span id="word5">text </span>
26+
<span id="word6">to </span>
27+
<span id="word7">experiment </span>
28+
<span id="word8">with </span>
29+
<span id="word9">text </span>
30+
<span id="word10">range </span>
31+
<span id="word11">selection </span>
32+
<span id="word12">and </span>
33+
<span id="word13">highlighting.</span>
34+
</p>
35+
<button onclick="highlightSelection()">Highlight Selection</button>
36+
<p id="selectedRange"></p>
37+
<script>
38+
document.addEventListener('selectionchange', () => {
39+
const selection = window.getSelection();
40+
if (selection.rangeCount > 0) {
41+
const range = selection.getRangeAt(0);
42+
const selectedText = range.toString();
43+
document.getElementById('selectedRange').textContent = selectedText;
44+
}
45+
});
46+
function highlightSelection() {
47+
const selection = window.getSelection();
48+
if (selection.rangeCount > 0) {
49+
const range = selection.getRangeAt(0);
50+
const span = document.createElement('span');
51+
span.className = 'highlight';
52+
const contents = range.cloneContents();
53+
span.appendChild(contents);
54+
range.deleteContents();
55+
range.insertNode(span);
56+
}
57+
}
58+
</script>
59+
</body>
60+
</html>

app/components/ui/badge.tsx

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
1-
import * as React from "react"
2-
import { cva, type VariantProps } from "class-variance-authority"
1+
import { cva, type VariantProps } from 'class-variance-authority'
2+
import * as React from 'react'
33

44
import { cn } from '#app/utils/misc.tsx'
55

66
const badgeVariants = cva(
7-
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8-
{
9-
variants: {
10-
variant: {
11-
default:
12-
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13-
secondary:
14-
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15-
destructive:
16-
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17-
outline: "text-foreground",
18-
},
19-
},
20-
defaultVariants: {
21-
variant: "default",
22-
},
23-
}
7+
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
8+
{
9+
variants: {
10+
variant: {
11+
default:
12+
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
13+
secondary:
14+
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
15+
destructive:
16+
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
17+
outline: 'text-foreground',
18+
},
19+
},
20+
defaultVariants: {
21+
variant: 'default',
22+
},
23+
},
2424
)
2525

2626
export interface BadgeProps
27-
extends React.HTMLAttributes<HTMLDivElement>,
28-
VariantProps<typeof badgeVariants> {}
27+
extends React.HTMLAttributes<HTMLDivElement>,
28+
VariantProps<typeof badgeVariants> {}
2929

3030
function Badge({ className, variant, ...props }: BadgeProps) {
31-
return (
32-
<div className={cn(badgeVariants({ variant }), className)} {...props} />
33-
)
31+
return (
32+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
33+
)
3434
}
3535

3636
export { Badge, badgeVariants }

app/components/ui/collapsible.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
1+
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'
22

33
const Collapsible = CollapsiblePrimitive.Root
44

app/components/ui/radio-group.tsx

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,47 @@
1-
import * as React from "react"
2-
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
1+
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group'
2+
import * as React from 'react'
33
// import { Circle } from "lucide-react"
44

55
import { cn } from '#app/utils/misc.tsx'
6-
import { Icon } from "./icon.tsx"
6+
import { Icon } from './icon.tsx'
77

88
const RadioGroup = React.forwardRef<
9-
React.ElementRef<typeof RadioGroupPrimitive.Root>,
10-
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
9+
React.ElementRef<typeof RadioGroupPrimitive.Root>,
10+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
1111
>(({ className, ...props }, ref) => {
12-
return (
13-
<RadioGroupPrimitive.Root
14-
className={cn("grid gap-2", className)}
15-
{...props}
16-
ref={ref}
17-
/>
18-
)
12+
return (
13+
<RadioGroupPrimitive.Root
14+
className={cn('grid gap-2', className)}
15+
{...props}
16+
ref={ref}
17+
/>
18+
)
1919
})
2020
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
2121

2222
const RadioGroupItem = React.forwardRef<
23-
React.ElementRef<typeof RadioGroupPrimitive.Item>,
24-
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
23+
React.ElementRef<typeof RadioGroupPrimitive.Item>,
24+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
2525
>(({ className, ...props }, ref) => {
26-
return (
27-
<RadioGroupPrimitive.Item
28-
ref={ref}
29-
className={cn(
30-
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
31-
className
32-
)}
33-
{...props}
34-
>
35-
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
36-
<Icon name="chevron-right" size="sm" className="h-2.5 w-2.5 fill-current text-current" />
37-
{/* <Circle className="h-2.5 w-2.5 fill-current text-current" /> */}
38-
</RadioGroupPrimitive.Indicator>
39-
</RadioGroupPrimitive.Item>
40-
)
26+
return (
27+
<RadioGroupPrimitive.Item
28+
ref={ref}
29+
className={cn(
30+
'aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
31+
className,
32+
)}
33+
{...props}
34+
>
35+
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
36+
<Icon
37+
name="chevron-right"
38+
size="sm"
39+
className="h-2.5 w-2.5 fill-current text-current"
40+
/>
41+
{/* <Circle className="h-2.5 w-2.5 fill-current text-current" /> */}
42+
</RadioGroupPrimitive.Indicator>
43+
</RadioGroupPrimitive.Item>
44+
)
4145
})
4246
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
4347

0 commit comments

Comments
 (0)