mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
add Greeting modal component
This commit is contained in:
157
docs/docs/ui.mdx
157
docs/docs/ui.mdx
@@ -21,78 +21,12 @@ The `web` subsection contains multiple subsections of its own, should you wish t
|
|||||||
| `credit` | Developer credit & GitHub Link | <PageLink to="#credit">➡️</PageLink> |
|
| `credit` | Developer credit & GitHub Link | <PageLink to="#credit">➡️</PageLink> |
|
||||||
| `dns_provider` | DNS over HTTPS Provider | <PageLink to="#dns_provider">➡️</PageLink> |
|
| `dns_provider` | DNS over HTTPS Provider | <PageLink to="#dns_provider">➡️</PageLink> |
|
||||||
| `external_link` | Link to external site | <PageLink to="#external_link">➡️</PageLink> |
|
| `external_link` | Link to external site | <PageLink to="#external_link">➡️</PageLink> |
|
||||||
|
| `greeting` | Greeting Modal | <PageLink to="#greeting">➡️</PageLink> |
|
||||||
| `logo` | Logo & Favicons | <PageLink to="#logo">➡️</PageLink> |
|
| `logo` | Logo & Favicons | <PageLink to="#logo">➡️</PageLink> |
|
||||||
| `opengraph` | [OpenGraph](https://ogp.me/) | <PageLink to="#opengraph">➡️</PageLink> |
|
| `opengraph` | [OpenGraph](https://ogp.me/) | <PageLink to="#opengraph">➡️</PageLink> |
|
||||||
| `terms` | Terms & Conditions | <PageLink to="#terms">➡️</PageLink> |
|
| `terms` | Terms & Conditions | <PageLink to="#terms">➡️</PageLink> |
|
||||||
| `theme` | Colors & Fonts | <PageLink to="#theme">➡️</PageLink> |
|
| `theme` | Colors & Fonts | <PageLink to="#theme">➡️</PageLink> |
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
web:
|
|
||||||
credit:
|
|
||||||
enable: true
|
|
||||||
dns_provider:
|
|
||||||
name: google
|
|
||||||
url: https://dns.google/resolve
|
|
||||||
external_link:
|
|
||||||
enable: true
|
|
||||||
title: PeeringDB
|
|
||||||
url: https://www.peeringdb.com/asn/{primary_asn}
|
|
||||||
help_menu:
|
|
||||||
enable: true
|
|
||||||
file: null
|
|
||||||
title: Help
|
|
||||||
logo:
|
|
||||||
dark: /images/hyperglass-light.png
|
|
||||||
favicons: ui/images/favicons/
|
|
||||||
height: null
|
|
||||||
light: /images/hyperglass-dark.png
|
|
||||||
width: 384
|
|
||||||
opengraph:
|
|
||||||
height: 1132
|
|
||||||
image: /images/hyperglass-opengraph.png
|
|
||||||
width: 7355
|
|
||||||
terms:
|
|
||||||
enable: true
|
|
||||||
file: null
|
|
||||||
title: Terms
|
|
||||||
text:
|
|
||||||
cache: Results will be cached for 2 minutes.
|
|
||||||
fqdn_tooltip: "Use {protocol}"
|
|
||||||
query_location: Location
|
|
||||||
query_target: Target
|
|
||||||
query_type: Query Type
|
|
||||||
query_vrf: Routing Table
|
|
||||||
subtitle: AS65001
|
|
||||||
title: hyperglass
|
|
||||||
title_mode: text_only
|
|
||||||
theme:
|
|
||||||
default_color_mode: light
|
|
||||||
colors:
|
|
||||||
black: "#262626"
|
|
||||||
blue: "#314cb6"
|
|
||||||
cyan: "#118ab2"
|
|
||||||
danger: "#d84b4b"
|
|
||||||
error: "#ff6b35"
|
|
||||||
gray: "#c1c7cc"
|
|
||||||
green: "#35b246"
|
|
||||||
orange: "#ff6b35"
|
|
||||||
pink: "#f2607d"
|
|
||||||
primary: "#118ab2"
|
|
||||||
purple: "#8d30b5"
|
|
||||||
red: "#d84b4b"
|
|
||||||
secondary: "#314cb6"
|
|
||||||
success: "#35b246"
|
|
||||||
teal: "#35b299"
|
|
||||||
warning: "#edae49"
|
|
||||||
white: "#f7f7f7"
|
|
||||||
yellow: "#edae49"
|
|
||||||
fonts:
|
|
||||||
body: Nunito
|
|
||||||
mono: Fira Code
|
|
||||||
```
|
|
||||||
|
|
||||||
## `credit`
|
## `credit`
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
| Parameter | Type | Default | Description |
|
||||||
@@ -117,14 +51,24 @@ If your organization's policy allows, and you don't mind, I request that you kee
|
|||||||
| `title` | String | `'PeeringDB'` | Link title/label |
|
| `title` | String | `'PeeringDB'` | Link title/label |
|
||||||
| `url` | String | `'https://www.peeringdb.com/asn/{primary_asn}'` | Target URL. `{primary_asn}` will be replaced with the `primary_asn` value from <Link to="/docs/configuration#global-settings">Global Settings</Link> |
|
| `url` | String | `'https://www.peeringdb.com/asn/{primary_asn}'` | Target URL. `{primary_asn}` will be replaced with the `primary_asn` value from <Link to="/docs/configuration#global-settings">Global Settings</Link> |
|
||||||
|
|
||||||
|
## `greeting`
|
||||||
|
|
||||||
|
| Parameter | Type | Default | Description |
|
||||||
|
| :--------- | :-----: | :----------- | :------------------------------------------------------------------------------------------- |
|
||||||
|
| `enable` | Boolean | `false` | Enable or disable the greeting modal. |
|
||||||
|
| `file` | String | | Path to a [markdown](https://www.markdownguide.org/) file containing the modal body content. |
|
||||||
|
| `title` | String | `'Welcome'` | Modal title. |
|
||||||
|
| `button` | String | `'Continue'` | Button text. |
|
||||||
|
| `required` | Boolean | `false` | If `true` the user must click the button in order to submit a query. |
|
||||||
|
|
||||||
## `logo`
|
## `logo`
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
| Parameter | Type | Default | Description |
|
||||||
| :-------- | :-----: | :----------------------------------------------------------- | :------------------------------------------- |
|
| :-------- | :-----: | :------------------------------ | :------------------------------------------- |
|
||||||
| `light` | String | `'hyperglass/hyperglass/static/images/hyperglass-dark.png'` | Path to logo that will be used in light mode |
|
| `light` | String | `'images/hyperglass-dark.png'` | Path to logo that will be used in light mode |
|
||||||
| `dark` | String | `'hyperglass/hyperglass/static/images/hyperglass-light.png'` | Path to logo that will be used in dark mode |
|
| `dark` | String | `'images/hyperglass-light.png'` | Path to logo that will be used in dark mode |
|
||||||
| `width` | Integer | `384` | Maximum logo width in pixels |
|
| `width` | Integer | `384` | Maximum logo width in pixels |
|
||||||
| `height` | Integer | | Maximum logo height in pixels |
|
| `height` | Integer | | Maximum logo height in pixels |
|
||||||
|
|
||||||
## `opengraph`
|
## `opengraph`
|
||||||
|
|
||||||
@@ -218,3 +162,70 @@ Currently, only [Google Fonts](https://fonts.google.com/) are supported.
|
|||||||
| :-------- | :----: | :----------------------- | :-------------------------------------- |
|
| :-------- | :----: | :----------------------- | :-------------------------------------- |
|
||||||
| `body` | String | <Font name='Nunito'/> | Main body font |
|
| `body` | String | <Font name='Nunito'/> | Main body font |
|
||||||
| `mono` | String | <Font name='Fira Code'/> | Monospace font, used for command output |
|
| `mono` | String | <Font name='Fira Code'/> | Monospace font, used for command output |
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
web:
|
||||||
|
credit:
|
||||||
|
enable: true
|
||||||
|
dns_provider:
|
||||||
|
name: google
|
||||||
|
url: https://dns.google/resolve
|
||||||
|
external_link:
|
||||||
|
enable: true
|
||||||
|
title: PeeringDB
|
||||||
|
url: https://www.peeringdb.com/asn/{primary_asn}
|
||||||
|
help_menu:
|
||||||
|
enable: true
|
||||||
|
file: null
|
||||||
|
title: Help
|
||||||
|
logo:
|
||||||
|
dark: /images/hyperglass-light.png
|
||||||
|
favicons: ui/images/favicons/
|
||||||
|
height: null
|
||||||
|
light: /images/hyperglass-dark.png
|
||||||
|
width: 384
|
||||||
|
opengraph:
|
||||||
|
height: 1132
|
||||||
|
image: /images/hyperglass-opengraph.png
|
||||||
|
width: 7355
|
||||||
|
terms:
|
||||||
|
enable: true
|
||||||
|
file: null
|
||||||
|
title: Terms
|
||||||
|
text:
|
||||||
|
cache: Results will be cached for 2 minutes.
|
||||||
|
fqdn_tooltip: "Use {protocol}"
|
||||||
|
query_location: Location
|
||||||
|
query_target: Target
|
||||||
|
query_type: Query Type
|
||||||
|
query_vrf: Routing Table
|
||||||
|
subtitle: AS65001
|
||||||
|
title: hyperglass
|
||||||
|
title_mode: text_only
|
||||||
|
theme:
|
||||||
|
default_color_mode: light
|
||||||
|
colors:
|
||||||
|
black: "#262626"
|
||||||
|
blue: "#314cb6"
|
||||||
|
cyan: "#118ab2"
|
||||||
|
danger: "#d84b4b"
|
||||||
|
error: "#ff6b35"
|
||||||
|
gray: "#c1c7cc"
|
||||||
|
green: "#35b246"
|
||||||
|
orange: "#ff6b35"
|
||||||
|
pink: "#f2607d"
|
||||||
|
primary: "#118ab2"
|
||||||
|
purple: "#8d30b5"
|
||||||
|
red: "#d84b4b"
|
||||||
|
secondary: "#314cb6"
|
||||||
|
success: "#35b246"
|
||||||
|
teal: "#35b299"
|
||||||
|
warning: "#edae49"
|
||||||
|
white: "#f7f7f7"
|
||||||
|
yellow: "#edae49"
|
||||||
|
fonts:
|
||||||
|
body: Nunito
|
||||||
|
mono: Fira Code
|
||||||
|
```
|
||||||
|
@@ -391,6 +391,12 @@ def _build_vrf_help():
|
|||||||
return all_help
|
return all_help
|
||||||
|
|
||||||
|
|
||||||
|
content_greeting = get_markdown(
|
||||||
|
config_path=params.web.greeting,
|
||||||
|
default="",
|
||||||
|
params={"title": params.web.greeting.title},
|
||||||
|
)
|
||||||
|
|
||||||
content_vrf = _build_vrf_help()
|
content_vrf = _build_vrf_help()
|
||||||
|
|
||||||
content_help_params = copy.copy(content_params)
|
content_help_params = copy.copy(content_params)
|
||||||
@@ -436,6 +442,7 @@ _frontend_params.update(
|
|||||||
"terms": content_terms,
|
"terms": content_terms,
|
||||||
"credit": content_credit,
|
"credit": content_credit,
|
||||||
"vrf": content_vrf,
|
"vrf": content_vrf,
|
||||||
|
"greeting": content_greeting,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@@ -69,6 +69,23 @@ class HelpMenu(HyperglassModel):
|
|||||||
title: StrictStr = "Help"
|
title: StrictStr = "Help"
|
||||||
|
|
||||||
|
|
||||||
|
class Greeting(HyperglassModel):
|
||||||
|
"""Validation model for greeting modal."""
|
||||||
|
|
||||||
|
enable: StrictBool = False
|
||||||
|
file: Optional[FilePath]
|
||||||
|
title: StrictStr = "Welcome"
|
||||||
|
button: StrictStr = "Continue"
|
||||||
|
required: StrictBool = False
|
||||||
|
|
||||||
|
@validator("file")
|
||||||
|
def validate_file(cls, value, values):
|
||||||
|
"""Ensure file is specified if greeting is enabled."""
|
||||||
|
if values["enable"] and value is None:
|
||||||
|
raise ValueError("Greeting is enabled, but no file is specified.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class Logo(HyperglassModel):
|
class Logo(HyperglassModel):
|
||||||
"""Validation model for logo configuration."""
|
"""Validation model for logo configuration."""
|
||||||
|
|
||||||
@@ -214,6 +231,7 @@ class Web(HyperglassModel):
|
|||||||
credit: Credit = Credit()
|
credit: Credit = Credit()
|
||||||
dns_provider: DnsOverHttps = DnsOverHttps()
|
dns_provider: DnsOverHttps = DnsOverHttps()
|
||||||
external_link: ExternalLink = ExternalLink()
|
external_link: ExternalLink = ExternalLink()
|
||||||
|
greeting: Greeting = Greeting()
|
||||||
help_menu: HelpMenu = HelpMenu()
|
help_menu: HelpMenu = HelpMenu()
|
||||||
logo: Logo = Logo()
|
logo: Logo = Logo()
|
||||||
opengraph: OpenGraph = OpenGraph()
|
opengraph: OpenGraph = OpenGraph()
|
||||||
|
72
hyperglass/ui/components/Greeting.js
Normal file
72
hyperglass/ui/components/Greeting.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
ModalOverlay,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalFooter,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
useColorMode,
|
||||||
|
useDisclosure,
|
||||||
|
} from "@chakra-ui/core";
|
||||||
|
import MarkDown from "~/components/MarkDown";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
const bg = { light: "white", dark: "black" };
|
||||||
|
const color = { light: "black", dark: "white" };
|
||||||
|
|
||||||
|
const AnimatedModalContent = motion.custom(ModalContent);
|
||||||
|
const AnimatedModalOverlay = motion.custom(ModalOverlay);
|
||||||
|
|
||||||
|
const Greeting = ({ greetingConfig, content, onClickThrough }) => {
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure(true);
|
||||||
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
onClickThrough(true);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
onClose={handleClick}
|
||||||
|
isOpen={isOpen}
|
||||||
|
size="full"
|
||||||
|
isCentered
|
||||||
|
closeOnOverlayClick={!greetingConfig.required}
|
||||||
|
>
|
||||||
|
<AnimatedModalOverlay
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 0.3, delay: 0.7 }}
|
||||||
|
/>
|
||||||
|
<AnimatedModalContent
|
||||||
|
initial={{ scale: 0.5, opacity: 0 }}
|
||||||
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
|
transition={{ duration: 0.3, delay: 0.7 }}
|
||||||
|
bg={bg[colorMode]}
|
||||||
|
color={color[colorMode]}
|
||||||
|
py={4}
|
||||||
|
borderRadius="md"
|
||||||
|
maxW={["95%", "75%", "75%", "75%"]}
|
||||||
|
>
|
||||||
|
<ModalHeader>{greetingConfig.title}</ModalHeader>
|
||||||
|
{!greetingConfig.required && <ModalCloseButton />}
|
||||||
|
<ModalBody>
|
||||||
|
<MarkDown content={content} />
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button variantColor="primary" onClick={handleClick}>
|
||||||
|
{greetingConfig.button}
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</AnimatedModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Greeting.displayName = "Greeting";
|
||||||
|
|
||||||
|
export default Greeting;
|
@@ -16,7 +16,7 @@ import useConfig from "~/components/HyperglassProvider";
|
|||||||
|
|
||||||
format.extend(String.prototype, {});
|
format.extend(String.prototype, {});
|
||||||
|
|
||||||
const formSchema = config =>
|
const formSchema = (config) =>
|
||||||
yup.object().shape({
|
yup.object().shape({
|
||||||
query_location: yup
|
query_location: yup
|
||||||
.array()
|
.array()
|
||||||
@@ -28,7 +28,7 @@ const formSchema = config =>
|
|||||||
query_vrf: yup.string(),
|
query_vrf: yup.string(),
|
||||||
query_target: yup
|
query_target: yup
|
||||||
.string()
|
.string()
|
||||||
.required(config.messages.no_input.format({ field: config.web.text.query_target }))
|
.required(config.messages.no_input.format({ field: config.web.text.query_target })),
|
||||||
});
|
});
|
||||||
|
|
||||||
const FormRow = ({ children, ...props }) => (
|
const FormRow = ({ children, ...props }) => (
|
||||||
@@ -44,11 +44,11 @@ const FormRow = ({ children, ...props }) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const HyperglassForm = React.forwardRef(
|
const HyperglassForm = React.forwardRef(
|
||||||
({ isSubmitting, setSubmitting, setFormData, ...props }, ref) => {
|
({ isSubmitting, setSubmitting, setFormData, greetingAck, setGreetingAck, ...props }, ref) => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const { handleSubmit, register, setValue, errors } = useForm({
|
const { handleSubmit, register, setValue, errors } = useForm({
|
||||||
validationSchema: formSchema(config),
|
validationSchema: formSchema(config),
|
||||||
defaultValues: { query_vrf: "default" }
|
defaultValues: { query_vrf: "default" },
|
||||||
});
|
});
|
||||||
|
|
||||||
const [queryLocation, setQueryLocation] = useState([]);
|
const [queryLocation, setQueryLocation] = useState([]);
|
||||||
@@ -59,21 +59,26 @@ const HyperglassForm = React.forwardRef(
|
|||||||
const [fqdnTarget, setFqdnTarget] = useState("");
|
const [fqdnTarget, setFqdnTarget] = useState("");
|
||||||
const [displayTarget, setDisplayTarget] = useState("");
|
const [displayTarget, setDisplayTarget] = useState("");
|
||||||
const [families, setFamilies] = useState([]);
|
const [families, setFamilies] = useState([]);
|
||||||
const onSubmit = values => {
|
const onSubmit = (values) => {
|
||||||
setFormData(values);
|
if (!greetingAck && config.web.greeting.required) {
|
||||||
setSubmitting(true);
|
window.location.reload(false);
|
||||||
|
setGreetingAck(false);
|
||||||
|
} else {
|
||||||
|
setFormData(values);
|
||||||
|
setSubmitting(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLocChange = locObj => {
|
const handleLocChange = (locObj) => {
|
||||||
setQueryLocation(locObj.value);
|
setQueryLocation(locObj.value);
|
||||||
const allVrfs = [];
|
const allVrfs = [];
|
||||||
const deviceVrfs = [];
|
const deviceVrfs = [];
|
||||||
locObj.value.map(loc => {
|
locObj.value.map((loc) => {
|
||||||
const locVrfs = [];
|
const locVrfs = [];
|
||||||
config.devices[loc].vrfs.map(vrf => {
|
config.devices[loc].vrfs.map((vrf) => {
|
||||||
locVrfs.push({
|
locVrfs.push({
|
||||||
label: vrf.display_name,
|
label: vrf.display_name,
|
||||||
value: vrf.id
|
value: vrf.id,
|
||||||
});
|
});
|
||||||
deviceVrfs.push([{ id: vrf.id, ipv4: vrf.ipv4, ipv6: vrf.ipv6 }]);
|
deviceVrfs.push([{ id: vrf.id, ipv4: vrf.ipv4, ipv6: vrf.ipv6 }]);
|
||||||
});
|
});
|
||||||
@@ -89,10 +94,10 @@ const HyperglassForm = React.forwardRef(
|
|||||||
deviceVrfs.length !== 0 &&
|
deviceVrfs.length !== 0 &&
|
||||||
intersecting.length !== 0 &&
|
intersecting.length !== 0 &&
|
||||||
deviceVrfs
|
deviceVrfs
|
||||||
.filter(v => intersecting.every(i => i.id === v.id))
|
.filter((v) => intersecting.every((i) => i.id === v.id))
|
||||||
.reduce((a, b) => a.concat(b))
|
.reduce((a, b) => a.concat(b))
|
||||||
.filter(v => v.id === "default")
|
.filter((v) => v.id === "default")
|
||||||
.map(v => {
|
.map((v) => {
|
||||||
v.ipv4 === true && ipv4++;
|
v.ipv4 === true && ipv4++;
|
||||||
v.ipv6 === true && ipv6++;
|
v.ipv6 === true && ipv6++;
|
||||||
});
|
});
|
||||||
@@ -107,7 +112,7 @@ const HyperglassForm = React.forwardRef(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = e => {
|
const handleChange = (e) => {
|
||||||
setValue(e.field, e.value);
|
setValue(e.field, e.value);
|
||||||
e.field === "query_location"
|
e.field === "query_location"
|
||||||
? handleLocChange(e)
|
? handleLocChange(e)
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { Flex, useColorMode } from "@chakra-ui/core";
|
import { Flex, useColorMode, useDisclosure } from "@chakra-ui/core";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import HyperglassForm from "~/components/HyperglassForm";
|
import HyperglassForm from "~/components/HyperglassForm";
|
||||||
import Results from "~/components/Results";
|
import Results from "~/components/Results";
|
||||||
import Header from "~/components/Header";
|
import Header from "~/components/Header";
|
||||||
import Footer from "~/components/Footer";
|
import Footer from "~/components/Footer";
|
||||||
|
import Greeting from "~/components/Greeting";
|
||||||
import Meta from "~/components/Meta";
|
import Meta from "~/components/Meta";
|
||||||
import useConfig from "~/components/HyperglassProvider";
|
import useConfig from "~/components/HyperglassProvider";
|
||||||
import Debugger from "~/components/Debugger";
|
import Debugger from "~/components/Debugger";
|
||||||
|
import useSessionStorage from "~/hooks/useSessionStorage";
|
||||||
|
|
||||||
const AnimatedForm = motion.custom(HyperglassForm);
|
const AnimatedForm = motion.custom(HyperglassForm);
|
||||||
|
|
||||||
@@ -19,7 +21,9 @@ const Layout = () => {
|
|||||||
const { colorMode } = useColorMode();
|
const { colorMode } = useColorMode();
|
||||||
const [isSubmitting, setSubmitting] = useState(false);
|
const [isSubmitting, setSubmitting] = useState(false);
|
||||||
const [formData, setFormData] = useState({});
|
const [formData, setFormData] = useState({});
|
||||||
|
const [greetingAck, setGreetingAck] = useSessionStorage("hyperglass-greeting-ack", false);
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
|
|
||||||
const handleFormReset = () => {
|
const handleFormReset = () => {
|
||||||
containerRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
|
containerRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -68,6 +72,8 @@ const Layout = () => {
|
|||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSubmitting}
|
||||||
setSubmitting={setSubmitting}
|
setSubmitting={setSubmitting}
|
||||||
setFormData={setFormData}
|
setFormData={setFormData}
|
||||||
|
greetingAck={greetingAck}
|
||||||
|
setGreetingAck={setGreetingAck}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
@@ -75,6 +81,13 @@ const Layout = () => {
|
|||||||
<Footer />
|
<Footer />
|
||||||
{config.developer_mode && <Debugger />}
|
{config.developer_mode && <Debugger />}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
{config.web.greeting.enable && !greetingAck && (
|
||||||
|
<Greeting
|
||||||
|
greetingConfig={config.web.greeting}
|
||||||
|
content={config.content.greeting}
|
||||||
|
onClickThrough={setGreetingAck}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
47
hyperglass/ui/hooks/useSessionStorage.js
Normal file
47
hyperglass/ui/hooks/useSessionStorage.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
react-use: useSessionStorage
|
||||||
|
https://github.com/streamich/react-use/blob/master/src/useSessionStorage.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const useSessionStorage = (key, initialValue, raw) => {
|
||||||
|
const isClient = typeof window === "object";
|
||||||
|
if (!isClient) {
|
||||||
|
return [initialValue, () => {}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [state, setState] = useState(() => {
|
||||||
|
try {
|
||||||
|
const sessionStorageValue = sessionStorage.getItem(key);
|
||||||
|
if (typeof sessionStorageValue !== "string") {
|
||||||
|
sessionStorage.setItem(
|
||||||
|
key,
|
||||||
|
raw ? String(initialValue) : JSON.stringify(initialValue)
|
||||||
|
);
|
||||||
|
return initialValue;
|
||||||
|
} else {
|
||||||
|
return raw ? sessionStorageValue : JSON.parse(sessionStorageValue || "null");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// If user is in private mode or has storage restriction
|
||||||
|
// sessionStorage can throw. JSON.parse and JSON.stringify
|
||||||
|
// cat throw, too.
|
||||||
|
return initialValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
const serializedState = raw ? String(state) : JSON.stringify(state);
|
||||||
|
sessionStorage.setItem(key, serializedState);
|
||||||
|
} catch {
|
||||||
|
// If user is in private mode or has storage restriction
|
||||||
|
// sessionStorage can throw. Also JSON.stringify can throw.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [state, setState];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSessionStorage;
|
Reference in New Issue
Block a user