├── .nvmrc ├── requires-install.txt ├── PRs ├── requirements.txt └── app.py ├── requirements.txt ├── .github ├── FUNDING.yml └── workflows │ └── python-test.yml ├── assets ├── logo.png └── datepicker.gif ├── requires-dev.txt ├── .prettierrc ├── src └── ts │ ├── utils │ ├── dates.ts │ ├── charts.ts │ ├── editorRegistry.ts │ ├── combobox.ts │ ├── highlightJsAdapter.ts │ └── anchor.ts │ ├── components │ ├── core │ │ ├── radio │ │ │ ├── RadioGroupContext.tsx │ │ │ └── RadioCard.tsx │ │ ├── chip │ │ │ ├── ChipGroupContext.tsx │ │ │ └── ChipGroup.tsx │ │ ├── stepper │ │ │ ├── StepperCompleted.tsx │ │ │ └── StepperStep.tsx │ │ ├── menu │ │ │ ├── MenuTarget.tsx │ │ │ ├── SubMenuTarget.tsx │ │ │ ├── MenuDivider.tsx │ │ │ ├── MenuLabel.tsx │ │ │ ├── MenuDropdown.tsx │ │ │ └── SubMenuDropdown.tsx │ │ ├── table │ │ │ ├── TableTd.tsx │ │ │ ├── TableTh.tsx │ │ │ ├── TableTr.tsx │ │ │ ├── TableTbody.tsx │ │ │ ├── TableTfoot.tsx │ │ │ ├── TableThead.tsx │ │ │ ├── TableCaption.tsx │ │ │ └── TableScrollContainer.tsx │ │ ├── popover │ │ │ ├── PopoverTarget.tsx │ │ │ ├── PopoverDropdown.tsx │ │ │ └── Popover.tsx │ │ ├── hovercard │ │ │ ├── HoverCardTarget.tsx │ │ │ ├── HoverCardDropdown.tsx │ │ │ └── HoverCard.tsx │ │ ├── Loader.tsx │ │ ├── Overlay.tsx │ │ ├── progress │ │ │ ├── ProgressRoot.tsx │ │ │ ├── ProgressLabel.tsx │ │ │ ├── Progress.tsx │ │ │ └── ProgressSection.tsx │ │ ├── accordion │ │ │ ├── AccordionPanel.tsx │ │ │ ├── AccordionItem.tsx │ │ │ └── AccordionControl.tsx │ │ ├── Box.tsx │ │ ├── Space.tsx │ │ ├── Text.tsx │ │ ├── modal │ │ │ ├── ManagedModal.tsx │ │ │ ├── Modal.tsx │ │ │ └── ModalStack.tsx │ │ ├── scrollarea │ │ │ └── ScrollAreaAutosize.tsx │ │ ├── appshell │ │ │ ├── AppShellMain.tsx │ │ │ ├── AppShellSection.tsx │ │ │ ├── AppShellAside.tsx │ │ │ ├── AppShellFooter.tsx │ │ │ ├── AppShellHeader.tsx │ │ │ └── AppShellNavbar.tsx │ │ ├── VisuallyHidden.tsx │ │ ├── list │ │ │ ├── ListItem.tsx │ │ │ └── List.tsx │ │ ├── Paper.tsx │ │ ├── Affix.tsx │ │ ├── Mark.tsx │ │ ├── Kbd.tsx │ │ ├── input │ │ │ └── InputWrapper.tsx │ │ ├── AspectRatio.tsx │ │ ├── avatar │ │ │ └── AvatarGroup.tsx │ │ ├── Center.tsx │ │ ├── tabs │ │ │ ├── TabsList.tsx │ │ │ ├── TabsPanel.tsx │ │ │ └── TabsTab.tsx │ │ ├── card │ │ │ ├── CardSection.tsx │ │ │ └── Card.tsx │ │ ├── Code.tsx │ │ ├── button │ │ │ ├── ButtonGroup.tsx │ │ │ ├── ActionIconGroup.tsx │ │ │ ├── ActionIcon.tsx │ │ │ ├── UnstyledButton.tsx │ │ │ └── CustomCopyButton.tsx │ │ ├── timeline │ │ │ └── TimelineItem.tsx │ │ ├── image │ │ │ ├── BackgroundImage.tsx │ │ │ └── Image.tsx │ │ ├── Fieldset.tsx │ │ ├── Breadcrumbs.tsx │ │ ├── Divider.tsx │ │ ├── tooltip │ │ │ └── FloatingTooltip.tsx │ │ ├── Collapse.tsx │ │ ├── Highlight.tsx │ │ ├── grid │ │ │ ├── GridCol.tsx │ │ │ └── Grid.tsx │ │ ├── Stack.tsx │ │ ├── color │ │ │ ├── ColorInput.tsx │ │ │ └── ColorPicker.tsx │ │ ├── Title.tsx │ │ ├── SimpleGrid.tsx │ │ ├── Container.tsx │ │ ├── RingProgress.tsx │ │ ├── LoadingOverlay.tsx │ │ ├── Blockquote.tsx │ │ ├── drawer │ │ │ ├── DrawerStack.tsx │ │ │ └── ManagedDrawer.tsx │ │ ├── Flex.tsx │ │ ├── NumberFormatter.tsx │ │ ├── ThemeIcon.tsx │ │ ├── Group.tsx │ │ ├── Anchor.tsx │ │ ├── checkbox │ │ │ ├── CheckboxGroup.tsx │ │ │ └── CheckboxCard.tsx │ │ ├── Skeleton.tsx │ │ ├── Spoiler.tsx │ │ ├── SemiCircleProgress.tsx │ │ ├── Burger.tsx │ │ ├── Rating.tsx │ │ └── Badge.tsx │ ├── charts │ │ ├── fragments │ │ │ ├── Sparkline.tsx │ │ │ ├── RadarChart.tsx │ │ │ ├── BubbleChart.tsx │ │ │ ├── PieChart.tsx │ │ │ ├── DonutChart.tsx │ │ │ └── ScatterChart.tsx │ │ ├── Sparkline.tsx │ │ └── ScatterChart.tsx │ ├── styles │ │ ├── TypographyStylesProvider.tsx │ │ ├── MantineProvider.tsx │ │ └── DirectionProvider.tsx │ ├── extensions │ │ ├── carousel │ │ │ └── CarouselSlide.tsx │ │ ├── nprogress │ │ │ ├── NavigationProgress.tsx │ │ │ └── NavigationProgressProvider.tsx │ │ └── codehighlight │ │ │ ├── InlineCodeHighlight.tsx │ │ │ ├── fragments │ │ │ ├── CodeHighlight.tsx │ │ │ ├── InlineCodeHighlight.tsx │ │ │ └── CodeHighlightTabs.tsx │ │ │ ├── CodeHighlight.tsx │ │ │ └── CodeHighlightTabs.tsx │ └── dates │ │ ├── DatesProvider.tsx │ │ ├── TimeGrid.tsx │ │ └── DatePicker.tsx │ └── props │ ├── table.ts │ ├── box.ts │ ├── styles.ts │ ├── transition.ts │ ├── loader.ts │ ├── progress.ts │ ├── button.ts │ ├── overlay.ts │ ├── scrollarea.ts │ ├── actionicon.ts │ ├── text.ts │ ├── combobox.ts │ └── tooltip.ts ├── shell.nix ├── tsconfig.json ├── .npmignore ├── dash_mantine_components └── styles.py ├── MANIFEST.in ├── tests ├── test_avatar.py ├── test_alert.py ├── test_anchor.py ├── test_tooltip.py ├── test_burger.py ├── test_highlight.py ├── test_switch.py ├── test_segmented_control.py ├── test_hovercard.py ├── test_direction_provider.py ├── test_timeline.py ├── test_typography_styles_provider.py ├── test_checkbox.py ├── test_checkboxcard.py └── charts │ └── test_bubble_chart.py ├── .Rbuildignore ├── justfile ├── LICENSE └── setup.py /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.14.0 -------------------------------------------------------------------------------- /requires-install.txt: -------------------------------------------------------------------------------- 1 | dash>=2 -------------------------------------------------------------------------------- /PRs/requirements.txt: -------------------------------------------------------------------------------- 1 | dash 2 | dash_mantine_components -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | dash[dev]>=2.5.1 2 | wheel 3 | build 4 | black -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: snehilvj 4 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snehilvj/dash-mantine-components/HEAD/assets/logo.png -------------------------------------------------------------------------------- /assets/datepicker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snehilvj/dash-mantine-components/HEAD/assets/datepicker.gif -------------------------------------------------------------------------------- /requires-dev.txt: -------------------------------------------------------------------------------- 1 | dash[ci,dev,testing]>=2 2 | pyyaml>=5.0 3 | pytest<8.1.0 4 | wheel 5 | selenium<4.3.0 6 | black 7 | build 8 | dash-iconify -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "bracketSpacing": true, 5 | "trailingComma": "es5" 6 | } 7 | -------------------------------------------------------------------------------- /src/ts/utils/dates.ts: -------------------------------------------------------------------------------- 1 | // is the date in the list of disabled dates 2 | export const isDisabled = (date: string, disabledDates: string[]) => { 3 | return disabledDates.includes(date); 4 | }; 5 | -------------------------------------------------------------------------------- /src/ts/components/core/radio/RadioGroupContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext } from 'react'; 2 | 3 | interface RadioGroupContextProps { 4 | radioOnClick?: (val?: string) => void; 5 | } 6 | 7 | const RadioGroupContext = createContext(null); 8 | 9 | export default RadioGroupContext; 10 | -------------------------------------------------------------------------------- /src/ts/components/core/chip/ChipGroupContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext } from 'react'; 2 | 3 | interface ChipGroupContextProps { 4 | chipOnClick?: (e: React.MouseEvent) => void; 5 | } 6 | 7 | const ChipGroupContext = createContext(null); 8 | 9 | export default ChipGroupContext; 10 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11"; 3 | pkgs = import nixpkgs { config = {}; overlays = []; }; 4 | in 5 | 6 | pkgs.mkShellNoCC { 7 | packages = with pkgs; [ 8 | python39 9 | python39Packages.pip 10 | nodejs 11 | just 12 | ]; 13 | 14 | REACT_VERSION = "18.2.0"; 15 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "baseUrl": "src/ts", 5 | "inlineSources": true, 6 | "sourceMap": true, 7 | "esModuleInterop": true, 8 | "moduleResolution": "node", 9 | "module": "esnext", 10 | "importHelpers": true, 11 | "lib": ["dom"] 12 | }, 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/ts/components/core/stepper/StepperCompleted.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface Props { 4 | /* Content */ 5 | children?: React.ReactNode; 6 | } 7 | 8 | /** StepperCompleted */ 9 | const StepperCompleted = (props: Props) => { 10 | const { children, ...others } = props; 11 | 12 | return <>{children}; 13 | }; 14 | 15 | export default StepperCompleted; 16 | -------------------------------------------------------------------------------- /src/ts/utils/charts.ts: -------------------------------------------------------------------------------- 1 | export const getClickData = (ev) => { 2 | return ev.activePayload[0]['payload']; 3 | }; 4 | 5 | export const getPieClickData = (ev) => { 6 | return ev.payload.payload; 7 | }; 8 | 9 | export const getScatterClickData = (ev) => { 10 | return ev.payload; 11 | }; 12 | 13 | export const isEventValid = (ev) => { 14 | return Object.keys(ev).length !== 0; 15 | }; 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # testing 5 | /coverage 6 | 7 | # misc 8 | .DS_Store 9 | .env.local 10 | .env.development.local 11 | .env.test.local 12 | .env.production.local 13 | 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Development folders and files 19 | public 20 | src 21 | scripts 22 | config 23 | .travis.yml 24 | CHANGELOG.md 25 | README.md 26 | -------------------------------------------------------------------------------- /dash_mantine_components/styles.py: -------------------------------------------------------------------------------- 1 | """ 2 | As of v1.2.0, optional CSS is bundled with the components, so separate imports are no longer needed. 3 | These variables are retained for backward compatibility with earlier versions. 4 | """ 5 | 6 | DATES = "" 7 | CODE_HIGHLIGHT = "" 8 | CHARTS = "" 9 | CAROUSEL = "" 10 | NOTIFICATIONS = "" 11 | NPROGRESS = "" 12 | RICH_TEXT_EDITOR = "" 13 | 14 | ALL = [] 15 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include dash_mantine_components/** *.js *.css *.html 2 | include dash_mantine_components/theme/* 3 | prune tests 4 | include README.md 5 | include LICENSE 6 | include requires-install.txt 7 | include requires-dev.txt 8 | include dash_mantine_components/package-info.json 9 | include package.json 10 | exclude dash_mantine_components/metadata.json 11 | exclude dash_mantine_components/proptypes.js -------------------------------------------------------------------------------- /src/ts/props/table.ts: -------------------------------------------------------------------------------- 1 | import { BoxProps } from 'props/box'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { StylesApiProps } from 'props/styles'; 4 | 5 | export interface TableElementProps 6 | extends BoxProps, 7 | StylesApiProps, 8 | DashBaseProps { 9 | /** Content */ 10 | children?: React.ReactNode; 11 | /* props passed to the table element */ 12 | tableProps?: object; 13 | } 14 | -------------------------------------------------------------------------------- /src/ts/props/box.ts: -------------------------------------------------------------------------------- 1 | import { BoxProps as MantineBoxProps } from '@mantine/core'; 2 | 3 | export type BoxProps = Omit; 4 | 5 | export interface BoxComponentProps extends BoxProps { 6 | /** Variant passed from parent component, sets `data-variant` */ 7 | variant?: string; 8 | /** Size passed from parent component, sets `data-size` if value is not number like */ 9 | size?: string | number; 10 | } 11 | -------------------------------------------------------------------------------- /src/ts/components/core/menu/MenuTarget.tsx: -------------------------------------------------------------------------------- 1 | import { BoxProps } from 'props/box'; 2 | import React from 'react'; 3 | 4 | interface Props { 5 | /** Content */ 6 | children: React.ReactNode; 7 | /** Target box wrapper props */ 8 | boxWrapperProps?: BoxProps; 9 | } 10 | 11 | /** MenuTarget */ 12 | const MenuTarget = (props: Props) => { 13 | const { children } = props; 14 | 15 | return <>{children}; 16 | }; 17 | 18 | export default MenuTarget; 19 | -------------------------------------------------------------------------------- /src/ts/components/core/table/TableTd.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from '@mantine/core'; 2 | import { TableElementProps } from 'props/table'; 3 | import React from 'react'; 4 | 5 | const TableTd = (props: TableElementProps) => { 6 | const { setProps, children, tableProps, ...others } = props; 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default TableTd; 16 | -------------------------------------------------------------------------------- /src/ts/components/core/table/TableTh.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from '@mantine/core'; 2 | import { TableElementProps } from 'props/table'; 3 | import React from 'react'; 4 | 5 | const TableTh = (props: TableElementProps) => { 6 | const { setProps, tableProps, children, ...others } = props; 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default TableTh; 16 | -------------------------------------------------------------------------------- /src/ts/components/core/table/TableTr.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from '@mantine/core'; 2 | import { TableElementProps } from 'props/table'; 3 | import React from 'react'; 4 | 5 | const TableTr = (props: TableElementProps) => { 6 | const { setProps, tableProps, children, ...others } = props; 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default TableTr; 16 | -------------------------------------------------------------------------------- /src/ts/components/core/menu/SubMenuTarget.tsx: -------------------------------------------------------------------------------- 1 | import { BoxProps } from 'props/box'; 2 | import React from 'react'; 3 | 4 | interface Props { 5 | /** Content */ 6 | children: React.ReactNode; 7 | /** Target box wrapper props */ 8 | boxWrapperProps?: BoxProps; 9 | } 10 | 11 | /** Sub MenuTarget */ 12 | const SubMenuTarget = (props: Props) => { 13 | const { children } = props; 14 | 15 | return <>{children}; 16 | }; 17 | 18 | export default SubMenuTarget; 19 | -------------------------------------------------------------------------------- /src/ts/components/core/popover/PopoverTarget.tsx: -------------------------------------------------------------------------------- 1 | import { BoxProps } from 'props/box'; 2 | import React from 'react'; 3 | 4 | interface Props { 5 | /** Content */ 6 | children: React.ReactNode; 7 | /** Target box wrapper props */ 8 | boxWrapperProps?: BoxProps; 9 | } 10 | 11 | /** PopoverTarget */ 12 | const PopoverTarget = (props: Props) => { 13 | const { children } = props; 14 | 15 | return <>{children}; 16 | }; 17 | 18 | export default PopoverTarget; 19 | -------------------------------------------------------------------------------- /src/ts/components/core/table/TableTbody.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from '@mantine/core'; 2 | import { TableElementProps } from 'props/table'; 3 | import React from 'react'; 4 | 5 | const TableTbody = (props: TableElementProps) => { 6 | const { setProps, tableProps, children, ...others } = props; 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default TableTbody; 16 | -------------------------------------------------------------------------------- /src/ts/components/core/table/TableTfoot.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from '@mantine/core'; 2 | import { TableElementProps } from 'props/table'; 3 | import React from 'react'; 4 | 5 | const TableTfoot = (props: TableElementProps) => { 6 | const { setProps, tableProps, children, ...others } = props; 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default TableTfoot; 16 | -------------------------------------------------------------------------------- /src/ts/components/core/table/TableThead.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from '@mantine/core'; 2 | import { TableElementProps } from 'props/table'; 3 | import React from 'react'; 4 | 5 | const TableThead = (props: TableElementProps) => { 6 | const { setProps, tableProps, children, ...others } = props; 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default TableThead; 16 | -------------------------------------------------------------------------------- /src/ts/components/core/hovercard/HoverCardTarget.tsx: -------------------------------------------------------------------------------- 1 | import { BoxProps } from 'props/box'; 2 | import React from 'react'; 3 | 4 | interface Props { 5 | /** Content */ 6 | children: React.ReactNode; 7 | /** Target box wrapper props */ 8 | boxWrapperProps?: BoxProps; 9 | } 10 | 11 | /** HoverCardTarget */ 12 | const HoverCardTarget = (props: Props) => { 13 | const { children } = props; 14 | 15 | return <>{children}; 16 | }; 17 | 18 | export default HoverCardTarget; 19 | -------------------------------------------------------------------------------- /src/ts/components/core/table/TableCaption.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from '@mantine/core'; 2 | import { TableElementProps } from 'props/table'; 3 | import React from 'react'; 4 | 5 | const TableCaption = (props: TableElementProps) => { 6 | const { setProps, tableProps, children, ...others } = props; 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | export default TableCaption; 16 | -------------------------------------------------------------------------------- /src/ts/utils/editorRegistry.ts: -------------------------------------------------------------------------------- 1 | export const editorInstances: Record = {}; 2 | 3 | export const getEditor = (id: string) => { 4 | // optional chaining so before the fragment exists it'll just return undefined 5 | // which does the right thing because clearly no rte is initialized yet! 6 | const api = editorInstances?.[id]; 7 | if (api) { 8 | return api; 9 | } 10 | throw new Error( 11 | `no RichTextEditor found with id: ${id}, or it's not initialized yet.` 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/ts/props/styles.ts: -------------------------------------------------------------------------------- 1 | export interface StylesApiProps { 2 | /** Adds custom CSS class names to inner elements of a component. See Styles API docs */ 3 | classNames?: object; 4 | /** Adds inline styles directly to inner elements of a component. See Styles API docs */ 5 | styles?: any; 6 | /** Remove all Mantine styling from the component */ 7 | unstyled?: boolean; 8 | /** variant */ 9 | variant?: string; 10 | /** Passes attributes to inner elements of a component. See Styles API docs. */ 11 | attributes?: any; 12 | } 13 | -------------------------------------------------------------------------------- /tests/test_avatar.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, html, _dash_renderer 2 | import dash_mantine_components as dmc 3 | 4 | _dash_renderer._set_react_version("18.2.0") 5 | 6 | 7 | def test_001av_avatar(dash_duo): 8 | app = Dash(__name__) 9 | 10 | avatar = dmc.Avatar("MK", id="avatar") 11 | 12 | app.layout = dmc.MantineProvider(html.Div([avatar])) 13 | 14 | dash_duo.start_server(app) 15 | 16 | # Wait for the app to load 17 | dash_duo.wait_for_text_to_equal("#avatar", "MK") 18 | 19 | assert dash_duo.get_logs() == [] 20 | -------------------------------------------------------------------------------- /tests/test_alert.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dash import Dash, html, _dash_renderer 3 | import dash_mantine_components as dmc 4 | 5 | _dash_renderer._set_react_version("18.2.0") 6 | 7 | 8 | def test_001al_alert(dash_duo): 9 | app = Dash(__name__) 10 | 11 | avatar = dmc.Alert("Alert text", id="alert", duration=200) 12 | 13 | app.layout = dmc.MantineProvider(html.Div([avatar])) 14 | 15 | dash_duo.start_server(app) 16 | 17 | # Wait for the app to load 18 | dash_duo.wait_for_text_to_equal("#alert", "Alert text") 19 | time.sleep(.3) 20 | assert dash_duo.get_logs() == [] 21 | -------------------------------------------------------------------------------- /src/ts/components/charts/fragments/Sparkline.tsx: -------------------------------------------------------------------------------- 1 | import { Sparkline as MantineSparkline } from '@mantine/charts'; 2 | import '@mantine/charts/styles.css'; 3 | import React from 'react'; 4 | import { getLoadingState } from '../../../utils/dash3'; 5 | import { Props } from '../Sparkline'; 6 | 7 | /** Sparkline */ 8 | const Sparkline = (props: Props) => { 9 | const { setProps, loading_state, ...others } = props; 10 | 11 | return ( 12 | 16 | ); 17 | }; 18 | 19 | export default Sparkline; 20 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | # ignore JS config files/folders 2 | node_modules/ 3 | coverage/ 4 | src/ 5 | lib/ 6 | .babelrc 7 | .builderrc 8 | .eslintrc 9 | .npmignore 10 | .editorconfig 11 | .eslintignore 12 | .prettierrc 13 | .circleci 14 | .github 15 | 16 | # demo folder has special meaning in R 17 | # this should hopefully make it still 18 | # allow for the possibility to make R demos 19 | demo/.*\.js 20 | demo/.*\.html 21 | demo/.*\.css 22 | 23 | # ignore Python files/folders 24 | setup.py 25 | usage.py 26 | setup.py 27 | requirements.txt 28 | MANIFEST.in 29 | CHANGELOG.md 30 | test/ 31 | # CRAN has weird LICENSE requirements 32 | LICENSE.txt 33 | ^.*\.Rproj$ 34 | ^\.Rproj\.user$ 35 | -------------------------------------------------------------------------------- /src/ts/components/core/Loader.tsx: -------------------------------------------------------------------------------- 1 | import { Loader as MantineLoader } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { LoaderProps } from 'props/loader'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../utils/dash3'; 6 | 7 | interface Props extends LoaderProps, DashBaseProps {} 8 | 9 | /** Loader */ 10 | const Loader = (props: Props) => { 11 | const { setProps, loading_state, ...others } = props; 12 | 13 | return ( 14 | 18 | ); 19 | }; 20 | 21 | export default Loader; 22 | -------------------------------------------------------------------------------- /src/ts/components/core/Overlay.tsx: -------------------------------------------------------------------------------- 1 | import { Overlay as MantineOverlay } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { OverlayProps } from 'props/overlay'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../utils/dash3'; 6 | 7 | interface Props extends OverlayProps, DashBaseProps {} 8 | 9 | /** Overlay */ 10 | const Overlay = (props: Props) => { 11 | const { setProps, loading_state, ...others } = props; 12 | 13 | return ( 14 | 18 | ); 19 | }; 20 | 21 | export default Overlay; 22 | -------------------------------------------------------------------------------- /src/ts/components/core/progress/ProgressRoot.tsx: -------------------------------------------------------------------------------- 1 | import { Progress } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { ProgressRootProps } from 'props/progress'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | 7 | export interface Props extends ProgressRootProps, DashBaseProps {} 8 | 9 | /** ProgressRoot */ 10 | const ProgressRoot = (props: Props) => { 11 | const { setProps, loading_state, ...others } = props; 12 | 13 | return ( 14 | 18 | ); 19 | }; 20 | 21 | export default ProgressRoot; 22 | -------------------------------------------------------------------------------- /src/ts/components/core/accordion/AccordionPanel.tsx: -------------------------------------------------------------------------------- 1 | import { Accordion } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Content */ 10 | children?: React.ReactNode; 11 | } 12 | 13 | /** AccordionPanel */ 14 | const AccordionPanel = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return {children}; 18 | }; 19 | 20 | export default AccordionPanel; 21 | -------------------------------------------------------------------------------- /src/ts/components/core/menu/MenuDivider.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps {} 9 | 10 | /** MenuDivider */ 11 | const MenuDivider = (props: Props) => { 12 | const { setProps, loading_state, ...others } = props; 13 | 14 | return ( 15 | 19 | ); 20 | }; 21 | 22 | export default MenuDivider; 23 | -------------------------------------------------------------------------------- /src/ts/props/transition.ts: -------------------------------------------------------------------------------- 1 | import { MantineTransition } from '@mantine/core'; 2 | 3 | export interface TransitionProps { 4 | /** If set element will not be unmounted from the DOM when it is hidden, `display: none` styles will be applied instead */ 5 | keepMounted?: boolean; 6 | /** Transition name or object */ 7 | transition?: MantineTransition; 8 | /** Transition duration in ms, `250` by default */ 9 | duration?: number; 10 | /** Exit transition duration in ms, `250` by default */ 11 | exitDuration?: number; 12 | /** Transition timing function, `theme.transitionTimingFunction` by default */ 13 | timingFunction?: string; 14 | /** Determines whether component should be mounted to the DOM */ 15 | mounted: boolean; 16 | } 17 | -------------------------------------------------------------------------------- /PRs/app.py: -------------------------------------------------------------------------------- 1 | import dash_mantine_components as dmc 2 | from dash import Dash, dcc 3 | 4 | app = Dash() 5 | 6 | component = dcc.Markdown( 7 | """ 8 | ### PR Sample App 9 | 10 | This sample app uses the unreleased version of dash-mantine-components based on this pull request. You can use it to test bug fixes or try new features. 11 | 12 | ### How to Use: 13 | 14 | 1. Replace this app with your own code. 15 | 1. The app is hosted live on PyCafe. 16 | 3. If you have a PyCafe account, you can save the app to your account. Otherwise, simply copy the URL. 17 | 4. Share the link in the pull request or anywhere else. 18 | 19 | """ 20 | ) 21 | 22 | 23 | app.layout = dmc.MantineProvider([component]) 24 | 25 | 26 | if __name__ == "__main__": 27 | app.run(debug=True) 28 | -------------------------------------------------------------------------------- /src/ts/components/core/Box.tsx: -------------------------------------------------------------------------------- 1 | import { Box as MantineBox } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../utils/dash3'; 6 | 7 | interface Props extends BoxProps, DashBaseProps { 8 | /* Content */ 9 | children?: React.ReactNode; 10 | } 11 | 12 | /** Box */ 13 | const Box = (props: Props) => { 14 | const { children, setProps, loading_state, ...others } = props; 15 | 16 | return ( 17 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | export default Box; 27 | -------------------------------------------------------------------------------- /src/ts/props/loader.ts: -------------------------------------------------------------------------------- 1 | import { MantineColor, MantineSize } from '@mantine/core'; 2 | import { BoxProps } from './box'; 3 | import { StylesApiProps } from './styles'; 4 | 5 | export interface LoaderProps extends BoxProps, StylesApiProps { 6 | /** Controls `width` and `height` of the loader. `Loader` has predefined `xs`-`xl` values. Numbers are converted to rem. Default value is `'md'` */ 7 | size?: MantineSize | (string & {}) | number; 8 | /** Key of `theme.colors` or any valid CSS color, default value is `theme.primaryColor` */ 9 | color?: MantineColor; 10 | /** Loader type, key of `loaders` prop, default value is `'oval'` */ 11 | type?: 'bars' | 'dots' | 'oval'; 12 | /** Overrides default loader with given content */ 13 | children?: React.ReactNode; 14 | } 15 | -------------------------------------------------------------------------------- /src/ts/components/core/Space.tsx: -------------------------------------------------------------------------------- 1 | import { Space as MantineSpace } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../utils/dash3'; 6 | 7 | interface Props extends BoxProps, DashBaseProps { 8 | /* Content */ 9 | children?: React.ReactNode; 10 | } 11 | 12 | /** Space */ 13 | const Space = (props: Props) => { 14 | const { children, setProps, loading_state, ...others } = props; 15 | 16 | return ( 17 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | export default Space; 27 | -------------------------------------------------------------------------------- /src/ts/components/core/Text.tsx: -------------------------------------------------------------------------------- 1 | import { Text as MantineText } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { TextProps } from 'props/text'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../utils/dash3'; 6 | 7 | interface Props extends TextProps, DashBaseProps { 8 | /** Content */ 9 | children?: React.ReactNode; 10 | } 11 | 12 | /** Text */ 13 | const Text = (props: Props) => { 14 | const { children, setProps, loading_state, ...others } = props; 15 | 16 | return ( 17 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | export default Text; 27 | -------------------------------------------------------------------------------- /src/ts/components/core/progress/ProgressLabel.tsx: -------------------------------------------------------------------------------- 1 | import { Progress } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Content */ 10 | children?: React.ReactNode; 11 | } 12 | 13 | /** ProgressLabel */ 14 | const ProgressLabel = (props: Props) => { 15 | const { setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | ); 23 | }; 24 | 25 | export default ProgressLabel; 26 | -------------------------------------------------------------------------------- /src/ts/components/styles/TypographyStylesProvider.tsx: -------------------------------------------------------------------------------- 1 | import { TypographyStylesProvider as MantineTypographyStylesProvider } from '@mantine/core'; 2 | import React from 'react'; 3 | import { BoxProps } from 'props/box'; 4 | import { DashBaseProps } from 'props/dash'; 5 | import { StylesApiProps } from 'props/styles'; 6 | 7 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 8 | /** Content to be styled. */ 9 | children?: React.ReactNode; 10 | } 11 | 12 | /* TypographyStylesProvider */ 13 | const TypographyStylesProvider = (props: Props) => { 14 | const { children, ...others } = props; 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | export default TypographyStylesProvider; 24 | -------------------------------------------------------------------------------- /tests/test_anchor.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, html, _dash_renderer 2 | import dash_mantine_components as dmc 3 | 4 | _dash_renderer._set_react_version("18.2.0") 5 | 6 | 7 | def test_001an_anchor(dash_duo): 8 | app = Dash(__name__) 9 | 10 | anchor = dmc.Anchor( 11 | children='link', 12 | href='#', 13 | target='_blank', 14 | anchorProps={ 15 | 'download': 'file.txt' 16 | }, 17 | id='anchor', 18 | ) 19 | app.layout = dmc.MantineProvider(html.Div([anchor])) 20 | 21 | dash_duo.start_server(app) 22 | 23 | # Wait for the app to load 24 | dash_duo.wait_for_text_to_equal("#anchor", "link") 25 | 26 | # Get attribute set using anchorProps 27 | assert dash_duo.find_element("#anchor").get_attribute("download") == "file.txt" 28 | 29 | assert dash_duo.get_logs() == [] -------------------------------------------------------------------------------- /tests/test_tooltip.py: -------------------------------------------------------------------------------- 1 | import time 2 | from dash import Dash, _dash_renderer 3 | import dash_mantine_components as dmc 4 | 5 | _dash_renderer._set_react_version("18.2.0") 6 | 7 | 8 | def test_001to_tooltip(dash_duo): 9 | app = Dash(__name__) 10 | 11 | component = dmc.Tooltip( 12 | dmc.Text("Text", id="text"), 13 | label="Tooltip text", 14 | ) 15 | 16 | app.layout = dmc.MantineProvider(component) 17 | 18 | dash_duo.start_server(app) 19 | 20 | # Wait for the app to load 21 | dash_duo.wait_for_text_to_equal("#text", "Text") 22 | dash_duo.find_element("#text").click() 23 | time.sleep(.2) 24 | 25 | # Find tooltip and assert its content 26 | tooltip = dash_duo.find_element(".mantine-Tooltip-tooltip") 27 | assert tooltip.text == "Tooltip text" 28 | 29 | assert dash_duo.get_logs() == [] 30 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | set dotenv-load := false 2 | 3 | # Generate components and build the bundle 4 | build: 5 | npm run build 6 | 7 | # Build the webpack bundle 8 | build-js: 9 | npm run build:js 10 | 11 | # Generate the components 12 | generate: 13 | npm run build:backends 14 | 15 | # Rebuild the bundle on change 16 | watch: 17 | npm run watch 18 | 19 | # Install pip requirements & node modules. 20 | install: 21 | pip install -r requirements.txt 22 | npm install 23 | 24 | # Package the application for distribution using python wheel. 25 | package: clean build 26 | python -m build 27 | 28 | # Publish the package to pypi using twine. 29 | publish: package 30 | npm publish 31 | twine upload dist/* 32 | 33 | # Remove dist & build directories 34 | clean: 35 | rm -rf dist 36 | rm -rf build 37 | 38 | format: 39 | npx prettier --write src/ts/ 40 | -------------------------------------------------------------------------------- /src/ts/components/core/modal/ManagedModal.tsx: -------------------------------------------------------------------------------- 1 | import { Modal as MantineModal } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { ModalProps } from 'props/modal'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React, { useEffect, useState } from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props 9 | extends Omit, 10 | Omit, 11 | Omit { 12 | /** Unique ID to identify this component. Required for use with StackModal */ 13 | id: string; 14 | } 15 | 16 | /** Managed Model for StackModal */ 17 | const ManagedModal = ({ 18 | children, 19 | setProps, 20 | loading_state, 21 | ...others 22 | }: Props) => { 23 | return <>{children}; 24 | }; 25 | 26 | export default ManagedModal; 27 | -------------------------------------------------------------------------------- /src/ts/components/core/scrollarea/ScrollAreaAutosize.tsx: -------------------------------------------------------------------------------- 1 | import { ScrollArea } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { ScrollAreaProps } from 'props/scrollarea'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | 7 | interface Props extends ScrollAreaProps, DashBaseProps { 8 | /** Content */ 9 | children?: React.ReactNode; 10 | } 11 | 12 | /** ScrollArea */ 13 | const ScrollAreaAutosize = (props: Props) => { 14 | const { setProps, loading_state, children, ...others } = props; 15 | 16 | return ( 17 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | export default ScrollAreaAutosize; 27 | -------------------------------------------------------------------------------- /src/ts/components/core/appshell/AppShellMain.tsx: -------------------------------------------------------------------------------- 1 | import { AppShell } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Content */ 10 | children: React.ReactNode; 11 | } 12 | 13 | /** AppShellMain */ 14 | const AppShellMain = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | export default AppShellMain; 28 | -------------------------------------------------------------------------------- /src/ts/components/core/menu/MenuLabel.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** Content */ 10 | children: React.ReactNode; 11 | } 12 | 13 | /** MenuLabel */ 14 | const MenuLabel = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | MenuLabel.dashChildrenUpdate = true; 28 | 29 | export default MenuLabel; 30 | -------------------------------------------------------------------------------- /src/ts/components/extensions/carousel/CarouselSlide.tsx: -------------------------------------------------------------------------------- 1 | import { Carousel } from '@mantine/carousel'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Content */ 10 | children?: React.ReactNode; 11 | } 12 | 13 | /** CarouselSlide */ 14 | const CarouselSlide = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | {children}{' '} 23 | 24 | ); 25 | }; 26 | 27 | export default CarouselSlide; 28 | -------------------------------------------------------------------------------- /src/ts/components/core/menu/MenuDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** Content */ 10 | children: React.ReactNode; 11 | } 12 | 13 | /** MenuDropdown */ 14 | const MenuDropdown = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | MenuDropdown.dashChildrenUpdate = true; 28 | 29 | export default MenuDropdown; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/VisuallyHidden.tsx: -------------------------------------------------------------------------------- 1 | import { VisuallyHidden as MantineVisuallyHidden } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Content */ 10 | children?: React.ReactNode; 11 | } 12 | 13 | /** VisuallyHidden */ 14 | const VisuallyHidden = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | export default VisuallyHidden; 28 | -------------------------------------------------------------------------------- /src/ts/components/core/list/ListItem.tsx: -------------------------------------------------------------------------------- 1 | import { List } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends DashBaseProps, BoxProps, StylesApiProps { 9 | /** Icon to replace item bullet */ 10 | icon?: React.ReactNode; 11 | /** Content */ 12 | children?: React.ReactNode; 13 | } 14 | 15 | /** ListItem */ 16 | const ListItem = (props: Props) => { 17 | const { setProps, loading_state, children, ...others } = props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default ListItem; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/Paper.tsx: -------------------------------------------------------------------------------- 1 | import { Paper as MantinePaper, PaperBaseProps } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props 9 | extends BoxProps, 10 | PaperBaseProps, 11 | StylesApiProps, 12 | DashBaseProps { 13 | /** Content */ 14 | children?: React.ReactNode; 15 | } 16 | 17 | /** Paper */ 18 | const Paper = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default Paper; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/menu/SubMenuDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { Menu } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** Content */ 10 | children: React.ReactNode; 11 | } 12 | 13 | /** Sub MenuDropdown */ 14 | const SubMenuDropdown = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | SubMenuDropdown.dashChildrenUpdate = true; 28 | 29 | export default SubMenuDropdown; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/Affix.tsx: -------------------------------------------------------------------------------- 1 | import { AffixBaseProps, Affix as MantineAffix } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props 9 | extends BoxProps, 10 | Omit, 11 | StylesApiProps, 12 | DashBaseProps { 13 | /* Content */ 14 | children?: React.ReactNode; 15 | } 16 | 17 | /** Affix */ 18 | const Affix = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default Affix; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/popover/PopoverDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { Popover } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** Content */ 10 | children: React.ReactNode; 11 | } 12 | 13 | /** PopoverDropdown */ 14 | const PopoverDropdown = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | PopoverDropdown.dashChildrenUpdate = true; 28 | 29 | export default PopoverDropdown; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/Mark.tsx: -------------------------------------------------------------------------------- 1 | import { MantineColor, Mark as MantineMark } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends DashBaseProps, BoxProps, StylesApiProps { 9 | /** Content */ 10 | children?: string; 11 | /** Key of `theme.colors` or any valid CSS color, `yellow` by default */ 12 | color?: MantineColor; 13 | } 14 | 15 | /** Mark */ 16 | const Mark = (props: Props) => { 17 | const { children, setProps, loading_state, ...others } = props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default Mark; 30 | -------------------------------------------------------------------------------- /src/ts/components/extensions/nprogress/NavigationProgress.tsx: -------------------------------------------------------------------------------- 1 | import { nprogress } from '@mantine/nprogress'; 2 | import { useEffect } from 'react'; 3 | 4 | interface Props { 5 | /** action */ 6 | action: 7 | | 'start' 8 | | 'stop' 9 | | 'increment' 10 | | 'decrement' 11 | | 'set' 12 | | 'reset' 13 | | 'complete'; 14 | /** value to set the progress bar to */ 15 | value?: number; 16 | } 17 | 18 | /** NavigationProgress */ 19 | const NavigationProgress = (props: Props) => { 20 | const { action, value } = props; 21 | 22 | window['np'] = nprogress; 23 | 24 | useEffect(() => { 25 | switch (action) { 26 | case 'set': 27 | nprogress.set(value); 28 | break; 29 | 30 | default: 31 | nprogress[action](); 32 | break; 33 | } 34 | }, [props]); 35 | }; 36 | 37 | export default NavigationProgress; 38 | -------------------------------------------------------------------------------- /src/ts/components/core/Kbd.tsx: -------------------------------------------------------------------------------- 1 | import { Kbd as MantineKbd, MantineSize } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Controls font-size and padding, `'sm'` by default */ 10 | size?: MantineSize | (string & {}); 11 | /** Keyboard key */ 12 | children?: React.ReactNode; 13 | } 14 | 15 | /** Kbd */ 16 | const Kbd = (props: Props) => { 17 | const { setProps, loading_state, children, ...others } = props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default Kbd; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/hovercard/HoverCardDropdown.tsx: -------------------------------------------------------------------------------- 1 | import { HoverCard } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** Content */ 10 | children: React.ReactNode; 11 | } 12 | 13 | /** HoverCardDropdown */ 14 | const HoverCardDropdown = (props: Props) => { 15 | const { children, setProps, loading_state, ...others } = props; 16 | 17 | return ( 18 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | HoverCardDropdown.dashChildrenUpdate = true; 28 | 29 | export default HoverCardDropdown; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/accordion/AccordionItem.tsx: -------------------------------------------------------------------------------- 1 | import { Accordion } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Value that is used to manage accordion state */ 10 | value: string; 11 | /** Content */ 12 | children?: React.ReactNode; 13 | } 14 | 15 | /** AccordionItem */ 16 | const AccordionItem = (props: Props) => { 17 | const { children, setProps, loading_state, ...others } = props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default AccordionItem; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/input/InputWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { Input as MantineInput } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { InputWrapperProps } from 'props/input'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | 7 | interface Props extends InputWrapperProps, DashBaseProps { 8 | /** Input wrapper content */ 9 | children?: React.ReactNode; 10 | /** Id of associated input */ 11 | htmlFor?: string; 12 | } 13 | 14 | /** InputWrapper */ 15 | const InputWrapper = (props: Props) => { 16 | const { setProps, loading_state, children, id, htmlFor, ...others } = props; 17 | 18 | return ( 19 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default InputWrapper; 30 | -------------------------------------------------------------------------------- /src/ts/components/extensions/codehighlight/InlineCodeHighlight.tsx: -------------------------------------------------------------------------------- 1 | import { BoxProps } from 'props/box'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { StylesApiProps } from 'props/styles'; 4 | import React, { Suspense } from 'react'; 5 | 6 | // eslint-disable-next-line no-inline-comments 7 | const LazyInlineCodeHighlight = React.lazy( 8 | () => 9 | import( 10 | /* webpackChunkName: "InlineCodeHighlight" */ './fragments/InlineCodeHighlight' 11 | ) 12 | ); 13 | 14 | export interface Props extends BoxProps, StylesApiProps, DashBaseProps { 15 | /** Code to highlight */ 16 | code: string; 17 | /** Code language, `'tsx'` by default */ 18 | language?: string; 19 | } 20 | 21 | /** InlineCodeHighlight */ 22 | const InlineCodeHighlight = (props: Props) => { 23 | return ( 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default InlineCodeHighlight; 31 | -------------------------------------------------------------------------------- /src/ts/components/core/AspectRatio.tsx: -------------------------------------------------------------------------------- 1 | import { AspectRatio as MantineAspectRatio } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /* Content */ 10 | children?: React.ReactNode; 11 | /** Aspect ratio, e.g. `16 / 9`, `4 / 3`, `1920 / 1080`, `1` by default */ 12 | ratio?: number; 13 | } 14 | 15 | /** AspectRatio */ 16 | const AspectRatio = (props: Props) => { 17 | const { children, setProps, loading_state, ...others } = props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default AspectRatio; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/avatar/AvatarGroup.tsx: -------------------------------------------------------------------------------- 1 | import { Avatar, MantineSpacing } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Negative space between Avatar components, `'sm'` by default */ 10 | spacing?: MantineSpacing; 11 | /** components */ 12 | children?: React.ReactNode; 13 | } 14 | 15 | /** AvatarGroup */ 16 | const AvatarGroup = (props: Props) => { 17 | const { children, setProps, loading_state, ...others } = props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default AvatarGroup; 30 | -------------------------------------------------------------------------------- /src/ts/components/charts/fragments/RadarChart.tsx: -------------------------------------------------------------------------------- 1 | import { RadarChart as MantineRadarChart } from '@mantine/charts'; 2 | import '@mantine/charts/styles.css'; 3 | import React from 'react'; 4 | import { getClickData, isEventValid } from '../../../utils/charts'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | import { Props } from '../RadarChart'; 7 | 8 | /** RadarChart */ 9 | const RadarChart = (props: Props) => { 10 | const { setProps, loading_state, clickData, radarChartProps, ...others } = 11 | props; 12 | 13 | const onClick = (ev) => { 14 | if (isEventValid(ev)) { 15 | setProps({ clickData: getClickData(ev) }); 16 | } 17 | }; 18 | 19 | const newProps = { ...radarChartProps, onClick }; 20 | 21 | return ( 22 | 27 | ); 28 | }; 29 | 30 | export default RadarChart; 31 | -------------------------------------------------------------------------------- /src/ts/components/core/appshell/AppShellSection.tsx: -------------------------------------------------------------------------------- 1 | import { AppShell } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Determines whether the section should take all available space, `false` by default */ 10 | grow?: boolean; 11 | /** Content */ 12 | children: React.ReactNode; 13 | } 14 | 15 | /** AppShellSection */ 16 | const AppShellSection = (props: Props) => { 17 | const { children, setProps, loading_state, ...others } = props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default AppShellSection; 30 | -------------------------------------------------------------------------------- /src/ts/components/core/Center.tsx: -------------------------------------------------------------------------------- 1 | import { Center as MantineCenter } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /* Content */ 10 | children?: React.ReactNode; 11 | /** Determines whether `inline-flex` should be used instead of `flex`, `false` by default */ 12 | inline?: boolean; 13 | } 14 | 15 | /** Centers content vertically and horizontally */ 16 | const Center = (props: Props) => { 17 | const { children, setProps, loading_state, ...others } = props; 18 | 19 | return ( 20 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | export default Center; 30 | -------------------------------------------------------------------------------- /src/ts/components/styles/MantineProvider.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | MantineProvider as MantineMantineProvider, 3 | MantineProviderProps, 4 | } from '@mantine/core'; 5 | import React from 'react'; 6 | 7 | import '@mantine/core/styles.css'; 8 | 9 | // Optional stylesheets must be imported after the core styles. 10 | // If these components are changed to load asynchronously (like charts), their styles can be imported within the component itself. 11 | import '@mantine/dates/styles.css'; 12 | import '@mantine/carousel/styles.css'; 13 | import '@mantine/nprogress/styles.css'; 14 | import '@mantine/notifications/styles.css'; 15 | 16 | interface Props extends MantineProviderProps { 17 | /** Unique ID to identify this component in Dash callbacks. */ 18 | id?: string; 19 | } 20 | 21 | /* MantineProvider */ 22 | const MantineProvider = (props: Props) => { 23 | const { children, ...others } = props; 24 | 25 | return ( 26 | {children} 27 | ); 28 | }; 29 | 30 | export default MantineProvider; 31 | -------------------------------------------------------------------------------- /src/ts/components/core/tabs/TabsList.tsx: -------------------------------------------------------------------------------- 1 | import { Tabs } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** `Tabs.Tab` components */ 10 | children: React.ReactNode; 11 | /** Determines whether tabs should take all available space, `false` by default */ 12 | grow?: boolean; 13 | /** Tabs alignment, `flex-start` by default */ 14 | justify?: React.CSSProperties['justifyContent']; 15 | } 16 | 17 | /** TabsList */ 18 | const TabsList = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default TabsList; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/tabs/TabsPanel.tsx: -------------------------------------------------------------------------------- 1 | import { Tabs } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** Panel content */ 10 | children: React.ReactNode; 11 | /** If set to `true`, the content will be kept mounted, even if `keepMounted` is set `false` in the parent `Tabs` component */ 12 | keepMounted?: boolean; 13 | /** Value of associated control */ 14 | value: string; 15 | } 16 | 17 | /** TabsPanel */ 18 | const TabsPanel = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default TabsPanel; 32 | -------------------------------------------------------------------------------- /src/ts/components/extensions/codehighlight/fragments/CodeHighlight.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | CodeHighlight as MantineCodeHighlight, 3 | CodeHighlightAdapterProvider, 4 | } from '@mantine/code-highlight'; 5 | import '@mantine/code-highlight/styles.css'; 6 | import './dmc-code.css'; 7 | import React from 'react'; 8 | import { getLoadingState } from '../../../../utils/dash3'; 9 | import { highlightJsAdapter } from '../../../../utils/highlightJsAdapter'; 10 | import { Props } from '../CodeHighlight'; 11 | 12 | /** Highlight code with highlight.js*/ 13 | const CodeHighlight = (props: Props) => { 14 | const { setProps, loading_state, className, ...others } = props; 15 | 16 | return ( 17 | 18 | 25 | 26 | ); 27 | }; 28 | 29 | export default CodeHighlight; 30 | -------------------------------------------------------------------------------- /src/ts/components/extensions/codehighlight/fragments/InlineCodeHighlight.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | InlineCodeHighlight as MantineInlineCodeHighlight, 3 | CodeHighlightAdapterProvider, 4 | } from '@mantine/code-highlight'; 5 | import '@mantine/code-highlight/styles.css'; 6 | import React from 'react'; 7 | import { getLoadingState } from '../../../../utils/dash3'; 8 | import { Props } from '../InlineCodeHighlight'; 9 | import { highlightJsAdapter } from '../../../../utils/highlightJsAdapter'; 10 | 11 | /** Highlight code inline with highlight.js */ 12 | const InlineCodeHighlight = (props: Props) => { 13 | const { setProps, loading_state, className, ...others } = props; 14 | 15 | return ( 16 | 17 | 24 | 25 | ); 26 | }; 27 | 28 | export default InlineCodeHighlight; 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Snehil Vijay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ts/components/core/accordion/AccordionControl.tsx: -------------------------------------------------------------------------------- 1 | import { Accordion } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Disables control button */ 10 | disabled?: boolean; 11 | /** Custom chevron icon */ 12 | chevron?: React.ReactNode; 13 | /** Control label */ 14 | children?: React.ReactNode; 15 | /** Icon displayed next to the label */ 16 | icon?: React.ReactNode; 17 | } 18 | 19 | /** AccordionControl */ 20 | const AccordionControl = (props: Props) => { 21 | const { children, setProps, loading_state, ...others } = props; 22 | 23 | return ( 24 | 28 | {children} 29 | 30 | ); 31 | }; 32 | 33 | export default AccordionControl; 34 | -------------------------------------------------------------------------------- /src/ts/components/core/card/CardSection.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | export interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Determines whether the section should have a border, `false` by default */ 10 | withBorder?: boolean; 11 | /** Determines whether the section should inherit padding from the parent `Card`, `false` by default */ 12 | inheritPadding?: boolean; 13 | /** Content */ 14 | children?: React.ReactNode; 15 | } 16 | 17 | /** CardSection */ 18 | const CardSection = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default CardSection; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/Code.tsx: -------------------------------------------------------------------------------- 1 | import { Code as MantineCode, MantineColor } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends DashBaseProps, BoxProps, StylesApiProps { 9 | /** Content */ 10 | children?: React.ReactNode; 11 | /** Key of `theme.colors` or any valid CSS color, controls `background-color` of the code, by default value is calculated based on color scheme */ 12 | color?: MantineColor; 13 | /** If set code will be rendered inside `pre`, `false` by default */ 14 | block?: boolean; 15 | } 16 | 17 | /** Code */ 18 | const Code = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default Code; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/appshell/AppShellAside.tsx: -------------------------------------------------------------------------------- 1 | import { AppShell } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Determines whether component should have a border, overrides `withBorder` prop on `AppShell` component */ 10 | withBorder?: boolean; 11 | /** Component `z-index`, by default inherited from the `AppShell` */ 12 | zIndex?: string | number; 13 | /** Content */ 14 | children: React.ReactNode; 15 | } 16 | 17 | /** AppShellAside */ 18 | const AppShellAside = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default AppShellAside; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/appshell/AppShellFooter.tsx: -------------------------------------------------------------------------------- 1 | import { AppShell } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Determines whether component should have a border, overrides `withBorder` prop on `AppShell` component */ 10 | withBorder?: boolean; 11 | /** Component `z-index`, by default inherited from the `AppShell` */ 12 | zIndex?: string | number; 13 | /** Content */ 14 | children: React.ReactNode; 15 | } 16 | 17 | /** AppShellFooter */ 18 | const AppShellFooter = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default AppShellFooter; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/appshell/AppShellHeader.tsx: -------------------------------------------------------------------------------- 1 | import { AppShell } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Determines whether component should have a border, overrides `withBorder` prop on `AppShell` component */ 10 | withBorder?: boolean; 11 | /** Component `z-index`, by default inherited from the `AppShell` */ 12 | zIndex?: string | number; 13 | /** Content */ 14 | children: React.ReactNode; 15 | } 16 | 17 | /** AppShellHeader */ 18 | const AppShellHeader = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default AppShellHeader; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/appshell/AppShellNavbar.tsx: -------------------------------------------------------------------------------- 1 | import { AppShell } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Determines whether component should have a border, overrides `withBorder` prop on `AppShell` component */ 10 | withBorder?: boolean; 11 | /** Component `z-index`, by default inherited from the `AppShell` */ 12 | zIndex?: string | number; 13 | /** Content */ 14 | children: React.ReactNode; 15 | } 16 | 17 | /** AppShellNavbar */ 18 | const AppShellNavbar = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default AppShellNavbar; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/button/ButtonGroup.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** `Button` components */ 10 | children?: React.ReactNode; 11 | /** Orientation of the group, `horizontal` by default */ 12 | orientation?: 'horizontal' | 'vertical'; 13 | /** `border-width` of the child `Button` components. Numbers are converted to rem. Default value in `1`. */ 14 | borderWidth?: number | string; 15 | } 16 | 17 | /** ButtonGroup */ 18 | const ButtonGroup = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default ButtonGroup; 32 | -------------------------------------------------------------------------------- /src/ts/components/core/button/ActionIconGroup.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** `ActionIcon` components only */ 10 | children?: React.ReactNode; 11 | /** Controls group orientation, `'horizontal'` by default */ 12 | orientation?: 'horizontal' | 'vertical'; 13 | /** `border-width` of the child `ActionIcon` components. Default value in `1` */ 14 | borderWidth?: number | string; 15 | } 16 | 17 | /** ActionIconGroup */ 18 | const ActionIconGroup = (props: Props) => { 19 | const { children, setProps, loading_state, ...others } = props; 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default ActionIconGroup; 32 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import json 2 | import io 3 | from setuptools import setup 4 | 5 | 6 | with open("package.json") as f: 7 | package = json.load(f) 8 | 9 | package_name = package["name"].replace(" ", "_").replace("-", "_") 10 | 11 | def read_req_file(req_type): 12 | with open(f"requires-{req_type}.txt", encoding="utf-8") as fp: 13 | requires = (line.strip() for line in fp) 14 | return [req for req in requires if req and not req.startswith("#")] 15 | 16 | setup( 17 | name=package_name, 18 | url=package["homepage"], 19 | version=package["version"], 20 | author=package["author"], 21 | author_email="snehilvj@outlook.com", 22 | packages=[package_name], 23 | include_package_data=True, 24 | license=package["license"], 25 | description=package.get("description", package_name), 26 | install_requires=read_req_file("install"), 27 | extras_require={ 28 | "dev": read_req_file("dev"), 29 | }, 30 | classifiers=[ 31 | "Framework :: Dash", 32 | "Framework :: Flask", 33 | ], 34 | long_description=io.open("README.md", encoding="utf-8").read(), 35 | long_description_content_type="text/markdown", 36 | ) 37 | -------------------------------------------------------------------------------- /src/ts/props/progress.ts: -------------------------------------------------------------------------------- 1 | import { MantineRadius, MantineSize } from '@mantine/core'; 2 | import { BoxProps } from './box'; 3 | import { StylesApiProps } from './styles'; 4 | 5 | export interface __ProgressRootProps extends BoxProps { 6 | /** Controls track height, `'md'` by default */ 7 | size?: MantineSize | (string & {}) | number; 8 | /** Key of `theme.radius` or any valid CSS value to set `border-radius`, `theme.defaultRadius` by default */ 9 | radius?: MantineRadius; 10 | /** Determines whether label text color should depend on `background-color`. If luminosity of the `color` prop is less than `theme.luminosityThreshold`, then `theme.white` will be used for text color, otherwise `theme.black`. Overrides `theme.autoContrast`. */ 11 | autoContrast?: boolean; 12 | /** Controls sections width transition duration, value is specified in ms, `100` by default */ 13 | transitionDuration?: number; 14 | /** Controls orientation default `'horizontal'` */ 15 | orientation?: 'horizontal' | 'vertical'; 16 | } 17 | export interface ProgressRootProps extends __ProgressRootProps, StylesApiProps { 18 | /** Content */ 19 | children?: React.ReactNode; 20 | } 21 | -------------------------------------------------------------------------------- /src/ts/components/core/timeline/TimelineItem.tsx: -------------------------------------------------------------------------------- 1 | import { MantineRadius } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | 7 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 8 | /** Item title, displayed next to the bullet */ 9 | title?: React.ReactNode; 10 | /** Content displayed below the title */ 11 | children?: React.ReactNode; 12 | /** React node that should be rendered inside the bullet – icon, image, avatar, etc. By default, large white dot is displayed. */ 13 | bullet?: React.ReactNode; 14 | /** Key of `theme.radius` or any valid CSS value to set `border-radius`, numbers are converted to rem, `'xl'` by default */ 15 | radius?: MantineRadius; 16 | /** Controls line border style, `'solid'` by default */ 17 | lineVariant?: 'solid' | 'dashed' | 'dotted'; 18 | } 19 | 20 | /** TimelineItem */ 21 | const TimelineItem = (props: Props) => { 22 | const { children, setProps, loading_state, ...others } = props; 23 | 24 | return <>{children}; 25 | }; 26 | 27 | export default TimelineItem; 28 | -------------------------------------------------------------------------------- /src/ts/components/extensions/nprogress/NavigationProgressProvider.tsx: -------------------------------------------------------------------------------- 1 | import { MantineColor } from '@mantine/core'; 2 | import { NavigationProgress } from '@mantine/nprogress'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import React from 'react'; 5 | 6 | interface Props extends DashBaseProps { 7 | /** Initial progress value, `0` by default */ 8 | initialProgress?: number; 9 | /** Key of `theme.colors` of any other valid CSS color, `theme.primaryColor` by default */ 10 | color?: MantineColor; 11 | /** Controls height of the progress bar */ 12 | size?: number; 13 | /** Step interval in ms, `500` by default */ 14 | stepInterval?: number; 15 | /** Determines whether the progress bar should be rendered within `Portal`, `true` by default */ 16 | withinPortal?: boolean; 17 | /** Progressbar z-index, `9999` by default */ 18 | zIndex?: React.CSSProperties['zIndex']; 19 | } 20 | 21 | /** NavigationProgressProvider */ 22 | const NavigationProgressProvider = (props: Props) => { 23 | const { setProps, loading_state, ...others } = props; 24 | 25 | return ; 26 | }; 27 | 28 | export default NavigationProgressProvider; 29 | -------------------------------------------------------------------------------- /src/ts/components/core/image/BackgroundImage.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | BackgroundImage as MantineBackgroundImage, 3 | MantineRadius, 4 | } from '@mantine/core'; 5 | import { BoxProps } from 'props/box'; 6 | import { DashBaseProps } from 'props/dash'; 7 | import { StylesApiProps } from 'props/styles'; 8 | import React, { useMemo } from 'react'; 9 | import { getLoadingState } from '../../../utils/dash3'; 10 | 11 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 12 | /** Key of `theme.radius` or any valid CSS value to set border-radius, numbers are converted to rem, `0` by default */ 13 | radius?: MantineRadius; 14 | /** Image url */ 15 | src: string; 16 | /** Content */ 17 | children?: React.ReactNode; 18 | } 19 | 20 | /** BackgroundImage */ 21 | const BackgroundImage = (props: Props) => { 22 | const { setProps, loading_state, children, ...others } = props; 23 | 24 | return ( 25 | 29 | {children} 30 | 31 | ); 32 | }; 33 | 34 | export default BackgroundImage; 35 | -------------------------------------------------------------------------------- /src/ts/components/core/Fieldset.tsx: -------------------------------------------------------------------------------- 1 | import { Fieldset as MantineFieldset, MantineRadius } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** disables all inputs and buttons inside the fieldset:*/ 10 | disabled?: boolean; 11 | /** Fieldset legend */ 12 | legend?: React.ReactNode; 13 | /** Key of `theme.radius` or any valid CSS value to set `border-radius`, `theme.defaultRadius` by default */ 14 | radius?: MantineRadius; 15 | /* Content */ 16 | children?: React.ReactNode; 17 | } 18 | 19 | /** Fieldset */ 20 | const Fieldset = (props: Props) => { 21 | const { children, setProps, loading_state, ...others } = props; 22 | 23 | return ( 24 | 28 | {children} 29 | 30 | ); 31 | }; 32 | 33 | export default Fieldset; 34 | -------------------------------------------------------------------------------- /src/ts/components/core/Breadcrumbs.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Breadcrumbs as MantineBreadcrumbs, 3 | MantineSpacing, 4 | } from '@mantine/core'; 5 | import { BoxProps } from 'props/box'; 6 | import { DashBaseProps } from 'props/dash'; 7 | import { StylesApiProps } from 'props/styles'; 8 | import React from 'react'; 9 | import { getLoadingState } from '../../utils/dash3'; 10 | 11 | export interface Props extends BoxProps, StylesApiProps, DashBaseProps { 12 | /** Separator between children, `'/'` by default */ 13 | separator?: React.ReactNode; 14 | /** Controls spacing between separator and breadcrumb, `'xs'` by default */ 15 | separatorMargin?: MantineSpacing; 16 | /** React nodes that should be separated with `separator` */ 17 | children: React.ReactNode; 18 | } 19 | 20 | /** Breadcrumbs */ 21 | const Breadcrumbs = (props: Props) => { 22 | const { children, setProps, loading_state, ...others } = props; 23 | 24 | return ( 25 | 29 | {children} 30 | 31 | ); 32 | }; 33 | 34 | export default Breadcrumbs; 35 | -------------------------------------------------------------------------------- /src/ts/components/core/Divider.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | MantineColor, 3 | Divider as MantineDivider, 4 | MantineSize, 5 | } from '@mantine/core'; 6 | import { BoxProps } from 'props/box'; 7 | import { DashBaseProps } from 'props/dash'; 8 | import { StylesApiProps } from 'props/styles'; 9 | import React from 'react'; 10 | 11 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 12 | /** Key of `theme.colors` or any valid CSS color value, by default value depends on color scheme */ 13 | color?: MantineColor; 14 | /** Controls width/height (depends on orientation), `'xs'` by default */ 15 | size?: MantineSize | number | (string & {}); 16 | /** Divider label, visible only when `orientation` is `horizontal` */ 17 | label?: React.ReactNode; 18 | /** Controls label position, `'left'` by default */ 19 | labelPosition?: 'left' | 'center' | 'right'; 20 | /** Controls orientation, `'horizontal'` by default */ 21 | orientation?: 'horizontal' | 'vertical'; 22 | } 23 | 24 | /** Divider */ 25 | const Divider = (props: Props) => { 26 | const { setProps, loading_state, ...others } = props; 27 | 28 | return ; 29 | }; 30 | 31 | export default Divider; 32 | -------------------------------------------------------------------------------- /src/ts/components/dates/DatesProvider.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | DatesProviderProps, 3 | DatesProvider as MantineDatesProvider, 4 | } from '@mantine/dates'; 5 | import dayjs from 'dayjs'; 6 | import React, { useEffect } from 'react'; 7 | import customParseFormat from 'dayjs/plugin/customParseFormat'; 8 | 9 | const REGISTERED = {}; 10 | 11 | interface Props extends DatesProviderProps { 12 | /** Unique ID to identify this component in Dash callbacks. */ 13 | id?: string; 14 | /** Update props to trigger callbacks. */ 15 | setProps: (props: Record) => void; 16 | } 17 | 18 | /** DatesProvider */ 19 | const DatesProvider = (props: Props) => { 20 | const { settings, children, setProps, ...others } = props; 21 | const { locale } = settings; 22 | 23 | if (!REGISTERED[locale]) { 24 | REGISTERED[locale] = true; 25 | const localeObject = window[`dayjs_locale_${locale}`]; 26 | if (localeObject) { 27 | dayjs.locale(locale, localeObject); 28 | } 29 | } 30 | 31 | return ( 32 | 33 | {children} 34 | 35 | ); 36 | }; 37 | 38 | export default DatesProvider; 39 | -------------------------------------------------------------------------------- /tests/test_burger.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, html, Output, Input, _dash_renderer, callback 2 | import dash_mantine_components as dmc 3 | 4 | _dash_renderer._set_react_version("18.2.0") 5 | 6 | 7 | def test_001bu_burger(dash_duo): 8 | app = Dash(__name__) 9 | 10 | app.layout = dmc.MantineProvider( 11 | html.Div( 12 | [dmc.Burger(id="burger-button", opened=False, lineSize=2, ), dmc.Text(id="burger-state", mt="md")] 13 | ) 14 | ) 15 | 16 | @callback(Output("burger-state", "children"), Input("burger-button", "opened")) 17 | def is_open(opened): 18 | return str(opened) 19 | 20 | dash_duo.start_server(app) 21 | 22 | # Wait for the app to load 23 | dash_duo.wait_for_text_to_equal("#burger-state", "False") 24 | 25 | # Open Burger 26 | burger = dash_duo.find_element("#burger-button") 27 | burger.click() 28 | 29 | # Verify the Burger opens when clicking the button 30 | dash_duo.wait_for_text_to_equal("#burger-state", "True") 31 | 32 | # Close Popover 33 | burger.click() 34 | 35 | # Verify the Burger closes when clicked again 36 | dash_duo.wait_for_text_to_equal("#burger-state", "False") 37 | 38 | assert dash_duo.get_logs() == [] 39 | 40 | -------------------------------------------------------------------------------- /src/ts/components/core/button/ActionIcon.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon as MantineActionIcon } from '@mantine/core'; 2 | import { ActionIconProps } from 'props/actionicon'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | 7 | interface Props extends ActionIconProps, DashBaseProps { 8 | /** An integer that represents the number of times that this element has been clicked on */ 9 | n_clicks?: number; 10 | } 11 | 12 | /** ActionIcon */ 13 | const ActionIcon = ({ 14 | children, 15 | setProps, 16 | loading_state, 17 | disabled, 18 | n_clicks = 0, 19 | ...others 20 | }: Props) => { 21 | const increment = () => { 22 | if (!disabled) { 23 | setProps({ 24 | n_clicks: n_clicks + 1, 25 | }); 26 | } 27 | }; 28 | 29 | return ( 30 | 36 | {children} 37 | 38 | ); 39 | }; 40 | 41 | export default ActionIcon; 42 | -------------------------------------------------------------------------------- /src/ts/props/button.ts: -------------------------------------------------------------------------------- 1 | import { MantineRadius, MantineSize } from '@mantine/core'; 2 | import React from 'react'; 3 | 4 | export interface __BaseButtonProps { 5 | /** Key of `theme.radius` or any valid CSS value to set border-radius. Numbers are converted to rem. `theme.defaultRadius` by default. */ 6 | radius?: MantineRadius; 7 | /** Sets `disabled` and `data-disabled` attributes on the button element */ 8 | disabled?: boolean; 9 | /** `X` icon `width` and `height`, `80%` by default */ 10 | iconSize?: number | string; 11 | /** Content rendered inside the button, for example `VisuallyHidden` with label for screen readers */ 12 | children?: React.ReactNode; 13 | /** Replaces default close icon. If set, `iconSize` prop is ignored. */ 14 | icon?: React.ReactNode; 15 | } 16 | 17 | export interface __CloseButtonProps extends __BaseButtonProps { 18 | /** Controls width and height of the button. Numbers are converted to rem. `'md'` by default. */ 19 | size?: MantineSize | (string & {}) | number; 20 | } 21 | 22 | export interface __ClearButtonProps extends __BaseButtonProps { 23 | /** Size of the button, by default value is based on input context */ 24 | size?: MantineSize | (string & {}); 25 | } 26 | -------------------------------------------------------------------------------- /src/ts/components/core/modal/Modal.tsx: -------------------------------------------------------------------------------- 1 | import { Modal as MantineModal } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { ModalProps } from 'props/modal'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React, { useEffect, useState } from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props 9 | extends ModalProps, 10 | Omit, 11 | DashBaseProps {} 12 | 13 | /** Modal */ 14 | const Modal = ({ 15 | children, 16 | setProps, 17 | loading_state, 18 | opened = false, 19 | ...others 20 | }: Props) => { 21 | const [open, setOpen] = useState(opened); 22 | 23 | useEffect(() => { 24 | setOpen(opened); 25 | }, [opened]); 26 | 27 | const onClose = () => { 28 | setOpen(false); 29 | setProps({ opened: false }); 30 | }; 31 | 32 | return ( 33 | 39 | {children} 40 | 41 | ); 42 | }; 43 | 44 | export default Modal; 45 | -------------------------------------------------------------------------------- /src/ts/components/core/tooltip/FloatingTooltip.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Tooltip } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { TooltipBaseProps } from 'props/tooltip'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends TooltipBaseProps, DashBaseProps { 9 | /** Offset from mouse in px, `10` by default */ 10 | offset?: number; 11 | /** Target box wrapper props */ 12 | boxWrapperProps?: BoxProps; 13 | } 14 | 15 | /** FloatingTooltip */ 16 | const FloatingTooltip = (props: Props) => { 17 | const { children, boxWrapperProps, setProps, loading_state, ...others } = 18 | props; 19 | const boxProps = { 20 | w: 'fit-content', 21 | key: 'tooltip-target', 22 | ...boxWrapperProps, 23 | }; 24 | 25 | return ( 26 | 30 | {children} 31 | 32 | ); 33 | }; 34 | 35 | FloatingTooltip.dashChildrenUpdate = true; 36 | 37 | export default FloatingTooltip; 38 | -------------------------------------------------------------------------------- /src/ts/props/overlay.ts: -------------------------------------------------------------------------------- 1 | import { MantineRadius } from '@mantine/core'; 2 | import { BoxProps } from './box'; 3 | import { StylesApiProps } from './styles'; 4 | 5 | export interface OverlayProps extends BoxProps, StylesApiProps { 6 | /** Controls overlay `background-color` opacity 0–1, disregarded when `gradient` prop is set, `0.6` by default */ 7 | backgroundOpacity?: number; 8 | /** Overlay `background-color`, `#000` by default */ 9 | color?: React.CSSProperties['backgroundColor']; 10 | /** Overlay background blur, `0` by default */ 11 | blur?: number | string; 12 | /** Changes overlay to gradient. If set, `color` prop is ignored */ 13 | gradient?: string; 14 | /** Overlay z-index, `200` by default */ 15 | zIndex?: string | number; 16 | /** Key of `theme.radius` or any valid CSS value to set border-radius, `0` by default */ 17 | radius?: MantineRadius; 18 | /** Content inside overlay */ 19 | children?: React.ReactNode; 20 | /** Determines whether content inside overlay should be vertically and horizontally centered, `false` by default */ 21 | center?: boolean; 22 | /** Determines whether overlay should have fixed position instead of absolute, `false` by default */ 23 | fixed?: boolean; 24 | } 25 | -------------------------------------------------------------------------------- /src/ts/components/core/Collapse.tsx: -------------------------------------------------------------------------------- 1 | import { Collapse as MantineCollapse } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../utils/dash3'; 6 | 7 | interface Props extends BoxProps, DashBaseProps { 8 | /** Opened state */ 9 | opened?: boolean; 10 | /** Transition duration in ms, `200` by default */ 11 | transitionDuration?: number; 12 | /** Transition timing function, default value is `ease` */ 13 | transitionTimingFunction?: string; 14 | /** Determines whether opacity should be animated, `true` by default */ 15 | animateOpacity?: boolean; 16 | /** Content */ 17 | children?: React.ReactNode; 18 | /** Keep element in DOM when collapsed, useful for nested collapses */ 19 | keepMounted?: boolean; 20 | } 21 | 22 | /** Collapse */ 23 | const Collapse = ({ 24 | setProps, 25 | loading_state, 26 | opened = false, 27 | ...others 28 | }: Props) => { 29 | return ( 30 | 35 | ); 36 | }; 37 | 38 | export default Collapse; 39 | -------------------------------------------------------------------------------- /src/ts/components/core/Highlight.tsx: -------------------------------------------------------------------------------- 1 | import { MantineColor, Highlight as MantineHighlight } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { TextProps } from 'props/text'; 4 | import React from 'react'; 5 | import { getLoadingState } from '../../utils/dash3'; 6 | 7 | interface Props extends DashBaseProps, TextProps { 8 | /** Substring or an array of substrings to highlight in `children` */ 9 | highlight: string | string[]; 10 | /** Key of `theme.colors` or any valid CSS color, passed to `Mark` component `color` prop, `yellow` by default */ 11 | color?: MantineColor | string; 12 | /** Styles applied to `mark` elements. Note CSS properties are camelCase, for example `highlightStyles={"backgroundColor": "blue"}` */ 13 | highlightStyles?: {}; 14 | /** Content */ 15 | children?: string; 16 | } 17 | 18 | /** Highlight */ 19 | const Highlight = (props: Props) => { 20 | const { children, setProps, loading_state, ...others } = props; 21 | 22 | return ( 23 | 27 | {children} 28 | 29 | ); 30 | }; 31 | 32 | export default Highlight; 33 | -------------------------------------------------------------------------------- /src/ts/components/core/grid/GridCol.tsx: -------------------------------------------------------------------------------- 1 | import { Grid, StyleProp } from '@mantine/core'; 2 | import { ColSpan } from '@mantine/core/lib/components/Grid/GridCol/GridCol'; 3 | import { BoxProps } from 'props/box'; 4 | import { DashBaseProps } from 'props/dash'; 5 | import { StylesApiProps } from 'props/styles'; 6 | import React from 'react'; 7 | import { getLoadingState } from '../../../utils/dash3'; 8 | 9 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 10 | /* Content */ 11 | children?: React.ReactNode; 12 | /** Column span, `12` by default */ 13 | span?: StyleProp; 14 | /** Column order, can be used to reorder columns at different viewport sizes */ 15 | order?: StyleProp; 16 | /** Column offset on the left side – number of columns that should be left empty before this column */ 17 | offset?: StyleProp; 18 | } 19 | 20 | /** GridCol */ 21 | const GridCol = (props: Props) => { 22 | const { children, setProps, loading_state, ...others } = props; 23 | 24 | return ( 25 | 29 | {children} 30 | 31 | ); 32 | }; 33 | 34 | export default GridCol; 35 | -------------------------------------------------------------------------------- /src/ts/components/core/Stack.tsx: -------------------------------------------------------------------------------- 1 | import { MantineSpacing, Stack as MantineStack } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /* Content */ 10 | children?: React.ReactNode; 11 | /** Key of `theme.spacing` or any valid CSS value to set `gap` property, numbers are converted to rem, `'md'` by default */ 12 | gap?: MantineSpacing; 13 | /** Controls `align-items` CSS property, `'stretch'` by default */ 14 | align?: React.CSSProperties['alignItems']; 15 | /** Controls `justify-content` CSS property, `'flex-start'` by default */ 16 | justify?: React.CSSProperties['justifyContent']; 17 | } 18 | 19 | /** Stack */ 20 | const Stack = (props: Props) => { 21 | const { children, setProps, loading_state, ...others } = props; 22 | 23 | return ( 24 | 28 | {children} 29 | 30 | ); 31 | }; 32 | 33 | export default Stack; 34 | -------------------------------------------------------------------------------- /src/ts/components/core/progress/Progress.tsx: -------------------------------------------------------------------------------- 1 | import { MantineColor, Progress as MantineProgress } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { __ProgressRootProps } from 'props/progress'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | export interface Props 9 | extends __ProgressRootProps, 10 | DashBaseProps, 11 | StylesApiProps { 12 | /** Value of the progress */ 13 | value: number; 14 | /** Key of `theme.colors` or any valid CSS value, `theme.primaryColor` by default */ 15 | color?: MantineColor; 16 | /** Determines whether the section should have stipes, `false` by default */ 17 | striped?: boolean; 18 | /** Determines whether the sections stripes should be animated, if set, `striped` prop is ignored, `false` by default */ 19 | animated?: boolean; 20 | } 21 | 22 | /** Progress */ 23 | const Progress = (props: Props) => { 24 | const { setProps, loading_state, ...others } = props; 25 | 26 | return ( 27 | 31 | ); 32 | }; 33 | 34 | export default Progress; 35 | -------------------------------------------------------------------------------- /.github/workflows/python-test.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | tags: 7 | - v* 8 | pull_request: 9 | branches: [master] 10 | 11 | jobs: 12 | test-react: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-python@v3 17 | with: 18 | python-version: 3.12 19 | - name: 'Setup Chrome and chromedriver' 20 | uses: nanasess/setup-chromedriver@v2 21 | 22 | - name: 'Setup chromedriver environment' 23 | run: | 24 | export DISPLAY=:99 25 | chromedriver --url-base=/wd/hub & 26 | - name: Start XVFB 27 | run: Xvfb :99 & 28 | 29 | - name: Setup uv 30 | run: | 31 | curl -LsSf https://astral.sh/uv/install.sh | sh 32 | uv venv 33 | 34 | - name: Install package 35 | run: | 36 | source .venv/bin/activate 37 | uv pip install --upgrade pip 38 | uv pip install wheel 39 | uv pip install ".[dev]" 40 | npm i 41 | npm run build 42 | timeout-minutes: 20 43 | - name: Run tests 44 | run: | 45 | source .venv/bin/activate 46 | uv pip install -e . 47 | pytest --headless 48 | 49 | -------------------------------------------------------------------------------- /src/ts/components/core/image/Image.tsx: -------------------------------------------------------------------------------- 1 | import { Image as MantineImage, MantineRadius } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React, { useMemo } from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Key of `theme.radius` or any valid CSS value to set `border-radius`, `0` by default */ 10 | radius?: MantineRadius; 11 | /** Controls `object-fit` style, `'cover'` by default */ 12 | fit?: React.CSSProperties['objectFit']; 13 | /** Image url that will be used as a fallback in case `src` prop is not set or image cannot be loaded */ 14 | fallbackSrc?: string; 15 | /** Image url */ 16 | src?: any; 17 | /** Image alt text, used as title for placeholder if image was not loaded */ 18 | alt?: string; 19 | } 20 | 21 | /** Image */ 22 | const Image = (props: Props) => { 23 | const { setProps, loading_state, ...others } = props; 24 | 25 | return ( 26 | 30 | ); 31 | }; 32 | 33 | export default Image; 34 | -------------------------------------------------------------------------------- /src/ts/components/core/table/TableScrollContainer.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { ScrollAreaProps } from 'props/scrollarea'; 5 | import React from 'react'; 6 | 7 | interface Props extends BoxProps, DashBaseProps { 8 | /** `min-width` of the `Table` at which it should become scrollable */ 9 | minWidth: React.CSSProperties['minWidth']; 10 | /** `max-height` of the `Table` at which it should become scrollable */ 11 | maxHeight?: React.CSSProperties['maxHeight']; 12 | /** Type of the scroll container, `native` to use native scrollbars, `scrollarea` to use `ScrollArea` component, `scrollarea` by default */ 13 | type?: 'native' | 'scrollarea'; 14 | /** Props passed down to `ScrollArea` component, not applicable with `type="native"` */ 15 | scrollAreaProps?: ScrollAreaProps; 16 | /** Content rendered inside the scroll container */ 17 | children?: React.ReactNode; 18 | } 19 | 20 | /** Table ScrollArea */ 21 | const TableScrollContainer = (props: Props) => { 22 | const { setProps, children, ...others } = props; 23 | 24 | return ( 25 | {children} 26 | ); 27 | }; 28 | 29 | export default TableScrollContainer; 30 | -------------------------------------------------------------------------------- /tests/test_highlight.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, html 2 | import dash_mantine_components as dmc 3 | 4 | 5 | def test_001hi_highlight_component(dash_duo): 6 | app = Dash(__name__) 7 | 8 | app.layout = dmc.MantineProvider( 9 | dmc.Highlight( 10 | "You can change styles of highlighted parts", 11 | highlight=["highlighted"], 12 | highlightStyles={ 13 | "backgroundColor": "blue", 14 | "color": "white", 15 | }, 16 | id="highlight-text", 17 | ) 18 | ) 19 | 20 | dash_duo.start_server(app) 21 | 22 | # Wait for app to load 23 | dash_duo.wait_for_text_to_equal( 24 | "#highlight-text", "You can change styles of highlighted parts" 25 | ) 26 | 27 | # Find the highlighted element within the dmc.Highlight component 28 | highlight_element = dash_duo.find_element("#highlight-text mark") 29 | 30 | # Check if the "highlighted" text has a blue background and white text 31 | assert ( 32 | highlight_element.value_of_css_property("background-color") 33 | == "rgba(0, 0, 255, 1)" 34 | ) # Blue background 35 | assert ( 36 | highlight_element.value_of_css_property("color") == "rgba(255, 255, 255, 1)" 37 | ) # White text 38 | 39 | assert dash_duo.get_logs() == [] 40 | -------------------------------------------------------------------------------- /src/ts/components/core/color/ColorInput.tsx: -------------------------------------------------------------------------------- 1 | import { ColorInput as MantineColorInput } from '@mantine/core'; 2 | import { useDidUpdate } from '@mantine/hooks'; 3 | import { ColorInputProps } from 'props/color'; 4 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 5 | import React, { useState } from 'react'; 6 | import { setPersistence, getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends ColorInputProps, PersistenceProps, DashBaseProps {} 9 | 10 | /** ColorInput */ 11 | const ColorInput = (props: Props) => { 12 | const { 13 | setProps, 14 | loading_state, 15 | value, 16 | persistence, 17 | persisted_props, 18 | persistence_type, 19 | ...others 20 | } = props; 21 | 22 | const [color, setColor] = useState(value); 23 | 24 | useDidUpdate(() => { 25 | setProps({ value: color }); 26 | }, [color]); 27 | 28 | useDidUpdate(() => { 29 | setColor(value); 30 | }, [value]); 31 | 32 | return ( 33 | 39 | ); 40 | }; 41 | 42 | setPersistence(ColorInput); 43 | 44 | export default ColorInput; 45 | -------------------------------------------------------------------------------- /src/ts/components/core/color/ColorPicker.tsx: -------------------------------------------------------------------------------- 1 | import { ColorPicker as MantineColorPicker } from '@mantine/core'; 2 | import { useDidUpdate } from '@mantine/hooks'; 3 | import { ColorPickerProps } from 'props/color'; 4 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 5 | import React, { useState } from 'react'; 6 | import { setPersistence, getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends ColorPickerProps, PersistenceProps, DashBaseProps {} 9 | 10 | /** ColorPicker */ 11 | const ColorPicker = (props: Props) => { 12 | const { 13 | setProps, 14 | loading_state, 15 | value, 16 | persistence, 17 | persisted_props, 18 | persistence_type, 19 | ...others 20 | } = props; 21 | 22 | const [color, setColor] = useState(value); 23 | 24 | useDidUpdate(() => { 25 | setProps({ value: color }); 26 | }, [color]); 27 | 28 | useDidUpdate(() => { 29 | setColor(value); 30 | }, [value]); 31 | 32 | return ( 33 | 39 | ); 40 | }; 41 | 42 | setPersistence(ColorPicker); 43 | 44 | export default ColorPicker; 45 | -------------------------------------------------------------------------------- /src/ts/components/styles/DirectionProvider.tsx: -------------------------------------------------------------------------------- 1 | import { DirectionProvider as MantineDirectionProvider } from '@mantine/core'; 2 | import React, { useEffect } from 'react'; 3 | import { PersistenceProps } from 'props/dash'; 4 | import { setPersistence } from '../../utils/dash3'; 5 | 6 | interface Props extends PersistenceProps { 7 | /** Your application */ 8 | children: React.ReactNode; 9 | /** Direction `ltr` by default */ 10 | direction?: 'rtl' | 'ltr'; 11 | /** Unique ID to identify this component in Dash callbacks. */ 12 | id?: string; 13 | } 14 | 15 | /* DirectionProvider set direction for all components inside it */ 16 | const DirectionProvider = (props: Props) => { 17 | const { 18 | children, 19 | direction = 'ltr', 20 | persistence, 21 | persisted_props, 22 | persistence_type, 23 | ...others 24 | } = props; 25 | 26 | useEffect(() => { 27 | if (direction && typeof document !== 'undefined') { 28 | document.documentElement.dir = direction; 29 | } 30 | }, [direction]); 31 | 32 | return ( 33 | 34 | {children} 35 | 36 | ); 37 | }; 38 | 39 | setPersistence(DirectionProvider, ['direction']); 40 | 41 | export default DirectionProvider; 42 | -------------------------------------------------------------------------------- /src/ts/components/core/Title.tsx: -------------------------------------------------------------------------------- 1 | import { Title as MantineTitle, TitleOrder, TitleSize } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 9 | /** Determines which tag will be used (h1-h6), controls `font-size` style if `size` prop is not set, `1` by default */ 10 | order?: TitleOrder; 11 | /** Changes title size, if not set, then size is controlled by `order` prop */ 12 | size?: TitleSize; 13 | /** Number of lines after which Text will be truncated */ 14 | lineClamp?: number; 15 | /** Controls `text-wrap` property, `'wrap'` by default */ 16 | textWrap?: 'wrap' | 'nowrap' | 'balance' | 'pretty' | 'stable'; 17 | /** Content */ 18 | children?: React.ReactNode; 19 | } 20 | 21 | /** Title */ 22 | const Title = (props: Props) => { 23 | const { children, setProps, loading_state, ...others } = props; 24 | 25 | return ( 26 | 30 | {children} 31 | 32 | ); 33 | }; 34 | 35 | export default Title; 36 | -------------------------------------------------------------------------------- /src/ts/components/core/SimpleGrid.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | SimpleGrid as MantineSimpleGrid, 3 | MantineSpacing, 4 | StyleProp, 5 | } from '@mantine/core'; 6 | import { BoxProps } from 'props/box'; 7 | import { DashBaseProps } from 'props/dash'; 8 | import { StylesApiProps } from 'props/styles'; 9 | import React from 'react'; 10 | import { getLoadingState } from '../../utils/dash3'; 11 | 12 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 13 | /* Content */ 14 | children?: React.ReactNode; 15 | /** Number of columns, `1` by default */ 16 | cols?: StyleProp; 17 | /** Spacing between columns, `'md'` by default */ 18 | spacing?: StyleProp; 19 | /** Spacing between rows, `'md'` by default */ 20 | verticalSpacing?: StyleProp; 21 | /** Determines typeof of queries that are used for responsive styles, 'media' by default */ 22 | type?: 'media' | 'container'; 23 | } 24 | 25 | /** SimpleGrid */ 26 | const SimpleGrid = (props: Props) => { 27 | const { children, setProps, loading_state, ...others } = props; 28 | 29 | return ( 30 | 34 | {children} 35 | 36 | ); 37 | }; 38 | 39 | export default SimpleGrid; 40 | -------------------------------------------------------------------------------- /src/ts/utils/combobox.ts: -------------------------------------------------------------------------------- 1 | export const filterSelected = (options, values) => { 2 | if ( 3 | !options || 4 | options.length === 0 || 5 | values === null || 6 | values === undefined 7 | ) { 8 | return null; 9 | } 10 | 11 | const extractValues = (optionList) => { 12 | let extractedValues = []; 13 | 14 | for (const option of optionList) { 15 | if (typeof option === 'string') { 16 | extractedValues.push(option); 17 | } else if ('value' in option && 'label' in option) { 18 | extractedValues.push(option.value); 19 | } else if ('group' in option && 'items' in option) { 20 | extractedValues = extractedValues.concat( 21 | extractValues(option.items) 22 | ); 23 | } 24 | } 25 | 26 | return extractedValues; 27 | }; 28 | 29 | // Extract all valid option values 30 | const optionValues = extractValues(options); 31 | 32 | if (Array.isArray(values)) { 33 | // Return filtered array if values is an array (for MultiSelect and TagsInput) 34 | return values.filter((value) => optionValues.includes(value)); 35 | } else { 36 | // Return a single value if values is not an array (for Select) 37 | return optionValues.includes(values) ? values : null; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/ts/components/core/Container.tsx: -------------------------------------------------------------------------------- 1 | import { Container as MantineContainer, MantineSize } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /* Content */ 10 | children?: React.ReactNode; 11 | /** Sets `max-width` of the container, value is not responsive – it is the same for all screen sizes. Numbers are converted to rem. Ignored when `fluid` prop is set. `'md'` by default */ 12 | size?: MantineSize | (string & {}) | number; 13 | /** Determines whether the container should take 100% of its parent width. If set, `size` prop is ignored. `false` by default. */ 14 | fluid?: boolean; 15 | /** Centering strategy. Default value: 'block'*/ 16 | strategy?: 'block' | 'grid'; 17 | } 18 | 19 | /** Container */ 20 | const Container = (props: Props) => { 21 | const { children, setProps, loading_state, ...others } = props; 22 | 23 | return ( 24 | 28 | {children} 29 | 30 | ); 31 | }; 32 | 33 | export default Container; 34 | -------------------------------------------------------------------------------- /tests/test_switch.py: -------------------------------------------------------------------------------- 1 | import dash_mantine_components as dmc 2 | from dash_iconify import DashIconify 3 | from dash import Dash, Input, Output, _dash_renderer, clientside_callback 4 | _dash_renderer._set_react_version("18.2.0") 5 | 6 | 7 | def test_001sw_switch(dash_duo): 8 | theme_toggle = dmc.Switch( 9 | offLabel=DashIconify(icon="radix-icons:sun", width=15, color=dmc.DEFAULT_THEME["colors"]["yellow"][8]), 10 | onLabel=DashIconify(icon="fa6-regular:moon", width=15, color=dmc.DEFAULT_THEME["colors"]["yellow"][6]), 11 | id="color-scheme-switch", 12 | ) 13 | 14 | app = Dash() 15 | 16 | app.layout = dmc.MantineProvider( 17 | [theme_toggle, dmc.Text("Your page content")], 18 | id='app-theme' 19 | ) 20 | 21 | clientside_callback( 22 | """ 23 | (switchOn) => { 24 | return [switchOn ? 'dark' : 'light', dash_clientside.no_update] 25 | } 26 | """, 27 | Output('app-theme', 'forceColorScheme'), 28 | Output("color-scheme-switch", "id"), 29 | Input("color-scheme-switch", "checked"), 30 | ) 31 | 32 | dash_duo.start_server(app) 33 | 34 | # find the sun icon (initial state) 35 | icon = dash_duo.find_element(".iconify.iconify--radix-icons") 36 | 37 | icon.click() 38 | 39 | # find the moon icon 40 | icon = dash_duo.find_element(".iconify.iconify--fa6-regular") 41 | 42 | assert dash_duo.get_logs() == [] 43 | -------------------------------------------------------------------------------- /src/ts/components/core/progress/ProgressSection.tsx: -------------------------------------------------------------------------------- 1 | import { MantineColor, Progress } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Value of the section in 0–100 range */ 10 | value: number; 11 | /** Determines whether `aria-*` props should be added to the root element, `true` by default */ 12 | withAria?: boolean; 13 | /** Key of `theme.colors` or any valid CSS value, `theme.primaryColor` by default */ 14 | color?: MantineColor; 15 | /** Determines whether the section should have stipes, `false` by default */ 16 | striped?: boolean; 17 | /** Determines whether the sections stripes should be animated, if set, `striped` prop is ignored, `false` by default */ 18 | animated?: boolean; 19 | /** Content */ 20 | children?: React.ReactNode; 21 | } 22 | 23 | /** ProgressSection */ 24 | const ProgressSection = (props: Props) => { 25 | const { setProps, loading_state, ...others } = props; 26 | 27 | return ( 28 | 32 | ); 33 | }; 34 | 35 | export default ProgressSection; 36 | -------------------------------------------------------------------------------- /src/ts/components/extensions/codehighlight/CodeHighlight.tsx: -------------------------------------------------------------------------------- 1 | import { BoxProps } from 'props/box'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { StylesApiProps } from 'props/styles'; 4 | import React, { Suspense } from 'react'; 5 | 6 | // eslint-disable-next-line no-inline-comments 7 | const LazyCodeHighlight = React.lazy( 8 | () => 9 | import( 10 | /* webpackChunkName: "CodeHighlight" */ './fragments/CodeHighlight' 11 | ) 12 | ); 13 | 14 | export interface Props extends BoxProps, StylesApiProps, DashBaseProps { 15 | /** Code to highlight */ 16 | code: string; 17 | /** Code language, `'tsx'` by default */ 18 | language: string; 19 | /** Determines whether copy button should be displayed, `true` by default */ 20 | withCopyButton?: boolean; 21 | /** Copy tooltip label, `'Copy code'` by default */ 22 | copyLabel?: string; 23 | /** Copied tooltip label, `'Copied'` by default */ 24 | copiedLabel?: string; 25 | /** Determines whether code should be highlighted only after component is mounted to the dom (disables code highlight on server), `false` by default */ 26 | highlightOnClient?: boolean; 27 | } 28 | 29 | /** CodeHighlight */ 30 | const CodeHighlight = (props: Props) => { 31 | return ( 32 | 33 | 34 | 35 | ); 36 | }; 37 | 38 | export default CodeHighlight; 39 | -------------------------------------------------------------------------------- /tests/test_segmented_control.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, html, Output, Input, _dash_renderer 2 | import dash_mantine_components as dmc 3 | 4 | _dash_renderer._set_react_version("18.2.0") 5 | 6 | 7 | def test_001se_segmented_control(dash_duo): 8 | app = Dash() 9 | app.layout = dmc.MantineProvider( 10 | [ 11 | dmc.SegmentedControl( 12 | id="segmented", 13 | data=[ 14 | {"label": "a", "value": "a"}, 15 | {"label": "b", "value": "b", "disabled": True}, 16 | {"label": "c", "value": "c"}, 17 | ], 18 | value="a", 19 | ), 20 | html.Div(id="output"), 21 | ] 22 | ) 23 | 24 | @app.callback( 25 | Output("output", "children"), 26 | Input("segmented", "value"), 27 | ) 28 | def update(choice): 29 | return f"{choice=}" 30 | 31 | dash_duo.start_server(app) 32 | 33 | # Wait for the app to load 34 | dash_duo.wait_for_text_to_equal("#output", "choice='a'") 35 | 36 | option_b = dash_duo.find_element("input[value='b']") 37 | 38 | # Verify that "b" is disabled 39 | assert option_b.get_attribute("disabled") == "true" 40 | 41 | option_c = dash_duo.find_element("input[value='c']").find_element_by_xpath("./..") 42 | option_c.click() 43 | dash_duo.wait_for_text_to_equal("#output", "choice='c'") 44 | 45 | assert dash_duo.get_logs() == [] 46 | -------------------------------------------------------------------------------- /src/ts/components/core/RingProgress.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | MantineColor, 3 | RingProgress as MantineRingProgress, 4 | } from '@mantine/core'; 5 | import { BoxProps } from 'props/box'; 6 | import { DashBaseProps } from 'props/dash'; 7 | import { StylesApiProps } from 'props/styles'; 8 | import React from 'react'; 9 | import { getLoadingState } from '../../utils/dash3'; 10 | 11 | interface RingProgressSection { 12 | value: number; 13 | color: MantineColor; 14 | tooltip?: React.ReactNode; 15 | } 16 | 17 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 18 | /** Label displayed in the center of the ring */ 19 | label?: React.ReactNode; 20 | /** Ring thickness */ 21 | thickness?: number; 22 | /** Width and height of the progress ring */ 23 | size?: number; 24 | /** Sets whether the edges of the progress circle are rounded */ 25 | roundCaps?: boolean; 26 | /** Ring sections */ 27 | sections: RingProgressSection[]; 28 | /** Color of the root section, key of theme.colors or CSS color value */ 29 | rootColor?: MantineColor; 30 | } 31 | 32 | /** RingProgress */ 33 | const RingProgress = (props: Props) => { 34 | const { setProps, loading_state, ...others } = props; 35 | 36 | return ( 37 | 41 | ); 42 | }; 43 | 44 | export default RingProgress; 45 | -------------------------------------------------------------------------------- /src/ts/components/dates/TimeGrid.tsx: -------------------------------------------------------------------------------- 1 | import { TimeGrid as MantineTimeGrid, getTimeRange } from '@mantine/dates'; 2 | import { useDidUpdate } from '@mantine/hooks'; 3 | import { BoxProps } from 'props/box'; 4 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 5 | import { TimeGridProps } from 'props/dates'; 6 | import { StylesApiProps } from 'props/styles'; 7 | import React, { useState } from 'react'; 8 | import { setPersistence } from '../../utils/dash3'; 9 | 10 | interface Props extends DashBaseProps, PersistenceProps, TimeGridProps { 11 | /** Value for controlled component */ 12 | value?: string; 13 | } 14 | 15 | /** TimeGrid captures time value from the user with a predefined set of options */ 16 | const TimeGrid = ({ 17 | setProps, 18 | value = '', 19 | timeRangeData, 20 | data, 21 | persistence, 22 | persisted_props, 23 | persistence_type, 24 | ...others 25 | }: Props) => { 26 | const [time, setTime] = useState(value); 27 | 28 | useDidUpdate(() => { 29 | setProps({ value: time }); 30 | }, [time]); 31 | 32 | useDidUpdate(() => { 33 | setTime(value); 34 | }, [value]); 35 | 36 | return ( 37 | 43 | ); 44 | }; 45 | 46 | setPersistence(TimeGrid); 47 | 48 | export default TimeGrid; 49 | -------------------------------------------------------------------------------- /src/ts/components/charts/fragments/BubbleChart.tsx: -------------------------------------------------------------------------------- 1 | import { BubbleChart as MantineBubbleChart } from '@mantine/charts'; 2 | import '@mantine/charts/styles.css'; 3 | import React from 'react'; 4 | import { getScatterClickData, isEventValid } from '../../../utils/charts'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | import { parseFuncProps } from '../../../utils/prop-functions'; 7 | import { Props } from '../BubbleChart'; 8 | 9 | /** BubbleChart */ 10 | const BubbleChart = ({ 11 | setProps, 12 | loading_state, 13 | clickData, 14 | hoverData, 15 | scatterProps, 16 | data, 17 | dataKey, 18 | range, 19 | ...others 20 | }: Props) => { 21 | const onClick = (ev) => { 22 | if (isEventValid(ev)) { 23 | setProps({ clickData: getScatterClickData(ev) }); 24 | } 25 | }; 26 | 27 | const onMouseOver = (ev) => { 28 | if (isEventValid(ev)) { 29 | setProps({ hoverData: getScatterClickData(ev) }); 30 | } 31 | }; 32 | 33 | const newProps = { ...scatterProps, onClick, onMouseOver }; 34 | 35 | return ( 36 | 44 | ); 45 | }; 46 | 47 | export default BubbleChart; 48 | -------------------------------------------------------------------------------- /src/ts/components/core/LoadingOverlay.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingOverlay as MantineLoadingOverlay } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { LoaderProps } from 'props/loader'; 5 | import { OverlayProps } from 'props/overlay'; 6 | import { StylesApiProps } from 'props/styles'; 7 | import { TransitionProps } from 'props/transition'; 8 | import React from 'react'; 9 | import { getLoadingState } from '../../utils/dash3'; 10 | 11 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 12 | /** Props passed down to `Transition` component, `{ transition: 'fade', duration: 0 }` by default */ 13 | transitionProps?: TransitionProps; 14 | /** Props passed down to `Loader` component */ 15 | loaderProps?: LoaderProps; 16 | /** Props passed down to `Overlay` component */ 17 | overlayProps?: OverlayProps; 18 | /** Determines whether the overlay should be visible, `false` by default */ 19 | visible?: boolean; 20 | /** Controls overlay `z-index`, `400` by default */ 21 | zIndex?: string | number; 22 | } 23 | 24 | /** LoadingOverlay */ 25 | const LoadingOverlay = (props: Props) => { 26 | const { setProps, loading_state, ...others } = props; 27 | 28 | return ( 29 | 33 | ); 34 | }; 35 | 36 | export default LoadingOverlay; 37 | -------------------------------------------------------------------------------- /tests/test_hovercard.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, html, _dash_renderer 2 | import dash_mantine_components as dmc 3 | 4 | _dash_renderer._set_react_version("18.2.0") 5 | 6 | def test_001ho_hovercard(dash_duo): 7 | app = Dash(__name__) 8 | 9 | def make_component(boxWrapperProps, id): 10 | return dmc.HoverCard( 11 | [ 12 | dmc.HoverCardTarget( 13 | dmc.Button("trigger", fullWidth=True), 14 | boxWrapperProps=boxWrapperProps, 15 | ), 16 | dmc.HoverCardDropdown(["hover content"]), 17 | ], id=id 18 | ) 19 | 20 | app.layout = dmc.MantineProvider( 21 | html.Div([ 22 | make_component(None, "hover1"), 23 | make_component({"w": "100%"}, "hover2") 24 | ], style={"color": "yellow"}) 25 | ) 26 | 27 | dash_duo.start_server(app) 28 | 29 | 30 | # Locate the first component 31 | element = dash_duo.find_element("#hover1-target") 32 | 33 | # Verify the HoverTargetCard has the default style 34 | style_attribute = element.get_attribute("style") 35 | assert style_attribute == "width: fit-content;" 36 | 37 | # Locate the second component 38 | element = dash_duo.find_element("#hover2-target") 39 | 40 | # Verify the HoverTargetCard has the styles passed from boxWrapperProps 41 | style_attribute = element.get_attribute("style") 42 | assert style_attribute == "width: 100%;" 43 | 44 | assert dash_duo.get_logs() == [] -------------------------------------------------------------------------------- /src/ts/components/core/tabs/TabsTab.tsx: -------------------------------------------------------------------------------- 1 | import { MantineColor, Tabs } from '@mantine/core'; 2 | import { Props as UnstyledButtonProps } from 'components/core/button/UnstyledButton'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props 9 | extends Omit< 10 | UnstyledButtonProps, 11 | 'n_clicks' | 'variant' | 'classNames' | 'styles' | 'vars' 12 | >, 13 | DashBaseProps, 14 | StylesApiProps { 15 | /** Value of associated panel */ 16 | value: string; 17 | /** Tab label */ 18 | children?: React.ReactNode; 19 | /** Content displayed on the right side of the label, for example, icon */ 20 | rightSection?: React.ReactNode; 21 | /** Content displayed on the left side of the label, for example, icon */ 22 | leftSection?: React.ReactNode; 23 | /** Key of `theme.colors` or any valid CSS color, controls control color based on `variant` */ 24 | color?: MantineColor; 25 | } 26 | 27 | /** TabsTab */ 28 | const TabsTab = (props: Props) => { 29 | const { children, setProps, loading_state, ...others } = props; 30 | 31 | return ( 32 | 36 | {children} 37 | 38 | ); 39 | }; 40 | 41 | export default TabsTab; 42 | -------------------------------------------------------------------------------- /src/ts/components/core/Blockquote.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Blockquote as MantineBlockquote, 3 | MantineColor, 4 | MantineRadius, 5 | } from '@mantine/core'; 6 | import { BoxProps } from 'props/box'; 7 | import { DashBaseProps } from 'props/dash'; 8 | import { StylesApiProps } from 'props/styles'; 9 | import { getLoadingState } from '../../utils/dash3'; 10 | 11 | import React from 'react'; 12 | 13 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 14 | /** Blockquote icon, displayed on the top left */ 15 | icon?: React.ReactNode; 16 | /** Controls icon `width` and `height`, numbers are converted to rem, `40` by default */ 17 | iconSize?: number | string; 18 | /** Key of `theme.colors` or any valid CSS color, `theme.primaryColor` by default */ 19 | color?: MantineColor; 20 | /** Key of `theme.radius` or any valid CSS value to set `border-radius`, `theme.defaultRadius` by default */ 21 | radius?: MantineRadius; 22 | /** Reference to a cited quote */ 23 | cite?: React.ReactNode; 24 | /** Content */ 25 | children?: React.ReactNode; 26 | } 27 | 28 | /** Blockquote */ 29 | const Blockquote = (props: Props) => { 30 | const { children, setProps, loading_state, ...others } = props; 31 | 32 | return ( 33 | 37 | {children} 38 | 39 | ); 40 | }; 41 | 42 | export default Blockquote; 43 | -------------------------------------------------------------------------------- /src/ts/components/core/button/UnstyledButton.tsx: -------------------------------------------------------------------------------- 1 | import { UnstyledButton as MantineUnstyledButton } from '@mantine/core'; 2 | import { BoxComponentProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | export interface Props 9 | extends Omit, 10 | DashBaseProps, 11 | StylesApiProps { 12 | /** Button content */ 13 | children?: React.ReactNode; 14 | /** An integer that represents the number of times that this element has been clicked on */ 15 | n_clicks?: number; 16 | /** Indicates disabled state */ 17 | disabled?: boolean; 18 | } 19 | 20 | /** UnstyledButton */ 21 | const UnstyledButton = ({ 22 | children, 23 | setProps, 24 | loading_state, 25 | disabled, 26 | n_clicks = 0, 27 | ...others 28 | }: Props) => { 29 | const increment = () => { 30 | if (!disabled) { 31 | setProps({ 32 | n_clicks: n_clicks + 1, 33 | }); 34 | } 35 | }; 36 | 37 | return ( 38 | 44 | {children} 45 | 46 | ); 47 | }; 48 | 49 | export default UnstyledButton; 50 | -------------------------------------------------------------------------------- /src/ts/components/core/card/Card.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Card as MantineCard, 3 | MantineRadius, 4 | MantineShadow, 5 | MantineSpacing, 6 | } from '@mantine/core'; 7 | import { BoxProps } from 'props/box'; 8 | import { DashBaseProps } from 'props/dash'; 9 | import { StylesApiProps } from 'props/styles'; 10 | import React from 'react'; 11 | import { getLoadingState } from '../../../utils/dash3'; 12 | 13 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 14 | /** Key of `theme.shadows` or any valid CSS value to set `box-shadow`, `none` by default */ 15 | shadow?: MantineShadow; 16 | /** Key of `theme.radius` or any valid CSS value to set border-radius, numbers are converted to rem, `theme.defaultRadius` by default */ 17 | radius?: MantineRadius; 18 | /** Determines whether the card should have border, border color depends on color scheme, `false` by default */ 19 | withBorder?: boolean; 20 | /** Controls `padding`, key of `theme.spacing` or any valid CSS value, `'md'` by default */ 21 | padding?: MantineSpacing; 22 | /** Card content */ 23 | children?: React.ReactNode; 24 | } 25 | 26 | /** Card */ 27 | const Card = (props: Props) => { 28 | const { children, setProps, loading_state, ...others } = props; 29 | 30 | return ( 31 | 35 | {children} 36 | 37 | ); 38 | }; 39 | 40 | export default Card; 41 | -------------------------------------------------------------------------------- /src/ts/props/scrollarea.ts: -------------------------------------------------------------------------------- 1 | import { BoxProps } from 'props/box'; 2 | import { StylesApiProps } from 'props/styles'; 3 | import React from 'react'; 4 | 5 | export interface ScrollAreaProps extends BoxProps, StylesApiProps { 6 | /** Scrollbar size, any valid CSS value for width/height, numbers are converted to rem, default value is 0.75rem */ 7 | scrollbarSize?: number | string; 8 | /** 9 | * Defines scrollbars behavior, `hover` by default 10 | * - `hover` – scrollbars are visible when mouse is over the scroll area 11 | * - `scroll` – scrollbars are visible when the scroll area is scrolled 12 | * - `always` – scrollbars are always visible 13 | * - `never` – scrollbars are always hidden 14 | * - `auto` – similar to `overflow: auto` – scrollbars are always visible when the content is overflowing 15 | * */ 16 | type?: 'auto' | 'always' | 'scroll' | 'hover' | 'never'; 17 | /** Scroll hide delay in ms, applicable only when type is set to `hover` or `scroll`, `1000` by default */ 18 | scrollHideDelay?: number; 19 | /** Axis at which scrollbars must be rendered, `'xy'` by default */ 20 | scrollbars?: 'x' | 'y' | 'xy' | false; 21 | /** Determines whether scrollbars should be offset with padding on given axis, `false` by default */ 22 | offsetScrollbars?: boolean | 'x' | 'y' | 'present'; 23 | /** Defines `overscroll-behavior` of the viewport. https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior */ 24 | overscrollBehavior?: React.CSSProperties['overscrollBehavior']; 25 | } 26 | -------------------------------------------------------------------------------- /src/ts/components/core/drawer/DrawerStack.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStackComponent } from '../../../utils/stack-factory'; 3 | import { Drawer, useDrawersStack } from '@mantine/core'; 4 | import { DashBaseProps } from 'props/dash'; 5 | 6 | interface DrawerStackProps extends DashBaseProps { 7 | /** ManagedDrawer content */ 8 | children?: React.ReactElement[]; 9 | /** Current opened state of each drawer. Read only */ 10 | state?: Record; 11 | /** Opens one or more drawers by ID. Accepts a single ID (string or dict) or a list of IDs. */ 12 | open?: string | Record | (string | Record)[]; 13 | /** Closes one or more drawers by ID. Accepts a single ID (string or dict) or a list of IDs. */ 14 | close?: string | Record | (string | Record)[]; 15 | /** Toggles one or more drawers by ID. Accepts a single ID (string or dict) or a list of IDs. */ 16 | toggle?: string | Record | (string | Record)[]; 17 | /** Closes all drawers in the DrawerStack */ 18 | closeAll?: boolean; 19 | } 20 | 21 | const DrawerStackComponent = createStackComponent({ 22 | name: 'DrawerStack', 23 | useStackHook: useDrawersStack, 24 | OuterStack: Drawer.Stack, 25 | InnerComponent: Drawer, 26 | expectedType: 'ManagedDrawer', 27 | }); 28 | 29 | /** Use DrawerStack to render multiple drawers at the same time */ 30 | const DrawerStack = (props: DrawerStackProps) => ( 31 | 32 | ); 33 | 34 | export default DrawerStack; 35 | -------------------------------------------------------------------------------- /src/ts/components/charts/fragments/PieChart.tsx: -------------------------------------------------------------------------------- 1 | import { PieChart as MantinePieChart } from '@mantine/charts'; 2 | import '@mantine/charts/styles.css'; 3 | import React, { useState } from 'react'; 4 | import { getPieClickData, isEventValid } from '../../../utils/charts'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | import { Props } from '../PieChart'; 7 | 8 | /** PieChart */ 9 | const PieChart = (props: Props) => { 10 | const { 11 | setProps, 12 | loading_state, 13 | clickData, 14 | hoverData, 15 | clickSeriesName, 16 | hoverSeriesName, 17 | pieProps, 18 | ...others 19 | } = props; 20 | 21 | const onClick = (ev) => { 22 | if (isEventValid(ev)) { 23 | const clickdata = getPieClickData(ev); 24 | setProps({ 25 | clickData: clickdata, 26 | clickSeriesName: clickdata['name'], 27 | }); 28 | } 29 | }; 30 | 31 | const onMouseOver = (ev) => { 32 | if (isEventValid(ev)) { 33 | const hoverdata = getPieClickData(ev); 34 | setProps({ 35 | hoverData: hoverdata, 36 | hoverSeriesName: hoverdata['name'], 37 | }); 38 | } 39 | }; 40 | 41 | const newProps = { ...pieProps, onClick, onMouseOver }; 42 | 43 | return ( 44 | 49 | ); 50 | }; 51 | 52 | export default PieChart; 53 | -------------------------------------------------------------------------------- /src/ts/components/core/Flex.tsx: -------------------------------------------------------------------------------- 1 | import { Flex as MantineFlex, MantineSize, StyleProp } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /* Content */ 10 | children?: React.ReactNode; 11 | /** `gap` CSS property */ 12 | gap?: StyleProp; 13 | /** `row-gap` CSS property */ 14 | rowGap?: StyleProp; 15 | /** `column-gap` CSS property */ 16 | columnGap?: StyleProp; 17 | /** `align-items` CSS property */ 18 | align?: StyleProp; 19 | /** `justify-content` CSS property */ 20 | justify?: StyleProp; 21 | /** `flex-wrap` CSS property */ 22 | wrap?: StyleProp; 23 | /** `flex-direction` CSS property */ 24 | direction?: StyleProp; 25 | } 26 | 27 | /** Flex */ 28 | const Flex = (props: Props) => { 29 | const { children, setProps, loading_state, ...others } = props; 30 | 31 | return ( 32 | 36 | {children} 37 | 38 | ); 39 | }; 40 | 41 | export default Flex; 42 | -------------------------------------------------------------------------------- /src/ts/components/core/modal/ModalStack.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Modal, useModalsStack } from '@mantine/core'; 3 | import { createStackComponent } from '../../../utils/stack-factory'; 4 | import { DashBaseProps } from 'props/dash'; 5 | 6 | interface ModalStackProps extends DashBaseProps { 7 | /** ManagedModal content */ 8 | children?: React.ReactElement[]; 9 | /** Current opened state of each modal. Read only */ 10 | state?: Record; 11 | /** Opens one or more modals by ID. Accepts a single ID (string or dict) or a list of IDs. */ 12 | open?: string | Record | (string | Record)[]; 13 | /** Closes one or more modals by ID. Accepts a single ID (string or dict) or a list of IDs. */ 14 | close?: string | Record | (string | Record)[]; 15 | /** Toggles one or more modals by ID. Accepts a single ID (string or dict) or a list of IDs. */ 16 | toggle?: string | Record | (string | Record)[]; 17 | /** Closes all modals in the ModalStack */ 18 | closeAll?: boolean; 19 | } 20 | 21 | const ModalStackComponent = createStackComponent({ 22 | name: 'ModalStack', 23 | useStackHook: useModalsStack, 24 | OuterStack: Modal.Stack, 25 | InnerComponent: Modal, 26 | expectedType: 'ManagedModal', 27 | }); 28 | 29 | /** Use ModalStack component to render multiple modals at the same time.*/ 30 | const ModalStack = (props: ModalStackProps) => ( 31 | 32 | ); 33 | 34 | ModalStack.displayName = 'ModalStack'; 35 | 36 | export default ModalStack; 37 | -------------------------------------------------------------------------------- /src/ts/components/charts/fragments/DonutChart.tsx: -------------------------------------------------------------------------------- 1 | import { DonutChart as MantineDonutChart } from '@mantine/charts'; 2 | import '@mantine/charts/styles.css'; 3 | import React, { useState } from 'react'; 4 | import { getPieClickData, isEventValid } from '../../../utils/charts'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | import { Props } from '../DonutChart'; 7 | 8 | /** DonutChart */ 9 | const DonutChart = (props: Props) => { 10 | const { 11 | setProps, 12 | loading_state, 13 | clickData, 14 | hoverData, 15 | clickSeriesName, 16 | hoverSeriesName, 17 | pieProps, 18 | ...others 19 | } = props; 20 | 21 | const onClick = (ev) => { 22 | if (isEventValid(ev)) { 23 | const clickdata = getPieClickData(ev); 24 | setProps({ 25 | clickData: clickdata, 26 | clickSeriesName: clickdata['name'], 27 | }); 28 | } 29 | }; 30 | 31 | const onMouseOver = (ev) => { 32 | if (isEventValid(ev)) { 33 | const hoverdata = getPieClickData(ev); 34 | setProps({ 35 | hoverData: hoverdata, 36 | hoverSeriesName: hoverdata['name'], 37 | }); 38 | } 39 | }; 40 | 41 | const newProps = { ...pieProps, onClick, onMouseOver }; 42 | 43 | return ( 44 | 49 | ); 50 | }; 51 | 52 | export default DonutChart; 53 | -------------------------------------------------------------------------------- /src/ts/components/core/NumberFormatter.tsx: -------------------------------------------------------------------------------- 1 | import { NumberFormatter as MantineNumberFormatter } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import React from 'react'; 4 | import { getLoadingState } from '../../utils/dash3'; 5 | 6 | interface Props extends DashBaseProps { 7 | /** Value to format */ 8 | value?: number | string; 9 | /** Determines whether negative values are allowed, `true` by default */ 10 | allowNegative?: boolean; 11 | /** Limits the number of digits that are displayed after the decimal point, by default there is no limit */ 12 | decimalScale?: number; 13 | /** Character used as a decimal separator, `'.'` by default */ 14 | decimalSeparator?: string; 15 | /** If set, 0s are added after `decimalSeparator` to match given `decimalScale`. `false` by default */ 16 | fixedDecimalScale?: boolean; 17 | /** Prefix added before the value */ 18 | prefix?: string; 19 | /** Suffix added after the value */ 20 | suffix?: string; 21 | /** Defines the thousand grouping style */ 22 | thousandsGroupStyle?: 'thousand' | 'lakh' | 'wan' | 'none'; 23 | /** A character used to separate thousands, `','` by default */ 24 | thousandSeparator?: string | boolean; 25 | } 26 | 27 | /** NumberFormatter */ 28 | const NumberFormatter = (props: Props) => { 29 | const { setProps, loading_state, ...others } = props; 30 | 31 | return ( 32 | 36 | ); 37 | }; 38 | 39 | export default NumberFormatter; 40 | -------------------------------------------------------------------------------- /src/ts/components/core/hovercard/HoverCard.tsx: -------------------------------------------------------------------------------- 1 | import { Box, HoverCard as MantineHoverCard } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { PopoverProps } from 'props/popover'; 4 | import React from 'react'; 5 | import { getLoadingState, getChildLayout } from '../../../utils/dash3'; 6 | 7 | interface Props extends Omit, DashBaseProps { 8 | /** Open delay in ms */ 9 | openDelay?: number; 10 | /** Close delay in ms */ 11 | closeDelay?: number; 12 | } 13 | 14 | /** HoverCard */ 15 | const HoverCard = (props: Props) => { 16 | const { children, setProps, loading_state, ...others } = props; 17 | 18 | return ( 19 | 23 | {React.Children.map(children, (child: any, index) => { 24 | const { type: childType, props: childProps } = 25 | getChildLayout(child); 26 | if (childType === 'HoverCardTarget') { 27 | const { boxWrapperProps } = childProps; 28 | return ( 29 | 30 | 31 | {child} 32 | 33 | 34 | ); 35 | } 36 | return child; 37 | })} 38 | 39 | ); 40 | }; 41 | 42 | export default HoverCard; 43 | -------------------------------------------------------------------------------- /src/ts/components/core/button/CustomCopyButton.tsx: -------------------------------------------------------------------------------- 1 | import { CopyButton as MantineCopyButton } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import React from 'react'; 4 | import { resolveProp } from '../../../utils/prop-functions'; 5 | 6 | interface Props { 7 | /** Value to be copied to clipboard */ 8 | value: string; 9 | /** Copied status timeout in ms, `1000` by default */ 10 | timeout?: number; 11 | /** Function that receives {copied, copy} and returns a component See https://www.dash-mantine-components.com/functions-as-props*/ 12 | children?: any; 13 | /** Unique ID to identify this component in Dash callbacks. */ 14 | id?: string; 15 | /** Update props to trigger callbacks. */ 16 | setProps: (props: Record) => void; 17 | } 18 | 19 | /** CustomCopyButton - custom component with copy to clipboard functionality */ 20 | const CustomCopyButton = ({ 21 | value, 22 | timeout = 1000, 23 | children, 24 | setProps, 25 | }: Props) => { 26 | return ( 27 | 28 | {({ copied, copy }) => { 29 | const renderFunc = resolveProp(children); 30 | 31 | // If resolveProp returns a function, call it with {copied, copy} 32 | if (typeof renderFunc === 'function') { 33 | return renderFunc({ copied, copy }); 34 | } 35 | // Otherwise return the resolved value directly 36 | return renderFunc; 37 | }} 38 | 39 | ); 40 | }; 41 | 42 | export default CustomCopyButton; 43 | -------------------------------------------------------------------------------- /tests/test_direction_provider.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import dash 4 | from dash import callback, Input, Output, ctx, _dash_renderer 5 | import dash_mantine_components as dmc 6 | 7 | _dash_renderer._set_react_version("18.2.0") 8 | 9 | 10 | def test_001di_direction_provider(dash_duo): 11 | """Test that DirectionProvider sets the HTML dir attribute""" 12 | 13 | app = dash.Dash() 14 | 15 | app.layout = dmc.DirectionProvider([ 16 | dmc.MantineProvider([ 17 | dmc.Button("Set RTL", id="rtl-btn"), 18 | dmc.Button("Set LTR", id="ltr-btn"), 19 | ]) 20 | ], direction="ltr", id="direction-provider") 21 | 22 | @callback( 23 | Output("direction-provider", "direction"), 24 | Input("rtl-btn", "n_clicks"), 25 | Input("ltr-btn", "n_clicks"), 26 | prevent_initial_call=True 27 | ) 28 | def set_direction(rtl_clicks, ltr_clicks): 29 | if ctx.triggered_id == "rtl-btn": 30 | return "rtl" 31 | elif ctx.triggered_id == "ltr-btn": 32 | return "ltr" 33 | return dash.no_update 34 | 35 | dash_duo.start_server(app) 36 | 37 | # Test RTL 38 | dash_duo.find_element("#rtl-btn").click() 39 | time.sleep(.5) 40 | 41 | # Check that HTML dir attribute is set 42 | html_element = dash_duo.driver.find_element("tag name", "html") 43 | assert html_element.get_attribute("dir") == "rtl" 44 | 45 | # Test LTR 46 | dash_duo.find_element("#ltr-btn").click() 47 | time.sleep(.5) 48 | html_element = dash_duo.driver.find_element("tag name", "html") 49 | 50 | assert html_element.get_attribute("dir") == "ltr" 51 | 52 | assert dash_duo.get_logs() == [] -------------------------------------------------------------------------------- /src/ts/components/core/ThemeIcon.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | MantineColor, 3 | MantineGradient, 4 | MantineRadius, 5 | MantineSize, 6 | ThemeIcon as MantineThemeIcon, 7 | } from '@mantine/core'; 8 | import { BoxProps } from 'props/box'; 9 | import { DashBaseProps } from 'props/dash'; 10 | import { StylesApiProps } from 'props/styles'; 11 | import React from 'react'; 12 | 13 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 14 | /** Controls width and height of the button. Numbers are converted to rem. `'md'` by default. */ 15 | size?: MantineSize | (string & {}) | number; 16 | /** Key of `theme.colors` or any valid CSS color. Default value is `theme.primaryColor`. */ 17 | color?: MantineColor; 18 | /** Key of `theme.radius` or any valid CSS value to set border-radius. Numbers are converted to rem. `theme.defaultRadius` by default. */ 19 | radius?: MantineRadius; 20 | /** Gradient data used when `variant="gradient"`, default value is `theme.defaultGradient` */ 21 | gradient?: MantineGradient; 22 | /** Icon displayed inside the component */ 23 | children?: React.ReactNode; 24 | /** Determines whether button text color with filled variant should depend on `background-color`. If luminosity of the `color` prop is less than `theme.luminosityThreshold`, then `theme.white` will be used for text color, otherwise `theme.black`. Overrides `theme.autoContrast`. */ 25 | autoContrast?: boolean; 26 | } 27 | 28 | /** ThemeIcon */ 29 | const ThemeIcon = (props: Props) => { 30 | const { children, setProps, loading_state, ...others } = props; 31 | 32 | return {children}; 33 | }; 34 | 35 | export default ThemeIcon; 36 | -------------------------------------------------------------------------------- /src/ts/utils/highlightJsAdapter.ts: -------------------------------------------------------------------------------- 1 | import hljs from 'highlight.js/lib/core'; 2 | import { createHighlightJsAdapter } from '@mantine/code-highlight'; 3 | 4 | import python from 'highlight.js/lib/languages/python'; 5 | import javascript from 'highlight.js/lib/languages/javascript'; 6 | import typescript from 'highlight.js/lib/languages/typescript'; 7 | import html from 'highlight.js/lib/languages/xml'; 8 | import css from 'highlight.js/lib/languages/css'; 9 | import json from 'highlight.js/lib/languages/json'; 10 | import yaml from 'highlight.js/lib/languages/yaml'; 11 | import bash from 'highlight.js/lib/languages/bash'; 12 | import sql from 'highlight.js/lib/languages/sql'; 13 | import markdown from 'highlight.js/lib/languages/markdown'; 14 | 15 | hljs.registerLanguage('python', python); 16 | hljs.registerLanguage('py', python); 17 | hljs.registerLanguage('javascript', javascript); 18 | hljs.registerLanguage('js', javascript); 19 | 20 | hljs.registerLanguage('typescript', typescript); 21 | hljs.registerLanguage('ts', typescript); 22 | 23 | hljs.registerLanguage('html', html); 24 | hljs.registerLanguage('xml', html); 25 | 26 | hljs.registerLanguage('css', css); 27 | 28 | hljs.registerLanguage('json', json); 29 | 30 | hljs.registerLanguage('yaml', yaml); 31 | hljs.registerLanguage('yml', yaml); 32 | 33 | hljs.registerLanguage('bash', bash); 34 | hljs.registerLanguage('sh', bash); 35 | 36 | hljs.registerLanguage('sql', sql); 37 | 38 | hljs.registerLanguage('markdown', markdown); 39 | hljs.registerLanguage('md', markdown); 40 | // prevents runtime error if the language specified is not included here 41 | hljs.registerLanguage('plaintext', markdown); 42 | 43 | export const highlightJsAdapter = createHighlightJsAdapter(hljs); 44 | -------------------------------------------------------------------------------- /tests/test_timeline.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | from dash import Dash, html, Output, Input, _dash_renderer, callback, no_update 4 | import dash_mantine_components as dmc 5 | 6 | _dash_renderer._set_react_version("18.2.0") 7 | 8 | 9 | def test_001ti_timeline(dash_duo): 10 | # prepare the timeline items 11 | timeline_items = [ 12 | dmc.TimelineItem( 13 | title=char, 14 | children=dmc.Text(char) 15 | ) 16 | for char in list('ABCDE') 17 | ] 18 | 19 | app = Dash() 20 | app.layout = dmc.MantineProvider( 21 | html.Div( 22 | [ 23 | dmc.Button("Shuffle timeline-items", id="button"), 24 | dmc.Timeline( 25 | # active=1, 26 | bulletSize=15, 27 | lineWidth=2, 28 | id="timeline-items", 29 | children=timeline_items, 30 | ) 31 | ] 32 | ) 33 | ) 34 | 35 | @callback( 36 | Output('timeline-items', 'children'), 37 | Output('timeline-items', 'active'), 38 | Input('button', 'n_clicks'), 39 | prevent_initial_call=True 40 | ) 41 | def update_output(n_clicks): 42 | if n_clicks: 43 | number_displayed = random.randint(0, len(timeline_items)) 44 | return random.sample(timeline_items, number_displayed), random.randint(0, number_displayed) 45 | else: 46 | return no_update, no_update 47 | 48 | dash_duo.start_server(app) 49 | 50 | 51 | # update timeline 52 | btn = dash_duo.find_element("#button") 53 | btn.click() 54 | time.sleep(1) 55 | 56 | assert dash_duo.get_logs() == [] 57 | -------------------------------------------------------------------------------- /src/ts/components/core/Group.tsx: -------------------------------------------------------------------------------- 1 | import { Group as MantineGroup, MantineSpacing } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingState } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /* Content */ 10 | children?: React.ReactNode; 11 | /** Controls `justify-content` CSS property, `'flex-start'` by default */ 12 | justify?: React.CSSProperties['justifyContent']; 13 | /** Controls `align-items` CSS property, `'center'` by default */ 14 | align?: React.CSSProperties['alignItems']; 15 | /** Controls `flex-wrap` CSS property, `'wrap'` by default */ 16 | wrap?: React.CSSProperties['flexWrap']; 17 | /** Key of `theme.spacing` or any valid CSS value for `gap`, numbers are converted to rem, `'md'` by default */ 18 | gap?: MantineSpacing; 19 | /** Determines whether each child element should have `flex-grow: 1` style, `false` by default */ 20 | grow?: boolean; 21 | /** Determines whether children should take only dedicated amount of space (`max-width` style is set based on the number of children), `true` by default */ 22 | preventGrowOverflow?: boolean; 23 | } 24 | 25 | /** Group */ 26 | const Group = (props: Props) => { 27 | const { children, setProps, loading_state, ...others } = props; 28 | 29 | return ( 30 | 34 | {children} 35 | 36 | ); 37 | }; 38 | 39 | export default Group; 40 | -------------------------------------------------------------------------------- /src/ts/components/core/list/List.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | List as MantineList, 3 | MantineSize, 4 | MantineSpacing, 5 | } from '@mantine/core'; 6 | import { BoxProps } from 'props/box'; 7 | import { DashBaseProps } from 'props/dash'; 8 | import { StylesApiProps } from 'props/styles'; 9 | import React from 'react'; 10 | import { getLoadingState } from '../../../utils/dash3'; 11 | 12 | interface Props extends DashBaseProps, BoxProps, StylesApiProps { 13 | /** `List.Item` components only */ 14 | children?: React.ReactNode; 15 | /** List type: `ol` or `ul`, `'unordered'` by default */ 16 | type?: 'ordered' | 'unordered'; 17 | /** Determines whether list items should be offset with padding, `false` by default */ 18 | withPadding?: boolean; 19 | /** Controls `font-size` and `line-height`, `'md'` by default */ 20 | size?: MantineSize; 21 | /** Icon that replaces list item dot */ 22 | icon?: React.ReactNode; 23 | /** Key of `theme.spacing` or any valid CSS value to set spacing between items, `0` by default */ 24 | spacing?: MantineSpacing; 25 | /** Determines whether items must be centered with their icon, `false` by default */ 26 | center?: boolean; 27 | /** Controls `list-style-type`, by default inferred from `type` */ 28 | listStyleType?: React.CSSProperties['listStyleType']; 29 | } 30 | 31 | /** List */ 32 | const List = (props: Props) => { 33 | const { setProps, loading_state, children, ...others } = props; 34 | 35 | return ( 36 | 40 | {children} 41 | 42 | ); 43 | }; 44 | 45 | export default List; 46 | -------------------------------------------------------------------------------- /src/ts/components/charts/fragments/ScatterChart.tsx: -------------------------------------------------------------------------------- 1 | import { ScatterChart as MantineScatterChart } from '@mantine/charts'; 2 | import '@mantine/charts/styles.css'; 3 | import React from 'react'; 4 | import { getScatterClickData, isEventValid } from '../../../utils/charts'; 5 | import { getLoadingState } from '../../../utils/dash3'; 6 | import { parseFuncProps } from '../../../utils/prop-functions'; 7 | import { Props } from '../ScatterChart'; 8 | 9 | /** ScatterChart */ 10 | const ScatterChart = ({ 11 | setProps, 12 | loading_state, 13 | clickData, 14 | hoverData, 15 | clickSeriesName, 16 | hoverSeriesName, 17 | scatterProps, 18 | data, 19 | dataKey, 20 | ...others 21 | }: Props) => { 22 | const onClick = (ev) => { 23 | if (isEventValid(ev)) { 24 | const clickdata = getScatterClickData(ev); 25 | setProps({ 26 | clickData: clickdata, 27 | clickSeriesName: clickdata['name'], 28 | }); 29 | } 30 | }; 31 | 32 | const onMouseOver = (ev) => { 33 | if (isEventValid(ev)) { 34 | const clickdata = getScatterClickData(ev); 35 | setProps({ 36 | hoverData: clickdata, 37 | hoverSeriesName: clickdata['name'], 38 | }); 39 | } 40 | }; 41 | 42 | const newProps = { ...scatterProps, onClick, onMouseOver }; 43 | 44 | return ( 45 | 52 | ); 53 | }; 54 | 55 | export default ScatterChart; 56 | -------------------------------------------------------------------------------- /src/ts/props/actionicon.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MantineColor, 3 | MantineGradient, 4 | MantineRadius, 5 | MantineSize, 6 | } from '@mantine/core'; 7 | import { BoxProps } from 'props/box'; 8 | import { LoaderProps } from 'props/loader'; 9 | import { StylesApiProps } from 'props/styles'; 10 | import React from 'react'; 11 | 12 | export interface ActionIconProps extends BoxProps, StylesApiProps { 13 | /** Determines whether `Loader` component should be displayed instead of the `children`, `false` by default */ 14 | loading?: boolean; 15 | /** Props added to the `Loader` component (only visible when `loading` prop is set) */ 16 | loaderProps?: LoaderProps; 17 | /** Controls width and height of the button. Numbers are converted to rem. `'md'` by default. */ 18 | size?: MantineSize | (string & {}) | number; 19 | /** Key of `theme.colors` or any valid CSS color. Default value is `theme.primaryColor`. */ 20 | color?: MantineColor; 21 | /** Key of `theme.radius` or any valid CSS value to set border-radius. Numbers are converted to rem. `theme.defaultRadius` by default. */ 22 | radius?: MantineRadius; 23 | /** Gradient data used when `variant="gradient"`, default value is `theme.defaultGradient` */ 24 | gradient?: MantineGradient; 25 | /** Sets `disabled` and `data-disabled` attributes on the button element */ 26 | disabled?: boolean; 27 | /** Icon displayed inside the button */ 28 | children?: React.ReactNode; 29 | /** Determines whether button text color with filled variant should depend on `background-color`. If luminosity of the `color` prop is less than `theme.luminosityThreshold`, then `theme.white` will be used for text color, otherwise `theme.black`. Overrides `theme.autoContrast`. */ 30 | autoContrast?: boolean; 31 | } 32 | -------------------------------------------------------------------------------- /src/ts/props/text.ts: -------------------------------------------------------------------------------- 1 | import { MantineGradient, MantineSize } from '@mantine/core'; 2 | import { BoxProps } from './box'; 3 | import { __BaseInputProps } from './input'; 4 | import { StylesApiProps } from './styles'; 5 | 6 | type TextTruncate = 'end' | 'start' | boolean; 7 | 8 | export interface TextProps extends BoxProps, StylesApiProps { 9 | /** Controls `font-size` and `line-height`, `'md'` by default */ 10 | size?: MantineSize | (string & {}); 11 | /** Number of lines after which Text will be truncated */ 12 | lineClamp?: number; 13 | /** Side on which Text must be truncated, if `true`, text is truncated from the start */ 14 | truncate?: TextTruncate; 15 | /** Sets `line-height` to 1 for centering, `false` by default */ 16 | inline?: boolean; 17 | /** Determines whether font properties should be inherited from the parent, `false` by default */ 18 | inherit?: boolean; 19 | /** Gradient configuration, ignored when `variant` is not `gradient`, `theme.defaultGradient` by default */ 20 | gradient?: MantineGradient; 21 | /** Shorthand for `component="span"`, `false` by default, default root element is `p` */ 22 | span?: boolean; 23 | } 24 | 25 | export interface TextareaProps 26 | extends BoxProps, 27 | __BaseInputProps, 28 | StylesApiProps { 29 | /** Determines whether the textarea height should grow with its content, `false` by default */ 30 | autosize?: boolean; 31 | /** Maximum rows for autosize textarea to grow, ignored if `autosize` prop is not set */ 32 | maxRows?: number; 33 | /** Minimum rows of autosize textarea, ignored if `autosize` prop is not set */ 34 | minRows?: number; 35 | /** Controls `resize` CSS property, `'none'` by default */ 36 | resize?: React.CSSProperties['resize']; 37 | } 38 | -------------------------------------------------------------------------------- /src/ts/utils/anchor.ts: -------------------------------------------------------------------------------- 1 | import isAbsoluteUrl from 'is-absolute-url'; 2 | import { isNil } from 'ramda'; 3 | import { MouseEvent } from 'react'; 4 | 5 | export type TargetProps = '_blank' | '_self'; 6 | 7 | /* 8 | * event polyfill for IE 9 | * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent 10 | */ 11 | function CustomEvent(event, params) { 12 | // eslint-disable-next-line no-param-reassign 13 | params = params || { 14 | bubbles: false, 15 | cancelable: false, 16 | // eslint-disable-next-line no-undefined 17 | detail: undefined, 18 | }; 19 | const evt = document.createEvent('CustomEvent'); 20 | evt.initCustomEvent( 21 | event, 22 | params.bubbles, 23 | params.cancelable, 24 | params.detail 25 | ); 26 | return evt; 27 | } 28 | CustomEvent.prototype = window.Event.prototype; 29 | 30 | export const onClick = ( 31 | ev: MouseEvent, 32 | href: string, 33 | target: TargetProps, 34 | refresh: boolean 35 | ) => { 36 | const hasModifiers = ev.metaKey || ev.shiftKey || ev.altKey || ev.ctrlKey; 37 | 38 | if (hasModifiers) { 39 | return; 40 | } 41 | 42 | if (!href) return; 43 | 44 | if (href && isAbsoluteUrl(href)) { 45 | return; 46 | } 47 | 48 | if (target !== '_self' && !isNil(target)) { 49 | return; 50 | } 51 | 52 | const win: Window = window; 53 | 54 | // prevent anchor from updating location 55 | ev.preventDefault(); 56 | 57 | if (refresh) { 58 | win.location = href; 59 | } else { 60 | win.history.pushState({}, '', href); 61 | window.dispatchEvent(CustomEvent('_dashprivate_pushstate', undefined)); 62 | } 63 | // scroll back to top 64 | win.scrollTo(0, 0); 65 | }; 66 | -------------------------------------------------------------------------------- /src/ts/components/core/Anchor.tsx: -------------------------------------------------------------------------------- 1 | import { sanitizeUrl } from '@braintree/sanitize-url'; 2 | import { Anchor as MantineAnchor } from '@mantine/core'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { TextProps } from 'props/text'; 5 | import React, { MouseEvent, useMemo } from 'react'; 6 | import { TargetProps, onClick } from '../../utils/anchor'; 7 | import { getLoadingState } from '../../utils/dash3'; 8 | 9 | interface Props extends Omit, DashBaseProps { 10 | /** Content */ 11 | children?: React.ReactNode; 12 | /** Target */ 13 | target?: TargetProps; 14 | /** href */ 15 | href: string; 16 | /** Whether to refresh the page */ 17 | refresh?: boolean; 18 | /** Determines in which cases link should have `text-decoration: underline` styles, `hover` by default */ 19 | underline?: 'always' | 'hover' | 'never'; 20 | /** Props passed down to the `Anchor` component */ 21 | anchorProps?: Record; 22 | } 23 | 24 | /** Anchor */ 25 | const Anchor = (props: Props) => { 26 | const { 27 | href, 28 | target, 29 | refresh, 30 | children, 31 | setProps, 32 | loading_state, 33 | anchorProps, 34 | ...others 35 | } = props; 36 | 37 | const sanitizedHref = useMemo(() => sanitizeUrl(href), [href]); 38 | 39 | return ( 40 | ) => 44 | onClick(ev, sanitizedHref, target, refresh) 45 | } 46 | href={sanitizedHref} 47 | target={target} 48 | {...others} 49 | > 50 | {children} 51 | 52 | ); 53 | }; 54 | 55 | export default Anchor; 56 | -------------------------------------------------------------------------------- /src/ts/components/core/checkbox/CheckboxGroup.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox, MantineSize } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 4 | import { InputWrapperProps } from 'props/input'; 5 | import { StylesApiProps } from 'props/styles'; 6 | import React from 'react'; 7 | import { setPersistence, getLoadingState } from '../../../utils/dash3'; 8 | 9 | interface Props 10 | extends BoxProps, 11 | StylesApiProps, 12 | DashBaseProps, 13 | PersistenceProps, 14 | InputWrapperProps { 15 | /** `Checkbox` components and any other elements */ 16 | children: React.ReactNode; 17 | /** Controlled component value */ 18 | value?: string[]; 19 | /** Props passed down to the root element (`Input.Wrapper` component) */ 20 | wrapperProps?: Record; 21 | /** Controls size of the `Input.Wrapper`, `'sm'` by default */ 22 | size?: MantineSize | (string & {}); 23 | /** If set, value cannot be changed */ 24 | readOnly?: boolean; 25 | } 26 | 27 | /** CheckboxGroup */ 28 | const CheckboxGroup = ({ 29 | children, 30 | setProps, 31 | loading_state, 32 | persistence, 33 | persisted_props, 34 | persistence_type, 35 | value = [], 36 | ...others 37 | }: Props) => { 38 | const safeValue = Array.isArray(value) ? value : []; 39 | const onChange = (value: string[]) => { 40 | setProps({ value }); 41 | }; 42 | 43 | return ( 44 | 50 | {children} 51 | 52 | ); 53 | }; 54 | 55 | setPersistence(CheckboxGroup); 56 | 57 | export default CheckboxGroup; 58 | -------------------------------------------------------------------------------- /src/ts/components/core/Skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton as MantineSkeleton } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { getLoadingStateChildren } from '../../utils/dash3'; 7 | 8 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 9 | /** Determines whether Skeleton overlay should be displayed, `true` by default */ 10 | visible?: boolean; 11 | /** Skeleton `height`, numbers are converted to rem, `auto` by default */ 12 | height?: React.CSSProperties['height']; 13 | /** Skeleton `width`, numbers are converted to rem, `100%` by default, ignored when `circle` prop is set */ 14 | width?: React.CSSProperties['width']; 15 | /** If set, Skeleton `width` and `border-radius` are equal to its `height`, `false` by default */ 16 | circle?: boolean; 17 | /** Key of `theme.radius` or any valid CSS value to set border-radius. Numbers are converted to rem. `theme.defaultRadius` by default. */ 18 | radius?: React.CSSProperties['borderRadius']; 19 | /** Determines whether Skeleton should be animated, `true` by default */ 20 | animate?: boolean; 21 | /** Content */ 22 | children?: React.ReactNode; 23 | } 24 | 25 | /** Skeleton */ 26 | const Skeleton = ({ 27 | setProps, 28 | visible = true, 29 | loading_state, 30 | children, 31 | ...others 32 | }: Props) => { 33 | return ( 34 | 40 | {children} 41 | 42 | ); 43 | }; 44 | 45 | // for dash2 46 | Skeleton._dashprivate_isLoadingComponent = true; 47 | 48 | export default Skeleton; 49 | -------------------------------------------------------------------------------- /src/ts/components/charts/Sparkline.tsx: -------------------------------------------------------------------------------- 1 | import { AreaChartCurveType } from '@mantine/charts'; 2 | import { SparklineTrendColors } from '@mantine/charts/lib/Sparkline/Sparkline'; 3 | import { MantineColor } from '@mantine/core'; 4 | import { BoxProps } from 'props/box'; 5 | import { DashBaseProps } from 'props/dash'; 6 | import { StylesApiProps } from 'props/styles'; 7 | import React, { Suspense } from 'react'; 8 | 9 | // eslint-disable-next-line no-inline-comments 10 | const LazySparkline = React.lazy( 11 | () => import(/* webpackChunkName: "Sparkline" */ './fragments/Sparkline') 12 | ); 13 | 14 | export interface Props extends BoxProps, StylesApiProps, DashBaseProps { 15 | /** Data used to render the chart */ 16 | data: number[]; 17 | /** Key of `theme.colors` or any valid CSS color, `theme.primaryColor` by default */ 18 | color?: MantineColor; 19 | /** Determines whether the chart fill should be a gradient, `true` by default */ 20 | withGradient?: boolean; 21 | /** Controls fill opacity of the area, `0.6` by default */ 22 | fillOpacity?: number; 23 | /** Type of the curve, `'linear'` by default */ 24 | curveType?: AreaChartCurveType; 25 | /** Area stroke width, `2` by default */ 26 | strokeWidth?: number; 27 | /** If set, `color` prop is ignored and chart color is determined by the difference between first and last value. */ 28 | trendColors?: SparklineTrendColors; 29 | /** Props passed down to recharts `Area` component */ 30 | areaProps?: object; 31 | /** Determines whether points with `null` values should be connected, `true` by default */ 32 | connectNulls?: boolean; 33 | } 34 | 35 | /** Sparkline */ 36 | const Sparkline = (props: Props) => { 37 | return ( 38 | 39 | 40 | 41 | ); 42 | }; 43 | export default Sparkline; 44 | -------------------------------------------------------------------------------- /tests/test_typography_styles_provider.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, _dash_renderer, dcc 2 | from selenium.webdriver.common.by import By 3 | 4 | import dash_mantine_components as dmc 5 | 6 | _dash_renderer._set_react_version("18.2.0") 7 | 8 | 9 | def _get_effective_background_color(element): 10 | current_element = element 11 | while current_element: 12 | background_color = current_element.value_of_css_property("background-color") 13 | if background_color and not _is_transparent(background_color): 14 | return background_color 15 | parent = current_element.find_element(By.XPATH, "./..") 16 | if current_element == parent: 17 | break 18 | current_element = parent 19 | return None 20 | 21 | 22 | def _is_transparent(color): 23 | return color in ("rgba(0, 0, 0, 0)", "transparent", "") 24 | 25 | 26 | def test_001ts_typography_styles_provider(dash_duo): 27 | app = Dash(__name__) 28 | content = "Hello world!" 29 | 30 | app.layout = dmc.MantineProvider( 31 | [ 32 | dcc.Markdown(content, dangerously_allow_html=True, id="default"), 33 | dmc.TypographyStylesProvider( 34 | dcc.Markdown(content, dangerously_allow_html=True, id="styled") 35 | ), 36 | ], 37 | ) 38 | 39 | dash_duo.start_server(app) 40 | 41 | # The default code element should have a white background color. 42 | code_default = dash_duo.find_element("#default").find_element_by_tag_name("code") 43 | assert _get_effective_background_color(code_default) == "rgba(255, 255, 255, 1)" 44 | # The mantine styled code element should have a light gray background color. 45 | code_styled = dash_duo.find_element("#styled").find_element_by_tag_name("code") 46 | assert _get_effective_background_color(code_styled) == "rgba(248, 249, 250, 1)" 47 | 48 | assert dash_duo.get_logs() == [] 49 | -------------------------------------------------------------------------------- /tests/test_checkbox.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, html, Output, Input, _dash_renderer 2 | import dash_mantine_components as dmc 3 | 4 | _dash_renderer._set_react_version("18.2.0") 5 | 6 | 7 | def checkboxgroup_app(**kwargs): 8 | app = Dash(__name__) 9 | 10 | app.layout = dmc.MantineProvider( 11 | html.Div( 12 | [ 13 | dmc.CheckboxGroup( 14 | id="checkbox-group", 15 | children=dmc.Group( 16 | [ 17 | dmc.Checkbox(value="option1", label="Option 1", id="o1"), 18 | dmc.Checkbox(value="option2", label="Option 2", disabled=True, id="o2"), 19 | dmc.Checkbox(value="option3", label="Option 3", id="o3"), 20 | ] 21 | ), 22 | **kwargs, 23 | ), 24 | html.Div(id="output"), 25 | ] 26 | ) 27 | ) 28 | 29 | @app.callback(Output("output", "children"), Input("checkbox-group", "value")) 30 | def update_output(selected_values): 31 | return f"Selected: {selected_values}" 32 | 33 | return app 34 | 35 | 36 | def test_001chb_checkbox_group(dash_duo): 37 | 38 | app = checkboxgroup_app() 39 | dash_duo.start_server(app) 40 | 41 | # Wait for the app to load 42 | dash_duo.wait_for_element("div[aria-labelledby='checkbox-group-label']") 43 | 44 | option1 = dash_duo.find_element("#o1") 45 | option1.click() 46 | dash_duo.wait_for_text_to_equal("#output", "Selected: ['option1']") 47 | 48 | option2 = dash_duo.find_element("#o2") 49 | option2.click() # Not clickable, check is done below 50 | 51 | option3 = dash_duo.find_element("#o3") 52 | option3.click() 53 | dash_duo.wait_for_text_to_equal("#output", "Selected: ['option1', 'option3']") 54 | 55 | assert dash_duo.get_logs() == [] 56 | 57 | -------------------------------------------------------------------------------- /tests/test_checkboxcard.py: -------------------------------------------------------------------------------- 1 | from dash import Dash, html, Output, Input, _dash_renderer 2 | import dash_mantine_components as dmc 3 | 4 | _dash_renderer._set_react_version("18.2.0") 5 | 6 | 7 | def checkboxgroup_app(**kwargs): 8 | app = Dash(__name__) 9 | 10 | app.layout = dmc.MantineProvider( 11 | html.Div( 12 | [ 13 | dmc.CheckboxGroup( 14 | id="checkbox-group", 15 | children=dmc.Group( 16 | [ 17 | dmc.CheckboxCard(value="option1", children=dmc.CheckboxIndicator(), id="o1"), 18 | dmc.CheckboxCard(value="option2", disabled=True, id="o2"), 19 | dmc.CheckboxCard(value="option3", children=dmc.Text("Text"), id="o3"), 20 | ] 21 | ), 22 | **kwargs, 23 | ), 24 | html.Div(id="output"), 25 | ] 26 | ) 27 | ) 28 | 29 | @app.callback(Output("output", "children"), Input("checkbox-group", "value")) 30 | def update_output(selected_values): 31 | return f"Selected: {selected_values}" 32 | 33 | return app 34 | 35 | 36 | def test_001chc_checkbox_group(dash_duo): 37 | 38 | app = checkboxgroup_app() 39 | dash_duo.start_server(app) 40 | 41 | # Wait for the app to load 42 | dash_duo.wait_for_element("div[aria-labelledby='checkbox-group-label']") 43 | 44 | option1 = dash_duo.find_element("#o1") 45 | option1.click() 46 | dash_duo.wait_for_text_to_equal("#output", "Selected: ['option1']") 47 | 48 | option2 = dash_duo.find_element("#o2") 49 | option2.click() # Not clickable, check is done below 50 | 51 | option3 = dash_duo.find_element("#o3") 52 | option3.click() 53 | dash_duo.wait_for_text_to_equal("#output", "Selected: ['option1', 'option3']") 54 | 55 | assert dash_duo.get_logs() == [] 56 | 57 | -------------------------------------------------------------------------------- /src/ts/components/core/popover/Popover.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Popover as MantinePopover } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { PopoverProps } from 'props/popover'; 4 | import React from 'react'; 5 | import { getLoadingState, getChildLayout } from '../../../utils/dash3'; 6 | 7 | interface Props extends PopoverProps, DashBaseProps {} 8 | 9 | /** The Popover component can be used to display additional content in a dropdown element, triggered by a user interaction with a target element. */ 10 | const Popover = ({ 11 | children, 12 | opened = false, 13 | setProps, 14 | loading_state, 15 | ...others 16 | }: Props) => { 17 | return ( 18 | !_opened && setProps({ opened: false })} 22 | {...others} 23 | > 24 | {React.Children.map(children, (child: any, index) => { 25 | const { type: childType, props: childProps } = 26 | getChildLayout(child); 27 | 28 | if (childType === 'PopoverTarget') { 29 | const { boxWrapperProps } = childProps; 30 | const boxProps = { w: 'fit-content', ...boxWrapperProps }; 31 | 32 | return ( 33 | 34 | setProps({ opened: !opened })} 36 | {...boxProps} 37 | > 38 | {child} 39 | 40 | 41 | ); 42 | } 43 | return child; 44 | })} 45 | 46 | ); 47 | }; 48 | 49 | export default Popover; 50 | -------------------------------------------------------------------------------- /src/ts/components/core/checkbox/CheckboxCard.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | CheckboxCard as MantineCheckboxCard, 3 | MantineRadius, 4 | MantineSize, 5 | } from '@mantine/core'; 6 | import { BoxProps } from 'props/box'; 7 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 8 | import { StylesApiProps } from 'props/styles'; 9 | import React from 'react'; 10 | import { setPersistence, getLoadingState } from '../../../utils/dash3'; 11 | 12 | interface Props 13 | extends BoxProps, 14 | StylesApiProps, 15 | DashBaseProps, 16 | PersistenceProps { 17 | /** State of check box */ 18 | checked?: boolean; 19 | /** Uncontrolled component default value */ 20 | defaultChecked?: boolean; 21 | /** Determines whether the card should have border, `true` by default */ 22 | withBorder?: boolean; 23 | /** Key of `theme.radius` or any valid CSS value to set `border-radius,` `theme.defaultRadius` by default */ 24 | radius?: MantineRadius; 25 | /** To be used with checkbox group */ 26 | value?: string; 27 | /** Props passed down to the root element */ 28 | wrapperProps?: Record; 29 | /** Determines whether CheckboxCard is disabled and non-selectable */ 30 | disabled?: boolean; 31 | /** CheckboxCard content */ 32 | children?: React.ReactNode; 33 | } 34 | 35 | /** CheckboxCard */ 36 | const CheckboxCard = ({ 37 | children, 38 | setProps, 39 | loading_state, 40 | persistence, 41 | persisted_props, 42 | persistence_type, 43 | ...others 44 | }: Props) => { 45 | return ( 46 | setProps({ checked: state })} 49 | {...others} 50 | > 51 | {children} 52 | 53 | ); 54 | }; 55 | 56 | setPersistence(CheckboxCard, ['checked']); 57 | 58 | export default CheckboxCard; 59 | -------------------------------------------------------------------------------- /src/ts/components/core/Spoiler.tsx: -------------------------------------------------------------------------------- 1 | import { Spoiler as MantineSpoiler } from '@mantine/core'; 2 | import { useDidUpdate } from '@mantine/hooks'; 3 | import { BoxProps } from 'props/box'; 4 | import { DashBaseProps } from 'props/dash'; 5 | import { StylesApiProps } from 'props/styles'; 6 | import React, { useState } from 'react'; 7 | import { getLoadingState } from '../../utils/dash3'; 8 | 9 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 10 | /** Maximum height of the visible content, when this point is reached spoiler appears, `100` by default */ 11 | maxHeight?: number; 12 | /** Label for close spoiler action */ 13 | hideLabel: React.ReactNode; 14 | /** Label for open spoiler action */ 15 | showLabel: React.ReactNode; 16 | /** Initial spoiler state, true to wrap content in spoiler, false to show content without spoiler, opened state is updated on mount */ 17 | initialState?: boolean; 18 | /** Spoiler reveal transition duration in ms, set 0 or null to turn off animation, `200` by default */ 19 | transitionDuration?: number; 20 | /** Content */ 21 | children?: React.ReactNode; 22 | /** Controlled expanded state value */ 23 | expanded?: boolean; 24 | } 25 | 26 | /** Spoiler */ 27 | const Spoiler = ({ 28 | setProps, 29 | loading_state, 30 | expanded = false, 31 | children, 32 | ...others 33 | }: Props) => { 34 | const [opened, setOpened] = useState(expanded); 35 | 36 | useDidUpdate(() => { 37 | setProps({ expanded: opened }); 38 | }, [opened]); 39 | 40 | useDidUpdate(() => { 41 | setOpened(expanded); 42 | }, [expanded]); 43 | 44 | return ( 45 | 51 | {children} 52 | 53 | ); 54 | }; 55 | 56 | export default Spoiler; 57 | -------------------------------------------------------------------------------- /src/ts/components/core/stepper/StepperStep.tsx: -------------------------------------------------------------------------------- 1 | import { MantineColor } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | 7 | interface Props extends BoxProps, DashBaseProps, StylesApiProps { 8 | /** Step index, controlled by Stepper component **/ 9 | step?: number; 10 | /** Step state, controlled by Stepper component */ 11 | state?: 'stepInactive' | 'stepProgress' | 'stepCompleted'; 12 | /** Key of `theme.colors`, by default controlled by Stepper component */ 13 | color?: MantineColor; 14 | /** Determines whether the icon should be displayed */ 15 | withIcon?: boolean; 16 | /** Step icon, defaults to step index + 1 when rendered within Stepper */ 17 | icon?: React.ReactNode; 18 | /** Step icon displayed when step is completed */ 19 | completedIcon?: React.ReactNode; 20 | /** Step icon displayed when step is in progress */ 21 | progressIcon?: React.ReactNode; 22 | /** Step label, render after icon */ 23 | label?: React.ReactNode; 24 | /** Step description */ 25 | description?: React.ReactNode; 26 | /** Icon wrapper size */ 27 | iconSize?: number; 28 | /** Icon position relative to step body, controlled by Stepper component */ 29 | iconPosition?: 'right' | 'left'; 30 | /** Indicates loading state of the step */ 31 | loading?: boolean; 32 | /** Set to false to disable clicks on step */ 33 | allowStepClick?: boolean; 34 | /** Should step selection be allowed */ 35 | allowStepSelect?: boolean; 36 | /** Component orientation */ 37 | orientation?: 'vertical' | 'horizontal'; 38 | /* Content */ 39 | children?: React.ReactNode; 40 | } 41 | 42 | /** StepperStep */ 43 | const StepperStep = (props: Props) => { 44 | const { children, setProps, loading_state, ...others } = props; 45 | 46 | return <>{children}; 47 | }; 48 | 49 | export default StepperStep; 50 | -------------------------------------------------------------------------------- /src/ts/components/core/drawer/ManagedDrawer.tsx: -------------------------------------------------------------------------------- 1 | import { Drawer as MantineDrawer, MantineRadius } from '@mantine/core'; 2 | import { DashBaseProps } from 'props/dash'; 3 | import { ModalBaseOverlayProps, ModalBaseProps } from 'props/modal'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React, { useEffect, useState } from 'react'; 6 | import { getLoadingState } from '../../../utils/dash3'; 7 | 8 | type DrawerPosition = 'bottom' | 'left' | 'right' | 'top'; 9 | 10 | interface Props 11 | extends Omit, 12 | Omit, 13 | Omit { 14 | /** Unique ID to identify this component. Required for use with DrawerStack */ 15 | id: string; 16 | /** Side of the screen on which drawer will be opened, `'left'` by default */ 17 | position?: DrawerPosition; 18 | /** Key of `theme.radius` or any valid CSS value to set `border-radius`, numbers are converted to rem, `0` by default */ 19 | radius?: MantineRadius; 20 | /** Drawer container offset from the viewport end, `0` by default */ 21 | offset?: number | string; 22 | /** Drawer title */ 23 | title?: React.ReactNode; 24 | /** Determines whether the overlay should be rendered, `true` by default */ 25 | withOverlay?: boolean; 26 | /** Props passed down to the `Overlay` component, can be used to configure opacity, `background-color`, styles and other properties */ 27 | overlayProps?: ModalBaseOverlayProps; 28 | /** Drawer content */ 29 | children?: React.ReactNode; 30 | /** Determines whether the close button should be rendered, `true` by default */ 31 | withCloseButton?: boolean; 32 | /** Props passed down to the close button */ 33 | closeButtonProps?: object; 34 | } 35 | 36 | /** Managed Drawer for DrawerStack */ 37 | const ManagedDrawer = ({ 38 | children, 39 | setProps, 40 | loading_state, 41 | ...others 42 | }: Props) => { 43 | return <>{children}; 44 | }; 45 | 46 | export default ManagedDrawer; 47 | -------------------------------------------------------------------------------- /src/ts/props/combobox.ts: -------------------------------------------------------------------------------- 1 | import { ComboboxData, MantineSize } from '@mantine/core'; 2 | import { __PopoverProps } from './popover'; 3 | import { StylesApiProps } from './styles'; 4 | 5 | interface ComboboxProps extends __PopoverProps, StylesApiProps { 6 | /** Combobox content */ 7 | children?: React.ReactNode; 8 | /** Controls items `font-size` and `padding`, `'sm'` by default */ 9 | size?: MantineSize | (string & {}); 10 | /** Controls `padding` of the dropdown, `4` by default */ 11 | dropdownPadding?: React.CSSProperties['padding']; 12 | /** Determines whether selection should be reset when option is hovered, `false` by default */ 13 | resetSelectionOnOptionHover?: boolean; 14 | /** Determines whether Combobox value can be changed */ 15 | readOnly?: boolean; 16 | } 17 | 18 | export interface ComboboxLikeProps { 19 | /** Data used to generate options */ 20 | data?: ComboboxData; 21 | /** Controlled dropdown opened state */ 22 | dropdownOpened?: boolean; 23 | /** Determines whether the first option should be selected when value changes, `false` by default */ 24 | selectFirstOptionOnChange?: boolean; 25 | /** Props passed down to `Combobox` component */ 26 | comboboxProps?: ComboboxProps; 27 | /** Maximum number of options displayed at a time, `Infinity` by default */ 28 | limit?: number; 29 | /** Determines whether the options should be wrapped with `ScrollArea.AutoSize`, `true` by default */ 30 | withScrollArea?: boolean; 31 | /** `max-height` of the dropdown, only applicable when `withScrollArea` prop is `true`, `250` by default */ 32 | maxDropdownHeight?: number | string; 33 | /** 34 | * A function to render content of the option, replaces the default content of the option. See https://www.dash-mantine-components.com/functions-as-props 35 | */ 36 | renderOption?: any; 37 | /** 38 | * A Function based on which items are filtered and sorted. See https://www.dash-mantine-components.com/functions-as-props 39 | */ 40 | filter?: any; 41 | } 42 | -------------------------------------------------------------------------------- /src/ts/components/core/grid/Grid.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Grid as MantineGrid, 3 | MantineSpacing, 4 | StyleProp, 5 | MantineSize, 6 | } from '@mantine/core'; 7 | import { BoxProps } from 'props/box'; 8 | import { DashBaseProps } from 'props/dash'; 9 | import { StylesApiProps } from 'props/styles'; 10 | import React from 'react'; 11 | import { getLoadingState } from '../../../utils/dash3'; 12 | 13 | export type GridBreakpoints = Record; 14 | 15 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 16 | /* Content */ 17 | children?: React.ReactNode; 18 | /** Gutter between columns, key of `theme.spacing` or any valid CSS value, `'md'` by default */ 19 | gutter?: StyleProp; 20 | /** Determines whether columns in the last row should expand to fill all available space, `false` by default */ 21 | grow?: boolean; 22 | /** Sets `justify-content`, `flex-start` by default */ 23 | justify?: React.CSSProperties['justifyContent']; 24 | /** Sets `align-items`, `stretch` by default */ 25 | align?: React.CSSProperties['alignItems']; 26 | /** Number of columns in each row, `12` by default */ 27 | columns?: number; 28 | /** Sets `overflow` CSS property on the root element, `'visible'` by default */ 29 | overflow?: React.CSSProperties['overflow']; 30 | /** Determines typeof of queries that are used for responsive styles, `'media'` by default */ 31 | type?: 'media' | 'container'; 32 | /** Breakpoints values, only applicable when `type="container"` is set, ignored when `type` is not set or `type="media"` is set. */ 33 | breakpoints?: GridBreakpoints; 34 | } 35 | 36 | /** Grid */ 37 | const Grid = (props: Props) => { 38 | const { children, setProps, loading_state, ...others } = props; 39 | 40 | return ( 41 | 45 | {children} 46 | 47 | ); 48 | }; 49 | 50 | export default Grid; 51 | -------------------------------------------------------------------------------- /src/ts/components/core/SemiCircleProgress.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | MantineColor, 3 | SemiCircleProgress as MantineSemiCircleProgess, 4 | } from '@mantine/core'; 5 | import { BoxProps } from 'props/box'; 6 | import { DashBaseProps } from 'props/dash'; 7 | import { StylesApiProps } from 'props/styles'; 8 | import React from 'react'; 9 | import { getLoadingState } from '../../utils/dash3'; 10 | 11 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 12 | /** Progress value from `0` to `100` */ 13 | value: number; 14 | 15 | /** Diameter of the svg in px, `200` by default */ 16 | size?: number; 17 | 18 | /** Circle thickness in px, `12` by default */ 19 | thickness?: number; 20 | 21 | /** Orientation of the circle, `'up'` by default */ 22 | orientation?: 'up' | 'down'; 23 | 24 | /** Direction from which the circle is filled, `'left-to-right'` by default */ 25 | fillDirection?: 'right-to-left' | 'left-to-right'; 26 | 27 | /** Key of `theme.colors` or any valid CSS color value, `theme.primaryColor` by default */ 28 | filledSegmentColor?: MantineColor; 29 | 30 | /** Key of `theme.colors` or any valid CSS color value, by default the value is determined based on the color scheme value */ 31 | emptySegmentColor?: MantineColor; 32 | 33 | /** Transition duration of filled section styles changes in ms, `0` by default */ 34 | transitionDuration?: number; 35 | 36 | /** Label rendered inside the circle */ 37 | label?: React.ReactNode; 38 | 39 | /** Label position relative to the circle center, `'bottom'` by default */ 40 | labelPosition?: 'center' | 'bottom'; 41 | } 42 | 43 | /** Use to represent progress with semi circle diagram */ 44 | const SemiCircleProgress = (props: Props) => { 45 | const { setProps, loading_state, ...others } = props; 46 | 47 | return ( 48 | 52 | ); 53 | }; 54 | 55 | export default SemiCircleProgress; 56 | -------------------------------------------------------------------------------- /src/ts/components/core/Burger.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Burger as MantineBurger, 3 | MantineColor, 4 | MantineSize, 5 | } from '@mantine/core'; 6 | import { BoxProps } from 'props/box'; 7 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 8 | import { StylesApiProps } from 'props/styles'; 9 | import React from 'react'; 10 | import { setPersistence, getLoadingState } from '../../utils/dash3'; 11 | 12 | interface Props 13 | extends BoxProps, 14 | StylesApiProps, 15 | DashBaseProps, 16 | PersistenceProps { 17 | /** Controls burger `width` and `height`, numbers are converted to rem, `'md'` by default */ 18 | size?: MantineSize | (string & {}) | number; 19 | /** Key of `theme.colors` of any valid CSS value, by default `theme.white` in dark color scheme and `theme.black` in light */ 20 | color?: MantineColor; 21 | /** Height of the burger lines */ 22 | lineSize?: number; 23 | /** State of the burger, when `true` burger is transformed into X, `false` by default */ 24 | opened?: boolean; 25 | /** `transition-duration` property value in ms, `300` by default */ 26 | transitionDuration?: number; 27 | /** `transition-timing-function` property value, `'ease'` by default */ 28 | transitionTimingFunction?: string; 29 | } 30 | 31 | /** The Burger component renders a customizable hamburger menu button which can toggle between open and closed states. */ 32 | const Burger = ({ 33 | setProps, 34 | loading_state, 35 | opened = false, 36 | persistence, 37 | persisted_props, 38 | persistence_type, 39 | ...others 40 | }: Props) => { 41 | const onClick = () => { 42 | setProps({ 43 | opened: !opened, 44 | }); 45 | }; 46 | 47 | return ( 48 | 54 | ); 55 | }; 56 | 57 | setPersistence(Burger, ['opened']); 58 | 59 | export default Burger; 60 | -------------------------------------------------------------------------------- /src/ts/components/extensions/codehighlight/CodeHighlightTabs.tsx: -------------------------------------------------------------------------------- 1 | import { CodeHighlightTabsCode } from '@mantine/code-highlight'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React, { Suspense } from 'react'; 6 | 7 | // eslint-disable-next-line no-inline-comments 8 | const LazyCodeHighlightTabs = React.lazy( 9 | () => 10 | import( 11 | /* webpackChunkName: "CodeHighlightTabs" */ './fragments/CodeHighlightTabs' 12 | ) 13 | ); 14 | 15 | export interface Props extends BoxProps, StylesApiProps, DashBaseProps { 16 | /** Code to highlight with meta data (file name and icon) */ 17 | code: CodeHighlightTabsCode | CodeHighlightTabsCode[]; 18 | /** Index of controlled active tab state */ 19 | activeTab?: number; 20 | /** Determines whether header with file names and copy button should be rendered, `true` by default */ 21 | withHeader?: boolean; 22 | /** Copy tooltip label, `'Copy code'` by default */ 23 | copyLabel?: string; 24 | /** Copied tooltip label, `'Copied'` by default */ 25 | copiedLabel?: string; 26 | /** `max-height` of code in collapsed state */ 27 | maxCollapsedHeight?: React.CSSProperties['maxHeight']; 28 | /** Uncontrolled expanded state initial value */ 29 | defaultExpanded?: boolean; 30 | /** Expand button label and tooltip, `'Expand code'` by default */ 31 | expandCodeLabel?: string; 32 | /** Collapse button label and tooltip, `'Collapse code'` by default */ 33 | collapseCodeLabel?: string; 34 | /** Determines whether to show the expand button, `false` by default */ 35 | withExpandButton?: boolean; 36 | /** Determines whether copy button should be displayed, `true` by default */ 37 | withCopyButton?: boolean; 38 | } 39 | 40 | /** CodeHighlightTabs */ 41 | const CodeHighlightTabs = (props: Props) => { 42 | return ( 43 | 44 | 45 | 46 | ); 47 | }; 48 | 49 | export default CodeHighlightTabs; 50 | -------------------------------------------------------------------------------- /src/ts/props/tooltip.ts: -------------------------------------------------------------------------------- 1 | import { FloatingPosition, MantineColor, MantineRadius } from '@mantine/core'; 2 | import { BoxProps } from './box'; 3 | import { StylesApiProps } from './styles'; 4 | 5 | export interface TooltipBaseProps extends BoxProps, StylesApiProps { 6 | /** Target element, must support `ref` prop and `...others` */ 7 | children?: React.ReactNode; 8 | /** Tooltip position relative to target element (`Tooltip` component) or mouse (`Tooltip.Floating` component) */ 9 | position?: FloatingPosition; 10 | /** Tooltip content */ 11 | label: React.ReactNode; 12 | /** Determines whether tooltip should be rendered within `Portal`, `true` by default */ 13 | withinPortal?: boolean; 14 | /** Key of `theme.radius` or any valid CSS value to set border-radius, numbers are converted to rem, `theme.defaultRadius` by default */ 15 | radius?: MantineRadius; 16 | /** Key of `theme.colors` or any valid CSS color, controls tooltip background, by default set based on current color scheme */ 17 | color?: MantineColor; 18 | /** Determines whether content should be wrapped on to the next line, `false` by default */ 19 | multiline?: boolean; 20 | /** Tooltip z-index, `300` by default */ 21 | zIndex?: string | number; 22 | /** If set, tooltip element will not be rendered */ 23 | disabled?: boolean; 24 | /** Props to pass down to the portal when withinPortal is true */ 25 | portalProps?: object; 26 | /** Floating ui middlewares to configure position handling, `{ flip: true, shift: true, inline: false }` by default */ 27 | middlewares?: object; 28 | /** 29 | * Determines whether tooltip text color should depend on background-color. If luminosity of the color prop is 30 | * less than theme.luminosityThreshold, then theme.white will be used for text color, otherwise theme.black. 31 | * Overrides theme.autoContrast. 32 | */ 33 | autoContrast?: boolean; 34 | /** Selector, ref of an element or element itself that should be used for positioning */ 35 | target?: React.RefObject | HTMLElement | null | string; 36 | } 37 | -------------------------------------------------------------------------------- /src/ts/components/core/Rating.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | MantineColor, 3 | Rating as MantineRating, 4 | MantineSize, 5 | } from '@mantine/core'; 6 | import { useDidUpdate } from '@mantine/hooks'; 7 | import { BoxProps } from 'props/box'; 8 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 9 | import { StylesApiProps } from 'props/styles'; 10 | import React, { useState } from 'react'; 11 | import { setPersistence } from '../../utils/dash3'; 12 | 13 | interface Props 14 | extends BoxProps, 15 | StylesApiProps, 16 | DashBaseProps, 17 | PersistenceProps { 18 | /** Value for controlled component */ 19 | value?: number; 20 | /** Icon displayed when the symbol is empty */ 21 | emptySymbol?: React.ReactNode; 22 | /** Icon displayed when the symbol is full */ 23 | fullSymbol?: React.ReactNode; 24 | /** Number of fractions each item can be divided into, `1` by default */ 25 | fractions?: number; 26 | /** Controls component size, `'sm'` by default */ 27 | size?: MantineSize | number | (string & {}); 28 | /** Number of controls, `5` by default */ 29 | count?: number; 30 | /** `name` attribute passed down to all inputs. By default, `name` is generated randomly. */ 31 | name?: string; 32 | /** If set, the user cannot interact with the component, `false` by default */ 33 | readOnly?: boolean; 34 | /** If set, only the selected symbol changes to full symbol when selected, `false` by default */ 35 | highlightSelectedOnly?: boolean; 36 | /** Key of `theme.colors` or any CSS color value, `'yellow'` by default */ 37 | color?: MantineColor; 38 | } 39 | 40 | /** Rating */ 41 | const Rating = ({ setProps, loading_state, value = 0, ...others }: Props) => { 42 | const [val, setVal] = useState(value); 43 | 44 | useDidUpdate(() => { 45 | setProps({ value: val }); 46 | }, [val]); 47 | 48 | useDidUpdate(() => { 49 | setVal(value); 50 | }, [value]); 51 | 52 | return ; 53 | }; 54 | 55 | setPersistence(Rating); 56 | 57 | export default Rating; 58 | -------------------------------------------------------------------------------- /src/ts/components/core/chip/ChipGroup.tsx: -------------------------------------------------------------------------------- 1 | import { Chip } from '@mantine/core'; 2 | import { useDidUpdate } from '@mantine/hooks'; 3 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 4 | import React, { useState } from 'react'; 5 | import ChipGroupContext from './ChipGroupContext'; 6 | import { setPersistence, getLoadingState } from '../../../utils/dash3'; 7 | 8 | interface Props extends DashBaseProps, PersistenceProps { 9 | /** Determines whether it is allowed to select multiple values, `false` by default */ 10 | multiple?: boolean; 11 | /** When using multiple=true, value must be a list */ 12 | value?: string[] | string | null; 13 | /** `Chip` components and any other elements */ 14 | children?: React.ReactNode; 15 | /** Allow to deselect Chip in Radio mode */ 16 | deselectable?: boolean; 17 | } 18 | 19 | /** ChipGroup */ 20 | const ChipGroup = (props: Props) => { 21 | const { 22 | children, 23 | value, 24 | setProps, 25 | persistence, 26 | persisted_props, 27 | persistence_type, 28 | loading_state, 29 | deselectable, 30 | ...others 31 | } = props; 32 | const [val, setVal] = useState(value); 33 | 34 | useDidUpdate(() => { 35 | setVal(value); 36 | }, [value]); 37 | 38 | useDidUpdate(() => { 39 | setProps({ value: val }); 40 | }, [val]); 41 | 42 | const handleChipClick = (event: React.MouseEvent) => { 43 | if (event.currentTarget.value === value) { 44 | setVal(null); 45 | } 46 | }; 47 | 48 | return ( 49 | 55 | 58 | {children} 59 | 60 | 61 | ); 62 | }; 63 | 64 | setPersistence(ChipGroup); 65 | 66 | export default ChipGroup; 67 | -------------------------------------------------------------------------------- /src/ts/components/dates/DatePicker.tsx: -------------------------------------------------------------------------------- 1 | import { DatePicker as MantineDatePicker } from '@mantine/dates'; 2 | import { useDidUpdate } from '@mantine/hooks'; 3 | import { BoxProps } from 'props/box'; 4 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 5 | import { DatePickerBaseProps } from 'props/dates'; 6 | import { StylesApiProps } from 'props/styles'; 7 | import React, { useState } from 'react'; 8 | import { setPersistence } from '../../utils/dash3'; 9 | import { resolveProp, parseFuncProps } from '../../utils/prop-functions'; 10 | import { isDisabled } from '../../utils/dates'; 11 | 12 | interface Props 13 | extends DashBaseProps, 14 | PersistenceProps, 15 | BoxProps, 16 | DatePickerBaseProps, 17 | StylesApiProps { 18 | /** Specifies days that should be disabled */ 19 | disabledDates?: string[]; 20 | } 21 | 22 | /** Inline date, multiple dates and dates range picker */ 23 | const DatePicker = ({ 24 | setProps, 25 | value, 26 | type, 27 | disabledDates, 28 | persistence, 29 | persisted_props, 30 | persistence_type, 31 | ...others 32 | }: Props) => { 33 | const normalize = (val: any) => { 34 | if (type === 'multiple' || type === 'range') 35 | return Array.isArray(val) ? val : []; 36 | return val ?? null; 37 | }; 38 | 39 | const [date, setDate] = useState(() => normalize(value)); 40 | 41 | useDidUpdate(() => { 42 | setDate(normalize(value)); 43 | }, [value]); 44 | 45 | useDidUpdate(() => { 46 | setProps({ value: date }); 47 | }, [date]); 48 | 49 | const isExcluded = (date: string) => isDisabled(date, disabledDates || []); 50 | 51 | return ( 52 | 63 | ); 64 | }; 65 | 66 | setPersistence(DatePicker); 67 | 68 | export default DatePicker; 69 | -------------------------------------------------------------------------------- /src/ts/components/core/radio/RadioCard.tsx: -------------------------------------------------------------------------------- 1 | import { RadioCard as MantineRadioCard, MantineRadius } from '@mantine/core'; 2 | import { BoxProps } from 'props/box'; 3 | import { DashBaseProps, PersistenceProps } from 'props/dash'; 4 | import { StylesApiProps } from 'props/styles'; 5 | import React from 'react'; 6 | import { setPersistence, getLoadingState } from '../../../utils/dash3'; 7 | import RadioGroupContext from './RadioGroupContext'; 8 | 9 | interface Props 10 | extends BoxProps, 11 | StylesApiProps, 12 | DashBaseProps, 13 | PersistenceProps { 14 | /** Checked state */ 15 | checked?: boolean; 16 | /** Determines whether the card should have border, `true` by default */ 17 | withBorder?: boolean; 18 | /** Key of `theme.radius` or any valid CSS value to set `border-radius,` "xl" by default */ 19 | radius?: MantineRadius; 20 | /** To be used with Radio group */ 21 | value?: string; 22 | /** Value used to associate all related radio cards, required for accessibility if used outside of `Radio.Group` */ 23 | name?: string; 24 | /** Props passed down to the root element */ 25 | wrapperProps?: Record; 26 | /** Determines whether RadioCard is disabled and non-selectable */ 27 | disabled?: boolean; 28 | /** RadioCard content */ 29 | children?: React.ReactNode; 30 | } 31 | 32 | /** RadioCard */ 33 | const RadioCard = (props: Props) => { 34 | const { 35 | children, 36 | setProps, 37 | loading_state, 38 | persistence, 39 | persisted_props, 40 | persistence_type, 41 | value, 42 | ...others 43 | } = props; 44 | 45 | const { radioOnClick } = React.useContext(RadioGroupContext) || {}; 46 | 47 | return ( 48 | radioOnClick(value) : null} 51 | value={value} 52 | {...others} 53 | > 54 | {children} 55 | 56 | ); 57 | }; 58 | 59 | setPersistence(RadioCard, ['checked']); 60 | 61 | export default RadioCard; 62 | -------------------------------------------------------------------------------- /src/ts/components/extensions/codehighlight/fragments/CodeHighlightTabs.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | CodeHighlightTabs as MantineCodeHighlightTabs, 3 | CodeHighlightAdapterProvider, 4 | } from '@mantine/code-highlight'; 5 | import '@mantine/code-highlight/styles.css'; 6 | import './dmc-code.css'; 7 | import React from 'react'; 8 | import { 9 | getLoadingState, 10 | newRenderDashComponents, 11 | getContextPath, 12 | } from '../../../../utils/dash3'; 13 | import { Props } from '../CodeHighlightTabs'; 14 | import { isEmpty } from 'ramda'; 15 | import { highlightJsAdapter } from '../../../../utils/highlightJsAdapter'; 16 | 17 | /** Display code in tabs and highlight code with highlight.js */ 18 | const CodeHighlightTabs = (props: Props) => { 19 | const { setProps, loading_state, code, className, ...others } = props; 20 | const componentPath = getContextPath(); 21 | const renderedCode = []; 22 | 23 | if (Array.isArray(code)) { 24 | code.forEach((item, index) => { 25 | renderedCode.push( 26 | newRenderDashComponents( 27 | item, 28 | ['icon'], 29 | !isEmpty(componentPath) 30 | ? [...componentPath, 'props', 'code', index] 31 | : [] 32 | ) 33 | ); 34 | }); 35 | } else { 36 | renderedCode.push( 37 | newRenderDashComponents( 38 | code, 39 | ['icon'], 40 | !isEmpty(componentPath) 41 | ? [...componentPath, 'props', 'code'] 42 | : [] 43 | ) 44 | ); 45 | } 46 | 47 | return ( 48 | 49 | 57 | 58 | ); 59 | }; 60 | 61 | CodeHighlightTabs.dashChildrenUpdate = true; 62 | 63 | export default CodeHighlightTabs; 64 | -------------------------------------------------------------------------------- /tests/charts/test_bubble_chart.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver import ActionChains 2 | from dash import Dash, Output, Input, _dash_renderer, callback 3 | import dash_mantine_components as dmc 4 | import json 5 | 6 | _dash_renderer._set_react_version("18.2.0") 7 | 8 | data = [ 9 | {"hour": "08:00", "index": 1, "value": 150}, 10 | {"hour": "10:00", "index": 1, "value": 166}, 11 | {"hour": "12:00", "index": 1, "value": 170}, 12 | {"hour": "14:00", "index": 1, "value": 150}, 13 | ] 14 | 15 | component = dmc.Group( 16 | [ 17 | dmc.BubbleChart( 18 | id="figure", 19 | gridColor="gray.5", 20 | textColor="gray.9", 21 | h=60, 22 | data=data, 23 | range=[16, 225], 24 | label="Sales/hour", 25 | color="lime.6", 26 | dataKey={"x": "hour", "y": "index", "z": "value"} 27 | ), 28 | 29 | dmc.Text(id="clickdata"), 30 | dmc.Text(id="hoverdata"), 31 | ] 32 | ) 33 | 34 | 35 | def test_001bu_bubblechart(dash_duo): 36 | app = Dash(__name__, external_stylesheets=dmc.styles.ALL) 37 | 38 | app.layout = dmc.MantineProvider(component) 39 | 40 | @callback( 41 | Output("clickdata", "children"), 42 | Output("hoverdata", "children"), 43 | Input("figure", "clickData"), 44 | Input("figure", "hoverData"), 45 | ) 46 | def update(clickdata, hoverdata): 47 | return json.dumps(clickdata), json.dumps(hoverdata) 48 | 49 | dash_duo.start_server(app) 50 | 51 | # Wait for the app to load 52 | dash_duo.wait_for_text_to_equal("#clickdata", "null") 53 | 54 | # Target the bars 55 | areas = dash_duo.find_elements( 56 | ".recharts-layer.recharts-scatter-symbol" 57 | ) 58 | 59 | 60 | assert len(areas) > 0, "No areas found in the chart" 61 | actions = ActionChains(dash_duo.driver) 62 | actions.move_to_element(areas[0]).click().perform() 63 | 64 | expected_output = ( 65 | '{"hour": "08:00", "index": 1, "value": 150}' 66 | ) 67 | 68 | dash_duo.wait_for_text_to_equal("#clickdata", expected_output) 69 | dash_duo.wait_for_text_to_equal("#hoverdata", expected_output) 70 | 71 | assert dash_duo.get_logs() == [] 72 | -------------------------------------------------------------------------------- /src/ts/components/charts/ScatterChart.tsx: -------------------------------------------------------------------------------- 1 | import { ScatterChartSeries } from '@mantine/charts/lib/ScatterChart/ScatterChart'; 2 | import { BoxProps } from 'props/box'; 3 | import { GridChartBaseProps } from 'props/charts'; 4 | import { DashBaseProps } from 'props/dash'; 5 | import { StylesApiProps } from 'props/styles'; 6 | import React, { Suspense } from 'react'; 7 | 8 | // eslint-disable-next-line no-inline-comments 9 | const LazyScatterChart = React.lazy( 10 | () => 11 | import( 12 | /* webpackChunkName: "ScatterChart" */ './fragments/ScatterChart' 13 | ) 14 | ); 15 | 16 | export interface Props 17 | extends BoxProps, 18 | Omit, 19 | StylesApiProps, 20 | DashBaseProps { 21 | /** Keys that should be used to retrieve data from the data array on x and y axis */ 22 | dataKey: { 23 | x: string; 24 | y: string; 25 | }; 26 | /** Data that is used to build the chart */ 27 | data: ScatterChartSeries[]; 28 | /** Units displayed after value on axis and inside the tooltip */ 29 | unit?: { 30 | x?: string; 31 | y?: string; 32 | }; 33 | /** Labels that should be used instead of keys names in the tooltip */ 34 | labels?: { 35 | x?: string; 36 | y?: string; 37 | }; 38 | /** Props passed down to recharts `ScatterChart` component */ 39 | scatterChartProps?: object; 40 | /** Props passed down to recharts `Scatter` component */ 41 | scatterProps?: object; 42 | /** Click data */ 43 | clickData?: Record; 44 | /** Hover data */ 45 | hoverData?: Record; 46 | /** If set, displays labels next to points for the given axis */ 47 | pointLabels?: 'x' | 'y'; 48 | /** Name of the series that was clicked */ 49 | clickSeriesName?: Record; 50 | /** Name of the series that is hovered*/ 51 | hoverSeriesName?: Record; 52 | } 53 | 54 | /** ScatterChart */ 55 | const ScatterChart = (props: Props) => { 56 | return ( 57 | 58 | 59 | 60 | ); 61 | }; 62 | 63 | export default ScatterChart; 64 | -------------------------------------------------------------------------------- /src/ts/components/core/Badge.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Badge as MantineBadge, 3 | MantineColor, 4 | MantineGradient, 5 | MantineRadius, 6 | MantineSize, 7 | } from '@mantine/core'; 8 | import { BoxProps } from 'props/box'; 9 | import { DashBaseProps } from 'props/dash'; 10 | import { StylesApiProps } from 'props/styles'; 11 | import React from 'react'; 12 | import { getLoadingState } from '../../utils/dash3'; 13 | 14 | interface Props extends BoxProps, StylesApiProps, DashBaseProps { 15 | /** Controls `font-size`, `height` and horizontal `padding`, `'md'` by default */ 16 | size?: MantineSize | (string & {}); 17 | /** If set, badge `min-width` becomes equal to its `height` and horizontal padding is removed */ 18 | circle?: boolean; 19 | /** Key of `theme.radius` or any valid CSS value to set `border-radius`, `'xl'` by default */ 20 | radius?: MantineRadius; 21 | /** Key of `theme.colors` or any valid CSS color, `theme.primaryColor` by default */ 22 | color?: MantineColor; 23 | /** Gradient configuration used when `variant="gradient"`, default value is `theme.defaultGradient` */ 24 | gradient?: MantineGradient; 25 | /** Content displayed on the left side of the badge label */ 26 | leftSection?: React.ReactNode; 27 | /** Content displayed on the right side of the badge label */ 28 | rightSection?: React.ReactNode; 29 | /** Determines whether Badge should take 100% of its parent width, `false` by default */ 30 | fullWidth?: boolean; 31 | /** Main badge content */ 32 | children?: React.ReactNode; 33 | /** Determines whether text color with filled variant should depend on `background-color`. If luminosity of the `color` prop is less than `theme.luminosityThreshold`, then `theme.white` will be used for text color, otherwise `theme.black`. Overrides `theme.autoContrast`. */ 34 | autoContrast?: boolean; 35 | } 36 | 37 | /** Badge */ 38 | const Badge = (props: Props) => { 39 | const { children, setProps, loading_state, ...others } = props; 40 | 41 | return ( 42 | 46 | {children} 47 | 48 | ); 49 | }; 50 | 51 | export default Badge; 52 | --------------------------------------------------------------------------------