Skip to content

Commit

Permalink
feat: hover metadata feature playground [uli-website](#691)
Browse files Browse the repository at this point in the history
* feat: create story page with dummy text

* feat: check text for metadata

* fix: delete story page, add slur metadata json, fix metadata box positioning

* chore: shift slur-metadata to slurs folder
  • Loading branch information
maanasb01 authored Feb 13, 2025
1 parent 65fc70c commit c8f3314
Show file tree
Hide file tree
Showing 3 changed files with 3,006 additions and 2 deletions.
6 changes: 4 additions & 2 deletions uli-website/src/components/molecules/AppShell.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export default function AppShell({ children }) {

return (
<Grommet theme={Theme}>
<main>
<main
style={{ display: "flex", flexDirection: "column", minHeight: "100vh" }}
>
<Helmet>
<meta charSet="utf-8" />
<title>Uli</title>
Expand All @@ -32,7 +34,7 @@ export default function AppShell({ children }) {
</Helmet>

<NavBar />
{children}
<Box flex="grow">{children}</Box>
{/* <Box align="center" pad={"medium"}>
<Box direction="row" gap={"medium"}>
<NavLink to={"/privacy-policy"}>Privacy Policy</NavLink>
Expand Down
244 changes: 244 additions & 0 deletions uli-website/src/pages/check-text.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import React from "react";
import { Box, Text, Button, TextArea, Heading } from "grommet";
import AppShell from "../components/molecules/AppShell";
import { useState } from "react";
import styled from "styled-components";
import slurMetadata from "../slurs/slurs-metadata.json";

const CustomTextArea = styled(TextArea)`
&:focus {
box-shadow: 0 0 0 4px #ffde2155;
}
`;

export default function Story() {
const [hoveredSlur, setHoveredSlur] = useState(null);
const [input, setInput] = useState(null);
const [displayText, setDisplayText] = useState("");

const slurs = slurMetadata;

const getHighlightedText = (text, words) => {
if (!text) return "";

const regex = new RegExp(
`\\b(${words.map((w) => w.label).join("|")})\\b`,
"gi"
);

return text.split(regex).map((part, index) => {
const wordData = words.find(
(w) => w.label.toLowerCase() === part.toLowerCase()
);

return wordData ? (
<span
key={index}
style={{
backgroundColor: "#ffde2155",
boxShadow: "0px 0px 5px #ffde21",
cursor: "pointer",

display: "inline",
whiteSpace: "pre-wrap",
}}
onMouseEnter={(e) => {
setHoveredSlur({
metadata: wordData,
position: { x: "-9999px", y: "-9999px" },
});

const rect = e.currentTarget.getBoundingClientRect();
// Settimeout to wait for the re-render.
setTimeout(() => {
let tooltipContainer =
document.getElementById("metadata-tooltip");

let position = {
x: rect.left + window.scrollX,
y: rect.bottom + 10 + window.scrollY,
};

const screenHeight = window.innerHeight;
const screenWidth = window.innerWidth;

const tooltipHeight = tooltipContainer.offsetHeight;

const spaceBelow = screenHeight - rect.bottom;

if (spaceBelow < tooltipHeight + 10) {
position.y = rect.top - tooltipHeight - 10 + window.scrollY;
}

if (position.x + 290 > screenWidth) {
position.x = screenWidth - 320 + window.scrollX;
}

setHoveredSlur({ metadata: wordData, position });
}, 0);
}}
onMouseLeave={() => setHoveredSlur(null)}
>
{part}
</span>
) : (
part
);
});
};

function onCheck() {
setDisplayText(getHighlightedText(input, slurs));
}

return (
<AppShell>
<Box align="center">
<Heading margin={{horizontal:"large"}} level={3}>Check Text for Slur Metadata</Heading>
<Box width={"large"} height={"full"}>
<CustomTextArea
value={input}
resize={"vertical"}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter text here to check... "
style={{
width: "full",
minHeight: "12em",
fontWeight: "400",
padding: "0.5em",
}}
/>
<Box flex width={"full"} direction="row" gap="1em">
<Button
color={"lightGrey"}
primary
onClick={() => {
setDisplayText("");
setInput("");
}}
label={"Reset"}
style={{ width: "fit-content" }}
margin={{ top: "0.5em" }}
/>
<Button
color={"#ede09f"}
primary
onClick={onCheck}
label={"Check"}
style={{ width: "fit-content" }}
margin={{ top: "0.5em" }}
/>
</Box>
<Text size="large" margin={{ top: "1em" }}>
Output:
</Text>
<p style={{ whiteSpace: "pre-wrap" }}>
{displayText
? displayText
: "Check a Text to Display Result Here. . ."}
</p>
{hoveredSlur && (
<div
id="metadata-tooltip"
className="absolute"
style={{
position: "absolute",
left: hoveredSlur.position.x,
top: hoveredSlur.position.y,
}}
>
<HoverSlurMetadata slurDetails={hoveredSlur.metadata} />
</div>
)}
</Box>
</Box>
</AppShell>
);
}

const HoverCategoryBubble = ({ data }) => {
return (
<Box>
<Box
round="small"
background="#FADA5E"
pad="xsmall"
width="fit-content"
wrap={true}
style={{
fontFamily: "Raleway",
fontSize: "9px",
color: "black",
}}
>
<Text size="small">{data}</Text>
</Box>
</Box>
);
};

const HoverSlurMetadata = ({ slurDetails }) => {
return (
<Box
pad="small"
background="antiquewhite"
border={{ color: "black", size: "xsmall" }}
round="small"
width="18rem"
justify="start"
align="start"
elevation="small"
style={{
fontFamily: "Raleway",
fontSize: "9px",
color: "black",
textAlign: "left",
lineHeight: "1.5",
minWidth: "288px",
}}
>
{slurDetails["level_of_severity"] && (
<Text>
<b>Level of Severity:</b> {slurDetails["level_of_severity"]}
</Text>
)}
{slurDetails["casual"] && (
<Text>
<b>Casual:</b> {slurDetails["casual"]}
</Text>
)}
{slurDetails["appropriated"] && (
<Text>
<b>Appropriated:</b> {slurDetails["appropriated"]}
</Text>
)}
{slurDetails["appropriation_context"] && (
<Text>
<b>If, Appropriated, Is it by Community or Others?:</b>{" "}
{slurDetails["appropriation_context"]}
</Text>
)}
{slurDetails["meaning"] && (
<Text>
<b>What Makes it Problematic?:</b> {slurDetails["meaning"]}
</Text>
)}

{slurDetails["categories"] && slurDetails["categories"].length > 0 && (
<Box>
<Text>
<b>Categories:</b>
</Text>
<Box direction="row" gap="xsmall" cssGap wrap>
{slurDetails["categories"].map((category, index) => (
<HoverCategoryBubble
key={index}
data={category.replace(/_/g, " ")}
/>
))}
</Box>
</Box>
)}
</Box>
);
};
Loading

0 comments on commit c8f3314

Please sign in to comment.