Skip to content

Commit

Permalink
Closes #173: Implement customizable highlighting of text output
Browse files Browse the repository at this point in the history
  • Loading branch information
thatmattlove committed Dec 14, 2021
1 parent 5bf69f7 commit bd0eb65
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 24 deletions.
52 changes: 30 additions & 22 deletions hyperglass/models/config/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class Menu(HyperglassModel):
order: StrictInt = 0

@validator("content")
def validate_content(cls, value):
def validate_content(cls: "Menu", value: str) -> str:
"""Read content from file if a path is provided."""

if len(value) < 260:
Expand Down Expand Up @@ -135,14 +135,14 @@ class Text(HyperglassModel):
ip_button: StrictStr = "My IP"

@validator("title_mode")
def validate_title_mode(cls, value):
def validate_title_mode(cls: "Text", value: str) -> str:
"""Set legacy logo_title to logo_subtitle."""
if value == "logo_title":
value = "logo_subtitle"
return value

@validator("cache_prefix")
def validate_cache_prefix(cls, value):
def validate_cache_prefix(cls: "Text", value: str) -> str:
"""Ensure trailing whitespace."""
return " ".join(value.split()) + " "

Expand Down Expand Up @@ -172,21 +172,16 @@ class ThemeColors(HyperglassModel):
danger: t.Optional[Color]

@validator(*FUNC_COLOR_MAP.keys(), pre=True, always=True)
def validate_colors(cls, value, values, field):
"""Set default functional color mapping.
Arguments:
value {str|None} -- Functional color
values {str} -- Already-validated colors
Returns:
{str} -- Mapped color.
"""
def validate_colors(
cls: "ThemeColors", value: str, values: t.Dict[str, t.Optional[str]], field
) -> str:
"""Set default functional color mapping."""
if value is None:
default_color = FUNC_COLOR_MAP[field.name]
value = str(values[default_color])
return value

def dict(self, *args, **kwargs):
def dict(self, *args: t.Any, **kwargs: t.Any) -> t.Dict[str, str]:
"""Return dict for colors only."""
return {k: v.as_hex() for k, v in self.__dict__.items()}

Expand All @@ -213,20 +208,32 @@ class DnsOverHttps(HyperglassModel):
url: StrictStr = ""

@root_validator
def validate_dns(cls, values):
"""Assign url field to model based on selected provider.
Arguments:
values {dict} -- Dict of selected provider
Returns:
{dict} -- Dict with url attribute
"""
def validate_dns(cls: "DnsOverHttps", values: t.Dict[str, str]) -> t.Dict[str, str]:
"""Assign url field to model based on selected provider."""
provider = values["name"]
values["url"] = DNS_OVER_HTTPS[provider]
return values


class HighlightPattern(HyperglassModel):
"""Validation model for highlight pattern configuration."""

pattern: StrictStr
label: t.Optional[StrictStr] = None
color: StrictStr = "primary"

@validator("color")
def validate_color(cls: "HighlightPattern", value: str) -> str:
"""Ensure highlight color is a valid theme color."""
colors = list(ThemeColors.__fields__.keys())
color_list = "\n - ".join(("", *colors))
if value not in colors:
raise ValueError(
"{!r} is not a supported color. Must be one of:{!s}".format(value, color_list)
)
return value


class Web(HyperglassModel):
"""Validation model for all web/browser-related configuration."""

Expand All @@ -247,6 +254,7 @@ class Web(HyperglassModel):
location_display_mode: LocationDisplayMode = "auto"
custom_javascript: t.Optional[FilePath]
custom_html: t.Optional[FilePath]
highlight: t.List[HighlightPattern] = []


class WebPublic(Web):
Expand Down
57 changes: 57 additions & 0 deletions hyperglass/ui/components/output/highlighted.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { memo } from 'react';
import { Badge, Tooltip, useStyleConfig } from '@chakra-ui/react';
import isEqual from 'react-fast-compare';
import replace from 'react-string-replace';

import type { TooltipProps } from '@chakra-ui/react';
import type { Highlight as HighlightConfig } from '~/types';

interface HighlightedProps {
patterns: HighlightConfig[];
children: string;
}

interface HighlightProps {
label: string | null;
colorScheme: string;
children: React.ReactNode;
}

const Highlight = (props: HighlightProps): JSX.Element => {
const { colorScheme, label, children } = props;
const { bg, color } = useStyleConfig('Button', { colorScheme }) as TooltipProps;
return (
<Tooltip label={label} bg={bg} color={color} hasArrow>
<Badge colorScheme={colorScheme}>{children}</Badge>
</Tooltip>
);
};

const _Highlighted = (props: HighlightedProps): JSX.Element => {
const { patterns, children } = props;
let result: React.ReactNodeArray = [];
let times: number = 0;

for (const config of patterns) {
let toReplace: string | React.ReactNodeArray = children;
if (times !== 0) {
toReplace = result;
}
result = replace(toReplace, new RegExp(`(${config.pattern})`, 'gm'), (m, i) => (
<Highlight key={`${m + i}`} label={config.label} colorScheme={config.color}>
{m}
</Highlight>
));
times++;
}

return (
<>
{result.map(r => (
<>{r}</>
))}
</>
);
};

export const Highlighted = memo(_Highlighted, isEqual);
11 changes: 9 additions & 2 deletions hyperglass/ui/components/output/text.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Box } from '@chakra-ui/react';
import { useColorValue } from '~/context';
import { useColorValue, useConfig } from '~/context';
import { Highlighted } from './highlighted';

import type { TTextOutput } from './types';

Expand All @@ -11,6 +12,10 @@ export const TextOutput: React.FC<TTextOutput> = (props: TTextOutput) => {
const selectionBg = useColorValue('black', 'white');
const selectionColor = useColorValue('white', 'black');

const {
web: { highlight },
} = useConfig();

return (
<Box
p={3}
Expand All @@ -33,7 +38,9 @@ export const TextOutput: React.FC<TTextOutput> = (props: TTextOutput) => {
}}
{...rest}
>
{children.split('\\n').join('\n').replace(/\n\n/g, '\n')}
<Highlighted patterns={highlight}>
{children.split('\\n').join('\n').replace(/\n\n/g, '\n')}
</Highlighted>
</Box>
);
};
1 change: 1 addition & 0 deletions hyperglass/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"react-markdown": "^5.0.3",
"react-query": "^3.16.0",
"react-select": "^5.2.1",
"react-string-replace": "^v0.5.0",
"react-table": "^7.7.0",
"remark-gfm": "^1.0.0",
"string-format": "^2.0.0",
Expand Down
8 changes: 8 additions & 0 deletions hyperglass/ui/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ interface _Credit {
enable: boolean;
}

interface _Highlight {
pattern: string;
label: string | null;
color: string;
}

interface _Web {
credit: _Credit;
dns_provider: { name: string; url: string };
Expand All @@ -97,6 +103,7 @@ interface _Web {
text: _Text;
theme: _ThemeConfig;
location_display_mode: 'auto' | 'gallery' | 'dropdown';
highlight: _Highlight[];
}

type _DirectiveBase = {
Expand Down Expand Up @@ -201,3 +208,4 @@ export type Greeting = CamelCasedProperties<_Greeting>;
export type Logo = CamelCasedProperties<_Logo>;
export type Link = CamelCasedProperties<_Link>;
export type Menu = CamelCasedProperties<_Menu>;
export type Highlight = CamelCasedProperties<_Highlight>;
5 changes: 5 additions & 0 deletions hyperglass/ui/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7058,6 +7058,11 @@ react-simple-animate@^3.3.12:
resolved "https://registry.yarnpkg.com/react-simple-animate/-/react-simple-animate-3.3.12.tgz#ddea0f230feb3c1f069fbdb0a26e735e0b233265"
integrity sha512-lFXjxD6ficcpOMsHfcDs1jqdkCve6jNlJnubOCzVOLswFDRANsaLN4KwpezDuliEFz8Q1zyj4J7Tmj3KMRnPcg==

react-string-replace@^v0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/react-string-replace/-/react-string-replace-0.5.0.tgz#d563ea608f98c449eea5eb3c7511fe806dba7025"
integrity sha512-xtfotmm+Gby5LjfYg3s5+eT6bnwgOIdZRAdHTouLY/7SicDtX4JXjG7CAGaGDcS0ax4nsaaEVNRxArSa8BgQKw==

react-style-singleton@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.1.1.tgz#ce7f90b67618be2b6b94902a30aaea152ce52e66"
Expand Down

0 comments on commit bd0eb65

Please sign in to comment.