mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
Docs: Feedback component v2.0
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7242 GitOrigin-RevId: 4398205fc74b0a9745d15ade4c78a21467b7475f
This commit is contained in:
parent
f82eaf7ea5
commit
af639392ce
@ -2,11 +2,11 @@ import React from 'react';
|
||||
import ActualDocItem from '@theme/DocItem';
|
||||
import HasuraConBanner from '@site/src/components/HasuraConBanner';
|
||||
import GraphQLWithHasuraBanner from '@site/src/components/GraphQLWithHasuraBanner';
|
||||
import PageHelpful from '@site/src/components/PageHelpful';
|
||||
import CustomFooter from '@site/src/components/CustomFooter';
|
||||
import styles from './styles.module.scss';
|
||||
import {ScrollToFeedbackButton} from "@site/src/components/Feedback/ScrollToFeedbackButton";
|
||||
|
||||
const CustomDocItem = (props) => {
|
||||
const CustomDocItem = props => {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
@ -17,7 +17,8 @@ const CustomDocItem = (props) => {
|
||||
>
|
||||
<ActualDocItem {...props} />
|
||||
<div className={styles['custom_doc_item_footer']}>
|
||||
<PageHelpful />
|
||||
{/*<PageHelpful />*/}
|
||||
<ScrollToFeedbackButton/>
|
||||
<HasuraConBanner {...props} />
|
||||
<GraphQLWithHasuraBanner />
|
||||
<CustomFooter />
|
||||
|
164
docs/src/components/Feedback/Feedback.tsx
Normal file
164
docs/src/components/Feedback/Feedback.tsx
Normal file
@ -0,0 +1,164 @@
|
||||
import React, {ReactNode, useRef, useState} from 'react';
|
||||
import {saTrack} from '@site/src/utils/segmentAnalytics';
|
||||
import styles from './styles.module.scss';
|
||||
export const Feedback = ({metadata}: {metadata: any}) => {
|
||||
const [rating, setRating] = useState<1 | 2 | 3 | 4 | 5 | null>(null);
|
||||
const [notes, setNotes] = useState<string | null>(null);
|
||||
const [errorText, setErrorText] = useState<string | null>(null);
|
||||
const [hoveredScore, setHoveredScore] = useState<Number | null>(null);
|
||||
const [textAreaLabel, setTextAreaLabel] = useState<ReactNode | null>(null);
|
||||
const [textAreaPlaceholder, setTextAreaPlaceholder] = useState<string>('This section is optional ✌️');
|
||||
const [isSubmitSuccess, setIsSubmitSuccess] = useState<boolean>(false);
|
||||
|
||||
const submitDisabled = rating === null || (rating < 4 && (notes === null || notes === ''));
|
||||
|
||||
const scores: (1 | 2 | 3 | 4 | 5)[] = [1, 2, 3, 4, 5];
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (rating === null) {
|
||||
setErrorText('Please select a score.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (rating < 4 && notes === null) {
|
||||
setErrorText(
|
||||
"Because this doc wasn't up to scratch please provide us with some feedback of where we can improve."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const sendData = async () => {
|
||||
const myHeaders = new Headers();
|
||||
myHeaders.append('Content-Type', 'application/json');
|
||||
|
||||
const raw = JSON.stringify({
|
||||
feedback: {
|
||||
isHelpful: rating >= 4 ? `👍` : `👎`,
|
||||
score: rating,
|
||||
notes,
|
||||
pageTitle: document.title,
|
||||
url: window.location.href,
|
||||
},
|
||||
});
|
||||
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: myHeaders,
|
||||
body: raw,
|
||||
redirect: 'follow',
|
||||
}
|
||||
|
||||
fetch('https://us-central1-websitecloud-352908.cloudfunctions.net/docs-feedback', requestOptions)
|
||||
.then(response => response.text())
|
||||
.catch(error => console.error('error', error));
|
||||
};
|
||||
|
||||
if (window.location.hostname === 'localhost') {
|
||||
alert('Testing feedback (not) sent!');
|
||||
setRating(null);
|
||||
setNotes(null);
|
||||
setIsSubmitSuccess(true);
|
||||
return;
|
||||
}
|
||||
|
||||
sendData().then(() => {
|
||||
saTrack('Responded to Did You Find This Page Helpful', {
|
||||
label: 'Responded to Did You Find This Page Helpful',
|
||||
response: rating >= 4 ? 'YES' : 'NO',
|
||||
pageUrl: window.location.href,
|
||||
});
|
||||
setRating(null);
|
||||
setNotes(null);
|
||||
setIsSubmitSuccess(true);
|
||||
}).catch((e) => {console.error(e)});
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
const handleScoreClick = (scoreItem: 1 | 2 | 3 | 4 | 5) => {
|
||||
if (scoreItem === rating) {
|
||||
setRating(null);
|
||||
setErrorText(null);
|
||||
setHoveredScore(null);
|
||||
return
|
||||
}
|
||||
setErrorText(null);
|
||||
setRating(scoreItem);
|
||||
if (scoreItem < 4) {
|
||||
setTextAreaLabel(<>
|
||||
<p>What can we do to improve it? Please be as detailed as you like.</p>
|
||||
<p>Real human beings read every single review.</p>
|
||||
</>);
|
||||
setTextAreaPlaceholder('This section is required... how can we do better? ✍️');
|
||||
}
|
||||
if (scoreItem >= 4) {
|
||||
setTextAreaLabel(
|
||||
<>
|
||||
<p>Any general feedback you'd like to add?</p>
|
||||
<p>We'll take it all... tell us how well we're doing or where we can improve.</p>
|
||||
<p>Real human beings read every single review.</p>
|
||||
</>
|
||||
);
|
||||
setTextAreaPlaceholder('This section is optional ✌️');
|
||||
}
|
||||
};
|
||||
|
||||
// Do not show on Intro page
|
||||
if (metadata.source === '@site/docs/index.mdx') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.feedback} id={'feedback'}>
|
||||
<div className={styles.form}>
|
||||
<div className={styles.topSection}>
|
||||
<h3>What did you think of this doc?</h3>
|
||||
|
||||
{isSubmitSuccess ?
|
||||
<div className={styles.successMessage}>
|
||||
<p>Thanks for your feedback.</p>
|
||||
<p>Feel free to review as many docs pages as you like!</p>
|
||||
</div>
|
||||
: <div className={styles.numberRow}>
|
||||
{scores.map((star, index) => (
|
||||
<span key={star} onClick={() => handleScoreClick(star)}
|
||||
onMouseEnter={() => setHoveredScore(index + 1)}
|
||||
onMouseLeave={() => setHoveredScore(-1)}>
|
||||
{rating >= star ? (
|
||||
<svg width="45" height="45" viewBox="0 0 24 24">
|
||||
<path fill="#ffc107"
|
||||
d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="45" height="45" viewBox="0 0 24 24">
|
||||
<path fill={hoveredScore > index ? '#ffc107' : '#B1BCC7'}
|
||||
d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/>
|
||||
</svg>
|
||||
)}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div style={rating ? {display: "block"} : {display: "none"}}>
|
||||
<div className={styles.textAreaLabel}>{textAreaLabel}</div>
|
||||
<textarea
|
||||
className={styles.textarea}
|
||||
value={notes ?? ''}
|
||||
placeholder={textAreaPlaceholder ?? ''}
|
||||
rows={5}
|
||||
onChange={e => setNotes(e.target.value)}
|
||||
/>
|
||||
<div className={styles.errorAndButton}>
|
||||
<p className={styles.errorText}>{errorText}</p>
|
||||
<div className={styles.buttonContainer}>
|
||||
<button className={submitDisabled ? styles.buttonDisabled : ''} onClick={() => handleSubmit()}>Send your
|
||||
review!
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
18
docs/src/components/Feedback/ScrollToFeedbackButton.tsx
Normal file
18
docs/src/components/Feedback/ScrollToFeedbackButton.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import styles from "./styles.module.scss";
|
||||
import Hand from "@site/static/img/mascot-hand.png";
|
||||
import React from "react";
|
||||
|
||||
export const ScrollToFeedbackButton = () => {
|
||||
|
||||
const scrollToFeedback = () => {
|
||||
const feedbackElement = document.getElementById('feedback');
|
||||
const y = feedbackElement.getBoundingClientRect().top + window.scrollY - 100;
|
||||
window.scrollTo({top: y, behavior: 'smooth'});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.scrollToWrapper} onClick={scrollToFeedback}>
|
||||
Feedback 👋
|
||||
</div>
|
||||
)
|
||||
}
|
191
docs/src/components/Feedback/styles.module.scss
Normal file
191
docs/src/components/Feedback/styles.module.scss
Normal file
@ -0,0 +1,191 @@
|
||||
.scrollToWrapper {
|
||||
position: fixed;
|
||||
display: grid;
|
||||
place-items: center center;
|
||||
color: white;
|
||||
bottom: 85px;
|
||||
right: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
background-color: var(--ifm-color-primary-light);
|
||||
box-shadow: var(--ifm-global-shadow-tl);
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(3px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.feedback {
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background-color: #f2f5f7;
|
||||
margin-top: 2rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.feedback {
|
||||
background-color: var(--color-gray-82);
|
||||
}
|
||||
}
|
||||
|
||||
.numberRow {
|
||||
display: flex;
|
||||
margin-top: 15px;
|
||||
height: 45px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.numberCircle {
|
||||
display: grid;
|
||||
place-items: center center;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--ifm-global-shadow-tl);
|
||||
padding: 0.25rem;
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.numberActive {
|
||||
display: grid;
|
||||
place-items: center center;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--ifm-global-shadow-tl);
|
||||
padding: 0.25rem;
|
||||
cursor: pointer;
|
||||
background-color: var(--ifm-color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: grid;
|
||||
text-align: center;
|
||||
|
||||
h3 {
|
||||
color: var(--ifm-heading-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
//p {
|
||||
// margin-bottom: 0.5rem;
|
||||
//}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 1.1rem;
|
||||
padding: 0.75rem;
|
||||
font-family: var(--ifm-font-family-base);
|
||||
color: var(--color-gray-74);
|
||||
margin-top: 1rem;
|
||||
box-shadow: var(--ifm-global-shadow-lw);
|
||||
width: 100%;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-gray-36);
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
//margin-top: 20px;
|
||||
margin-left: auto;
|
||||
background: var(--ifm-color-primary);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
font-family: var(--ifm-font-family-base);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.textAreaLabel {
|
||||
margin-top: 1rem;
|
||||
p {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonDisabled {
|
||||
background-color: var(--ifm-color-gray-500) !important;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
input,
|
||||
textarea {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.topSection {
|
||||
}
|
||||
|
||||
.bottomSection {
|
||||
}
|
||||
|
||||
.successMessage {
|
||||
display: block;
|
||||
place-items: center center;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: var(--ifm-color-primary);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
font-family: var(--ifm-font-family-base);
|
||||
p {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
html[data-theme='dark'] {
|
||||
.successMessage {
|
||||
color: var(--ifm-color-primary-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.errorText {
|
||||
color: var(--ifm-color-primary);
|
||||
margin-top: .8rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 500;
|
||||
font-size: 1.1rem;
|
||||
font-family: var(--ifm-font-family-base);
|
||||
}
|
||||
html[data-theme='dark'] {
|
||||
.errorText {
|
||||
color: var(--ifm-color-primary-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
.errorAndButton {
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
span {
|
||||
transition: .1s ease-out;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
transform: translateY(-3px) scale(1.1);
|
||||
transform-origin: center center;
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
import React, { Fragment, useState } from 'react';
|
||||
import { saTrack } from '@site/src/utils/segmentAnalytics';
|
||||
import styles from './styles.module.scss';
|
||||
import Hand from '@site/static/img/mascot-hand.png';
|
||||
|
||||
// Sleepy time for space between animations / state after submission
|
||||
function wait(ms = 0) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
const PageHelpful = () => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [score, setScore] = useState<number>(10);
|
||||
const [notes, setNotes] = useState<string>('');
|
||||
const [hasResponse, setHasResponse] = useState<boolean>(false);
|
||||
|
||||
function handleNotes(e) {
|
||||
setNotes(e.target.value);
|
||||
}
|
||||
|
||||
const scores = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
||||
const recordResponse = async () => {
|
||||
// POST request to Cloud Function
|
||||
const sendData = async () => {
|
||||
var myHeaders = new Headers();
|
||||
myHeaders.append('Content-Type', 'application/json');
|
||||
|
||||
var raw = JSON.stringify({
|
||||
feedback: {
|
||||
isHelpful: score >= 7 ? `👍` : `👎`,
|
||||
score,
|
||||
notes,
|
||||
pageTitle: document.title,
|
||||
url: window.location.href,
|
||||
},
|
||||
});
|
||||
|
||||
var requestOptions = {
|
||||
method: 'POST',
|
||||
headers: myHeaders,
|
||||
body: raw,
|
||||
redirect: 'follow',
|
||||
};
|
||||
|
||||
fetch('https://us-central1-websitecloud-352908.cloudfunctions.net/docs-feedback', requestOptions)
|
||||
.then((response) => response.text())
|
||||
.catch((error) => console.log('error', error));
|
||||
};
|
||||
|
||||
sendData();
|
||||
|
||||
// For testing, this has been commented out so as not to introduce noise into our analytics
|
||||
saTrack('Responded to Did You Find This Page Helpful', {
|
||||
label: 'Responded to Did You Find This Page Helpful',
|
||||
response: score >= 7 ? 'YES' : 'NO',
|
||||
pageUrl: window.location.href,
|
||||
});
|
||||
|
||||
// Clear state for next response
|
||||
setHasResponse(true);
|
||||
setIsExpanded(false);
|
||||
setScore(10);
|
||||
setNotes('');
|
||||
await wait(1000);
|
||||
setHasResponse(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={isExpanded ? `${styles.wrapper} ${styles.expanded}` : `${styles.wrapper}`}
|
||||
onClick={() => !isExpanded && setIsExpanded(true)}
|
||||
>
|
||||
{!isExpanded ? (
|
||||
<div className={styles.emoji}>{!hasResponse ? <img src={Hand} /> : <p>✅</p>}</div>
|
||||
) : (
|
||||
<div className={styles.feedback}>
|
||||
<svg
|
||||
onClick={() => setIsExpanded(false)}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
strokeWidth={1.5}
|
||||
stroke='currentColor'
|
||||
className={styles.close}
|
||||
>
|
||||
<path strokeLinecap='round' strokeLinejoin='round' d='M6 18L18 6M6 6l12 12' />
|
||||
</svg>
|
||||
<div className={styles.form}>
|
||||
<h3>Help us with some docs feedback!</h3>
|
||||
<p>On a scale of 1 to 10, how helpful would you rate this page?</p>
|
||||
<small>1 meaning the page is not helpful at all and 10 meaning you found what you needed quickly.</small>
|
||||
<div className={styles.numberRow}>
|
||||
{scores.map((scoreItem) => (
|
||||
<div
|
||||
key={scoreItem}
|
||||
className={score === scoreItem ? styles.numberActive : ''}
|
||||
onClick={() => setScore(scoreItem)}
|
||||
onKeyDown={() => setScore(scoreItem)}
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
>
|
||||
{scoreItem}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<br />
|
||||
<p>
|
||||
Any general feedback you'd like to share? We'll take it all...tell us how well we're doing or where can
|
||||
improve!
|
||||
</p>
|
||||
<textarea
|
||||
value={notes}
|
||||
placeholder='This section is optional ✌️'
|
||||
rows='5'
|
||||
onChange={(e) => handleNotes(e)}
|
||||
/>
|
||||
<button onClick={() => recordResponse()}>Send it!</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageHelpful;
|
@ -1,177 +0,0 @@
|
||||
.wrapper {
|
||||
position: fixed;
|
||||
display: grid;
|
||||
place-items: center center;
|
||||
bottom: 75px;
|
||||
right: 15px;
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
color: var(--docsearch-text-color);
|
||||
background: var(--ifm-card-background-color);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: cubic-bezier(1, 0, 0, 1) 0.5s;
|
||||
box-shadow: var(--ifm-global-shadow-tl);
|
||||
|
||||
svg {
|
||||
height: 75%;
|
||||
width: 75%;
|
||||
cursor: pointer;
|
||||
color: var(--ifm-heading-color);
|
||||
}
|
||||
}
|
||||
|
||||
.emoji {
|
||||
display: grid;
|
||||
place-items: center center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
height: 35px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
:hover {
|
||||
animation-name: wave-animation;
|
||||
animation-duration: 2.5s;
|
||||
animation-iteration-count: 1;
|
||||
transform-origin: 70% 70%;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.expanded {
|
||||
width: 80vw;
|
||||
height: 500px;
|
||||
padding: 20px 20px;
|
||||
opacity: 1;
|
||||
border-radius: 8px;
|
||||
z-index: 1000;
|
||||
cursor: default;
|
||||
overflow-y: scroll;
|
||||
|
||||
// media query for mobile
|
||||
@media (max-width: 768px) {
|
||||
width: 90vw;
|
||||
height: 80svh;
|
||||
}
|
||||
}
|
||||
|
||||
.feedback {
|
||||
display: grid;
|
||||
place-items: start start;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.numberRow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
|
||||
div {
|
||||
display: grid;
|
||||
place-items: center center;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--ifm-global-shadow-tl);
|
||||
padding: 0.25rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:hover {
|
||||
box-shadow: var(--ifm-global-shadow-lw);
|
||||
}
|
||||
}
|
||||
|
||||
.numberActive {
|
||||
background: var(--ifm-color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
height: 20px !important;
|
||||
width: 20px !important;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: grid;
|
||||
text-align: left;
|
||||
|
||||
h3 {
|
||||
color: var(--ifm-heading-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 1.1rem;
|
||||
padding: 0.75rem;
|
||||
font-family: var(--ifm-font-family-base);
|
||||
// color: var(--color-gray-74);
|
||||
margin-top: 1rem;
|
||||
box-shadow: var(--ifm-global-shadow-lw);
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 20px;
|
||||
margin-left: auto;
|
||||
background: var(--ifm-color-primary);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
font-family: var(--ifm-font-family-base);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wave-animation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
10% {
|
||||
transform: rotate(14deg);
|
||||
}
|
||||
20% {
|
||||
transform: rotate(-8deg);
|
||||
}
|
||||
30% {
|
||||
transform: rotate(14deg);
|
||||
}
|
||||
40% {
|
||||
transform: rotate(-4deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
60% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
@ -81,6 +81,22 @@
|
||||
--color-gray-8: #e7ebef;
|
||||
--color-gray-12: #dce2e8;
|
||||
--color-gray-16: #cfd8df;
|
||||
//new
|
||||
--color-gray-20: #c2d0d8;
|
||||
--color-gray-24: #b6c6ce;
|
||||
--color-gray-28: #a9bcc4;
|
||||
--color-gray-32: #9db2ba;
|
||||
--color-gray-36: #91a8b0;
|
||||
--color-gray-40: #849ea6;
|
||||
--color-gray-44: #78949c;
|
||||
--color-gray-48: #6c8a92;
|
||||
--color-gray-52: #608088;
|
||||
--color-gray-56: #54767e;
|
||||
--color-gray-60: #486c74;
|
||||
--color-gray-64: #3c626a;
|
||||
--color-gray-68: #305860;
|
||||
--color-gray-72: #244e56;
|
||||
//end new
|
||||
--color-gray-74: #344658;
|
||||
--color-gray-78: #2c3b4b;
|
||||
--color-gray-82: #23303d;
|
||||
|
61
docs/src/theme/DocItem/Footer/index.js
Normal file
61
docs/src/theme/DocItem/Footer/index.js
Normal file
@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { ThemeClassNames } from '@docusaurus/theme-common';
|
||||
import { useDoc } from '@docusaurus/theme-common/internal';
|
||||
import LastUpdated from '@theme/LastUpdated';
|
||||
import EditThisPage from '@theme/EditThisPage';
|
||||
import TagsListInline from '@theme/TagsListInline';
|
||||
import styles from './styles.module.css';
|
||||
import { Feedback } from '@site/src/components/Feedback/Feedback';
|
||||
function TagsRow(props) {
|
||||
return (
|
||||
<div className={clsx(ThemeClassNames.docs.docFooterTagsRow, 'row margin-bottom--sm')}>
|
||||
<div className="col">
|
||||
<TagsListInline {...props} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function EditMetaRow({ editUrl, lastUpdatedAt, lastUpdatedBy, formattedLastUpdatedAt }) {
|
||||
return (
|
||||
<div className={clsx(ThemeClassNames.docs.docFooterEditMetaRow, 'row')}>
|
||||
<div className="col">{editUrl && <EditThisPage editUrl={editUrl} />}</div>
|
||||
|
||||
<div className={clsx('col', styles.lastUpdated)}>
|
||||
{(lastUpdatedAt || lastUpdatedBy) && (
|
||||
<LastUpdated
|
||||
lastUpdatedAt={lastUpdatedAt}
|
||||
formattedLastUpdatedAt={formattedLastUpdatedAt}
|
||||
lastUpdatedBy={lastUpdatedBy}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default function DocItemFooter() {
|
||||
const { metadata } = useDoc();
|
||||
const { editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags } = metadata;
|
||||
const canDisplayTagsRow = tags.length > 0;
|
||||
const canDisplayEditMetaRow = !!(editUrl || lastUpdatedAt || lastUpdatedBy);
|
||||
const canDisplayFooter = canDisplayTagsRow || canDisplayEditMetaRow;
|
||||
if (!canDisplayFooter) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Feedback metadata={metadata}/>
|
||||
<footer className={clsx(ThemeClassNames.docs.docFooter, 'docusaurus-mt-lg')}>
|
||||
{canDisplayTagsRow && <TagsRow tags={tags} />}
|
||||
{canDisplayEditMetaRow && (
|
||||
<EditMetaRow
|
||||
editUrl={editUrl}
|
||||
lastUpdatedAt={lastUpdatedAt}
|
||||
lastUpdatedBy={lastUpdatedBy}
|
||||
formattedLastUpdatedAt={formattedLastUpdatedAt}
|
||||
/>
|
||||
)}
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
}
|
11
docs/src/theme/DocItem/Footer/styles.module.css
Normal file
11
docs/src/theme/DocItem/Footer/styles.module.css
Normal file
@ -0,0 +1,11 @@
|
||||
.lastUpdated {
|
||||
margin-top: 0.2rem;
|
||||
font-style: italic;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
@media (min-width: 997px) {
|
||||
.lastUpdated {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user