mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-08 14:14:56 +03:00
Add READMORE button feature.
This commit is contained in:
parent
ebe8d3f68c
commit
884031b60c
77
src/common-elements/ReadMore.tsx
Normal file
77
src/common-elements/ReadMore.tsx
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import styled from '../styled-components';
|
||||||
|
import {ShelfIcon} from "./shelfs";
|
||||||
|
import {Markdown} from "../components";
|
||||||
|
|
||||||
|
|
||||||
|
export interface ReadMoreProps {
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReadMoreState {
|
||||||
|
open: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReadMoreWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: ${(props: ReadMoreState) => props.open ? 'auto': '5em'};
|
||||||
|
padding-bottom: ${(props: ReadMoreState) => props.open ? '3em' : '1em'};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ReadMoreButtonBackground = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
background-image: linear-gradient(to bottom, rgba(255,255,255,0.3), white);
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
padding: ${(props: ReadMoreState) => props.open ? '0': '2em'};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ReadMoreButton = styled.span`
|
||||||
|
background: #999;
|
||||||
|
color: #fff;
|
||||||
|
padding: 0.3em 0.75em;
|
||||||
|
border-radius: 2px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export class ReadMore extends React.Component<ReadMoreProps, ReadMoreState> {
|
||||||
|
constructor(props: ReadMoreProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
open: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.setState(prevState => ({
|
||||||
|
open: !prevState.open
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { content } = this.props;
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
return <ReadMoreWrapper open={false} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReadMoreWrapper open={this.state.open}>
|
||||||
|
<ReadMoreButtonBackground onClick={this.toggle} open={this.state.open}>
|
||||||
|
<ReadMoreButton>
|
||||||
|
<ShelfIcon
|
||||||
|
size={'1.5em'}
|
||||||
|
color={'white'}
|
||||||
|
direction={this.state.open ? 'up' : 'down'}
|
||||||
|
/>
|
||||||
|
{this.state.open ? 'CLOSE' : 'READ MORE'}
|
||||||
|
</ReadMoreButton>
|
||||||
|
</ReadMoreButtonBackground>
|
||||||
|
<Markdown source={content} />
|
||||||
|
</ReadMoreWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||||
import { OperationModel as OperationType } from '../../services/models';
|
import { OperationModel as OperationType } from '../../services/models';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
import { Extensions } from '../Fields/Extensions';
|
import { Extensions } from '../Fields/Extensions';
|
||||||
|
import { ReadMore } from "../../common-elements/ReadMore";
|
||||||
|
|
||||||
const OperationRow = styled(Row)`
|
const OperationRow = styled(Row)`
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
|
@ -31,10 +32,24 @@ const Description = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ReadmorePattern = /<!--\s*READMORE\s*-->/;
|
||||||
|
|
||||||
export interface OperationProps {
|
export interface OperationProps {
|
||||||
operation: OperationType;
|
operation: OperationType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function splitDescription(description) {
|
||||||
|
if (!description) {
|
||||||
|
return ["", ""];
|
||||||
|
} else {
|
||||||
|
// str#split split at all occurence of matched string.
|
||||||
|
// So "a<!--READMORE-->b<!--READMORE-->c" --> ["a", "<!..>", "b", "<!..>", "c"]
|
||||||
|
const [head, ...rest] = description.split(ReadmorePattern);
|
||||||
|
const more = rest.join("");
|
||||||
|
return [head, more];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Operation extends React.Component<OperationProps> {
|
export class Operation extends React.Component<OperationProps> {
|
||||||
render() {
|
render() {
|
||||||
|
@ -43,6 +58,7 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
const { name: summary, description, deprecated, externalDocs } = operation;
|
const { name: summary, description, deprecated, externalDocs } = operation;
|
||||||
const hasDescription = !!(description || externalDocs);
|
const hasDescription = !!(description || externalDocs);
|
||||||
|
|
||||||
|
const [headDescription, moreDescription] = splitDescription(description);
|
||||||
return (
|
return (
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{options => (
|
||||||
|
@ -55,7 +71,8 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
|
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
|
||||||
{hasDescription && (
|
{hasDescription && (
|
||||||
<Description>
|
<Description>
|
||||||
{description !== undefined && <Markdown source={description} />}
|
{description !== undefined && <Markdown source={headDescription} />}
|
||||||
|
<ReadMore content={moreDescription}/>
|
||||||
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
||||||
</Description>
|
</Description>
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user