From b58b98cd7d8bedba738f1ae2fc2eef441f7d6ee0 Mon Sep 17 00:00:00 2001 From: sydney-runkle Date: Tue, 23 Apr 2024 16:30:42 -0500 Subject: [PATCH] initial docstrings for basic components --- docs/components.md | 6 +- src/npm-fastui/src/models.d.ts | 69 +++- .../fastui/components/__init__.py | 295 +++++++++++++++++- 3 files changed, 352 insertions(+), 18 deletions(-) diff --git a/docs/components.md b/docs/components.md index 09f4b37e..6c431732 100644 --- a/docs/components.md +++ b/docs/components.md @@ -1,5 +1,7 @@ ::: fastui.components options: -show_root_heading: true +docstring_options: +ignore_init_summary: false +members: - Text - Paragraph - PageTitle - Div - Page - Heading - Markdown - Code - Json - Button - Link - LinkList - Navbar - Modal - ServerLoad - Image - Iframe - FireEvent - Error - Spinner - Toast - Custom - Table - Pagination - Display - Details - Form - FormField - ModelForm - Footer - AnyComponent - FormFieldBoolean - FormFieldFile - FormFieldInput - FormFieldSelect - FormFieldSelectSearch - + diff --git a/src/npm-fastui/src/models.d.ts b/src/npm-fastui/src/models.d.ts index e304607f..c5769ed3 100644 --- a/src/npm-fastui/src/models.d.ts +++ b/src/npm-fastui/src/models.d.ts @@ -70,35 +70,47 @@ export type DisplayMode = | 'inline_code' export type SelectOptions = SelectOption[] | SelectGroup[] +/** + * Text component that displays a string. + */ export interface Text { text: string type: 'Text' } +/** + * Paragraph component that displays a string as a paragraph. + */ export interface Paragraph { text: string className?: ClassName type: 'Paragraph' } /** - * This sets the title of the HTML page via the `document.title` property. + * Sets the title of the HTML page via the `document.title` property. */ export interface PageTitle { text: string type: 'PageTitle' } +/** + * A generic container component. + */ export interface Div { components: FastProps[] className?: ClassName type: 'Div' } /** - * Similar to `container` in many UI frameworks, this should be a reasonable root component for most pages. + * Similar to `container` in many UI frameworks, this acts as a root component for most pages. */ export interface Page { components: FastProps[] className?: ClassName type: 'Page' } +/** + * Heading component. + */ export interface Heading { text: string level: 1 | 2 | 3 | 4 | 5 | 6 @@ -106,12 +118,18 @@ export interface Heading { className?: ClassName type: 'Heading' } +/** + * Markdown component that renders markdown text. + */ export interface Markdown { text: string codeStyle?: string className?: ClassName type: 'Markdown' } +/** + * Code component that renders code with syntax highlighting. + */ export interface Code { text: string language?: string @@ -119,11 +137,17 @@ export interface Code { className?: ClassName type: 'Code' } +/** + * JSON component that renders JSON data. + */ export interface Json { value: JsonData className?: ClassName type: 'JSON' } +/** + * Button component. + */ export interface Button { text: string onClick?: AnyEvent @@ -159,6 +183,9 @@ export interface AuthEvent { url?: string type: 'auth' } +/** + * Link component. + */ export interface Link { components: FastProps[] onClick?: PageEvent | GoToEvent | BackEvent | AuthEvent @@ -168,12 +195,18 @@ export interface Link { className?: ClassName type: 'Link' } +/** + * List of Link components. + */ export interface LinkList { links: Link[] mode?: 'tabs' | 'vertical' | 'pagination' className?: ClassName type: 'LinkList' } +/** + * Navbar component used for moving between pages. + */ export interface Navbar { title?: string titleEvent?: PageEvent | GoToEvent | BackEvent | AuthEvent @@ -182,12 +215,18 @@ export interface Navbar { className?: ClassName type: 'Navbar' } +/** + * Footer component. + */ export interface Footer { links: Link[] extraText?: string className?: ClassName type: 'Footer' } +/** + * Modal component that displays a modal dialog. + */ export interface Modal { title: string body: FastProps[] @@ -209,6 +248,9 @@ export interface ServerLoad { method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' type: 'ServerLoad' } +/** + * Image container component. + */ export interface Image { src: string alt?: string @@ -228,6 +270,9 @@ export interface Image { className?: ClassName type: 'Image' } +/** + * Iframe component that displays content from a URL. + */ export interface Iframe { src: string title?: string @@ -238,6 +283,9 @@ export interface Iframe { sandbox?: string type: 'Iframe' } +/** + * Video component that displays a video or multiple videos. + */ export interface Video { sources: string[] autoplay?: boolean @@ -247,14 +295,20 @@ export interface Video { poster?: string width?: string | number height?: string | number - type: 'Video' className?: ClassName + type: 'Video' } +/** + * Fire an event. + */ export interface FireEvent { event: AnyEvent message?: string type: 'FireEvent' } +/** + * Utility component used to display an error. + */ export interface Error { title: string description: string @@ -263,11 +317,17 @@ export interface Error { type: 'Error' children?: ReactNode } +/** + * Spinner component that displays a loading spinner. + */ export interface Spinner { text?: string className?: ClassName type: 'Spinner' } +/** + * Custom component that allows for special data to be rendered. + */ export interface Custom { data: JsonData subType: string @@ -462,6 +522,9 @@ export interface ModelForm { | FormFieldSelectSearch )[] } +/** + * Toast component that displays a toast message (small temporary message). + */ export interface Toast { title: string body: FastProps[] diff --git a/src/python-fastui/fastui/components/__init__.py b/src/python-fastui/fastui/components/__init__.py index 6594863e..008da1ad 100644 --- a/src/python-fastui/fastui/components/__init__.py +++ b/src/python-fastui/fastui/components/__init__.py @@ -1,8 +1,6 @@ """ Component definitions. -NOTE: all imports should be "simple" so the namespace of the module is polluted as little as possible. - All CamelCase names in the namespace should be components. """ import typing as _t @@ -49,6 +47,7 @@ 'FireEvent', 'Error', 'Spinner', + 'Toast', 'Custom', # then we include components from other files 'Table', @@ -71,47 +70,81 @@ class Text(_p.BaseModel, extra='forbid'): + """Text component that displays a string.""" + text: str + """The text to display.""" + type: _t.Literal['Text'] = 'Text' + """The type of the component. Always 'Text'.""" class Paragraph(_p.BaseModel, extra='forbid'): + """Paragraph component that displays a string as a paragraph.""" + text: str + """The text to display.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the paragraph's HTML component.""" + type: _t.Literal['Paragraph'] = 'Paragraph' + """The type of the component. Always 'Paragraph'.""" class PageTitle(_p.BaseModel, extra='forbid'): - """ - This sets the title of the HTML page via the `document.title` property. - """ + """Sets the title of the HTML page via the `document.title` property.""" text: str + """The text to set as the page title.""" + type: _t.Literal['PageTitle'] = 'PageTitle' + """The type of the component. Always 'PageTitle'.""" class Div(_p.BaseModel, extra='forbid'): + """A generic container component.""" + components: '_t.List[AnyComponent]' + """List of components to render inside the div.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the div's HTML component.""" + type: _t.Literal['Div'] = 'Div' + """The type of the component. Always 'Div'.""" class Page(_p.BaseModel, extra='forbid'): - """ - Similar to `container` in many UI frameworks, this should be a reasonable root component for most pages. - """ + """Similar to `container` in many UI frameworks, this acts as a root component for most pages.""" components: '_t.List[AnyComponent]' + """List of components to render on the page.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the page's HTML component.""" + type: _t.Literal['Page'] = 'Page' + """The type of the component. Always 'Page'.""" class Heading(_p.BaseModel, extra='forbid'): + """Heading component.""" + text: str + """The text to display in the heading.""" + level: _t.Literal[1, 2, 3, 4, 5, 6] = 1 + """The level of the heading. 1 is the largest, 6 is the smallest.""" + html_id: _t.Union[str, None] = _p.Field(default=None, serialization_alias='htmlId') + """Optional HTML ID to apply to the heading's HTML component.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the page's HTML component.""" + type: _t.Literal['Heading'] = 'Heading' + """The type of the component. Always 'Heading'.""" @classmethod def __get_pydantic_json_schema__( @@ -126,64 +159,151 @@ def __get_pydantic_json_schema__( # see https://github.com/PrismJS/prism-themes # and https://cdn.jsdelivr.net/npm/react-syntax-highlighter@15.5.0/dist/esm/styles/prism/index.js CodeStyle = _te.Annotated[_t.Union[str, None], _p.Field(serialization_alias='codeStyle')] +""" +Code style to apply to a `Code` component. + +Attributes: + codeStyle: The code style to apply. If None, no style is applied. + +See Also: + - https://github.com/PrismJS/prism-themes + - https://cdn.jsdelivr.net/npm/react-syntax-highlighter@15.5.0/dist/esm/styles/prism/index.js +""" class Markdown(_p.BaseModel, extra='forbid'): + """Markdown component that renders markdown text.""" + text: str + """The markdown text to render.""" + code_style: CodeStyle = None + """Optional code style to apply to the markdown text.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the page's HTML component.""" + type: _t.Literal['Markdown'] = 'Markdown' + """The type of the component. Always 'Markdown'.""" class Code(_p.BaseModel, extra='forbid'): + """Code component that renders code with syntax highlighting.""" + text: str + """The code to render.""" + language: _t.Union[str, None] = None + """Optional language of the code. If None, no syntax highlighting is applied.""" + code_style: CodeStyle = None + """Optional code style to apply to the code.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the page's HTML component.""" + type: _t.Literal['Code'] = 'Code' + """The type of the component. Always 'Code'.""" class Json(_p.BaseModel, extra='forbid'): + """JSON component that renders JSON data.""" + value: _types.JsonData + """The JSON data to render.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the page's HTML component.""" + type: _t.Literal['JSON'] = 'JSON' + """The type of the component. Always 'JSON'.""" class Button(_p.BaseModel, extra='forbid'): + """Button component.""" + text: str + """The text to display on the button.""" + on_click: _t.Union[events.AnyEvent, None] = _p.Field(default=None, serialization_alias='onClick') + """Optional event to trigger when the button is clicked.""" + html_type: _t.Union[_t.Literal['button', 'reset', 'submit'], None] = _p.Field( default=None, serialization_alias='htmlType' ) + """Optional HTML type of the button. If None, defaults to 'button'.""" + named_style: _class_name.NamedStyleField = None + """Optional named style to apply to the button.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the button's HTML component.""" + type: _t.Literal['Button'] = 'Button' + """The type of the component. Always 'Button'.""" class Link(_p.BaseModel, extra='forbid'): + """Link component.""" + components: '_t.List[AnyComponent]' + """List of components to render attached to the link.""" + on_click: _t.Union[events.AnyEvent, None] = _p.Field(default=None, serialization_alias='onClick') + """Optional event to trigger when the link is clicked.""" + mode: _t.Union[_t.Literal['navbar', 'footer', 'tabs', 'vertical', 'pagination'], None] = None + """Optional mode of the link.""" + active: _t.Union[str, bool, None] = None + """Optional active state of the link.""" + locked: _t.Union[bool, None] = None + """Optional locked state of the link.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the link's HTML component.""" + type: _t.Literal['Link'] = 'Link' + """The type of the component. Always 'Link'.""" class LinkList(_p.BaseModel, extra='forbid'): + """List of Link components.""" + links: _t.List[Link] + """List of links to render.""" + mode: _t.Union[_t.Literal['tabs', 'vertical', 'pagination'], None] = None + """Optional mode of the link list.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the link list's HTML component.""" + type: _t.Literal['LinkList'] = 'LinkList' + """The type of the component. Always 'LinkList'.""" class Navbar(_p.BaseModel, extra='forbid'): + """Navbar component used for moving between pages.""" + title: _t.Union[str, None] = None + """Optional title to display in the navbar.""" + title_event: _t.Union[events.AnyEvent, None] = _p.Field(default=None, serialization_alias='titleEvent') + """Optional event to trigger when the title is clicked. Often used to navigate to the home page.""" + start_links: _t.List[Link] = _p.Field(default=[], serialization_alias='startLinks') + """List of links to render at the start of the navbar.""" + end_links: _t.List[Link] = _p.Field(default=[], serialization_alias='endLinks') + """List of links to render at the end of the navbar.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the navbar's HTML component.""" + type: _t.Literal['Navbar'] = 'Navbar' + """The type of the component. Always 'Navbar'.""" @classmethod def __get_pydantic_json_schema__( @@ -196,41 +316,86 @@ def __get_pydantic_json_schema__( class Footer(_p.BaseModel, extra='forbid'): + """Footer component.""" + links: _t.List[Link] + """List of links to render in the footer.""" + extra_text: _t.Union[str, None] = _p.Field(default=None, serialization_alias='extraText') + """Optional extra text to display in the footer.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the footer's HTML component.""" + type: _t.Literal['Footer'] = 'Footer' + """The type of the component. Always 'Footer'.""" class Modal(_p.BaseModel, extra='forbid'): + """Modal component that displays a modal dialog.""" + title: str + """The text displayed on the modal trigger button.""" + body: '_t.List[AnyComponent]' + """List of components to render in the modal body.""" + footer: '_t.Union[_t.List[AnyComponent], None]' = None + """Optional list of components to render in the modal footer.""" + open_trigger: _t.Union[events.PageEvent, None] = _p.Field(default=None, serialization_alias='openTrigger') + """Optional event to trigger when the modal is opened.""" + open_context: _t.Union[events.ContextType, None] = _p.Field(default=None, serialization_alias='openContext') + """Optional context to pass to the open trigger event.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the modal's HTML component.""" + type: _t.Literal['Modal'] = 'Modal' + """The type of the component. Always 'Modal'.""" class ServerLoad(_p.BaseModel, extra='forbid'): - """ - A component that will be replaced by the server with the component returned by the given URL. - """ + """A component that will be replaced by the server with the component returned by the given URL.""" path: str + """The URL to load the component from.""" + load_trigger: _t.Union[events.PageEvent, None] = _p.Field(default=None, serialization_alias='loadTrigger') + """Optional event to trigger when the component is loaded.""" + components: '_t.Union[_t.List[AnyComponent], None]' = None + """Optional list of components to render while the server is loading the new component(s).""" + sse: _t.Union[bool, None] = None + """Optional flag to enable server-sent events (SSE) for the server load.""" + sse_retry: _t.Union[int, None] = _p.Field(default=None, serialization_alias='sseRetry') + """Optional time in milliseconds to retry the SSE connection.""" + method: _t.Union[_t.Literal['GET', 'POST', 'PATCH', 'PUT', 'DELETE'], None] = None + """Optional HTTP method to use when loading the component.""" + type: _t.Literal['ServerLoad'] = 'ServerLoad' + """The type of the component. Always 'ServerLoad'.""" class Image(_p.BaseModel, extra='forbid'): + """Image container component.""" + src: str + """The URL of the image to display.""" + alt: _t.Union[str, None] = None + """Optional alt text for the image.""" + width: _t.Union[str, int, None] = None + """Optional width used to display the image.""" + height: _t.Union[str, int, None] = None + """Optional height used to display the image.""" + referrer_policy: _t.Union[ _t.Literal[ 'no-referrer', @@ -244,48 +409,115 @@ class Image(_p.BaseModel, extra='forbid'): ], None, ] = _p.Field(None, serialization_alias='referrerPolicy') + """Optional referrer policy for the image. Specifies what information to send when fetching the image. + + For more info, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy.""" + loading: _t.Union[_t.Literal['eager', 'lazy'], None] = None + """Optional loading strategy for the image.""" + on_click: _t.Union[events.AnyEvent, None] = _p.Field(default=None, serialization_alias='onClick') + """Optional event to trigger when the image is clicked.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the image's HTML component.""" + type: _t.Literal['Image'] = 'Image' + """The type of the component. Always 'Image'.""" class Iframe(_p.BaseModel, extra='forbid'): + """Iframe component that displays content from a URL.""" + src: _p.HttpUrl + """The URL of the content to display.""" + title: _t.Union[str, None] = None + """Optional title for the iframe.""" + width: _t.Union[str, int, None] = None + """Optional width used to display the iframe.""" + height: _t.Union[str, int, None] = None + """Optional height used to display the iframe.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the iframe's HTML component.""" + srcdoc: _t.Union[str, None] = None + """Optional HTML content to display in the iframe.""" + sandbox: _t.Union[str, None] = None + """Optional sandbox policy for the iframe. Specifies restrictions on the HTML content in the iframe.""" + type: _t.Literal['Iframe'] = 'Iframe' + """The type of the component. Always 'Iframe'.""" class Video(_p.BaseModel, extra='forbid'): + """Video component that displays a video or multiple videos.""" + sources: _t.List[_p.AnyUrl] + """List of URLs to the video sources.""" + autoplay: _t.Union[bool, None] = None + """Optional flag to enable autoplay for the video.""" + controls: _t.Union[bool, None] = None + """Optional flag to enable controls (pause, play, etc) for the video.""" + loop: _t.Union[bool, None] = None + """Optional flag to enable looping for the video.""" + muted: _t.Union[bool, None] = None + """Optional flag to mute the video.""" + poster: _t.Union[_p.AnyUrl, None] = None + """Optional URL to an image to display as the video poster (what is shown when the video is loading or until the user plays it).""" + width: _t.Union[str, int, None] = None + """Optional width used to display the video.""" + height: _t.Union[str, int, None] = None - type: _t.Literal['Video'] = 'Video' + """Optional height used to display the video.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the video's HTML component.""" + + type: _t.Literal['Video'] = 'Video' + """The type of the component. Always 'Video'.""" class FireEvent(_p.BaseModel, extra='forbid'): + """Fire an event.""" + event: events.AnyEvent - message: _t.Union[str, None] = None # defaults to blank + """The event to fire.""" + + message: _t.Union[str, None] = None + """Optional message to display when the event is fired. Defaults to a blank message.""" + type: _t.Literal['FireEvent'] = 'FireEvent' + """The type of the component. Always 'FireEvent'.""" class Error(_p.BaseModel, extra='forbid'): + """Utility component used to display an error.""" + title: str + """The title of the error.""" + description: str + """The description of the error.""" + status_code: _t.Union[int, None] = _p.Field(None, serialization_alias='statusCode') + """Optional status code of the error.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the error's HTML component.""" + type: _t.Literal['Error'] = 'Error' + """The type of the component. Always 'Error'.""" @classmethod def __get_pydantic_json_schema__( @@ -298,14 +530,28 @@ def __get_pydantic_json_schema__( class Spinner(_p.BaseModel, extra='forbid'): + """Spinner component that displays a loading spinner.""" + text: _t.Union[str, None] = None + """Optional text to display with the spinner.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the spinner's HTML component.""" + type: _t.Literal['Spinner'] = 'Spinner' + """The type of the component. Always 'Spinner'.""" class Toast(_p.BaseModel, extra='forbid'): + """Toast component that displays a toast message (small temporary message).""" + title: str + """The title of the toast.""" + body: '_t.List[AnyComponent]' + """List of components to render in the toast body.""" + + # TODO: change these before the release (top left, center, end, etc). Can be done with the toast bug fix. position: _t.Union[ _t.Literal[ 'top-start', @@ -320,18 +566,38 @@ class Toast(_p.BaseModel, extra='forbid'): ], None, ] = None + """Optional position of the toast.""" + open_trigger: _t.Union[events.PageEvent, None] = _p.Field(default=None, serialization_alias='openTrigger') + """Optional event to trigger when the toast is opened.""" + open_context: _t.Union[events.ContextType, None] = _p.Field(default=None, serialization_alias='openContext') + """Optional context to pass to the open trigger event.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the toast's HTML component.""" + type: _t.Literal['Toast'] = 'Toast' + """The type of the component. Always 'Toast'.""" class Custom(_p.BaseModel, extra='forbid'): + """Custom component that allows for special data to be rendered.""" + data: _types.JsonData + """The data to render in the custom component.""" + sub_type: str = _p.Field(serialization_alias='subType') + """The sub-type of the custom component.""" + library: _t.Union[str, None] = None + """Optional library to use for the custom component.""" + class_name: _class_name.ClassNameField = None + """Optional class name to apply to the custom component's HTML component.""" + type: _t.Literal['Custom'] = 'Custom' + """The type of the component. Always 'Custom'.""" AnyComponent = _te.Annotated[ @@ -370,3 +636,6 @@ class Custom(_p.BaseModel, extra='forbid'): ], _p.Field(discriminator='type'), ] +"""Union of all components. + +Pydantic discriminator field is set to 'type' to allow for efficient serialization and deserialization of the components."""