From 5f43db0181d0198e796c827c62653fda64a93ddf Mon Sep 17 00:00:00 2001 From: Mohammad Abbasi Date: Sat, 17 Sep 2022 12:51:43 +0430 Subject: [PATCH] make support rtl according to issue 1002 --- benchmark/index.html | 50 ++++++----- cli/template.hbs | 36 ++++---- config/docker/index.tpl.html | 1 + demo/index.html | 94 ++++++++++++-------- demo/playground/index.html | 44 ++++----- demo/ssr/index.ts | 3 +- package.json | 1 + src/common-elements/fields-layout.ts | 19 +++- src/common-elements/panels.ts | 3 +- src/common-elements/shelfs.tsx | 9 +- src/common-elements/tabs.ts | 1 + src/components/ApiInfo/styled.elements.ts | 9 +- src/components/Endpoint/styled.elements.ts | 3 + src/components/Fields/Field.tsx | 5 +- src/components/JsonViewer/style.ts | 2 +- src/components/Redoc/styled.elements.tsx | 3 +- src/components/Responses/styled.elements.ts | 4 +- src/components/SearchBox/styled.elements.tsx | 3 +- src/components/SideMenu/styled.elements.ts | 5 ++ src/components/StickySidebar/ChevronSvg.tsx | 1 + src/theme.ts | 8 +- 21 files changed, 184 insertions(+), 120 deletions(-) diff --git a/benchmark/index.html b/benchmark/index.html index 247406a0..552b8fd5 100644 --- a/benchmark/index.html +++ b/benchmark/index.html @@ -1,28 +1,30 @@ + + + ReDoc + + + + - redoc { - display: block; - } - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + diff --git a/cli/template.hbs b/cli/template.hbs index bc5c5f30..6a142981 100644 --- a/cli/template.hbs +++ b/cli/template.hbs @@ -1,23 +1,23 @@ - - - - {{title}} - - - - {{{redocHead}}} - {{#unless disableGoogleFont}}{{/unless}} - + + + {{title}} + + + + {{{redocHead}}} + {{#unless disableGoogleFont}}{{/unless}} + + - - {{{redocHTML}}} - + + {{{redocHTML}}} + \ No newline at end of file diff --git a/config/docker/index.tpl.html b/config/docker/index.tpl.html index 83258786..07d2dddb 100644 --- a/config/docker/index.tpl.html +++ b/config/docker/index.tpl.html @@ -19,6 +19,7 @@ href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet" /> + diff --git a/demo/index.html b/demo/index.html index 8675b407..d5174f0b 100644 --- a/demo/index.html +++ b/demo/index.html @@ -1,46 +1,64 @@ + + + ReDoc Interactive Demo + + - - - ReDoc Interactive Demo - - + + + + - - - - + + + + - redoc { - display: block; - } - - - + +
- -
+ - - - \ No newline at end of file + if (window.location.host === 'rebilly.github.io') { + ga('create', 'UA-81703547-1', 'auto'); + ga('send', 'pageview'); + } + + + diff --git a/demo/playground/index.html b/demo/playground/index.html index d322ee3e..f9c655c0 100644 --- a/demo/playground/index.html +++ b/demo/playground/index.html @@ -1,25 +1,27 @@ + + + + ReDoc + + + + - redoc { - display: block; - } - - - - - - - - - \ No newline at end of file + + + + diff --git a/demo/ssr/index.ts b/demo/ssr/index.ts index cd0fccf2..c1eec015 100644 --- a/demo/ssr/index.ts +++ b/demo/ssr/index.ts @@ -1,9 +1,9 @@ import { renderToString } from 'react-dom/server'; import * as React from 'react'; import { ServerStyleSheet } from 'styled-components'; -import { Redoc, createStore } from '../../'; import { readFileSync } from 'fs'; import { resolve } from 'path'; +import { createStore, Redoc } from '../../src'; const yaml = require('js-yaml'); const http = require('http'); @@ -38,6 +38,7 @@ const server = http.createServer(async (request, response) => { + ${css} diff --git a/package.json b/package.json index 9e99db71..04d40a3e 100644 --- a/package.json +++ b/package.json @@ -162,6 +162,7 @@ "stickyfill": "^1.1.1", "style-loader": "^3.3.1", "swagger2openapi": "^7.0.6", + "update-notifier": "^6.0.2", "url-template": "^2.0.8" }, "size-limit": [ diff --git a/src/common-elements/fields-layout.ts b/src/common-elements/fields-layout.ts index 9e7fce0b..3f66ffae 100644 --- a/src/common-elements/fields-layout.ts +++ b/src/common-elements/fields-layout.ts @@ -9,10 +9,16 @@ export const PropertiesTableCaption = styled.caption` `; export const PropertyCell = styled.td<{ kind?: string }>` - border-left: 1px solid ${props => props.theme.schema.linesColor}; + border-left: ${({ theme }) => + theme.typography.direction === 'rtl' ? 0 : '1px solid '} ${props => + props.theme.schema.linesColor}; + border-right: ${({ theme }) => + theme.typography.direction === 'rtl' ? '1px solid ' : 0} ${props => + props.theme.schema.linesColor}; box-sizing: border-box; position: relative; - padding: 10px 10px 10px 0; + padding: ${({ theme }) => + theme.typography.direction === 'rtl' ? '10px 0 10px 10px' : '10px 10px 10px 0'} ${media.lessThan('small')` display: block; @@ -22,7 +28,8 @@ export const PropertyCell = styled.td<{ kind?: string }>` tr:first-of-type > &, tr.last > & { border-left-width: 0; - background-position: top left; + background-position: ${({ theme }) => + theme.typography.direction === 'rtl' ? 'top right' : 'top left'}; background-repeat: no-repeat; background-size: 1px 100%; } @@ -122,7 +129,7 @@ export const PropertyDetailsCell = styled.td` export const PropertyBullet = styled.span` color: ${props => props.theme.schema.linesColor}; font-family: ${props => props.theme.typography.code.fontFamily}; - margin-right: 10px; + margin: ${({ theme }) => (theme.typography.direction === 'rtl' ? '0 0 0 10px ' : '0 10px 0 0')} &::before { content: ''; @@ -143,6 +150,10 @@ export const PropertyBullet = styled.span` } `; +export const WrappedShelfIcon = styled.i` + display: inline-block; +`; + export const InnerPropertiesWrap = styled.div` padding: ${({ theme }) => theme.schema.nestingSpacing}; `; diff --git a/src/common-elements/panels.ts b/src/common-elements/panels.ts index c434c503..cded675c 100644 --- a/src/common-elements/panels.ts +++ b/src/common-elements/panels.ts @@ -4,7 +4,8 @@ import styled, { media } from '../styled-components'; export const MiddlePanel = styled.div<{ compact?: boolean }>` width: calc(100% - ${props => props.theme.rightPanel.width}); padding: 0 ${props => props.theme.spacing.sectionHorizontal}px; - + direction: ${props => props.theme.typography.direction || 'ltr'}; + text-align: ${({ theme }) => (theme.typography.direction === 'rtl' ? 'right' : 'left')}; ${({ compact, theme }) => media.lessThan('medium', true)` width: 100%; diff --git a/src/common-elements/shelfs.tsx b/src/common-elements/shelfs.tsx index 8eb3d4a7..a669c960 100644 --- a/src/common-elements/shelfs.tsx +++ b/src/common-elements/shelfs.tsx @@ -37,9 +37,14 @@ export const ShelfIcon = styled(IntShelfIcon)` width: ${props => props.size || '18px'}; min-width: ${props => props.size || '18px'}; vertical-align: middle; - float: ${props => props.float || ''}; + float: ${props => (props.theme.typography.direction === 'rtl' ? 'right' : props.float || '')}; transition: transform 0.2s ease-out; - transform: rotateZ(${props => directionMap[props.direction || 'down']}); + transform: rotateZ( + ${props => + props.theme.typography.direction === 'rtl' + ? directionMap[props.direction === 'right' ? 'left' : 'down' || 'down'] + : directionMap[props.direction || 'down']} + ); polygon { fill: ${({ color, theme }) => diff --git a/src/common-elements/tabs.ts b/src/common-elements/tabs.ts index fe41e34f..8baa17b8 100644 --- a/src/common-elements/tabs.ts +++ b/src/common-elements/tabs.ts @@ -70,6 +70,7 @@ export const Tabs = styled(ReactTabs)` & > div > pre { padding: 0; + direction: ltr; } } `; diff --git a/src/components/ApiInfo/styled.elements.ts b/src/components/ApiInfo/styled.elements.ts index 72bebcd4..a254419d 100644 --- a/src/components/ApiInfo/styled.elements.ts +++ b/src/components/ApiInfo/styled.elements.ts @@ -28,7 +28,14 @@ export const DownloadButton = styled.a` export const InfoSpan = styled.span` &::before { content: '|'; - display: inline-block; + display: ${props => (props.theme.typography.direction === 'ltr' ? 'inline-block' : 'none')}; + opacity: 0.5; + width: ${delimiterWidth}px; + text-align: center; + } + &::after { + content: '|'; + display: ${props => (props.theme.typography.direction === 'rtl' ? 'inline-block' : 'none')}; opacity: 0.5; width: ${delimiterWidth}px; text-align: center; diff --git a/src/components/Endpoint/styled.elements.ts b/src/components/Endpoint/styled.elements.ts index d86b6d43..a475a891 100644 --- a/src/components/Endpoint/styled.elements.ts +++ b/src/components/Endpoint/styled.elements.ts @@ -30,6 +30,7 @@ export const EndpointInfo = styled.button<{ expanded?: boolean; inverted?: boole border: ${props => (props.inverted ? '0' : '1px solid transparent')}; border-bottom: ${props => (props.inverted ? '1px solid #ccc' : '0')}; transition: border-color 0.25s ease; + direction: ltr; ${props => (props.expanded && !props.inverted && `border-color: ${props.theme.colors.border.dark};`) || ''} @@ -69,6 +70,7 @@ export const ServersOverlay = styled.div<{ expanded: boolean }>` transition: all 0.25s ease; visibility: hidden; ${props => (props.expanded ? 'visibility: visible;' : 'transform: translateY(-50%) scaleY(0);')} + text-align: ${({ theme }) => (theme.typography.direction === 'rtl' ? 'right' : 'left')}; `; export const ServerItem = styled.div` @@ -76,6 +78,7 @@ export const ServerItem = styled.div` `; export const ServerUrl = styled.div` + text-align: left; padding: 5px; border: 1px solid #ccc; background: ${props => props.theme.rightPanel.servers.url.backgroundColor}; diff --git a/src/components/Fields/Field.tsx b/src/components/Fields/Field.tsx index 5c35b04b..bf5cc7e0 100644 --- a/src/components/Fields/Field.tsx +++ b/src/components/Fields/Field.tsx @@ -13,6 +13,7 @@ import { PropertyCellWithInner, PropertyDetailsCell, PropertyNameCell, + WrappedShelfIcon, } from '../../common-elements/fields-layout'; import { ShelfIcon } from '../../common-elements/'; import { Schema } from '../Schema/Schema'; @@ -76,7 +77,9 @@ export class Field extends React.Component { aria-label="expand properties" > {name} - + + + {labels} diff --git a/src/components/JsonViewer/style.ts b/src/components/JsonViewer/style.ts index 4fcb9ea4..08292683 100644 --- a/src/components/JsonViewer/style.ts +++ b/src/components/JsonViewer/style.ts @@ -8,7 +8,7 @@ export const jsonStyles = css` font-family: ${props => props.theme.typography.code.fontFamily}; font-size: ${props => props.theme.typography.code.fontSize}; - + direction: ltr; white-space: ${({ theme }) => (theme.typography.code.wrap ? 'pre-wrap' : 'pre')}; contain: content; overflow-x: auto; diff --git a/src/components/Redoc/styled.elements.tsx b/src/components/Redoc/styled.elements.tsx index 4118384e..2ca46e8c 100644 --- a/src/components/Redoc/styled.elements.tsx +++ b/src/components/Redoc/styled.elements.tsx @@ -10,6 +10,7 @@ export const RedocWrap = styled.div` display: flex; position: relative; text-align: left; + direction: ${theme.typography.direction}; -webkit-font-smoothing: ${theme.typography.smoothing}; font-smoothing: ${theme.typography.smoothing}; @@ -42,7 +43,7 @@ export const BackgroundStub = styled.div` position: absolute; top: 0; bottom: 0; - right: 0; + ${({ theme }) => (theme.typography.direction === 'rtl' ? 'left: 0;' : 'right: 0;')}; width: ${({ theme }) => { if (theme.rightPanel.width.endsWith('%')) { const percents = parseInt(theme.rightPanel.width, 10); diff --git a/src/components/Responses/styled.elements.ts b/src/components/Responses/styled.elements.ts index 7e456870..21f99449 100644 --- a/src/components/Responses/styled.elements.ts +++ b/src/components/Responses/styled.elements.ts @@ -12,7 +12,7 @@ export const StyledResponseTitle = styled(ResponseTitle)` margin-bottom: 4px; line-height: 1.5em; cursor: pointer; - + text-align: ${({ theme }) => (theme.typography.direction === 'rtl' ? 'right' : 'left')}; color: ${props => props.theme.colors.responses[props.type].color}; background-color: ${props => props.theme.colors.responses[props.type].backgroundColor}; &:focus { @@ -42,7 +42,7 @@ export const ResponseDetailsWrap = styled.div` `; export const HeadersCaption = styled(UnderlinedHeader.withComponent('caption'))` - text-align: left; + text-align: ${({ theme }) => (theme.typography.direction === 'rtl' ? 'right' : 'left')}; margin-top: 1em; caption-side: top; `; diff --git a/src/components/SearchBox/styled.elements.tsx b/src/components/SearchBox/styled.elements.tsx index 6cb70fed..1be3e600 100644 --- a/src/components/SearchBox/styled.elements.tsx +++ b/src/components/SearchBox/styled.elements.tsx @@ -29,6 +29,7 @@ export const SearchInput = styled.input.attrs(() => ({ color: ${props => props.theme.sidebar.textColor}; background-color: transparent; outline: none; + text-align: ${({ theme }) => (theme.typography.direction === 'rtl' ? 'center' : 'inherit')}; `; export const SearchIcon = styled((props: { className?: string }) => ( @@ -66,7 +67,7 @@ export const SearchResultsBox = styled.div` margin-top: 10px; line-height: 1.4; font-size: 0.9em; - + li { background-color: inherit; } diff --git a/src/components/SideMenu/styled.elements.ts b/src/components/SideMenu/styled.elements.ts index 8ca79a1a..271398aa 100644 --- a/src/components/SideMenu/styled.elements.ts +++ b/src/components/SideMenu/styled.elements.ts @@ -139,6 +139,10 @@ export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({ padding: 12.5px ${props => props.theme.spacing.unit * 4}px; ${({ depth, type, theme }) => (type === 'section' && depth > 1 && 'padding-left: ' + theme.spacing.unit * 8 + 'px;') || ''} + direction: ${({ type, theme }) => + ['section', 'group', 'tag'].indexOf(type || '') > -1 && theme.typography.direction === 'rtl' + ? 'rtl' + : 'ltr'}; display: flex; justify-content: space-between; font-family: ${props => props.theme.typography.headings.fontFamily}; @@ -156,6 +160,7 @@ export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({ } ${ShelfIcon} { + transform: ${({ theme }) => (theme.typography.direction === 'rtl' ? 'rotate(0deg)' : 'none')}; height: ${({ theme }) => theme.sidebar.arrow.size}; width: ${({ theme }) => theme.sidebar.arrow.size}; polygon { diff --git a/src/components/StickySidebar/ChevronSvg.tsx b/src/components/StickySidebar/ChevronSvg.tsx index 5343ddb1..51a114e0 100644 --- a/src/components/StickySidebar/ChevronSvg.tsx +++ b/src/components/StickySidebar/ChevronSvg.tsx @@ -62,5 +62,6 @@ const ChevronContainer = styled.div` align-self: center; display: flex; flex-direction: column; + direction: ltr; color: ${props => props.theme.colors.primary.main}; `; diff --git a/src/theme.ts b/src/theme.ts index 25a91224..5f659577 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -107,17 +107,17 @@ const defaultTheme: ThemeInterface = { fontWeightRegular: '400', fontWeightBold: '600', fontWeightLight: '300', - fontFamily: 'Roboto, sans-serif', + fontFamily: 'Roboto, sans-serif, Vazir', smoothing: 'antialiased', optimizeSpeed: true, headings: { - fontFamily: 'Montserrat, sans-serif', + fontFamily: 'Montserrat, sans-serif, Vazir', fontWeight: '400', lineHeight: '1.6em', }, code: { fontSize: '13px', - fontFamily: 'Courier, monospace', + fontFamily: 'Montserrat, sans-serif, Vazir', lineHeight: ({ typography }) => typography.lineHeight, fontWeight: ({ typography }) => typography.fontWeightRegular, color: '#e53935', @@ -318,7 +318,7 @@ export interface ResolvedThemeInterface { fontWeightRegular: string; fontWeightBold: string; fontFamily: string; - + direction?: 'ltr' | 'rtl'; smoothing: string; optimizeSpeed: boolean;