mirror of
https://github.com/Redocly/redoc.git
synced 2025-11-24 19:45:14 +03:00
Merge d67f3e208f into d41fd46f7c
This commit is contained in:
commit
2df3cae454
27364
demo/big-openapi.json
27364
demo/big-openapi.json
File diff suppressed because one or more lines are too long
|
|
@ -1,11 +1,11 @@
|
|||
import * as React from 'react';
|
||||
\import * as React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import styled from 'styled-components';
|
||||
import { RedocStandalone } from '../src';
|
||||
import ComboBox from './ComboBox';
|
||||
import FileInput from './components/FileInput';
|
||||
|
||||
const DEFAULT_SPEC = 'museum.yaml';
|
||||
const DEFAULT_SPEC = 'big-openapi.json';
|
||||
const NEW_VERSION_PETSTORE = 'openapi-3-1.yaml';
|
||||
|
||||
const demos = [
|
||||
|
|
|
|||
787
demo/museum.yaml
787
demo/museum.yaml
|
|
@ -1,787 +0,0 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: Redocly Museum API
|
||||
description: An imaginary, but delightful Museum API for interacting with museum services and information. Built with love by Redocly.
|
||||
version: 1.0.0
|
||||
contact:
|
||||
email: team@redocly.com
|
||||
url: 'https://redocly.com/docs/cli/'
|
||||
x-logo:
|
||||
url: 'https://redocly.github.io/redoc/museum-logo.png'
|
||||
altText: Museum logo
|
||||
license:
|
||||
name: MIT
|
||||
url: 'https://opensource.org/license/mit/ '
|
||||
servers:
|
||||
- url: 'https://api.fake-museum-example.com/v1'
|
||||
paths:
|
||||
/museum-hours:
|
||||
get:
|
||||
summary: Get museum hours
|
||||
description: Get upcoming museum operating hours
|
||||
operationId: getMuseumHours
|
||||
tags:
|
||||
- Operations
|
||||
x-badges:
|
||||
- name: 'Beta'
|
||||
position: before
|
||||
color: purple
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/StartDate'
|
||||
- $ref: '#/components/parameters/PaginationPage'
|
||||
- $ref: '#/components/parameters/PaginationLimit'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GetMuseumHoursResponse'
|
||||
examples:
|
||||
default:
|
||||
summary: Museum opening hours
|
||||
value:
|
||||
- date: '2023-09-11'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-12'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-13'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-17'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
closed:
|
||||
summary: The museum is closed
|
||||
value: []
|
||||
|
||||
'400':
|
||||
description: Bad request
|
||||
'404':
|
||||
description: Not found
|
||||
/special-events:
|
||||
post:
|
||||
security: []
|
||||
operationId: CreateSpecialEvent
|
||||
summary: Create special event
|
||||
tags:
|
||||
- Events
|
||||
x-badges:
|
||||
- name: 'Alpha'
|
||||
color: purple
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateSpecialEventRequest'
|
||||
examples:
|
||||
default_example:
|
||||
$ref: '#/components/examples/CreateSpecialEventRequestExample'
|
||||
responses:
|
||||
'200':
|
||||
description: success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SpecialEventResponse'
|
||||
examples:
|
||||
default_example:
|
||||
$ref: '#/components/examples/CreateSpecialEventResponseExample'
|
||||
'400':
|
||||
description: Bad request
|
||||
'404':
|
||||
description: Not found
|
||||
get:
|
||||
summary: List special events
|
||||
description: Return a list of upcoming special events at the museum.
|
||||
security: []
|
||||
operationId: listSpecialEvents
|
||||
x-badges:
|
||||
- name: 'Gamma'
|
||||
tags:
|
||||
- Events
|
||||
parameters:
|
||||
- name: startDate
|
||||
in: query
|
||||
description: The starting date to retrieve future operating hours from. Defaults to today's date.
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
example: 2023-02-23
|
||||
- name: endDate
|
||||
in: query
|
||||
description: The end of a date range to retrieve special events for. Defaults to 7 days after `startDate`.
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
example: 2023-04-18
|
||||
- name: page
|
||||
in: query
|
||||
description: The page number to retrieve.
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
example: 2
|
||||
- name: limit
|
||||
in: query
|
||||
description: The number of days per page.
|
||||
schema:
|
||||
type: integer
|
||||
default: 10
|
||||
maximum: 30
|
||||
example: 15
|
||||
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ListSpecialEventsResponse'
|
||||
examples:
|
||||
default_example:
|
||||
$ref: '#/components/examples/ListSpecialEventsResponseExample'
|
||||
'400':
|
||||
description: Bad request
|
||||
'404':
|
||||
description: Not found
|
||||
/special-events/{eventId}:
|
||||
get:
|
||||
summary: Get special event
|
||||
description: Get details about a special event.
|
||||
operationId: getSpecialEvent
|
||||
tags:
|
||||
- Events
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/EventId'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SpecialEventResponse'
|
||||
examples:
|
||||
default_example:
|
||||
$ref: '#/components/examples/GetSpecialEventResponseExample'
|
||||
'400':
|
||||
description: Bad request
|
||||
'404':
|
||||
description: Not found
|
||||
patch:
|
||||
summary: Update special event
|
||||
description: Update the details of a special event
|
||||
operationId: updateSpecialEvent
|
||||
tags:
|
||||
- Events
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/EventId'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateSpecialEventRequest'
|
||||
examples:
|
||||
default_example:
|
||||
$ref: '#/components/examples/UpdateSpecialEventRequestExample'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SpecialEventResponse'
|
||||
examples:
|
||||
default_example:
|
||||
$ref: '#/components/examples/UpdateSpecialEventResponseExample'
|
||||
'400':
|
||||
description: Bad request
|
||||
'404':
|
||||
description: Not found
|
||||
delete:
|
||||
summary: Delete special event
|
||||
description: Delete a special event from the collection. Allows museum to cancel planned events.
|
||||
operationId: deleteSpecialEvent
|
||||
tags:
|
||||
- Events
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/EventId'
|
||||
responses:
|
||||
'204':
|
||||
description: Success - no content
|
||||
'400':
|
||||
description: Bad request
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'404':
|
||||
description: Not found
|
||||
/tickets:
|
||||
post:
|
||||
summary: Buy museum tickets
|
||||
description: Purchase museum tickets for general entry or special events.
|
||||
operationId: buyMuseumTickets
|
||||
tags:
|
||||
- Tickets
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BuyMuseumTicketsRequest'
|
||||
examples:
|
||||
general_entry:
|
||||
$ref: '#/components/examples/BuyGeneralTicketsRequestExample'
|
||||
event_entry:
|
||||
$ref: '#/components/examples/BuyEventTicketsRequestExample'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BuyMuseumTicketsResponse'
|
||||
examples:
|
||||
general_entry:
|
||||
$ref: '#/components/examples/BuyGeneralTicketsResponseExample'
|
||||
event_entry:
|
||||
$ref: '#/components/examples/BuyEventTicketsResponseExample'
|
||||
'400':
|
||||
description: Bad request
|
||||
'404':
|
||||
description: Not found
|
||||
/tickets/{ticketId}/qr:
|
||||
get:
|
||||
summary: Get ticket QR code
|
||||
description: Return an image of your ticket with scannable QR code. Used for event entry.
|
||||
operationId: getTicketCode
|
||||
tags:
|
||||
- Tickets
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/TicketId'
|
||||
responses:
|
||||
'200':
|
||||
description: Scannable event ticket in image format
|
||||
content:
|
||||
image/png:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GetTicketCodeResponse'
|
||||
'400':
|
||||
description: Bad request
|
||||
'404':
|
||||
description: Not found
|
||||
components:
|
||||
schemas:
|
||||
SpecialEvent:
|
||||
description: Request payload for creating new special events at the museum.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the special event
|
||||
type: string
|
||||
example: Fossil lecture
|
||||
location:
|
||||
description: Location where the special event is held
|
||||
type: string
|
||||
example: Lecture theatre
|
||||
eventDescription:
|
||||
description: Description of the special event
|
||||
type: string
|
||||
example: Our panel of experts will share their favorite fossils and explain why they are so great.
|
||||
dates:
|
||||
description: List of planned dates for the special event
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: date
|
||||
example: 2024-03-29
|
||||
price:
|
||||
description: Price of a ticket for the special event
|
||||
type: number
|
||||
format: float
|
||||
example: 12.50
|
||||
|
||||
TicketType:
|
||||
description: Type of ticket being purchased. Use `general` for regular museum entry and `event` for tickets to special events.
|
||||
type: string
|
||||
enum:
|
||||
- event
|
||||
- general
|
||||
x-enumDescriptions:
|
||||
event: Special event ticket
|
||||
general: General museum entry ticket
|
||||
example: event
|
||||
Date:
|
||||
type: string
|
||||
format: date
|
||||
example: 2023-10-29
|
||||
Email:
|
||||
description: Email address for ticket purchaser.
|
||||
type: string
|
||||
format: email
|
||||
example: museum-lover@example.com
|
||||
Phone:
|
||||
description: Phone number for the ticket purchaser (optional).
|
||||
type: string
|
||||
example: +1(234)-567-8910
|
||||
BuyMuseumTicketsRequest:
|
||||
description: Request payload used for purchasing museum tickets.
|
||||
type: object
|
||||
properties:
|
||||
ticketType:
|
||||
$ref: '#/components/schemas/TicketType'
|
||||
eventId:
|
||||
description: Unique identifier for a special event. Required if purchasing tickets for the museum's special events.
|
||||
$ref: '#/components/schemas/EventId'
|
||||
ticketDate:
|
||||
description: Date that the ticket is valid for.
|
||||
$ref: '#/components/schemas/Date'
|
||||
email:
|
||||
$ref: '#/components/schemas/Email'
|
||||
phone:
|
||||
$ref: '#/components/schemas/Phone'
|
||||
required:
|
||||
- ticketType
|
||||
- ticketDate
|
||||
- email
|
||||
TicketMessage:
|
||||
description: Confirmation message after a ticket purchase.
|
||||
type: string
|
||||
example: Museum general entry ticket purchased
|
||||
TicketId:
|
||||
description: Unique identifier for museum ticket. Generated when purchased.
|
||||
type: string
|
||||
format: uuid
|
||||
example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c
|
||||
TicketConfirmation:
|
||||
description: Unique confirmation code used to verify ticket purchase.
|
||||
type: string
|
||||
example: ticket-event-a98c8f-7eb12
|
||||
BuyMuseumTicketsResponse:
|
||||
description: Details for a museum ticket after a successful purchase.
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
$ref: '#/components/schemas/TicketMessage'
|
||||
eventName:
|
||||
$ref: '#/components/schemas/EventName'
|
||||
ticketId:
|
||||
$ref: '#/components/schemas/TicketId'
|
||||
ticketType:
|
||||
$ref: '#/components/schemas/TicketType'
|
||||
ticketDate:
|
||||
description: Date the ticket is valid for.
|
||||
$ref: '#/components/schemas/Date'
|
||||
confirmationCode:
|
||||
$ref: '#/components/schemas/TicketConfirmation'
|
||||
required:
|
||||
- message
|
||||
- ticketId
|
||||
- ticketType
|
||||
- ticketDate
|
||||
- confirmationCode
|
||||
GetTicketCodeResponse:
|
||||
description: An image of a ticket with a QR code used for museum or event entry.
|
||||
type: string
|
||||
format: binary
|
||||
GetMuseumHoursResponse:
|
||||
description: List of museum operating hours for consecutive days.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/MuseumDailyHours'
|
||||
MuseumDailyHours:
|
||||
description: Daily operating hours for the museum.
|
||||
type: object
|
||||
properties:
|
||||
date:
|
||||
description: Date the operating hours apply to.
|
||||
$ref: '#/components/schemas/Date'
|
||||
example: 2024-12-31
|
||||
timeOpen:
|
||||
type: string
|
||||
pattern: '^([01]\d|2[0-3]):?([0-5]\d)$'
|
||||
description: Time the museum opens on a specific date. Uses 24 hour time format (`HH:mm`).
|
||||
example: 09:00
|
||||
timeClose:
|
||||
description: Time the museum closes on a specific date. Uses 24 hour time format (`HH:mm`).
|
||||
type: string
|
||||
pattern: '^([01]\d|2[0-3]):?([0-5]\d)$'
|
||||
example: 18:00
|
||||
required:
|
||||
- date
|
||||
- timeOpen
|
||||
- timeClose
|
||||
EventId:
|
||||
description: Identifier for a special event.
|
||||
type: string
|
||||
format: uuid
|
||||
example: 3be6453c-03eb-4357-ae5a-984a0e574a54
|
||||
EventName:
|
||||
type: string
|
||||
description: Name of the special event
|
||||
example: Pirate Coding Workshop
|
||||
EventLocation:
|
||||
type: string
|
||||
description: Location where the special event is held
|
||||
example: Computer Room
|
||||
EventDescription:
|
||||
type: string
|
||||
description: Description of the special event
|
||||
example: Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).
|
||||
EventDates:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Date'
|
||||
description: List of planned dates for the special event
|
||||
EventPrice:
|
||||
description: Price of a ticket for the special event
|
||||
type: number
|
||||
format: float
|
||||
example: 25
|
||||
CreateSpecialEventRequest:
|
||||
description: Request payload for creating new special events at the museum.
|
||||
properties:
|
||||
name:
|
||||
$ref: '#/components/schemas/EventName'
|
||||
location:
|
||||
$ref: '#/components/schemas/EventLocation'
|
||||
eventDescription:
|
||||
$ref: '#/components/schemas/EventDescription'
|
||||
dates:
|
||||
$ref: '#/components/schemas/EventDates'
|
||||
price:
|
||||
$ref: '#/components/schemas/EventPrice'
|
||||
required:
|
||||
- name
|
||||
- location
|
||||
- eventDescription
|
||||
- dates
|
||||
- price
|
||||
UpdateSpecialEventRequest:
|
||||
description: Request payload for updating an existing special event. Only included fields are updated in the event.
|
||||
properties:
|
||||
name:
|
||||
$ref: '#/components/schemas/EventName'
|
||||
location:
|
||||
$ref: '#/components/schemas/EventLocation'
|
||||
eventDescription:
|
||||
$ref: '#/components/schemas/EventDescription'
|
||||
dates:
|
||||
$ref: '#/components/schemas/EventDates'
|
||||
price:
|
||||
$ref: '#/components/schemas/EventPrice'
|
||||
ListSpecialEventsResponse:
|
||||
description: A list of upcoming special events
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SpecialEventResponse'
|
||||
SpecialEventResponse:
|
||||
description: Information about a special event.
|
||||
properties:
|
||||
eventId:
|
||||
$ref: '#/components/schemas/EventId'
|
||||
name:
|
||||
$ref: '#/components/schemas/EventName'
|
||||
location:
|
||||
$ref: '#/components/schemas/EventLocation'
|
||||
eventDescription:
|
||||
$ref: '#/components/schemas/EventDescription'
|
||||
dates:
|
||||
$ref: '#/components/schemas/EventDates'
|
||||
price:
|
||||
$ref: '#/components/schemas/EventPrice'
|
||||
required:
|
||||
- eventId
|
||||
- name
|
||||
- location
|
||||
- eventDescription
|
||||
- dates
|
||||
- price
|
||||
securitySchemes:
|
||||
MuseumPlaceholderAuth:
|
||||
type: http
|
||||
scheme: basic
|
||||
examples:
|
||||
BuyGeneralTicketsRequestExample:
|
||||
summary: General entry ticket
|
||||
value:
|
||||
ticketType: general
|
||||
ticketDate: 2023-09-07
|
||||
email: todd@example.com
|
||||
BuyEventTicketsRequestExample:
|
||||
summary: Special event ticket
|
||||
value:
|
||||
ticketType: general
|
||||
eventId: dad4bce8-f5cb-4078-a211-995864315e39
|
||||
ticketDate: 2023-09-05
|
||||
email: todd@example.com
|
||||
BuyGeneralTicketsResponseExample:
|
||||
summary: General entry ticket
|
||||
value:
|
||||
message: Museum general entry ticket purchased
|
||||
ticketId: 382c0820-0530-4f4b-99af-13811ad0f17a
|
||||
ticketType: general
|
||||
ticketDate: 2023-09-07
|
||||
confirmationCode: ticket-general-e5e5c6-dce78
|
||||
BuyEventTicketsResponseExample:
|
||||
summary: Special event ticket
|
||||
value:
|
||||
message: Museum special event ticket purchased
|
||||
ticketId: b811f723-17b2-44f7-8952-24b03e43d8a9
|
||||
eventName: Mermaid Treasure Identification and Analysis
|
||||
ticketType: event
|
||||
ticketDate: 2023-09-05
|
||||
confirmationCode: ticket-event-9c55eg-8v82a
|
||||
CreateSpecialEventRequestExample:
|
||||
summary: Create special event
|
||||
value:
|
||||
name: Mermaid Treasure Identification and Analysis
|
||||
location: Under the seaaa 🦀 🎶 🌊.
|
||||
eventDescription: Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.
|
||||
dates:
|
||||
- 2023-09-05
|
||||
- 2023-09-08
|
||||
price: 0
|
||||
CreateSpecialEventResponseExample:
|
||||
summary: Special event created
|
||||
value:
|
||||
eventId: dad4bce8-f5cb-4078-a211-995864315e39
|
||||
name: Mermaid Treasure Identification and Analysis
|
||||
location: Under the seaaa 🦀 🎶 🌊.
|
||||
eventDescription: Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.
|
||||
dates:
|
||||
- 2023-09-05
|
||||
- 2023-09-08
|
||||
price: 30
|
||||
GetSpecialEventResponseExample:
|
||||
summary: Get special event
|
||||
value:
|
||||
eventId: 6744a0da-4121-49cd-8479-f8cc20526495
|
||||
name: Time Traveler Tea Party
|
||||
location: Temporal Tearoom
|
||||
eventDescription: Sip tea with important historical figures.
|
||||
dates:
|
||||
- 2023-11-18
|
||||
- 2023-11-25
|
||||
- 2023-12-02
|
||||
price: 60
|
||||
ListSpecialEventsResponseExample:
|
||||
summary: List of special events
|
||||
value:
|
||||
- eventId: f3e0e76e-e4a8-466e-ab9c-ae36c15b8e97
|
||||
name: Sasquatch Ballet
|
||||
location: Seattle... probably
|
||||
eventDescription: They're big, they're hairy, but they're also graceful. Come learn how the biggest feet can have the lightest touch.
|
||||
dates:
|
||||
- 2023-12-15
|
||||
- 2023-12-22
|
||||
price: 40
|
||||
- eventId: 2f14374a-9c65-4ee5-94b7-fba66d893483
|
||||
name: Solar Telescope Demonstration
|
||||
location: Far from the sun.
|
||||
eventDescription: Look at the sun without going blind!
|
||||
dates:
|
||||
- 2023-09-07
|
||||
- 2023-09-14
|
||||
price: 50
|
||||
- eventId: 6aaa61ba-b2aa-4868-b803-603dbbf7bfdb
|
||||
name: Cook like a Caveman
|
||||
location: Fire Pit on East side
|
||||
eventDescription: Learn to cook on an open flame.
|
||||
dates:
|
||||
- 2023-11-10
|
||||
- 2023-11-17
|
||||
- 2023-11-24
|
||||
price: 5
|
||||
- eventId: 602b75e1-5696-4ab8-8c7a-f9e13580f910
|
||||
name: Underwater Basket Weaving
|
||||
location: Rec Center Pool next door.
|
||||
eventDescription: Learn to weave baskets underwater.
|
||||
dates:
|
||||
- 2023-09-12
|
||||
- 2023-09-15
|
||||
price: 15
|
||||
- eventId: dad4bce8-f5cb-4078-a211-995864315e39
|
||||
name: Mermaid Treasure Identification and Analysis
|
||||
location: Room Sea-12
|
||||
eventDescription: Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits — kindly donated by Ariel.
|
||||
dates:
|
||||
- 2023-09-05
|
||||
- 2023-09-08
|
||||
price: 30
|
||||
- eventId: 6744a0da-4121-49cd-8479-f8cc20526495
|
||||
name: Time Traveler Tea Party
|
||||
location: Temporal Tearoom
|
||||
eventDescription: Sip tea with important historical figures.
|
||||
dates:
|
||||
- 2023-11-18
|
||||
- 2023-11-25
|
||||
- 2023-12-02
|
||||
price: 60
|
||||
- eventId: 3be6453c-03eb-4357-ae5a-984a0e574a54
|
||||
name: Pirate Coding Workshop
|
||||
location: Computer Room
|
||||
eventDescription: Captain Blackbeard shares his love of the C...language. And possibly Arrrrr (R lang).
|
||||
dates:
|
||||
- 2023-10-29
|
||||
- 2023-10-30
|
||||
- 2023-10-31
|
||||
price: 45
|
||||
- eventId: 9d90d29a-2af5-4206-97d9-9ea9ceadcb78
|
||||
name: Llama Street Art Through the Ages
|
||||
location: Auditorium
|
||||
eventDescription: Llama street art?! Alpaca my bags -- let's go!
|
||||
dates:
|
||||
- 2023-10-29
|
||||
- 2023-10-30
|
||||
- 2023-10-31
|
||||
price: 45
|
||||
- eventId: a3c7b2c4-b5fb-4ef7-9322-00a919864957
|
||||
name: The Great Parrot Debate
|
||||
location: Outdoor Amphitheatre
|
||||
eventDescription: See leading parrot minds discuss important geopolitical issues.
|
||||
dates:
|
||||
- 2023-11-03
|
||||
- 2023-11-10
|
||||
price: 35
|
||||
- eventId: b92d46b7-4c5d-422b-87a5-287767e26f29
|
||||
name: Eat a Bunch of Corn
|
||||
location: Cafeteria
|
||||
eventDescription: We accidentally bought too much corn. Please come eat it.
|
||||
dates:
|
||||
- 2023-11-10
|
||||
- 2023-11-17
|
||||
- 2023-11-24
|
||||
price: 5
|
||||
UpdateSpecialEventRequestExample:
|
||||
summary: Update special event request
|
||||
value:
|
||||
location: On the beach.
|
||||
price: 15
|
||||
UpdateSpecialEventResponseExample:
|
||||
summary: Update special event
|
||||
value:
|
||||
eventId: dad4bce8-f5cb-4078-a211-995864315e39
|
||||
name: Mermaid Treasure Identification and Analysis
|
||||
location: On the beach.
|
||||
eventDescription: Join us as we review and classify a rare collection of 20 thingamabobs, gadgets, gizmos, whoosits, and whatsits, kindly donated by Ariel.
|
||||
dates:
|
||||
- 2023-09-05
|
||||
- 2023-09-08
|
||||
price: 15
|
||||
GetMuseumHours:
|
||||
summary: Museum opening hours
|
||||
value:
|
||||
- date: '2023-09-11'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-12'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-13'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-14'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-15'
|
||||
timeOpen: '10:00'
|
||||
timeClose: '16:00'
|
||||
- date: '2023-09-18'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-19'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-20'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-21'
|
||||
timeOpen: '09:00'
|
||||
timeClose: '18:00'
|
||||
- date: '2023-09-22'
|
||||
timeOpen: '10:00'
|
||||
timeClose: '16:00'
|
||||
ClosedMuseumHours:
|
||||
summary: The museum is closed
|
||||
value: []
|
||||
parameters:
|
||||
PaginationPage:
|
||||
name: page
|
||||
in: query
|
||||
description: The page number to retrieve.
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
example: 2
|
||||
PaginationLimit:
|
||||
name: limit
|
||||
in: query
|
||||
description: The number of days per page.
|
||||
schema:
|
||||
type: integer
|
||||
default: 10
|
||||
maximum: 30
|
||||
example: 15
|
||||
EventId:
|
||||
name: eventId
|
||||
in: path
|
||||
description: An identifier for a special event.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
example: dad4bce8-f5cb-4078-a211-995864315e39
|
||||
StartDate:
|
||||
name: startDate
|
||||
in: query
|
||||
description: The starting date to retrieve future operating hours from. Defaults to today's date.
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
example: 2023-02-23
|
||||
EndDate:
|
||||
name: endDate
|
||||
in: query
|
||||
description: The end of a date range to retrieve special events for. Defaults to 7 days after `startDate`.
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
example: 2023-04-18
|
||||
TicketId:
|
||||
name: ticketId
|
||||
in: path
|
||||
description: An identifier for a ticket to a museum event. Used to generate ticket image.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
example: a54a57ca-36f8-421b-a6b4-2e8f26858a4c
|
||||
|
||||
tags:
|
||||
- name: Operations
|
||||
x-displayName: About the museum
|
||||
description: Operational information about the museum.
|
||||
- name: Events
|
||||
x-displayName: Upcoming events
|
||||
description: Special events hosted by the Museum.
|
||||
- name: Tickets
|
||||
x-displayName: Buy tickets
|
||||
description: Museum tickets for general entrance or special events.
|
||||
|
||||
x-tagGroups:
|
||||
- name: Plan your visit
|
||||
tags:
|
||||
- Operations
|
||||
- Events
|
||||
- name: Purchases
|
||||
tags:
|
||||
- Tickets
|
||||
- name: Entities
|
||||
tags:
|
||||
- Schemas
|
||||
|
||||
security:
|
||||
- MuseumPlaceholderAuth: []
|
||||
|
|
@ -9,7 +9,7 @@ const swagger = window.location.search.indexOf('swagger') > -1;
|
|||
const userUrl = window.location.search.match(/url=(.*)$/);
|
||||
|
||||
const specUrl =
|
||||
(userUrl && userUrl[1]) || (swagger ? 'museum.yaml' : big ? 'big-openapi.json' : 'museum.yaml');
|
||||
(userUrl && userUrl[1]) || (swagger ? 'big-openapi.json' : big ? 'big-openapi.json' : 'big-openapi.json');
|
||||
|
||||
const options: RedocRawOptions = {
|
||||
nativeScrollbars: false,
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ export default (env: { playground?: boolean; bench?: boolean } = {}) => ({
|
|||
webpackIgnore(/json-schema-ref-parser\/lib\/dereference\.js/),
|
||||
webpackIgnore(/^\.\/SearchWorker\.worker$/),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: ['demo/museum.yaml'],
|
||||
patterns: ['demo/big-openapi.json'],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
|
|
|||
10
package.json
10
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "redoc",
|
||||
"version": "2.5.2",
|
||||
"version": "2.5.0",
|
||||
"description": "ReDoc",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -62,7 +62,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
|
||||
"@cypress/webpack-preprocessor": "^5.17.1",
|
||||
"@size-limit/file": "^11.1.4",
|
||||
"@types/chai": "^4.2.18",
|
||||
"@types/dompurify": "^2.2.2",
|
||||
|
|
@ -90,7 +89,6 @@
|
|||
"copy-webpack-plugin": "^9.0.0",
|
||||
"core-js": "^3.13.1",
|
||||
"css-loader": "^5.2.6",
|
||||
"cypress": "^13.8.1",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-to-json": "^3.6.2",
|
||||
"esbuild-loader": "^4.3.0",
|
||||
|
|
@ -126,7 +124,7 @@
|
|||
"url": "^0.11.1",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.2.1",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"workerize-loader": "github:redocly/workerize-loader#webpack-5-dist"
|
||||
},
|
||||
|
|
@ -147,8 +145,8 @@
|
|||
"lunr": "^2.3.9",
|
||||
"mark.js": "^8.11.1",
|
||||
"marked": "^4.3.0",
|
||||
"mobx-react": "9.2.0",
|
||||
"openapi-sampler": "^1.6.2",
|
||||
"mobx-react": "^9.1.1",
|
||||
"openapi-sampler": "^1.5.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"perfect-scrollbar": "^1.5.5",
|
||||
"polished": "^4.2.2",
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export const PropertiesTable = styled.table`
|
|||
border-collapse: separate;
|
||||
border-radius: 3px;
|
||||
font-size: ${props => props.theme.typography.fontSize};
|
||||
|
||||
direction: ltr;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export const H2 = styled.h2`
|
|||
${headerCommonMixin(2)};
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
margin: 0 0 20px;
|
||||
text-align: right;
|
||||
|
||||
${extensionsHook('H2')};
|
||||
`;
|
||||
|
|
@ -31,6 +32,7 @@ export const H2 = styled.h2`
|
|||
export const H3 = styled.h2`
|
||||
${headerCommonMixin(3)};
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
text-align: right;
|
||||
|
||||
${extensionsHook('H3')};
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
|||
{info.title} {version}
|
||||
</ApiHeader>
|
||||
{!hideDownloadButtons && (
|
||||
<p>
|
||||
<p style={{ 'direction' : 'ltr', 'textAlign':'left' }}>
|
||||
{l('downloadSpecification')}:
|
||||
{downloadUrls?.map(({ title, url }) => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,51 +1,211 @@
|
|||
import { H1, MiddlePanel } from '../../common-elements';
|
||||
import styled, { extensionsHook } from '../../styled-components';
|
||||
import * as classnames from 'classnames';
|
||||
import { darken } from 'polished';
|
||||
|
||||
const delimiterWidth = 15;
|
||||
import { deprecatedCss, ShelfIcon } from '../../common-elements';
|
||||
import styled, { css, media, ResolvedThemeInterface } from '../../styled-components';
|
||||
|
||||
export const ApiInfoWrap = MiddlePanel;
|
||||
|
||||
export const ApiHeader = styled(H1)`
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
${extensionsHook('ApiHeader')};
|
||||
`;
|
||||
|
||||
export const DownloadButton = styled.a`
|
||||
border: 1px solid ${props => props.theme.colors.primary.main};
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
font-weight: normal;
|
||||
margin-left: 0.5em;
|
||||
padding: 4px 8px 4px;
|
||||
export const OperationBadge = styled.span.attrs((props: { type: string; color?: string }) => ({
|
||||
className: `operation-type ${props.type}`,
|
||||
}))<{ type: string; color?: string }>`
|
||||
width: 9ex;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
||||
${extensionsHook('DownloadButton')};
|
||||
`;
|
||||
|
||||
export const InfoSpan = styled.span`
|
||||
&::before {
|
||||
content: '|';
|
||||
display: inline-block;
|
||||
opacity: 0.5;
|
||||
width: ${delimiterWidth}px;
|
||||
height: ${props => props.theme.typography.code.fontSize};
|
||||
line-height: ${props => props.theme.typography.code.fontSize};
|
||||
background-color: ${props => props.color || '#333'};
|
||||
border-radius: 3px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 6px 4px;
|
||||
font-size: 7px;
|
||||
font-family: Verdana, sans-serif; // web-safe
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
margin-top: 2px;
|
||||
margin-left: 6px;
|
||||
|
||||
&.get {
|
||||
background-color: ${({ theme }) => theme.colors.http.get};
|
||||
}
|
||||
|
||||
&:last-child::after {
|
||||
display: none;
|
||||
&.post {
|
||||
background-color: ${({ theme }) => theme.colors.http.post};
|
||||
}
|
||||
|
||||
&.put {
|
||||
background-color: ${({ theme }) => theme.colors.http.put};
|
||||
}
|
||||
|
||||
&.options {
|
||||
background-color: ${({ theme }) => theme.colors.http.options};
|
||||
}
|
||||
|
||||
&.patch {
|
||||
background-color: ${({ theme }) => theme.colors.http.patch};
|
||||
}
|
||||
|
||||
&.delete {
|
||||
background-color: ${({ theme }) => theme.colors.http.delete};
|
||||
}
|
||||
|
||||
&.basic {
|
||||
background-color: ${({ theme }) => theme.colors.http.basic};
|
||||
}
|
||||
|
||||
&.link {
|
||||
background-color: ${({ theme }) => theme.colors.http.link};
|
||||
}
|
||||
|
||||
&.head {
|
||||
background-color: ${({ theme }) => theme.colors.http.head};
|
||||
}
|
||||
|
||||
&.hook {
|
||||
background-color: ${({ theme }) => theme.colors.primary.main};
|
||||
}
|
||||
|
||||
&.schema {
|
||||
background-color: ${({ theme }) => theme.colors.http.basic};
|
||||
}
|
||||
`;
|
||||
|
||||
export const InfoSpanBoxWrap = styled.div`
|
||||
function menuItemActive(
|
||||
depth,
|
||||
{ theme }: { theme: ResolvedThemeInterface },
|
||||
option: string,
|
||||
): string {
|
||||
if (depth > 1) {
|
||||
return theme.sidebar.level1Items[option];
|
||||
} else if (depth === 1) {
|
||||
return theme.sidebar.groupItems[option];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export const MenuItemUl = styled.ul<{ $expanded: boolean }>`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
|
||||
&:first-child {
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
& & {
|
||||
font-size: 0.929em;
|
||||
}
|
||||
|
||||
${props => (props.$expanded ? '' : 'display: none;')};
|
||||
`;
|
||||
|
||||
export const MenuItemLi = styled.li<{ depth: number }>`
|
||||
list-style: none inside none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0;
|
||||
${props => (props.depth === 0 ? 'margin-top: 15px' : '')};
|
||||
`;
|
||||
|
||||
export const InfoSpanBox = styled.div`
|
||||
export const menuItemDepth = {
|
||||
0: css`
|
||||
opacity: 0.7;
|
||||
text-transform: ${({ theme }) => theme.sidebar.groupItems.textTransform};
|
||||
font-size: 0.8em;
|
||||
padding-bottom: 0;
|
||||
cursor: default;
|
||||
`,
|
||||
1: css`
|
||||
font-size: 0.929em;
|
||||
text-transform: ${({ theme }) => theme.sidebar.level1Items.textTransform};
|
||||
`,
|
||||
};
|
||||
|
||||
export interface MenuItemLabelType {
|
||||
$depth: number;
|
||||
$active: boolean;
|
||||
$deprecated?: boolean;
|
||||
$type?: string;
|
||||
}
|
||||
|
||||
export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({
|
||||
className: classnames('-depth' + props.$depth, {
|
||||
active: props.$active,
|
||||
}),
|
||||
}))<MenuItemLabelType>`
|
||||
cursor: pointer;
|
||||
color: ${props =>
|
||||
props.$active
|
||||
? menuItemActive(props.$depth, props, 'activeTextColor')
|
||||
: props.theme.sidebar.textColor};
|
||||
margin: 0;
|
||||
padding: 12.5px ${props => props.theme.spacing.unit * 4}px;
|
||||
${({ $depth, $type, theme }) =>
|
||||
($type === 'section' && $depth > 1 && 'padding-left: ' + theme.spacing.unit * 8 + 'px;') || ''}
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
// hide separator on new lines: idea from https://stackoverflow.com/a/31732902/1749888
|
||||
margin-left: -${delimiterWidth}px;
|
||||
justify-content: space-between;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
${props => menuItemDepth[props.$depth]};
|
||||
background-color: ${props =>
|
||||
props.$active
|
||||
? menuItemActive(props.$depth, props, 'activeBackgroundColor')
|
||||
: props.theme.sidebar.backgroundColor};
|
||||
|
||||
${props => (props.$deprecated && deprecatedCss) || ''};
|
||||
|
||||
&:hover {
|
||||
color: ${props => menuItemActive(props.$depth, props, 'activeTextColor')};
|
||||
background-color: ${props => menuItemActive(props.$depth, props, 'activeBackgroundColor')};
|
||||
}
|
||||
|
||||
${ShelfIcon} {
|
||||
height: ${({ theme }) => theme.sidebar.arrow.size};
|
||||
width: ${({ theme }) => theme.sidebar.arrow.size};
|
||||
polygon {
|
||||
fill: ${({ theme }) => theme.sidebar.arrow.color};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MenuItemTitle = styled.span<{ width?: string }>`
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: ${props => (props.width ? props.width : 'auto')};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
export const RedocAttribution = styled.div`
|
||||
${({ theme }) => css`
|
||||
font-size: 0.8em;
|
||||
margin-top: ${theme.spacing.unit * 2}px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
width: ${theme.sidebar.width};
|
||||
bottom: 0;
|
||||
background: ${theme.sidebar.backgroundColor};
|
||||
|
||||
a,
|
||||
a:visited,
|
||||
a:hover {
|
||||
color: ${theme.sidebar.textColor} !important;
|
||||
padding: ${theme.spacing.unit}px 0;
|
||||
border-top: 1px solid ${darken(0.1, theme.sidebar.backgroundColor)};
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
`};
|
||||
img {
|
||||
width: 15px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
${media.lessThan('small')`
|
||||
width: 100%;
|
||||
`};
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { ShelfIcon } from '../../common-elements';
|
||||
import { OperationModel } from '../../services';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
|
||||
|
||||
|
|
@ -59,7 +58,7 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
|||
color={inverted ? 'black' : 'white'}
|
||||
size={'20px'}
|
||||
direction={expanded ? 'up' : 'down'}
|
||||
style={{ marginRight: '-25px' }}
|
||||
style={{ marginRight: '-5px' }}
|
||||
/>
|
||||
</EndpointInfo>
|
||||
<ServersOverlay $expanded={expanded} aria-hidden={!expanded}>
|
||||
|
|
@ -70,7 +69,9 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
|||
const basePath = getBasePath(normalizedUrl);
|
||||
return (
|
||||
<ServerItem key={normalizedUrl}>
|
||||
<Markdown source={server.description || ''} compact={true} />
|
||||
<p style={{ textAlign: 'left', margin:'0 0 5px 0',padding:'0' }}>
|
||||
{server.description || ''}
|
||||
</p>
|
||||
<SelectOnClick>
|
||||
<ServerUrl>
|
||||
<span>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { css } from '../../styled-components';
|
||||
|
||||
export const jsonStyles = css`
|
||||
.redoc-json{
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.redoc-json code > .collapser {
|
||||
display: none;
|
||||
pointer-events: none;
|
||||
|
|
|
|||
|
|
@ -19,13 +19,14 @@ export function SanitizedMarkdownHTML({
|
|||
inline,
|
||||
compact,
|
||||
...rest
|
||||
}: StylingMarkdownProps & { html: string; className?: string; 'data-role'?: string }) {
|
||||
}: StylingMarkdownProps & { html: string; className?: string; 'data-role'?: string }) {
|
||||
const Wrap = inline ? StyledMarkdownSpan : StyledMarkdownBlock;
|
||||
|
||||
return (
|
||||
<OptionsConsumer>
|
||||
{options => (
|
||||
<Wrap
|
||||
style={{'direction':'rtl'}}
|
||||
className={'redoc-markdown ' + (rest.className || '')}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: sanitize(options.sanitize, rest.html),
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export const StyledMarkdownBlock = styled(
|
|||
>,
|
||||
)`
|
||||
font-family: ${props => props.theme.typography.fontFamily};
|
||||
direction: ltr;
|
||||
font-weight: ${props => props.theme.typography.fontWeightRegular};
|
||||
line-height: ${props => props.theme.typography.lineHeight};
|
||||
|
||||
|
|
@ -105,9 +106,10 @@ export const StyledMarkdownBlock = styled(
|
|||
blockquote {
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
padding: 0 15px;
|
||||
margin-top: 1em;
|
||||
padding: 0 0;
|
||||
color: #777;
|
||||
border-left: 4px solid #ddd;
|
||||
border-right: 4px solid #ddd;
|
||||
}
|
||||
|
||||
img {
|
||||
|
|
@ -117,7 +119,8 @@ export const StyledMarkdownBlock = styled(
|
|||
|
||||
ul,
|
||||
ol {
|
||||
padding-left: 2em;
|
||||
text-align: right;
|
||||
padding-right: 2em;
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ export class Redoc extends React.Component<RedocProps> {
|
|||
<StoreProvider value={store}>
|
||||
<OptionsProvider value={options}>
|
||||
<RedocWrap className="redoc-wrap">
|
||||
<ApiContentWrap className="api-content">
|
||||
<ApiInfo store={store} />
|
||||
<ContentItems items={menu.items as any} />
|
||||
</ApiContentWrap>
|
||||
<StickyResponsiveSidebar menu={menu} className="menu-content">
|
||||
<ApiLogo info={spec.info} />
|
||||
{(!options.disableSearch && (
|
||||
|
|
@ -55,10 +59,6 @@ export class Redoc extends React.Component<RedocProps> {
|
|||
null}
|
||||
<SideMenu menu={menu} />
|
||||
</StickyResponsiveSidebar>
|
||||
<ApiContentWrap className="api-content">
|
||||
<ApiInfo store={store} />
|
||||
<ContentItems items={menu.items as any} />
|
||||
</ApiContentWrap>
|
||||
<BackgroundStub />
|
||||
</RedocWrap>
|
||||
</OptionsProvider>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export const RedocWrap = styled.div`
|
|||
|
||||
export const ApiContentWrap = styled.div`
|
||||
z-index: 1;
|
||||
direction: rtl;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: calc(100% - ${props => props.theme.sidebar.width});
|
||||
|
|
@ -35,6 +36,10 @@ export const ApiContentWrap = styled.div`
|
|||
`};
|
||||
|
||||
contain: layout;
|
||||
|
||||
p {
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
|
||||
export const BackgroundStub = styled.div`
|
||||
|
|
@ -42,7 +47,7 @@ export const BackgroundStub = styled.div`
|
|||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
width: ${({ theme }) => {
|
||||
if (theme.rightPanel.width.endsWith('%')) {
|
||||
const percents = parseInt(theme.rightPanel.width, 10);
|
||||
|
|
@ -50,7 +55,7 @@ export const BackgroundStub = styled.div`
|
|||
} else {
|
||||
return theme.rightPanel.width;
|
||||
}
|
||||
}};
|
||||
}};
|
||||
${media.lessThan('medium', true)`
|
||||
display: none;
|
||||
`};
|
||||
|
|
|
|||
|
|
@ -148,8 +148,8 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
|||
<SearchInput
|
||||
value={this.state.term}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
placeholder="Search..."
|
||||
aria-label="Search"
|
||||
placeholder="جستجو ..."
|
||||
aria-label="جستجو"
|
||||
type="text"
|
||||
onChange={this.search}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ export const SearchInput = styled.input.attrs(() => ({
|
|||
}))`
|
||||
width: calc(100% - ${props => props.theme.spacing.unit * 8}px);
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
direction: rtl;
|
||||
margin: 0 ${props => props.theme.spacing.unit * 4}px;
|
||||
padding: 5px ${props => props.theme.spacing.unit * 2}px 5px
|
||||
${props => props.theme.spacing.unit * 4}px;
|
||||
|
|
@ -91,10 +93,10 @@ export const ClearIcon = styled.i`
|
|||
display: inline-block;
|
||||
width: ${props => props.theme.spacing.unit * 2}px;
|
||||
text-align: center;
|
||||
right: ${props => props.theme.spacing.unit * 4}px;
|
||||
left: ${props => props.theme.spacing.unit * 4}px;
|
||||
line-height: 2em;
|
||||
vertical-align: middle;
|
||||
margin-right: 2px;
|
||||
margin-left: 15px;
|
||||
cursor: pointer;
|
||||
font-style: normal;
|
||||
color: '#666';
|
||||
|
|
|
|||
|
|
@ -1,132 +1,212 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ShelfIcon } from '../../common-elements/shelfs';
|
||||
import type { IMenuItem } from '../../services';
|
||||
import { OperationModel } from '../../services';
|
||||
import { l } from '../../services/Labels';
|
||||
import { scrollIntoViewIfNeeded } from '../../utils';
|
||||
import { shortenHTTPVerb } from '../../utils/openapi';
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
import { MenuItems } from './MenuItems';
|
||||
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
||||
|
||||
export interface MenuItemProps {
|
||||
item: IMenuItem;
|
||||
onActivate?: (item: IMenuItem) => void;
|
||||
withoutChildren?: boolean;
|
||||
children?: React.ReactChild;
|
||||
{
|
||||
"name": "redoc",
|
||||
"version": "2.5.0",
|
||||
"description": "ReDoc",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Redocly/redoc"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.9",
|
||||
"npm": ">=3.0.0"
|
||||
},
|
||||
"author": "Roman Hotsiy <gotsijroman@gmail.com>",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"OpenAPI",
|
||||
"OpenAPI Specification",
|
||||
"Swagger",
|
||||
"JSON-Schema",
|
||||
"API",
|
||||
"REST",
|
||||
"documentation",
|
||||
"React.js"
|
||||
],
|
||||
"main": "bundles/redoc.lib.js",
|
||||
"browser": "bundles/redoc.browser.lib.js",
|
||||
"types": "typings/index.d.ts",
|
||||
"scripts": {
|
||||
"start": "webpack serve --mode=development --env playground --hot --config demo/webpack.config.ts",
|
||||
"start:prod": "webpack serve --env playground --mode=production --config demo/webpack.config.ts",
|
||||
"start:benchmark": "webpack serve --mode=production --env.bench --config demo/webpack.config.ts",
|
||||
"test": "npm run unit && npm run license-check",
|
||||
"unit": "jest --coverage",
|
||||
"test:update-snapshot": "jest --updateSnapshot",
|
||||
"e2e": "cypress run",
|
||||
"e2e-ci": "cypress run --record",
|
||||
"bundlesize": "size-limit",
|
||||
"ts-check": "tsc --noEmit --skipLibCheck",
|
||||
"cy:open": "cypress open",
|
||||
"bundle:clean": "rimraf bundles",
|
||||
"bundle:standalone": "webpack --env production --env standalone --mode=production",
|
||||
"bundle:lib": "webpack --mode=production && npm run declarations",
|
||||
"bundle:browser": "webpack --env production --env browser --mode=production",
|
||||
"bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:browser && npm run bundle:standalone",
|
||||
"declarations": "tsc --emitDeclarationOnly -p tsconfig.lib.json && cp -R src/types typings/",
|
||||
"stats": "webpack --env production --env standalone --json --profile --mode=production > stats.json",
|
||||
"prettier": "prettier --write \"src/**/*.{ts,tsx}\"",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
|
||||
"lint": "eslint --fix 'src/**/*.{js,ts,tsx}' --cache",
|
||||
"benchmark": "node ./benchmark/benchmark.js",
|
||||
"start:demo": "webpack serve --hot --config demo/webpack.config.ts --mode=development",
|
||||
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
||||
"publish-cdn": "scripts/publish-cdn.sh",
|
||||
"deploy:demo": "aws s3 sync demo/dist s3://production-redoc-demo --acl=public-read",
|
||||
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause;CC-BY-4.0;CC0-1.0;Python-2.0 ' --summary",
|
||||
"docker:build": "docker build -f config/docker/Dockerfile -t redoc .",
|
||||
"prepare": "husky install",
|
||||
"pre-commit": "pretty-quick --staged && npm run lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
|
||||
"@size-limit/file": "^11.1.4",
|
||||
"@types/chai": "^4.2.18",
|
||||
"@types/dompurify": "^2.2.2",
|
||||
"@types/enzyme": "^3.10.5",
|
||||
"@types/enzyme-to-json": "^1.5.3",
|
||||
"@types/jest": "^29.5.6",
|
||||
"@types/json-pointer": "^1.0.30",
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/mark.js": "^8.11.5",
|
||||
"@types/marked": "^4.0.3",
|
||||
"@types/node": "^15.6.1",
|
||||
"@types/prismjs": "^1.16.5",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/styled-components": "^5.1.1",
|
||||
"@types/tapable": "^2.2.2",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/webpack-env": "^1.18.0",
|
||||
"@types/yargs": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||
"@typescript-eslint/parser": "^5.55.0",
|
||||
"beautify-benchmark": "^0.2.4",
|
||||
"conventional-changelog-cli": "^3.0.0",
|
||||
"copy-webpack-plugin": "^9.0.0",
|
||||
"core-js": "^3.13.1",
|
||||
"css-loader": "^5.2.6",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-to-json": "^3.6.2",
|
||||
"esbuild-loader": "^4.3.0",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"eslint-plugin-react": "^7.34.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"fork-ts-checker-webpack-plugin": "^6.2.10",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"husky": "^7.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"license-checker": "^25.0.1",
|
||||
"lodash.noop": "^3.0.1",
|
||||
"mobx": "^6.10.2",
|
||||
"outdent": "^0.8.0",
|
||||
"prettier": "^2.3.2",
|
||||
"pretty-quick": "^3.0.0",
|
||||
"raf": "^3.4.1",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"shelljs": "^0.8.4",
|
||||
"size-limit": "^11.1.4",
|
||||
"style-loader": "^3.3.1",
|
||||
"styled-components": "^5.3.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.9.0",
|
||||
"unfetch": "^4.2.0",
|
||||
"url": "^0.11.1",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"workerize-loader": "github:redocly/workerize-loader#webpack-5-dist"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"core-js": "^3.1.4",
|
||||
"mobx": "^6.0.4",
|
||||
"react": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@redocly/openapi-core": "^1.4.0",
|
||||
"classnames": "^2.3.2",
|
||||
"decko": "^1.2.0",
|
||||
"dompurify": "^3.2.4",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"json-pointer": "^0.6.2",
|
||||
"lunr": "^2.3.9",
|
||||
"mark.js": "^8.11.1",
|
||||
"marked": "^4.3.0",
|
||||
"mobx-react": "^9.1.1",
|
||||
"openapi-sampler": "^1.5.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"perfect-scrollbar": "^1.5.5",
|
||||
"polished": "^4.2.2",
|
||||
"prismjs": "^1.29.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-tabs": "^6.0.2",
|
||||
"slugify": "~1.4.7",
|
||||
"stickyfill": "^1.1.1",
|
||||
"swagger2openapi": "^7.0.8",
|
||||
"url-template": "^2.0.8"
|
||||
},
|
||||
"size-limit": [
|
||||
{
|
||||
"path": "./bundles/redoc.standalone.js",
|
||||
"limit": "350 kB"
|
||||
},
|
||||
{
|
||||
"path": "./bundles/redoc.lib.js",
|
||||
"limit": "100 kB"
|
||||
},
|
||||
{
|
||||
"path": "./bundles/redoc.browser.lib.js",
|
||||
"limit": "100 kB"
|
||||
}
|
||||
],
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/src/setupTests.ts"
|
||||
],
|
||||
"preset": "ts-jest",
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{ts,tsx}"
|
||||
],
|
||||
"coverageReporters": [
|
||||
"json",
|
||||
"lcov",
|
||||
"text-summary"
|
||||
],
|
||||
"coveragePathIgnorePatterns": [
|
||||
"\\.d\\.ts$",
|
||||
"/benchmark/",
|
||||
"/node_modules/",
|
||||
"src/services/__tests__/models/helpers.ts"
|
||||
],
|
||||
"modulePathIgnorePatterns": [
|
||||
"/benchmark/",
|
||||
"src/services/__tests__/models/helpers.ts"
|
||||
],
|
||||
"snapshotSerializers": [
|
||||
"enzyme-to-json/serializer"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|less)$": "<rootDir>/src/empty.js"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
|
||||
@observer
|
||||
export class MenuItem extends React.Component<MenuItemProps> {
|
||||
ref = React.createRef<HTMLLabelElement>();
|
||||
|
||||
activate = (evt: React.MouseEvent<HTMLElement>) => {
|
||||
this.props.onActivate!(this.props.item);
|
||||
evt.stopPropagation();
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.scrollIntoViewIfActive();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.scrollIntoViewIfActive();
|
||||
}
|
||||
|
||||
scrollIntoViewIfActive() {
|
||||
if (this.props.item.active && this.ref.current) {
|
||||
scrollIntoViewIfNeeded(this.ref.current);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { item, withoutChildren } = this.props;
|
||||
return (
|
||||
<MenuItemLi
|
||||
tabIndex={0}
|
||||
onClick={this.activate}
|
||||
onKeyDown={evt => {
|
||||
// Space or Enter key will activate the menu item
|
||||
if (evt.key === 'Enter' || evt.key === ' ') {
|
||||
this.props.onActivate!(this.props.item);
|
||||
evt.stopPropagation();
|
||||
}
|
||||
}}
|
||||
depth={item.depth}
|
||||
data-item-id={item.id}
|
||||
role="menuitem"
|
||||
aria-label={item.sidebarLabel}
|
||||
aria-expanded={item.expanded}
|
||||
>
|
||||
{item.type === 'operation' ? (
|
||||
<OperationMenuItemContent {...this.props} item={item as OperationModel} />
|
||||
) : (
|
||||
<MenuItemLabel $depth={item.depth} $active={item.active} $type={item.type} ref={this.ref}>
|
||||
{item.type === 'schema' && <OperationBadge type="schema">schema</OperationBadge>}
|
||||
<MenuItemTitle width="calc(100% - 38px)" title={item.sidebarLabel}>
|
||||
{item.sidebarLabel}
|
||||
{this.props.children}
|
||||
</MenuItemTitle>
|
||||
{(item.depth > 0 && item.items.length > 0 && (
|
||||
<ShelfIcon float={'right'} direction={item.expanded ? 'down' : 'right'} />
|
||||
)) ||
|
||||
null}
|
||||
</MenuItemLabel>
|
||||
)}
|
||||
{!withoutChildren && item.items && item.items.length > 0 && (
|
||||
<MenuItems
|
||||
expanded={item.expanded}
|
||||
items={item.items}
|
||||
onActivate={this.props.onActivate}
|
||||
/>
|
||||
)}
|
||||
</MenuItemLi>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface OperationMenuItemContentProps {
|
||||
item: OperationModel;
|
||||
children?: React.ReactChild;
|
||||
}
|
||||
|
||||
export const OperationMenuItemContent = observer((props: OperationMenuItemContentProps) => {
|
||||
const { item } = props;
|
||||
const ref = React.createRef<HTMLLabelElement>();
|
||||
const { showWebhookVerb } = React.useContext(OptionsContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.item.active && ref.current) {
|
||||
scrollIntoViewIfNeeded(ref.current);
|
||||
}
|
||||
}, [props.item.active, ref]);
|
||||
|
||||
return (
|
||||
<MenuItemLabel
|
||||
$depth={item.depth}
|
||||
$active={item.active}
|
||||
$deprecated={item.deprecated}
|
||||
ref={ref}
|
||||
>
|
||||
{item.badges &&
|
||||
item.badges?.map(({ name, color }) => (
|
||||
<OperationBadge type="badge" color={color} key={name}>
|
||||
{name}
|
||||
</OperationBadge>
|
||||
))}
|
||||
{item.isWebhook ? (
|
||||
<OperationBadge type="hook">
|
||||
{showWebhookVerb ? item.httpVerb : l('webhook')}
|
||||
</OperationBadge>
|
||||
) : (
|
||||
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
||||
)}
|
||||
<MenuItemTitle tabIndex={0} width="calc(100% - 38px)">
|
||||
{item.sidebarLabel}
|
||||
{props.children}
|
||||
</MenuItemTitle>
|
||||
</MenuItemLabel>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ import { OptionsContext } from '../OptionsProvider';
|
|||
import { MenuItems } from './MenuItems';
|
||||
|
||||
import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar';
|
||||
import { RedocAttribution } from './styled.elements';
|
||||
import RedoclyLogo from './Logo';
|
||||
|
||||
@observer
|
||||
export class SideMenu extends React.Component<{ menu: MenuStore; className?: string }> {
|
||||
|
|
@ -27,12 +25,6 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
|
|||
}}
|
||||
>
|
||||
<MenuItems items={store.items} onActivate={this.activate} root={true} />
|
||||
<RedocAttribution>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://redocly.com/redoc/">
|
||||
<RedoclyLogo />
|
||||
API docs by Redocly
|
||||
</a>
|
||||
</RedocAttribution>
|
||||
</PerfectScrollbarWrap>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export const OperationBadge = styled.span.attrs((props: { type: string; color?:
|
|||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
margin-top: 2px;
|
||||
margin-left: 6px;
|
||||
|
||||
&.get {
|
||||
background-color: ${({ theme }) => theme.colors.http.get};
|
||||
|
|
@ -87,6 +88,8 @@ function menuItemActive(
|
|||
export const MenuItemUl = styled.ul<{ $expanded: boolean }>`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
|
||||
&:first-child {
|
||||
padding-bottom: 32px;
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ const defaultTheme: ThemeInterface = {
|
|||
},
|
||||
},
|
||||
sidebar: {
|
||||
width: '260px',
|
||||
width: '300px',
|
||||
backgroundColor: '#fafafa',
|
||||
textColor: '#333333',
|
||||
activeTextColor: theme =>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user