diff --git a/.gitignore b/.gitignore index e069e0ee..70f24f47 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,31 @@ coverage/ dist/ node_modules/ public/ -yarn.lock \ No newline at end of file +yarn.lock + +# IDEs and editors +.idea/ + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# System files +.DS_Store +Thumbs.db + +# Numerous always-ignore extensions +*.diff +*.err +*.log +*.orig +*.rej +*.swo +*.swp +*.vi +*.zip +*~ \ No newline at end of file diff --git a/README.md b/README.md index a26ec056..e7c25be4 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Several quick start options are available: -- [Download the latest release](https://github.com/coreui/coreui-react/archive/v4.9.0-beta.2.zip) +- [Download the latest release](https://github.com/coreui/coreui-react/archive/v4.11.1.zip) - Clone the repo: `git clone https://github.com/coreui/coreui-react.git` - Install with [npm](https://www.npmjs.com/): `npm install @coreui/react` - Install with [yarn](https://yarnpkg.com/): `yarn add @coreui/react` diff --git a/lerna.json b/lerna.json index c7f755c9..45bfb26a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "npmClient": "yarn", "packages": ["packages/*"], - "version": "4.9.0-beta.2", + "version": "4.11.1", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/package.json b/package.json index 6cdc756a..2bd8a7de 100644 --- a/package.json +++ b/package.json @@ -22,16 +22,24 @@ "test:update": "npm-run-all charts:test:update icons:test:update lib:test:update" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^5.59.11", - "@typescript-eslint/parser": "^5.59.11", - "eslint": "8.43.0", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.61.0", + "eslint": "8.44.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-unicorn": "^47.0.0", - "lerna": "^7.1.0", + "lerna": "^7.1.1", "npm-run-all": "^4.1.5", - "prettier": "^2.8.8" + "prettier": "^3.0.0" + }, + "overrides": { + "gatsby-remark-external-links": { + "unist-util-find": "1.0.2" + } + }, + "resolutions": { + "**/gatsby-remark-external-links/unist-util-find": "1.0.2" } } diff --git a/packages/coreui-icons-react b/packages/coreui-icons-react index 41c5ac74..8a617788 160000 --- a/packages/coreui-icons-react +++ b/packages/coreui-icons-react @@ -1 +1 @@ -Subproject commit 41c5ac7443275a0274a06661f0569b5026cdf917 +Subproject commit 8a617788ae9f1f634be14e8f98436145aaee6c61 diff --git a/packages/coreui-react-chartjs b/packages/coreui-react-chartjs index 0a395873..50682b5d 160000 --- a/packages/coreui-react-chartjs +++ b/packages/coreui-react-chartjs @@ -1 +1 @@ -Subproject commit 0a39587318f6a285003e01db331d977cecab58ee +Subproject commit 50682b5d072d8f4863935d8bc81ef42b188971b1 diff --git a/packages/coreui-react/README.md b/packages/coreui-react/README.md index d3472677..f94ece62 100644 --- a/packages/coreui-react/README.md +++ b/packages/coreui-react/README.md @@ -46,7 +46,7 @@ Several quick start options are available: -- [Download the latest release](https://github.com/coreui/coreui-react/archive/v4.9.0-beta.2.zip) +- [Download the latest release](https://github.com/coreui/coreui-react/archive/v4.11.1.zip) - Clone the repo: `git clone https://github.com/coreui/coreui-react.git` - Install with [npm](https://www.npmjs.com/): `npm install @coreui/react` - Install with [yarn](https://yarnpkg.com/): `yarn add @coreui/react` diff --git a/packages/coreui-react/package.json b/packages/coreui-react/package.json index f6a02330..f523e69a 100644 --- a/packages/coreui-react/package.json +++ b/packages/coreui-react/package.json @@ -1,6 +1,6 @@ { "name": "@coreui/react", - "version": "4.9.0-beta.2", + "version": "4.11.1", "description": "UI Components Library for React.js", "keywords": [ "react", @@ -38,29 +38,29 @@ }, "devDependencies": { "@popperjs/core": "^2.11.8", - "@rollup/plugin-commonjs": "^25.0.1", + "@rollup/plugin-commonjs": "^25.0.3", "@rollup/plugin-node-resolve": "^15.1.0", - "@rollup/plugin-typescript": "^11.1.1", - "@testing-library/jest-dom": "^5.16.5", + "@rollup/plugin-typescript": "^11.1.2", + "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^14.0.0", - "@types/react": "18.2.12", - "@types/react-dom": "^18.2.5", + "@types/react": "18.2.18", + "@types/react-dom": "^18.2.7", "@types/react-transition-group": "^4.4.6", "classnames": "^2.3.2", - "jest": "^29.5.0", - "jest-environment-jsdom": "^29.5.0", + "jest": "^29.6.2", + "jest-environment-jsdom": "^29.6.2", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-popper": "^2.3.0", "react-transition-group": "^4.4.5", - "rollup": "^3.25.1", - "ts-jest": "^29.1.0", - "tslib": "^2.5.3", + "rollup": "^3.27.0", + "ts-jest": "^29.1.1", + "tslib": "^2.6.1", "typescript": "^4.9.5" }, "peerDependencies": { - "@coreui/coreui": "4.3.0-beta.0", + "@coreui/coreui": "4.3.0", "react": ">=17", "react-dom": ">=17" } diff --git a/packages/coreui-react/src/components/conditional-portal/CConditionalPortal.tsx b/packages/coreui-react/src/components/conditional-portal/CConditionalPortal.tsx index aee4bc85..bbbe00cd 100644 --- a/packages/coreui-react/src/components/conditional-portal/CConditionalPortal.tsx +++ b/packages/coreui-react/src/components/conditional-portal/CConditionalPortal.tsx @@ -1,21 +1,45 @@ -import React, { FC, ReactNode } from 'react' +import React, { FC, ReactNode, useEffect, useState } from 'react' import { createPortal } from 'react-dom' import PropTypes from 'prop-types' +const getContainer = (container?: Element | (() => Element | null) | null) => { + if (container) { + return typeof container === 'function' ? container() : container + } + + return document.body +} + export interface CConditionalPortalProps { /** * @ignore */ children: ReactNode + /** + * An HTML element or function that returns a single element, with `document.body` as the default. + * + * @since v4.11.0 + */ + container?: Element | (() => Element | null) | null /** * Render some children into a different part of the DOM */ - portal: boolean + portal: boolean | any } -export const CConditionalPortal: FC = ({ children, portal }) => { - return typeof window !== 'undefined' && portal ? ( - createPortal(children, document.body) +export const CConditionalPortal: FC = ({ + children, + container, + portal, +}) => { + const [_container, setContainer] = useState>(null) + + useEffect(() => { + portal && setContainer(getContainer(container) || document.body) + }, [container, portal]) + + return typeof window !== 'undefined' && portal && _container ? ( + createPortal(children, _container) ) : ( <>{children} ) @@ -23,7 +47,8 @@ export const CConditionalPortal: FC = ({ children, port CConditionalPortal.propTypes = { children: PropTypes.node, - portal: PropTypes.bool.isRequired, + container: PropTypes.any, // HTMLElement + portal: PropTypes.bool, } CConditionalPortal.displayName = 'CConditionalPortal' diff --git a/packages/coreui-react/src/components/dropdown/CDropdown.tsx b/packages/coreui-react/src/components/dropdown/CDropdown.tsx index ca25f5f3..bc7ab4e3 100644 --- a/packages/coreui-react/src/components/dropdown/CDropdown.tsx +++ b/packages/coreui-react/src/components/dropdown/CDropdown.tsx @@ -51,6 +51,12 @@ export interface CDropdownProps extends HTMLAttributes Element | null) | null /** * Sets a darker color scheme to match a dark navbar. */ @@ -66,7 +72,7 @@ export interface CDropdownProps extends HTMLAttributes void /** @@ -147,6 +153,7 @@ export const CDropdown = forwardRef { export const CDropdownMenu = forwardRef( ({ children, className, component: Component = 'ul', ...rest }, ref) => { - const { alignment, dark, dropdownMenuRef, popper, portal, visible } = + const { alignment, container, dark, dropdownMenuRef, popper, portal, visible } = useContext(CDropdownContext) const forkedRef = useForkedRef(ref, dropdownMenuRef) return ( - + { * @ignore */ duration?: number + /** + * Puts the focus on the modal when shown. + * + * @since v4.10.0 + */ + focus?: boolean /** * Set modal to covers the entire user viewport. */ @@ -96,6 +102,7 @@ export const CModal = forwardRef( backdrop = true, className, duration = 150, + focus = true, fullscreen, keyboard = true, onClose, @@ -111,6 +118,7 @@ export const CModal = forwardRef( }, ref, ) => { + const activeElementRef = useRef(null) const modalRef = useRef(null) const modalContentRef = useRef(null) const forkedRef = useForkedRef(ref, modalRef) @@ -128,11 +136,16 @@ export const CModal = forwardRef( }, [visible]) useEffect(() => { - document.addEventListener('click', handleClickOutside) - document.addEventListener('keydown', handleKeyDown) + if (_visible) { + activeElementRef.current = document.activeElement as HTMLElement | null + document.addEventListener('mouseup', handleClickOutside) + document.addEventListener('keydown', handleKeyDown) + } else { + activeElementRef.current?.focus() + } return () => { - document.removeEventListener('click', handleClickOutside) + document.removeEventListener('mouseup', handleClickOutside) document.removeEventListener('keydown', handleKeyDown) } }, [_visible]) @@ -143,6 +156,7 @@ export const CModal = forwardRef( } setVisible(false) + return onClose && onClose() } @@ -163,7 +177,7 @@ export const CModal = forwardRef( setTimeout( () => { - modalRef.current?.focus() + focus && modalRef.current?.focus() }, transition ? duration : 0, ) @@ -186,10 +200,7 @@ export const CModal = forwardRef( }, [_visible]) const handleClickOutside = (event: Event) => { - if ( - modalContentRef.current && - !modalContentRef.current.contains(event.target as HTMLElement) - ) { + if (modalRef.current && modalRef.current == event.target) { handleDismiss() } } @@ -225,10 +236,13 @@ export const CModal = forwardRef( className, )} tabIndex={-1} - role="dialog" + {...(_visible + ? { 'aria-modal': true, role: 'dialog' } + : { 'aria-hidden': 'true' })} style={{ ...(state !== 'exited' && { display: 'block' }), }} + {...rest} ref={forkedRef} > ( scrollable={scrollable} size={size} > - - {children} - + {children} @@ -262,6 +274,7 @@ CModal.propTypes = { children: PropTypes.node, className: PropTypes.string, duration: PropTypes.number, + focus: PropTypes.bool, fullscreen: PropTypes.oneOfType([ PropTypes.bool, PropTypes.oneOf<'sm' | 'md' | 'lg' | 'xl' | 'xxl'>(['sm', 'md', 'lg', 'xl', 'xxl']), diff --git a/packages/coreui-react/src/components/nav/CNavGroup.tsx b/packages/coreui-react/src/components/nav/CNavGroup.tsx index 9d18ea45..1b100bca 100644 --- a/packages/coreui-react/src/components/nav/CNavGroup.tsx +++ b/packages/coreui-react/src/components/nav/CNavGroup.tsx @@ -36,6 +36,13 @@ export interface CNavGroupProps { idx?: string } +const isInVisibleGroup = (el1: string, el2: string) => { + const array1 = el1.toString().split('.') + const array2 = el2.toString().split('.') + + return array2.every((item, index) => item === array1[index]) +} + export const CNavGroup = forwardRef( ({ children, className, compact, idx, toggler, visible, ...rest }, ref) => { const [height, setHeight] = useState() @@ -45,12 +52,12 @@ export const CNavGroup = forwardRef( const [_visible, setVisible] = useState( Boolean( - visible || (idx && visibleGroup && visibleGroup.toString().startsWith(idx.toString())), + visible || (idx && visibleGroup && isInVisibleGroup(visibleGroup, idx)), ), ) useEffect(() => { - setVisible(Boolean(idx && visibleGroup && visibleGroup.toString().startsWith(idx.toString()))) + setVisible(Boolean(idx && visibleGroup && isInVisibleGroup(visibleGroup, idx))) }, [visibleGroup]) const handleTogglerOnCLick = (event: React.MouseEvent) => { diff --git a/packages/coreui-react/src/components/offcanvas/COffcanvas.tsx b/packages/coreui-react/src/components/offcanvas/COffcanvas.tsx index 886a5a76..29caf739 100644 --- a/packages/coreui-react/src/components/offcanvas/COffcanvas.tsx +++ b/packages/coreui-react/src/components/offcanvas/COffcanvas.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import classNames from 'classnames' import { Transition } from 'react-transition-group' -import { CBackdrop } from '../backdrop/CBackdrop' +import { CBackdrop } from '../backdrop' import { CConditionalPortal } from '../conditional-portal' import { useForkedRef } from '../../hooks' diff --git a/packages/coreui-react/src/components/popover/CPopover.tsx b/packages/coreui-react/src/components/popover/CPopover.tsx index f2b128b8..36b93afe 100644 --- a/packages/coreui-react/src/components/popover/CPopover.tsx +++ b/packages/coreui-react/src/components/popover/CPopover.tsx @@ -1,25 +1,31 @@ -import React, { FC, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react' -import { createPortal } from 'react-dom' +import React, { forwardRef, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react' import classNames from 'classnames' import PropTypes from 'prop-types' import { Transition } from 'react-transition-group' -import { usePopper } from '../../hooks' +import { CConditionalPortal } from '../conditional-portal' +import { useForkedRef, usePopper } from '../../hooks' import { fallbackPlacementsPropType, triggerPropType } from '../../props' import type { Placements, Triggers } from '../../types' -import { getRTLPlacement } from '../../utils' +import { getRTLPlacement, getTransitionDurationFromElement } from '../../utils' export interface CPopoverProps extends Omit, 'title' | 'content'> { /** * Apply a CSS fade transition to the popover. * - * @since 4.9.0-beta.2 + * @since 4.9.0 */ animation?: boolean /** * A string of all className you want applied to the component. */ className?: string + /** + * Appends the react popover to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`. + * + * @since v4.11.0 + */ + container?: Element | (() => Element | null) | null /** * Content node for your component. */ @@ -31,13 +37,13 @@ export interface CPopoverProps extends Omit, 'tit /** * The delay for displaying and hiding the popover (in milliseconds). When a numerical value is provided, the delay applies to both the hide and show actions. The object structure for specifying the delay is as follows: delay: `{ 'show': 500, 'hide': 100 }`. * - * @since 4.9.0-beta.2 + * @since 4.9.0 */ delay?: number | { show: number; hide: number } /** * Specify the desired order of fallback placements by providing a list of placements as an array. The placements should be prioritized based on preference. * - * @since 4.9.0-beta.2 + * @since 4.9.0 */ fallbackPlacements?: Placements | Placements[] /** @@ -68,103 +74,119 @@ export interface CPopoverProps extends Omit, 'tit visible?: boolean } -export const CPopover: FC = ({ - children, - animation = true, - className, - content, - delay = 0, - fallbackPlacements = ['top', 'right', 'bottom', 'left'], - offset = [0, 8], - onHide, - onShow, - placement = 'top', - title, - trigger = 'click', - visible, - ...rest -}) => { - const popoverRef = useRef(null) - const togglerRef = useRef(null) - const { initPopper, destroyPopper } = usePopper() - const [_visible, setVisible] = useState(visible) +export const CPopover = forwardRef( + ( + { + children, + animation = true, + className, + container, + content, + delay = 0, + fallbackPlacements = ['top', 'right', 'bottom', 'left'], + offset = [0, 8], + onHide, + onShow, + placement = 'top', + title, + trigger = 'click', + visible, + ...rest + }, + ref, + ) => { + const popoverRef = useRef(null) + const togglerRef = useRef(null) + const forkedRef = useForkedRef(ref, popoverRef) + const uID = useRef(`popover${Math.floor(Math.random() * 1_000_000)}`) + + const { initPopper, destroyPopper } = usePopper() + const [_visible, setVisible] = useState(visible) - const _delay = typeof delay === 'number' ? { show: delay, hide: delay } : delay + const _delay = typeof delay === 'number' ? { show: delay, hide: delay } : delay - const popperConfig = { - modifiers: [ - { - name: 'arrow', - options: { - element: '.popover-arrow', + const popperConfig = { + modifiers: [ + { + name: 'arrow', + options: { + element: '.popover-arrow', + }, }, - }, - { - name: 'flip', - options: { - fallbackPlacements: fallbackPlacements, + { + name: 'flip', + options: { + fallbackPlacements: fallbackPlacements, + }, }, - }, - { - name: 'offset', - options: { - offset: offset, + { + name: 'offset', + options: { + offset: offset, + }, }, - }, - ], - placement: getRTLPlacement(placement, togglerRef.current), - } - - useEffect(() => { - setVisible(visible) - }, [visible]) - - useEffect(() => { - if (_visible && togglerRef.current && popoverRef.current) { - initPopper(togglerRef.current, popoverRef.current, popperConfig) + ], + placement: getRTLPlacement(placement, togglerRef.current), } - return () => { - destroyPopper() - } - }, [_visible]) + useEffect(() => { + setVisible(visible) + }, [visible]) - const toggleVisible = (visible: boolean) => { - if (visible) { - setTimeout(() => setVisible(true), _delay.show) - return - } + const toggleVisible = (visible: boolean) => { + if (visible) { + setTimeout(() => setVisible(true), _delay.show) + return + } - setTimeout(() => setVisible(false), _delay.hide) - } + setTimeout(() => setVisible(false), _delay.hide) + } - return ( - <> - {React.cloneElement(children as React.ReactElement, { - ref: togglerRef, - ...((trigger === 'click' || trigger.includes('click')) && { - onClick: () => toggleVisible(!_visible), - }), - ...((trigger === 'focus' || trigger.includes('focus')) && { - onFocus: () => toggleVisible(true), - onBlur: () => toggleVisible(false), - }), - ...((trigger === 'hover' || trigger.includes('hover')) && { - onMouseEnter: () => toggleVisible(true), - onMouseLeave: () => toggleVisible(false), - }), - })} - {typeof window !== 'undefined' && - createPortal( + return ( + <> + {React.cloneElement(children as React.ReactElement, { + ...(_visible && { + 'aria-describedby': uID.current, + }), + ref: togglerRef, + ...((trigger === 'click' || trigger.includes('click')) && { + onClick: () => toggleVisible(!_visible), + }), + ...((trigger === 'focus' || trigger.includes('focus')) && { + onFocus: () => toggleVisible(true), + onBlur: () => toggleVisible(false), + }), + ...((trigger === 'hover' || trigger.includes('hover')) && { + onMouseEnter: () => toggleVisible(true), + onMouseLeave: () => toggleVisible(false), + }), + })} + { + if (togglerRef.current && popoverRef.current) { + initPopper(togglerRef.current, popoverRef.current, popperConfig) + } + + onShow + }} + onEntering={() => { + if (togglerRef.current && popoverRef.current) { + popoverRef.current.style.display = 'initial' + } + }} onExit={onHide} + onExited={() => { + destroyPopper() + }} timeout={{ enter: 0, - exit: 200, + exit: popoverRef.current + ? getTransitionDurationFromElement(popoverRef.current) + 50 + : 200, }} unmountOnExit > @@ -179,8 +201,12 @@ export const CPopover: FC = ({ }, className, )} - ref={popoverRef} + id={uID.current} + ref={forkedRef} role="tooltip" + style={{ + display: 'none', + }} {...rest} >
@@ -188,17 +214,18 @@ export const CPopover: FC = ({
{content}
)} -
, - document.body, - )} - - ) -} + +
+ + ) + }, +) CPopover.propTypes = { animation: PropTypes.bool, children: PropTypes.node, className: PropTypes.string, + container: PropTypes.any, content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), delay: PropTypes.oneOfType([ PropTypes.number, diff --git a/packages/coreui-react/src/components/sidebar/CSidebar.tsx b/packages/coreui-react/src/components/sidebar/CSidebar.tsx index 2eccb17c..509b3033 100644 --- a/packages/coreui-react/src/components/sidebar/CSidebar.tsx +++ b/packages/coreui-react/src/components/sidebar/CSidebar.tsx @@ -3,7 +3,7 @@ import { createPortal } from 'react-dom' import PropTypes from 'prop-types' import classNames from 'classnames' -import { CBackdrop } from '../backdrop/CBackdrop' +import { CBackdrop } from '../backdrop' import { isInViewport } from '../../utils' import { useForkedRef } from '../../hooks' diff --git a/packages/coreui-react/src/components/tooltip/CTooltip.tsx b/packages/coreui-react/src/components/tooltip/CTooltip.tsx index 32593916..07d88af6 100644 --- a/packages/coreui-react/src/components/tooltip/CTooltip.tsx +++ b/packages/coreui-react/src/components/tooltip/CTooltip.tsx @@ -1,25 +1,31 @@ -import React, { FC, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react' -import { createPortal } from 'react-dom' +import React, { forwardRef, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react' import classNames from 'classnames' import PropTypes from 'prop-types' import { Transition } from 'react-transition-group' -import { usePopper } from '../../hooks' +import { CConditionalPortal } from '../conditional-portal' +import { useForkedRef, usePopper } from '../../hooks' import { fallbackPlacementsPropType, triggerPropType } from '../../props' import type { Placements, Triggers } from '../../types' -import { getRTLPlacement } from '../../utils' +import { getRTLPlacement, getTransitionDurationFromElement } from '../../utils' export interface CTooltipProps extends Omit, 'content'> { /** * Apply a CSS fade transition to the tooltip. * - * @since 4.9.0-beta.2 + * @since 4.9.0 */ animation?: boolean /** * A string of all className you want applied to the component. */ className?: string + /** + * Appends the react tooltip to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`. + * + * @since v4.11.0 + */ + container?: Element | (() => Element | null) | null /** * Content node for your component. */ @@ -27,13 +33,13 @@ export interface CTooltipProps extends Omit, 'con /** * The delay for displaying and hiding the tooltip (in milliseconds). When a numerical value is provided, the delay applies to both the hide and show actions. The object structure for specifying the delay is as follows: delay: `{ 'show': 500, 'hide': 100 }`. * - * @since 4.9.0-beta.2 + * @since 4.9.0 */ delay?: number | { show: number; hide: number } /** * Specify the desired order of fallback placements by providing a list of placements as an array. The placements should be prioritized based on preference. * - * @since 4.9.0-beta.2 + * @since 4.9.0 */ fallbackPlacements?: Placements | Placements[] /** @@ -64,101 +70,118 @@ export interface CTooltipProps extends Omit, 'con visible?: boolean } -export const CTooltip: FC = ({ - children, - animation = true, - className, - content, - delay = 0, - fallbackPlacements = ['top', 'right', 'bottom', 'left'], - offset = [0, 6], - onHide, - onShow, - placement = 'top', - trigger = ['hover', 'focus'], - visible, - ...rest -}) => { - const tooltipRef = useRef(null) - const togglerRef = useRef(null) - const { initPopper, destroyPopper } = usePopper() - const [_visible, setVisible] = useState(visible) +export const CTooltip = forwardRef( + ( + { + children, + animation = true, + className, + container, + content, + delay = 0, + fallbackPlacements = ['top', 'right', 'bottom', 'left'], + offset = [0, 6], + onHide, + onShow, + placement = 'top', + trigger = ['hover', 'focus'], + visible, + ...rest + }, + ref, + ) => { + const tooltipRef = useRef(null) + const togglerRef = useRef(null) + const forkedRef = useForkedRef(ref, tooltipRef) + const uID = useRef(`tooltip${Math.floor(Math.random() * 1_000_000)}`) + + const { initPopper, destroyPopper } = usePopper() + const [_visible, setVisible] = useState(visible) - const _delay = typeof delay === 'number' ? { show: delay, hide: delay } : delay + const _delay = typeof delay === 'number' ? { show: delay, hide: delay } : delay - const popperConfig = { - modifiers: [ - { - name: 'arrow', - options: { - element: '.tooltip-arrow', + const popperConfig = { + modifiers: [ + { + name: 'arrow', + options: { + element: '.tooltip-arrow', + }, }, - }, - { - name: 'flip', - options: { - fallbackPlacements: fallbackPlacements, + { + name: 'flip', + options: { + fallbackPlacements: fallbackPlacements, + }, }, - }, - { - name: 'offset', - options: { - offset: offset, + { + name: 'offset', + options: { + offset: offset, + }, }, - }, - ], - placement: getRTLPlacement(placement, togglerRef.current), - } - - useEffect(() => { - setVisible(visible) - }, [visible]) - - useEffect(() => { - if (_visible && togglerRef.current && tooltipRef.current) { - initPopper(togglerRef.current, tooltipRef.current, popperConfig) + ], + placement: getRTLPlacement(placement, togglerRef.current), } - return () => { - destroyPopper() - } - }, [_visible]) + useEffect(() => { + setVisible(visible) + }, [visible]) - const toggleVisible = (visible: boolean) => { - if (visible) { - setTimeout(() => setVisible(true), _delay.show) - return - } + const toggleVisible = (visible: boolean) => { + if (visible) { + setTimeout(() => setVisible(true), _delay.show) + return + } - setTimeout(() => setVisible(false), _delay.hide) - } + setTimeout(() => setVisible(false), _delay.hide) + } - return ( - <> - {React.cloneElement(children as React.ReactElement, { - ref: togglerRef, - ...((trigger === 'click' || trigger.includes('click')) && { - onClick: () => toggleVisible(!_visible), - }), - ...((trigger === 'focus' || trigger.includes('focus')) && { - onFocus: () => toggleVisible(true), - onBlur: () => toggleVisible(false), - }), - ...((trigger === 'hover' || trigger.includes('hover')) && { - onMouseEnter: () => toggleVisible(true), - onMouseLeave: () => toggleVisible(false), - }), - })} - {typeof window !== 'undefined' && - createPortal( + return ( + <> + {React.cloneElement(children as React.ReactElement, { + ...(_visible && { + 'aria-describedby': uID.current, + }), + ref: togglerRef, + ...((trigger === 'click' || trigger.includes('click')) && { + onClick: () => toggleVisible(!_visible), + }), + ...((trigger === 'focus' || trigger.includes('focus')) && { + onFocus: () => toggleVisible(true), + onBlur: () => toggleVisible(false), + }), + ...((trigger === 'hover' || trigger.includes('hover')) && { + onMouseEnter: () => toggleVisible(true), + onMouseLeave: () => toggleVisible(false), + }), + })} + { + if (togglerRef.current && tooltipRef.current) { + initPopper(togglerRef.current, tooltipRef.current, popperConfig) + } + + onShow + }} + onEntering={() => { + if (togglerRef.current && tooltipRef.current) { + tooltipRef.current.style.display = 'initial' + } + }} onExit={onHide} + onExited={() => { + destroyPopper() + }} timeout={{ enter: 0, - exit: 200, + exit: tooltipRef.current + ? getTransitionDurationFromElement(tooltipRef.current) + 50 + : 200, }} unmountOnExit > @@ -173,24 +196,29 @@ export const CTooltip: FC = ({ }, className, )} - ref={tooltipRef} + id={uID.current} + ref={forkedRef} role="tooltip" + style={{ + display: 'none', + }} {...rest} >
{content}
)} -
, - document.body, - )} - - ) -} + +
+ + ) + }, +) CTooltip.propTypes = { animation: PropTypes.bool, children: PropTypes.node, + container: PropTypes.any, content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), delay: PropTypes.oneOfType([ PropTypes.number, diff --git a/packages/coreui-react/src/hooks/index.ts b/packages/coreui-react/src/hooks/index.ts index 5ee1b5e3..a5f43e64 100644 --- a/packages/coreui-react/src/hooks/index.ts +++ b/packages/coreui-react/src/hooks/index.ts @@ -1,5 +1,4 @@ -import { useColorModes } from './useColorModes' import { useForkedRef } from './useForkedRef' import { usePopper } from './usePopper' -export { useColorModes, useForkedRef, usePopper } +export { useForkedRef, usePopper } diff --git a/packages/coreui-react/src/hooks/usePopper.ts b/packages/coreui-react/src/hooks/usePopper.ts index 051a2718..898a402e 100644 --- a/packages/coreui-react/src/hooks/usePopper.ts +++ b/packages/coreui-react/src/hooks/usePopper.ts @@ -2,6 +2,8 @@ import { useRef } from 'react' import { createPopper } from '@popperjs/core' import type { Instance, Options } from '@popperjs/core' +import { executeAfterTransition } from '../utils' + interface UsePopperOutput { popper: Instance | undefined initPopper: (reference: HTMLElement, popper: HTMLElement, options: Partial) => void @@ -10,14 +12,20 @@ interface UsePopperOutput { export const usePopper = (): UsePopperOutput => { const _popper = useRef() + const el = useRef() const initPopper = (reference: HTMLElement, popper: HTMLElement, options: Partial) => { _popper.current = createPopper(reference, popper, options) + el.current = popper } const destroyPopper = () => { - if (_popper.current) { - _popper.current.destroy() + const popperInstance = _popper.current + + if (popperInstance && el.current) { + executeAfterTransition(() => { + popperInstance.destroy() + }, el.current) } _popper.current = undefined diff --git a/packages/coreui-react/src/utils/executeAfterTransition.ts b/packages/coreui-react/src/utils/executeAfterTransition.ts new file mode 100644 index 00000000..7b0bed80 --- /dev/null +++ b/packages/coreui-react/src/utils/executeAfterTransition.ts @@ -0,0 +1,46 @@ +import getTransitionDurationFromElement from './getTransitionDurationFromElement' + +const execute = (callback: () => void) => { + if (typeof callback === 'function') { + callback() + } +} + +const triggerTransitionEnd = (element: HTMLElement) => { + element.dispatchEvent(new Event('transitionend')) +} + +const executeAfterTransition = ( + callback: () => void, + transitionElement: HTMLElement, + waitForTransition = true, +) => { + if (!waitForTransition) { + execute(callback) + return + } + + const durationPadding = 5 + const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding + + let called = false + + const handler = ({ target }: { target: any }) => { + if (target !== transitionElement) { + return + } + + called = true + transitionElement.removeEventListener('transitionend', handler) + execute(callback) + } + + transitionElement.addEventListener('transitionend', handler) + setTimeout(() => { + if (!called) { + triggerTransitionEnd(transitionElement) + } + }, emulatedDuration) +} + +export default executeAfterTransition diff --git a/packages/coreui-react/src/utils/getRTLPlacement.ts b/packages/coreui-react/src/utils/getRTLPlacement.ts index cc60b801..87c38517 100644 --- a/packages/coreui-react/src/utils/getRTLPlacement.ts +++ b/packages/coreui-react/src/utils/getRTLPlacement.ts @@ -1,5 +1,5 @@ import { Placement } from '@popperjs/core' -import { isRTL } from '../utils' +import isRTL from './isRTL' const getRTLPlacement = (placement: string, element: HTMLDivElement | null): Placement => { switch (placement) { diff --git a/packages/coreui-react/src/utils/getTransitionDurationFromElement.ts b/packages/coreui-react/src/utils/getTransitionDurationFromElement.ts new file mode 100644 index 00000000..ffaf546d --- /dev/null +++ b/packages/coreui-react/src/utils/getTransitionDurationFromElement.ts @@ -0,0 +1,24 @@ +const getTransitionDurationFromElement = (element: HTMLElement) => { + if (!element) { + return 0 + } + + // Get transition-duration of the element + let { transitionDuration, transitionDelay } = window.getComputedStyle(element) + + const floatTransitionDuration = Number.parseFloat(transitionDuration) + const floatTransitionDelay = Number.parseFloat(transitionDelay) + + // Return 0 if element or transition duration is not found + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0 + } + + // If multiple durations are defined, take the first + transitionDuration = transitionDuration.split(',')[0] + transitionDelay = transitionDelay.split(',')[0] + + return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * 1000 +} + +export default getTransitionDurationFromElement \ No newline at end of file diff --git a/packages/coreui-react/src/utils/index.ts b/packages/coreui-react/src/utils/index.ts index d6f647f6..10dbd154 100644 --- a/packages/coreui-react/src/utils/index.ts +++ b/packages/coreui-react/src/utils/index.ts @@ -1,5 +1,13 @@ +import executeAfterTransition from './executeAfterTransition' import getRTLPlacement from './getRTLPlacement' +import getTransitionDurationFromElement from './getTransitionDurationFromElement' import isInViewport from './isInViewport' import isRTL from './isRTL' -export { getRTLPlacement, isInViewport, isRTL } +export { + executeAfterTransition, + getRTLPlacement, + getTransitionDurationFromElement, + isInViewport, + isRTL, +} diff --git a/packages/docs/build/api.js b/packages/docs/build/api.js index 72f52002..084599da 100644 --- a/packages/docs/build/api.js +++ b/packages/docs/build/api.js @@ -7,11 +7,15 @@ const path = require('node:path') const globby = require('globby') const docgen = require('react-docgen-typescript') -const GLOB = ['**/src/**/*.tsx'] +const GLOB = [ + '**/src/**/*.tsx', + '../node_modules/@coreui/icons-react/src/**/*.tsx', + '../node_modules/@coreui/react-chartjs/src/**/*.tsx', +] const GLOBBY_OPTIONS = { absolute: true, cwd: path.join(__dirname, '..', '..'), - gitignore: true, + gitignore: false, ignore: ['**/docs/**', '**/__tests__/**'], } const EXCLUDED_FILES = [] @@ -35,13 +39,20 @@ async function createMdx(file, filename, name, props) { } const pro = PRO_COMPONENTS.some((v) => file.includes(v)) - const relativeFilename = file.replace(GLOBBY_OPTIONS.cwd, '').replace('coreui-', '') + let relativeFilename + if (file.includes('node_modules')) { + relativeFilename = file.replace(path.join(file, '..', '..', '..'), '').replace('coreui-', '') + } else { + relativeFilename = file.replace(GLOBBY_OPTIONS.cwd, '').replace('coreui-', '') + } + + if (!pro) { + relativeFilename = relativeFilename.replace('-pro', '') + } let content = `\n` content += `\`\`\`jsx\n` - content += `import { ${name} } from '@coreui/${relativeFilename.split('/')[1]}${ - pro ? '-pro' : '' - }'\n` + content += `import { ${name} } from '@coreui/${relativeFilename.split('/')[1]}'\n` content += `// or\n` content += `import ${name} from '@coreui${relativeFilename.replace('.tsx', '')}'\n` content += `\`\`\`\n\n` diff --git a/packages/docs/content/api/CConditionalPortal.api.mdx b/packages/docs/content/api/CConditionalPortal.api.mdx index cb833359..4a269bf2 100644 --- a/packages/docs/content/api/CConditionalPortal.api.mdx +++ b/packages/docs/content/api/CConditionalPortal.api.mdx @@ -7,4 +7,5 @@ import CConditionalPortal from '@coreui/react/src/components/conditional-portal/ | Property | Description | Type | Default | | --- | --- | --- | --- | -| **portal** | Render some children into a different part of the DOM | `boolean` | - | +| **container** **_v4.11.0+_** | An HTML element or function that returns a single element, with `document.body` as the default. | `Element` \| `(() => Element)` | - | +| **portal** | Render some children into a different part of the DOM | `any` | - | diff --git a/packages/docs/content/api/CDropdown.api.mdx b/packages/docs/content/api/CDropdown.api.mdx index 451f42fc..7e4c3d32 100644 --- a/packages/docs/content/api/CDropdown.api.mdx +++ b/packages/docs/content/api/CDropdown.api.mdx @@ -11,9 +11,11 @@ import CDropdown from '@coreui/react/src/components/dropdown/CDropdown' | **autoClose** | Configure the auto close behavior of the dropdown:
- `true` - the dropdown will be closed by clicking outside or inside the dropdown menu.
- `false` - the dropdown will be closed by clicking the toggle button and manually calling hide or toggle method. (Also will not be closed by pressing esc key)
- `'inside'` - the dropdown will be closed (only) by clicking inside the dropdown menu.
- `'outside'` - the dropdown will be closed (only) by clicking outside the dropdown menu. | `boolean` \| `'inside'` \| `'outside'` | true | | **className** | A string of all className you want applied to the base component. | `string` | - | | **component** | Component used for the root node. Either a string to use a HTML element or a component. | `string` \| `ComponentClass` \| `FunctionComponent` | div | +| **container** **_v4.11.0+_** | Appends the react dropdown menu to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`. | `Element` \| `(() => Element)` | - | | **dark** | Sets a darker color scheme to match a dark navbar. | `boolean` | - | | **direction** | Sets a specified direction and location of the dropdown menu. | `'center'` \| `'dropup'` \| `'dropup-center'` \| `'dropend'` \| `'dropstart'` | - | -| **onHide** | Callback fired when the component requests to be hidden. | `() => void` | - | +| **offset** | Offset of the dropdown menu relative to its target. | `[number, number]` | [0, 2] | +| **onHide** **_4.9.0+_** | Callback fired when the component requests to be hidden. | `() => void` | - | | **onShow** | Callback fired when the component requests to be shown. | `() => void` | - | | **placement** | Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property. | `'auto'` \| `'top-end'` \| `'top'` \| `'top-start'` \| `'bottom-end'` \| `'bottom'` \| `'bottom-start'` \| `'right-start'` \| `'right'` \| `'right-end'` \| `'left-start'` \| `'left'` \| `'left-end'` | bottom-start | | **popper** | If you want to disable dynamic positioning set this property to `true`. | `boolean` | true | diff --git a/packages/docs/content/api/CModal.api.mdx b/packages/docs/content/api/CModal.api.mdx index 7134c041..635c688e 100644 --- a/packages/docs/content/api/CModal.api.mdx +++ b/packages/docs/content/api/CModal.api.mdx @@ -10,6 +10,7 @@ import CModal from '@coreui/react/src/components/modal/CModal' | **alignment** | Align the modal in the center or top of the screen. | `'top'` \| `'center'` | - | | **backdrop** | Apply a backdrop on body while modal is open. | `boolean` \| `'static'` | true | | **className** | A string of all className you want applied to the base component. | `string` | - | +| **focus** **_v4.10.0+_** | Puts the focus on the modal when shown. | `boolean` | true | | **fullscreen** | Set modal to covers the entire user viewport. | `boolean` \| `'sm'` \| `'md'` \| `'lg'` \| `'xl'` \| `'xxl'` | - | | **keyboard** | Closes the modal when escape key is pressed. | `boolean` | true | | **onClose** | Callback fired when the component requests to be closed. | `() => void` | - | diff --git a/packages/docs/content/api/CPopover.api.mdx b/packages/docs/content/api/CPopover.api.mdx index 0e991b9e..40ad6fb7 100644 --- a/packages/docs/content/api/CPopover.api.mdx +++ b/packages/docs/content/api/CPopover.api.mdx @@ -7,12 +7,16 @@ import CPopover from '@coreui/react/src/components/popover/CPopover' | Property | Description | Type | Default | | --- | --- | --- | --- | +| **animation** **_4.9.0+_** | Apply a CSS fade transition to the popover. | `boolean` | true | | **className** | A string of all className you want applied to the component. | `string` | - | +| **container** **_v4.11.0+_** | Appends the react popover to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`. | `Element` \| `(() => Element)` | - | | **content** | Content node for your component. | `ReactNode` | - | +| **delay** **_4.9.0+_** | The delay for displaying and hiding the popover (in milliseconds). When a numerical value is provided, the delay applies to both the hide and show actions. The object structure for specifying the delay is as follows: delay: `{ 'show': 500, 'hide': 100 }`. | `number` \| `{ show: number; hide: number; }` | 0 | +| **fallbackPlacements** **_4.9.0+_** | Specify the desired order of fallback placements by providing a list of placements as an array. The placements should be prioritized based on preference. | `Placements` \| `Placements[]` | ['top', 'right', 'bottom', 'left'] | | **offset** | Offset of the popover relative to its target. | `[number, number]` | [0, 8] | | **onHide** | Callback fired when the component requests to be hidden. | `() => void` | - | | **onShow** | Callback fired when the component requests to be shown. | `() => void` | - | -| **placement** | Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property. | `'auto'` \| `'top'` \| `'right'` \| `'bottom'` \| `'left'` | top | +| **placement** | Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property. | `'auto'` \| `'top'` \| `'bottom'` \| `'right'` \| `'left'` | top | | **title** | Title node for your component. | `ReactNode` | - | | **trigger** | Sets which event handlers you’d like provided to your toggle prop. You can specify one trigger or an array of them. | `'hover'` \| `'focus'` \| `'click'` | click | | **visible** | Toggle the visibility of popover component. | `boolean` | - | diff --git a/packages/docs/content/api/CProgress.api.mdx b/packages/docs/content/api/CProgress.api.mdx index c7c4388f..e7d61f16 100644 --- a/packages/docs/content/api/CProgress.api.mdx +++ b/packages/docs/content/api/CProgress.api.mdx @@ -11,7 +11,8 @@ import CProgress from '@coreui/react/src/components/progress/CProgress' | **className** | A string of all className you want applied to the component. | `string` | - | | **color** | Sets the color context of the component to one of CoreUI’s themed colors. | `'primary'` \| `'secondary'` \| `'success'` \| `'danger'` \| `'warning'` \| `'info'` \| `'dark'` \| `'light'` \| `string` | - | | **height** | Sets the height of the component. If you set that value the inner `` will automatically resize accordingly. | `number` | - | +| **progressBarClassName** **_4.9.0+_** | A string of all className you want applied to the `` component. | `string` | - | | **thin** | Makes progress bar thinner. | `boolean` | - | -| **value** | The percent to progress the ProgressBar (out of 100). | `number` | 0 | +| **value** | The percent to progress the ProgressBar (out of 100). | `number` | - | | **variant** | Set the progress bar variant to optional striped. | `'striped'` | - | | **white** | Change the default color to white. | `boolean` | - | diff --git a/packages/docs/content/api/CProgressStacked.api.mdx b/packages/docs/content/api/CProgressStacked.api.mdx new file mode 100644 index 00000000..109af3cf --- /dev/null +++ b/packages/docs/content/api/CProgressStacked.api.mdx @@ -0,0 +1,10 @@ + +```jsx +import { CProgressStacked } from '@coreui/react' +// or +import CProgressStacked from '@coreui/react/src/components/progress/CProgressStacked' +``` + +| Property | Description | Type | Default | +| --- | --- | --- | --- | +| **className** | A string of all className you want applied to the component. | `string` | - | diff --git a/packages/docs/content/api/CTooltip.api.mdx b/packages/docs/content/api/CTooltip.api.mdx index 010885cb..047a8ff8 100644 --- a/packages/docs/content/api/CTooltip.api.mdx +++ b/packages/docs/content/api/CTooltip.api.mdx @@ -7,11 +7,15 @@ import CTooltip from '@coreui/react/src/components/tooltip/CTooltip' | Property | Description | Type | Default | | --- | --- | --- | --- | +| **animation** **_4.9.0+_** | Apply a CSS fade transition to the tooltip. | `boolean` | true | | **className** | A string of all className you want applied to the component. | `string` | - | +| **container** **_v4.11.0+_** | Appends the react tooltip to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`. | `Element` \| `(() => Element)` | - | | **content** | Content node for your component. | `ReactNode` | - | -| **offset** | Offset of the popover relative to its target. | `[number, number]` | [0, 0] | +| **delay** **_4.9.0+_** | The delay for displaying and hiding the tooltip (in milliseconds). When a numerical value is provided, the delay applies to both the hide and show actions. The object structure for specifying the delay is as follows: delay: `{ 'show': 500, 'hide': 100 }`. | `number` \| `{ show: number; hide: number; }` | 0 | +| **fallbackPlacements** **_4.9.0+_** | Specify the desired order of fallback placements by providing a list of placements as an array. The placements should be prioritized based on preference. | `Placements` \| `Placements[]` | ['top', 'right', 'bottom', 'left'] | +| **offset** | Offset of the tooltip relative to its target. | `[number, number]` | [0, 6] | | **onHide** | Callback fired when the component requests to be hidden. | `() => void` | - | | **onShow** | Callback fired when the component requests to be shown. | `() => void` | - | -| **placement** | Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property. | `'auto'` \| `'top'` \| `'right'` \| `'bottom'` \| `'left'` | top | -| **trigger** | Sets which event handlers you’d like provided to your toggle prop. You can specify one trigger or an array of them. | `'hover'` \| `'focus'` \| `'click'` | hover | -| **visible** | Toggle the visibility of popover component. | `boolean` | - | +| **placement** | Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property. | `'auto'` \| `'top'` \| `'bottom'` \| `'right'` \| `'left'` | top | +| **trigger** | Sets which event handlers you’d like provided to your toggle prop. You can specify one trigger or an array of them. | `'hover'` \| `'focus'` \| `'click'` | ['hover', 'focus'] | +| **visible** | Toggle the visibility of tooltip component. | `boolean` | - | diff --git a/packages/docs/content/components/button-group.mdx b/packages/docs/content/components/button-group.mdx index 5d521743..d14bcb53 100644 --- a/packages/docs/content/components/button-group.mdx +++ b/packages/docs/content/components/button-group.mdx @@ -49,7 +49,7 @@ These classes can also be added to groups of links, as an alternative to the ` Active link Link - Link + Link ``` diff --git a/packages/docs/content/components/chart.mdx b/packages/docs/content/components/chart.mdx index 5de2d340..e4b8e43b 100644 --- a/packages/docs/content/components/chart.mdx +++ b/packages/docs/content/components/chart.mdx @@ -44,24 +44,8 @@ A line chart is a way of plotting data points on a line. Often, it is used to sh [Line Chart properties](https://www.chartjs.org/docs/latest/charts/line.html#dataset-properties) export const LineChartExample = () => { - const chartRef = useRef() - - useEffect(() => { - document.documentElement.addEventListener('ColorSchemeChange', () => { - if (chartRef.current) { - chartRef.current.options.plugins.legend.labels.color = getStyle('--cui-body-color') - chartRef.current.options.scales.x.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.x.ticks.color = getStyle('--cui-body-color') - chartRef.current.options.scales.y.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.y.ticks.color = getStyle('--cui-body-color') - chartRef.current.update() - } - }) - }, [chartRef]) - return ( { A bar chart provides a way of showing data values represented as vertical bars. It is sometimes used to show trend data, and the comparison of multiple data sets side by side. [Bar Chart properties](https://www.chartjs.org/docs/latest/charts/bar.html#dataset-properties) export const BarChartExample = () => { - const chartRef = useRef() - - useEffect(() => { - document.documentElement.addEventListener('ColorSchemeChange', () => { - if (chartRef.current) { - chartRef.current.options.plugins.legend.labels.color = getStyle('--cui-body-color') - chartRef.current.options.scales.x.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.x.ticks.color = getStyle('--cui-body-color') - chartRef.current.options.scales.y.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.y.ticks.color = getStyle('--cui-body-color') - chartRef.current.update() - } - }) - }, [chartRef]) - return ( { A radar chart is a way of showing multiple data points and the variation between them. They are often useful for comparing the points of two or more different data sets. [Radar Chart properties](https://www.chartjs.org/docs/latest/charts/radar.html#dataset-properties) export const RadarChartExample = () => { - const chartRef = useRef() - - useEffect(() => { - document.documentElement.addEventListener('ColorSchemeChange', () => { - if (chartRef.current) { - chartRef.current.options.plugins.legend.labels.color = getStyle('--cui-body-color') - chartRef.current.options.scales.r.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.r.ticks.color = getStyle('--cui-body-color') - chartRef.current.update() - } - }) - }, [chartRef]) - return ( { Pie and doughnut charts are probably the most commonly used charts. They are divided into segments, the arc of each segment shows the proportional value of each piece of data. [Doughnut and Pie Charts properties](https://www.chartjs.org/docs/latest/charts/doughnut.html#dataset-properties) export const DoughnutAndPieExample = () => { - const chartRef = useRef() - - useEffect(() => { - document.documentElement.addEventListener('ColorSchemeChange', () => { - if (chartRef.current) { - chartRef.current.options.plugins.legend.labels.color = getStyle('--cui-body-color') - chartRef.current.update() - } - }) - }, [chartRef]) - return ( { Polar area charts are similar to pie charts, but each segment has the same angle - the radius of the segment differs depending on the value. [Polar Area Chart properties](https://www.chartjs.org/docs/latest/charts/polar.html#dataset-properties) export const PolarAreaExample = () => { - const chartRef = useRef() - - useEffect(() => { - document.documentElement.addEventListener('ColorSchemeChange', () => { - if (chartRef.current) { - chartRef.current.options.plugins.legend.labels.color = getStyle('--cui-body-color') - chartRef.current.options.scales.r.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.update() - } - }) - }, [chartRef]) - return ( { A bubble chart is used to display three dimensions of data at the same time. The location of the bubble is determined by the first two dimensions and the corresponding horizontal and vertical axes. The third dimension is represented by the size of the individual bubbles. [Bubble Chart properties](https://www.chartjs.org/docs/latest/charts/bubble.html#dataset-properties) export const BubbleChartExample = () => { - const chartRef = useRef() - - useEffect(() => { - document.documentElement.addEventListener('ColorSchemeChange', () => { - if (chartRef.current) { - chartRef.current.options.plugins.legend.labels.color = getStyle('--cui-body-color') - chartRef.current.options.scales.x.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.x.ticks.color = getStyle('--cui-body-color') - chartRef.current.options.scales.y.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.y.ticks.color = getStyle('--cui-body-color') - chartRef.current.update() - } - }) - }, [chartRef]) - return ( { A bubble chart is used to display three dimensions of data at the same time. The location of the bubble is determined by the first two dimensions and the corresponding horizontal and vertical axes. The third dimension is represented by the size of the individual bubbles. [Scatter Chart properties](https://www.chartjs.org/docs/latest/charts/scatter.html#dataset-properties) export const ScatterChartExample = () => { - const chartRef = useRef() - - useEffect(() => { - document.documentElement.addEventListener('ColorSchemeChange', () => { - if (chartRef.current) { - chartRef.current.options.plugins.legend.labels.color = getStyle('--cui-body-color') - chartRef.current.options.scales.x.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.x.ticks.color = getStyle('--cui-body-color') - chartRef.current.options.scales.y.grid.color = getStyle('--cui-border-color-translucent') - chartRef.current.options.scales.y.ticks.color = getStyle('--cui-body-color') - chartRef.current.update() - } - }) - }, [chartRef]) - return ( ` to show ` +

Some example text that's free-flowing within the dropdown menu.

And this is more example text.

diff --git a/packages/docs/content/components/icon.mdx b/packages/docs/content/components/icon.mdx index 89caf47c..e014af45 100644 --- a/packages/docs/content/components/icon.mdx +++ b/packages/docs/content/components/icon.mdx @@ -1,7 +1,7 @@ --- title: React Icons Component name: Icon -description: React icons is a great resource for React developers, who can use its customizable SVG icons in their applications. It offers an extensive library of icons to choose from, which can be easily inserted into projects with just a few lines of code. Not only that, but users are also able to customize the appearance of these icons by setting various props on them. This provides developers with an efficient and flexible way to integrate useful graphical elements into their webpages without doing any extra work. +description: React icons library is a great resource for React developers, who can use its customizable SVG icons in their applications. It offers an extensive library of icons to choose from, which can be easily inserted into projects with just a few lines of code. Not only that, but users are also able to customize the appearance of these icons by setting various props on them. This provides developers with an efficient and flexible way to integrate useful graphical elements into their web pages without doing any extra work. menu: Components route: /components/icon --- @@ -21,28 +21,27 @@ import * as icon from '@coreui/icons'; ## Installation -To use React icons in your project, you will need to install it as a dependency: +To start using CoreUI React Icons in your project, you need to install it as a dependency. Follow the instructions below based on your package manager of choice: ### Npm ```bash -// CoreUI Icons library -npm install @coreui/icons - -// CIcon component -npm install @coreui/icons-react +npm install @coreui/icons @coreui/icons-react ``` ### Yarn ```bash -yarn add @coreui/icons -yarn add @coreui/icons-react +yarn add @coreui/icons @coreui/icons-react ``` ## Usage -### Single icon +Import react icons using one of these two options: + +### Single react icon + +To use a single react icon, import the `` component and the desired icon(s) from the `@coreui/icons` library. Then, include the `` component in your code and specify the icon prop with the imported icon variable. Additionally, you can set the desired size for the icon using the `size` prop. @@ -59,7 +58,9 @@ import { cilList, cilShieldAlt } from '@coreui/icons'; ... ``` -### All icons +### All react icons + +To use all icons available in the CoreUI React Icons package, import the CIcon component and the entire `@coreui/icons` library using the `* as` syntax. Then, reference the desired icon within the `icon` prop. ```jsx import CIcon from '@coreui/icons-react'; @@ -74,9 +75,125 @@ render() { ... ``` +### Color + +The CoreUI React Icon component offers versatile color customization options, empowering you to personalize the icons in multiple ways. You can effortlessly modify the colors by utilizing either class names or CSS variables, providing flexibility and ease in creating visually stunning and cohesive icon designs. + +#### Utility classes + +With some [color utility classes](https://coreui.io/docs/utilities/colors/), you may use color to convey message. + +```jsx preview + + + + + + + +``` + +#### CSS Variables + +CoreUI React Icons leverage local CSS variables, such as `--ci-primary-color` and `--ci-secondary-color` (for Duotone icons), to facilitate real-time customization. This allows developers to easily customize the icons by providing their own custom CSS variables. + +```jsx preview + + +``` + +### Sizing + +Set heights of react icons using size property like size="lg" and size="sm". + +```jsx preview + + + + + + +``` + + +## Accessibility + +It's crucial for react icons to be seen by as many people as possible because they have the power to communicate a variety of meaningful information. + +People who are blind, have low vision, or have other visual impairments make up approximately 10% of the world's population, and more than 5% of people worldwide have hearing loss that makes them unable to function normally. + +Therefore, it's crucial to make sure that the assistive equipment for people with disabilities, such as screen readers, either ignores or better understands the react icons you use online. + +Icons are used in one of two ways on websites, apps, and other digital spaces. + +#### Decorative Icons + +It is not necessary to declare an icon to visitors when they are using your website or app if you are utilizing it to offer extra decoration or branding. + +Additionally, if you use an icon to visually emphasize or add flair to content that is already present in your HTML, an assistive technology user does not need to see it again. + +In certain circumstances, the details of the icon ought to be concealed from the screenreader so as not to obstruct the intended message. + +#### Semantic Icons + +You need to make sure that consumers understand the meaning an icon is intended to represent by giving them text-based alternatives. + +This applies to both the content you're using icons to represent (such as the status of your shopping cart or the number of unread messages), as well react icons as interactive controls (such as buttons, form elements, toggles, etc.). + +### CoreUI React Icons and Accessibility + +Our React Icon component automatically takes care of accessibility concerns by adding appropriate elements. + +#### Decorative icons + +If your icons are only for decorative purposes, the CoreUI React Icon Component will ensure that assistive technology ignores the icon. In addition to referencing an icon as normal, the `aria-hidden=true` attribute has been introduced, so there is nothing else you need to do. + +If your markup looks like this: + +```jsx + + + +``` + +By including the `aria-hidden` attribute, CoreUI React Icon Component will afterwards automatically hide it from screenreaders. + +```html + +``` + +#### Semantic icons + +The description that you must set using the title property will be used by CoreUI React Icon Component to generate alternative text for the semantic icon. + +Thus, if your markup appears as follows: + +```jsx + + + +``` + +CoreUI React Icon Component will make the necessary adjustments so that only screenreaders can "see" the supporting elements that convey the message. + +```html + +``` + ## Available react icons -CoreUI React Icons package is delivered with more than 1500 icons in multiple formats SVG, PNG, and Webfonts. CoreUI Icons are beautifully crafted symbols for common actions and items. You can use them in your digital products for web or mobile app. +The CoreUI React Icons package includes a comprehensive library of more than 1500 icons, available in various formats such as SVG, PNG, and Webfonts. These popular icons are meticulously crafted symbols representing common actions and items. You can utilize them in your digital products, whether they are web or mobile applications, to enhance their visual appeal and user experience. + +By leveraging the capabilities of the React Icons component from CoreUI, you can easily incorporate visually appealing icons into their React applications, allowing for more engaging and intuitive user interfaces. export const LinearExample = () => { const icons = ['cilAccountLogout', 'cilActionRedo', 'cilActionUndo', 'cilAddressBook', 'cilAirplaneModeOff', 'cilAirplaneMode', 'cilAirplay', 'cilAlarm', 'cilAlbum', 'cilAlignCenter', 'cilAlignLeft', 'cilAlignRight', 'cilAmericanFootball', 'cilAnimal', 'cilAperture', 'cilApple', 'cilApplicationsSettings', 'cilApplications', 'cilAppsSettings', 'cilApps', 'cilArrowBottom', 'cilArrowCircleBottom', 'cilArrowCircleLeft', 'cilArrowCircleRight', 'cilArrowCircleTop', 'cilArrowLeft', 'cilArrowRight', 'cilArrowThickBottom', 'cilArrowThickFromBottom', 'cilArrowThickFromLeft', 'cilArrowThickFromRight', 'cilArrowThickFromTop', 'cilArrowThickLeft', 'cilArrowThickRight', 'cilArrowThickToBottom', 'cilArrowThickToLeft', 'cilArrowThickToRight', 'cilArrowThickToTop', 'cilArrowThickTop', 'cilArrowTop', 'cilAssistiveListeningSystem', 'cilAsteriskCircle', 'cilAsterisk', 'cilAt', 'cilAudioDescription', 'cilAudioSpectrum', 'cilAudio', 'cilAvTimer', 'cilBabyCarriage', 'cilBaby', 'cilBackspace', 'cilBadge', 'cilBalanceScale', 'cilBan', 'cilBank', 'cilBarChart', 'cilBarcode', 'cilBaseball', 'cilBasket', 'cilBasketball', 'cilBath', 'cilBathroom', 'cilBattery0', 'cilBattery3', 'cilBattery5', 'cilBatteryAlert', 'cilBatteryEmpty', 'cilBatteryFull', 'cilBatterySlash', 'cilBeachAccess', 'cilBeaker', 'cilBed', 'cilBellExclamation', 'cilBell', 'cilBike', 'cilBirthdayCake', 'cilBlind', 'cilBluetooth', 'cilBlurCircular', 'cilBlurLinear', 'cilBlur', 'cilBoatAlt', 'cilBold', 'cilBoltCircle', 'cilBolt', 'cilBook', 'cilBookmark', 'cilBorderAll', 'cilBorderBottom', 'cilBorderClear', 'cilBorderHorizontal', 'cilBorderInner', 'cilBorderLeft', 'cilBorderOuter', 'cilBorderRight', 'cilBorderStyle', 'cilBorderTop', 'cilBorderVertical', 'cilBowling', 'cilBraille', 'cilBriefcase', 'cilBrightness', 'cilBritishPound', 'cilBrowser', 'cilBrushAlt', 'cilBrush', 'cilBug', 'cilBuilding', 'cilBullhorn', 'cilBurger', 'cilBurn', 'cilBusAlt', 'cilCalculator', 'cilCalendarCheck', 'cilCalendar', 'cilCameraControl', 'cilCameraRoll', 'cilCamera', 'cilCarAlt', 'cilCaretBottom', 'cilCaretLeft', 'cilCaretRight', 'cilCaretTop', 'cilCart', 'cilCash', 'cilCasino', 'cilCast', 'cilCat', 'cilCc', 'cilCenterFocus', 'cilChartLine', 'cilChartPie', 'cilChart', 'cilChatBubble', 'cilCheckAlt', 'cilCheckCircle', 'cilCheck', 'cilChevronBottom', 'cilChevronCircleDownAlt', 'cilChevronCircleLeftAlt', 'cilChevronCircleRightAlt', 'cilChevronCircleUpAlt', 'cilChevronDoubleDown', 'cilChevronDoubleLeft', 'cilChevronDoubleRight', 'cilChevronDoubleUp', 'cilChevronLeft', 'cilChevronRight', 'cilChevronTop', 'cilChildFriendly', 'cilChild', 'cilCircle', 'cilClearAll', 'cilClipboard', 'cilClock', 'cilClone', 'cilClosedCaptioning', 'cilCloudDownload', 'cilCloudUpload', 'cilCloud', 'cilCloudy', 'cilCode', 'cilCoffee', 'cilCog', 'cilColorBorder', 'cilColorFill', 'cilColorPalette', 'cilColumns', 'cilCommand', 'cilCommentBubble', 'cilCommentSquare', 'cilCompass', 'cilCompress', 'cilContact', 'cilContrast', 'cilControl', 'cilCopy', 'cilCouch', 'cilCreditCard', 'cilCropRotate', 'cilCrop', 'cilCursorMove', 'cilCursor', 'cilCut', 'cilDataTransferDown', 'cilDataTransferUp', 'cilDeaf', 'cilDelete', 'cilDescription', 'cilDevices', 'cilDialpad', 'cilDiamond', 'cilDinner', 'cilDisabled', 'cilDog', 'cilDollar', 'cilDoor', 'cilDoubleQuoteSansLeft', 'cilDoubleQuoteSansRight', 'cilDrinkAlcohol', 'cilDrink', 'cilDrop', 'cilEco', 'cilEducation', 'cilElevator', 'cilEnvelopeClosed', 'cilEnvelopeLetter', 'cilEnvelopeOpen', 'cilEqualizer', 'cilEthernet', 'cilEuro', 'cilExcerpt', 'cilExitToApp', 'cilExpandDown', 'cilExpandLeft', 'cilExpandRight', 'cilExpandUp', 'cilExposure', 'cilExternalLink', 'cilEyedropper', 'cilFaceDead', 'cilFace', 'cilFactorySlash', 'cilFactory', 'cilFastfood', 'cilFax', 'cilFeaturedPlaylist', 'cilFile', 'cilFilterFrames', 'cilFilterPhoto', 'cilFilterSquare', 'cilFilterX', 'cilFilter', 'cilFindInPage', 'cilFingerprint', 'cilFire', 'cilFlagAlt', 'cilFlightTakeoff', 'cilFlipToBack', 'cilFlipToFront', 'cilFlip', 'cilFlower', 'cilFolderOpen', 'cilFolder', 'cilFont', 'cilFootball', 'cilFork', 'cilFridge', 'cilFrown', 'cilFullscreenExit', 'cilFullscreen', 'cilFunctionsAlt', 'cilFunctions', 'cilGamepad', 'cilGarage', 'cilGem', 'cilGif', 'cilGift', 'cilGlobeAlt', 'cilGolfAlt', 'cilGolf', 'cilGradient', 'cilGrain', 'cilGraph', 'cilGridSlash', 'cilGrid', 'cilGroup', 'cilHamburgerMenu', 'cilHandPointDown', 'cilHandPointLeft', 'cilHandPointRight', 'cilHandPointUp', 'cilHappy', 'cilHd', 'cilHdr', 'cilHeader', 'cilHeadphones', 'cilHealing', 'cilHeart', 'cilHighlighter', 'cilHighligt', 'cilHistory', 'cilHome', 'cilHospital', 'cilHotTub', 'cilHouse', 'cilHttps', 'cilImageBroken', 'cilImagePlus', 'cilImage', 'cilInbox', 'cilIndentDecrease', 'cilIndentIncrease', 'cilIndustrySlash', 'cilIndustry', 'cilInfinity', 'cilInfo', 'cilInputHdmi', 'cilInputPower', 'cilInput', 'cilInstitution', 'cilItalic', 'cilJustifyCenter', 'cilJustifyLeft', 'cilJustifyRight', 'cilKeyboard', 'cilLan', 'cilLanguage', 'cilLaptop', 'cilLayers', 'cilLeaf', 'cilLemon', 'cilLevelDown', 'cilLevelUp', 'cilLibraryAdd', 'cilLibraryBuilding', 'cilLibrary', 'cilLifeRing', 'cilLightbulb', 'cilLineSpacing', 'cilLineStyle', 'cilLineWeight', 'cilLinkAlt', 'cilLinkBroken', 'cilLink', 'cilListFilter', 'cilListHighPriority', 'cilListLowPriority', 'cilListNumberedRtl', 'cilListNumbered', 'cilListRich', 'cilList', 'cilLocationPin', 'cilLockLocked', 'cilLockUnlocked', 'cilLocomotive', 'cilLoop1', 'cilLoopCircular', 'cilLoop', 'cilLowVision', 'cilMagnifyingGlass', 'cilMap', 'cilMediaEject', 'cilMediaPause', 'cilMediaPlay', 'cilMediaRecord', 'cilMediaSkipBackward', 'cilMediaSkipForward', 'cilMediaStepBackward', 'cilMediaStepForward', 'cilMediaStop', 'cilMedicalCross', 'cilMeh', 'cilMemory', 'cilMenu', 'cilMic', 'cilMicrophone', 'cilMinus', 'cilMobileLandscape', 'cilMobile', 'cilMoney', 'cilMonitor', 'cilMoodBad', 'cilMoodGood', 'cilMoodVeryBad', 'cilMoodVeryGood', 'cilMoon', 'cilMouse', 'cilMouthSlash', 'cilMove', 'cilMovie', 'cilMugTea', 'cilMug', 'cilMusicNote', 'cilNewspaper', 'cilNoteAdd', 'cilNotes', 'cilObjectGroup', 'cilObjectUngroup', 'cilOpacity', 'cilOpentype', 'cilOptions', 'cilPaintBucket', 'cilPaint', 'cilPaperPlane', 'cilPaperclip', 'cilParagraph', 'cilPaw', 'cilPenAlt', 'cilPenNib', 'cilPen', 'cilPencil', 'cilPeople', 'cilPhone', 'cilPin', 'cilPizza', 'cilPlant', 'cilPlaylistAdd', 'cilPlus', 'cilPool', 'cilPowerStandby', 'cilPregnant', 'cilPrint', 'cilPushchair', 'cilPuzzle', 'cilQrCode', 'cilRain', 'cilRectangle', 'cilRecycle', 'cilReload', 'cilReportSlash', 'cilResizeBoth', 'cilResizeHeight', 'cilResizeWidth', 'cilRestaurant', 'cilRoom', 'cilRouter', 'cilRowing', 'cilRss', 'cilRuble', 'cilRunning', 'cilSad', 'cilSatelite', 'cilSave', 'cilSchool', 'cilScreenDesktop', 'cilScreenSmartphone', 'cilScrubber', 'cilSearch', 'cilSend', 'cilSettings', 'cilShareAll', 'cilShareAlt', 'cilShareBoxed', 'cilShare', 'cilShieldAlt', 'cilShortText', 'cilShower', 'cilSignLanguage', 'cilSignalCellular0', 'cilSignalCellular3', 'cilSignalCellular4', 'cilSim', 'cilSitemap', 'cilSmilePlus', 'cilSmile', 'cilSmokeFree', 'cilSmokeSlash', 'cilSmoke', 'cilSmokingRoom', 'cilSnowflake', 'cilSoccer', 'cilSofa', 'cilSortAlphaDown', 'cilSortAlphaUp', 'cilSortAscending', 'cilSortDescending', 'cilSortNumericDown', 'cilSortNumericUp', 'cilSpa', 'cilSpaceBar', 'cilSpeak', 'cilSpeaker', 'cilSpeech', 'cilSpeedometer', 'cilSpreadsheet', 'cilSquare', 'cilStarHalf', 'cilStar', 'cilStorage', 'cilStream', 'cilStrikethrough', 'cilSun', 'cilSwapHorizontal', 'cilSwapVertical', 'cilSwimming', 'cilSync', 'cilTablet', 'cilTag', 'cilTags', 'cilTask', 'cilTaxi', 'cilTennisBall', 'cilTennis', 'cilTerminal', 'cilTerrain', 'cilTextShapes', 'cilTextSize', 'cilTextSquare', 'cilTextStrike', 'cilText', 'cilThumbDown', 'cilThumbUp', 'cilToggleOff', 'cilToggleOn', 'cilToilet', 'cilTouchApp', 'cilTransfer', 'cilTranslate', 'cilTrash', 'cilTriangle', 'cilTruck', 'cilTv', 'cilUnderline', 'cilUsb', 'cilUserFemale', 'cilUserFollow', 'cilUserPlus', 'cilUserUnfollow', 'cilUserX', 'cilUser', 'cilVector', 'cilVerticalAlignBottom', 'cilVerticalAlignCenter', 'cilVerticalAlignTop', 'cilVideo', 'cilVideogame', 'cilViewColumn', 'cilViewModule', 'cilViewQuilt', 'cilViewStream', 'cilVoiceOverRecord', 'cilVoice', 'cilVolumeHigh', 'cilVolumeLow', 'cilVolumeOff', 'cilWalk', 'cilWallet', 'cilWallpaper', 'cilWarning', 'cilWatch', 'cilWc', 'cilWeightlifitng', 'cilWheelchair', 'cilWifiSignal0', 'cilWifiSignal1', 'cilWifiSignal2', 'cilWifiSignal3', 'cilWifiSignal4', 'cilWifiSignalOff', 'cilWindowMaximize', 'cilWindowMinimize', 'cilWindowRestore', 'cilWindow', 'cilWrapText', 'cilXCircle', 'cilX', 'cilYen', 'cilZoomIn', 'cilZoomOut', 'cilZoom'] @@ -190,6 +307,12 @@ export const TabPanesExample = () => { React Icons also provides a variety of customization options, such as the ability to change the size, color, and style of the icons, as well as the ability to add additional CSS classes to the icons. You can find more information on these customization options in the documentation. +## Premium icons + +If you find yourself in need of a greater selection of icons, we've got you covered with our [premium icon pack](https://coreui.io/icons/all/). This incredible package boasts an extensive collection of over 4,000 meticulously crafted icons, ensuring that you'll find the perfect representation for any concept or idea. Explore the vast range of options and unlock a world of visual possibilities. + +So if you need more icons our PRO package will be a great choice for you. + ## API ### CIcon diff --git a/packages/docs/content/components/modal.mdx b/packages/docs/content/components/modal.mdx index 4665d7d5..f5256409 100644 --- a/packages/docs/content/components/modal.mdx +++ b/packages/docs/content/components/modal.mdx @@ -69,9 +69,13 @@ export const LiveDemoExample = () => { return ( <> setVisible(!visible)}>Launch demo modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="LiveDemoExampleLabel" + > - Modal title + Modal title

Woohoo, you're reading this text in a modal!

@@ -96,9 +100,13 @@ const [visible, setVisible] = useState(false) return ( <> setVisible(!visible)}>Launch demo modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="LiveDemoExampleLabel" + > setVisible(false)}> - Modal title + Modal title

Woohoo, you're reading this text in a modal!

@@ -123,9 +131,14 @@ export const StaticBackdropExample = () => { return ( <> setVisible(!visible)}>Launch static backdrop modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="StaticBackdropExampleLabel" + > - Modal title + Modal title I will not close if you click outside me. Don't even try to press escape key. @@ -150,9 +163,14 @@ const [visible, setVisible] = useState(false) return ( <> setVisible(!visible)}>Launch static backdrop modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="StaticBackdropExampleLabel" + > - Modal title + Modal title I will not close if you click outside me. Don't even try to press escape key. @@ -177,9 +195,13 @@ export const ScrollingLongContentExample = () => { return ( <> setVisible(!visible)}>Launch demo modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="ScrollingLongContentExampleLabel" + > - Modal title + Modal title

@@ -281,9 +303,13 @@ const [visible, setVisible] = useState(false) return ( <> setVisible(!visible)}>Launch demo modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="ScrollingLongContentExampleLabel" + > - Modal title + Modal title

@@ -383,9 +409,14 @@ export const ScrollingLongContentExample2 = () => { return ( <> setVisible(!visible)}>Launch demo modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="ScrollingLongContentExampleLabel2" + > - Modal title + Modal title

@@ -487,9 +518,14 @@ const [visible, setVisible] = useState(false) return ( <> setVisible(!visible)}>Launch demo modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="ScrollingLongContentExampleLabel2" + > - Modal title + Modal title

@@ -591,9 +627,14 @@ export const VerticallyCenteredExample = () => { return ( <> setVisible(!visible)}>Vertically centered modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="VerticallyCenteredExample" + > - Modal title + Modal title Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis @@ -619,9 +660,14 @@ const [visible, setVisible] = useState(false) return ( <> setVisible(!visible)}>Vertically centered modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="VerticallyCenteredExample" + > - Modal title + Modal title Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, @@ -643,9 +689,15 @@ export const VerticallyCenteredScrollableExample = () => { return ( <> setVisible(!visible)}>Vertically centered scrollable modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="VerticallyCenteredScrollableExample" + > - Modal title + Modal title

@@ -690,9 +742,15 @@ const [visible, setVisible] = useState(false) return ( <> setVisible(!visible)}>Vertically centered scrollable modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="VerticallyCenteredScrollableExample" + > - Modal title + Modal title

@@ -737,9 +795,14 @@ export const TooltipsAndPopoversExample = () => { return ( <> setVisible(!visible)}>Launch demo modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="TooltipsAndPopoverExample" + > - Modal title + Modal title

Popover in a modal
@@ -781,9 +844,14 @@ const [visible, setVisible] = useState(false) return ( <> setVisible(!visible)}>Launch demo modal - setVisible(false)}> + setVisible(false)} + aria-labelledby="TooltipsAndPopoverExample" + > - Modal title + Modal title
Popover in a modal
@@ -816,16 +884,160 @@ return ( ) ``` +### Toggle between modals + +Toggle between multiple modals with some clever placement of the `visible` props. **Please note multiple modals cannot be opened at the same time** — this method simply toggles between two separate modals. + +export const ToggleBetweenModalsExample = () => { + const [visible, setVisible] = useState(false) + const [visible2, setVisible2] = useState(false) + return ( + <> + setVisible(!visible)}>Open first modal + setVisible(false)} + aria-labelledby="ToggleBetweenModalsExample1" + > + + Modal 1 title + + +

Show a second modal and hide this one with the button below.

+
+ + { + setVisible(false) + setVisible2(true) + }} + > + Open second modal + + +
+ { + setVisible(true) + setVisible2(false) + }} + aria-labelledby="ToggleBetweenModalsExample2" + > + + Modal 2 title + + +

Hide this modal and show the first with the button below.

+
+ + { + setVisible(true) + setVisible2(false) + }} + > + Back to first + + +
+ + ) +} + + + + + +```jsx +const [visible, setVisible] = useState(false) +const [visible2, setVisible2] = useState(false) +return ( + <> + setVisible(!visible)}>Open first modal + setVisible(false)} + aria-labelledby="ToggleBetweenModalsExample1" + > + + Modal 1 title + + +

Show a second modal and hide this one with the button below.

+
+ + { + setVisible(false) + setVisible2(true) + }} + > + Open second modal + + +
+ { + setVisible(true) + setVisible2(false) + }} + aria-labelledby="ToggleBetweenModalsExample2" + > + + Modal 2 title + + +

Hide this modal and show the first with the button below.

+
+ + { + setVisible(true) + setVisible2(false) + }} + > + Back to first + + +
+ +) +``` + +### Change animation + +The variable `$modal-fade-transform` determines the transform state of React Modal component before the modal fade-in animation, whereas the variable `$modal-show-transform` determines the transform state of Modal component after the modal fade-in animation. + +If you want a zoom-in animation, for example, set `$modal-fade-transform: scale(.8)`. + +### Remove animation + +For modals that simply appear rather than fade into view, set `transition` to `false`. + +```jsx +... +``` + +### Accessibility + +Be sure to add `aria-labelledby="..."`, referencing the modal title, to `` Additionally, you may give a description of your modal dialog with `aria-describedby` on ``. Note that you don’t need to add `role="dialog`, `aria-hidden="true"`, and `aria-modal="true"` since we already add it. + ## Optional sizes Modals have three optional sizes, available via modifier classes to be placed on a ``. These sizes kick in at certain breakpoints to avoid horizontal scrollbars on narrower viewports. -| Size | Property size | Modal max-width | -| --- | --- | --- | -| Small | `'sm'` | `300px` | -| Default | None | `500px` | -| Large | `'lg'` | `800px` | -| Extra large | `'xl'` | `1140px` | +| Size | Property size | Modal max-width | +| ----------- | ------------- | --------------- | +| Small | `'sm'` | `300px` | +| Default | None | `500px` | +| Large | `'lg'` | `800px` | +| Extra large | `'xl'` | `1140px` | export const OptionalSizesExample = () => { const [visibleXL, setVisibleXL] = useState(false) @@ -836,21 +1048,36 @@ export const OptionalSizesExample = () => { setVisibleXL(!visibleXL)}>Extra large modal setVisibleLg(!visibleLg)}>Large modal setVisibleSm(!visibleSm)}>Small modal - setVisibleXL(false)}> + setVisibleXL(false)} + aria-labelledby="OptionalSizesExample1" + > - Extra large modal + Extra large modal ... - setVisibleLg(false)}> + setVisibleLg(false)} + aria-labelledby="OptionalSizesExample2" + > - Large modal + Large modal ... - setVisibleSm(false)}> + setVisibleSm(false)} + aria-labelledby="OptionalSizesExample3" + > - Small modal + Small modal ... @@ -871,21 +1098,36 @@ return ( setVisibleXL(!visibleXL)}>Extra large modal setVisibleLg(!visibleLg)}>Large modal setVisibleSm(!visibleSm)}>Small modal - setVisibleXL(false)}> + setVisibleXL(false)} + aria-labelledby="OptionalSizesExample1" + > - Extra large modal + Extra large modal ... - setVisibleLg(false)}> + setVisibleLg(false)} + aria-labelledby="OptionalSizesExample2" + > - Large modal + Large modal ... - setVisibleSm(false)}> + setVisibleSm(false)} + aria-labelledby="OptionalSizesExample3" + > - Small modal + Small modal ... @@ -897,14 +1139,14 @@ return ( Another override is the option to pop up a React modal component that covers the user viewport, available via property `fullscrean`. -| Fullscrean | Availability | -| --- | --- | -| `true` | Always | -| `'sm'` | Below `576px` | -| `'md'` | Below `768px` | -| `'lg'` | Below `992px` | -| `'xl'` | Below `1200px` | -| `'xxl'` | Below `1400px` | +| Fullscrean | Availability | +| ---------- | -------------- | +| `true` | Always | +| `'sm'` | Below `576px` | +| `'md'` | Below `768px` | +| `'lg'` | Below `992px` | +| `'xl'` | Below `1200px` | +| `'xxl'` | Below `1400px` | export const FullscreenExample = () => { const [visible, setVisible] = useState(false) @@ -921,39 +1163,69 @@ export const FullscreenExample = () => { setVisibleLg(!visibleLg)}>Full screen below lg setVisibleXL(!visibleXL)}>Full screen below xl setVisibleXXL(!visibleXXL)}>Full screen below xxl - setVisible(false)}> + setVisible(false)} + aria-labelledby="FullscreenExample1" + > - Full screen + Full screen ... - setVisibleSm(false)}> + setVisibleSm(false)} + aria-labelledby="FullscreenExample2" + > - Full screen below sm + Full screen below sm ... - setVisibleMd(false)}> + setVisibleMd(false)} + aria-labelledby="FullscreenExample3" + > - Full screen below md + Full screen below md ... - setVisibleLg(false)}> + setVisibleLg(false)} + aria-labelledby="FullscreenExample4" + > - Full screen below lg + Full screen below lg ... - setVisibleXL(false)}> + setVisibleXL(false)} + aria-labelledby="FullscreenExample5" + > - Full screen below xl + Full screen below xl ... - setVisibleXXL(false)}> + setVisibleXXL(false)} + aria-labelledby="FullscreenExample6" + > - Full screen below xxl + Full screen below xxl ... @@ -980,39 +1252,69 @@ return ( setVisibleLg(!visibleLg)}>Full screen below lg setVisibleXL(!visibleXL)}>Full screen below xl setVisibleXXL(!visibleXXL)}>Full screen below xxl - setVisible(false)}> + setVisible(false)} + aria-labelledby="FullscreenExample1" + > - Full screen + Full screen ... - setVisibleSm(false)}> + setVisibleSm(false)} + aria-labelledby="FullscreenExample2" + > - Full screen below sm + Full screen below sm ... - setVisibleMd(false)}> + setVisibleMd(false)} + aria-labelledby="FullscreenExample3" + > - Full screen below md + Full screen below md ... - setVisibleLg(false)}> + setVisibleLg(false)} + aria-labelledby="FullscreenExample4" + > - Full screen below lg + Full screen below lg ... - setVisibleXL(false)}> + setVisibleXL(false)} + aria-labelledby="FullscreenExample5" + > - Full screen below xl + Full screen below xl ... - setVisibleXXL(false)}> + setVisibleXXL(false)} + aria-labelledby="FullscreenExample6" + > - Full screen below xxl + Full screen below xxl ... @@ -1026,29 +1328,29 @@ return ( React modals use local CSS variables on `.modal` and `.modal-backdrop` for enhanced real-time customization. Values for the CSS variables are set via Sass, so Sass customization is still supported, too. - + - + #### How to use CSS variables ```jsx -const vars = { +const vars = { '--my-css-var': 10, - '--my-another-css-var': "red" + '--my-another-css-var': 'red', } return ... ``` ### SASS variables - + ### SASS loops [Responsive fullscreen modals](#fullscreen-modal) are generated via the `$breakpoints` map and a loop in `scss/_modal.scss`. - + ## API diff --git a/packages/docs/content/components/navbar.mdx b/packages/docs/content/components/navbar.mdx index 873736fd..5a5d1aab 100644 --- a/packages/docs/content/components/navbar.mdx +++ b/packages/docs/content/components/navbar.mdx @@ -1444,10 +1444,6 @@ Variables for all navbars: -Variables for the [dark navbar](#color-schemes): - - - ### SASS loops [Responsive navbar expand/collapse classes](#responsive-behaviors) (e.g., `.navbar-expand-lg`) are combined with the `$breakpoints` map and generated through a loop in `scss/_navbar.scss`. diff --git a/packages/docs/content/components/navs-tabs.mdx b/packages/docs/content/components/navs-tabs.mdx index c0c8e9a9..96a335ed 100644 --- a/packages/docs/content/components/navs-tabs.mdx +++ b/packages/docs/content/components/navs-tabs.mdx @@ -172,7 +172,7 @@ Takes the basic nav from above and adds the `variant="tabs"` class to generate a ### Pills -Take that same HTML, but use `variant="pills"` instead: +Take that same code, but use `variant="pills"` instead: ```jsx preview @@ -337,43 +337,65 @@ export const TabPanesExample = () => { return ( <> - + setActiveKey(1)} > Home - + setActiveKey(2)} > Profile - + setActiveKey(3)} > Contact + + setActiveKey(4)} + > + Disabled + + - + Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui. - + Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft @@ -383,7 +405,7 @@ export const TabPanesExample = () => { sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park. - + Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie @@ -392,6 +414,15 @@ export const TabPanesExample = () => { mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr. + + Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic + lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork + tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. + DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh + mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. + Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. + Pitchfork sustainable tofu synth chambray yr. + ) @@ -406,53 +437,84 @@ const [activeKey, setActiveKey] = useState(1) return ( <> - + setActiveKey(1)} > Home - + setActiveKey(2)} > Profile - + setActiveKey(3)} > Contact + + setActiveKey(4)} + > + Disabled + + - + Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui. - + Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft - beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda - labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit - sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean - shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, - tumblr butcher vero sint qui sapiente accusamus tattooed echo park. + beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, + assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero + magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, + sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party + scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park. + + + Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic + lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork + tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie + helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. + Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro + mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog + stumptown. Pitchfork sustainable tofu synth chambray yr. - + Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. @@ -473,43 +535,65 @@ export const TabPanesPillsExample = () => { return ( <> - + setActiveKey(1)} > Home - + setActiveKey(2)} > Profile - + setActiveKey(3)} > Contact + + setActiveKey(4)} + > + Disabled + + - + Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui. - + Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft @@ -519,7 +603,7 @@ export const TabPanesPillsExample = () => { sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park. - + Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie @@ -528,6 +612,15 @@ export const TabPanesPillsExample = () => { mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr. + + Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic + lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork + tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. + DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh + mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. + Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. + Pitchfork sustainable tofu synth chambray yr. + ) @@ -542,53 +635,84 @@ const [activeKey, setActiveKey] = useState(1) return ( <> - + setActiveKey(1)} > Home - + setActiveKey(2)} > Profile - + setActiveKey(3)} > Contact + + setActiveKey(4)} + > + Disabled + + - + Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui. - + Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft - beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda - labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit - sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean - shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, - tumblr butcher vero sint qui sapiente accusamus tattooed echo park. + beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, + assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero + magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, + sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party + scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park. + + + Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic + lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork + tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie + helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. + Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro + mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog + stumptown. Pitchfork sustainable tofu synth chambray yr. - + Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. diff --git a/packages/docs/content/components/offcanvas.mdx b/packages/docs/content/components/offcanvas.mdx index a90301c5..02be75bd 100644 --- a/packages/docs/content/components/offcanvas.mdx +++ b/packages/docs/content/components/offcanvas.mdx @@ -26,14 +26,14 @@ import { Below is an offcanvas example that is shown by default (via `visible={true}`). Offcanvas includes support for a header with a close button and an optional body class for some initial `padding`. We suggest that you include offcanvas headers with dismiss actions whenever possible, or provide an explicit dismiss action. -```jsx preview className="docs-example-offcanvas bg-light p-0" +```jsx preview className="docs-example-offcanvas bg-body-tertiary p-0 overflow-hidden" Offcanvas - Content for the offcanvas goes here. You can place just about any Bootstrap component or custom + Content for the offcanvas goes here. You can place just about any React component or custom elements here. @@ -57,7 +57,7 @@ export const LiveDemoExample = () => { setVisible(false)} /> - Content for the offcanvas goes here. You can place just about any Bootstrap component or + Content for the offcanvas goes here. You can place just about any React component or custom elements here. @@ -80,7 +80,7 @@ return ( setVisible(false)} /> - Content for the offcanvas goes here. You can place just about any Bootstrap component or + Content for the offcanvas goes here. You can place just about any React component or custom elements here. @@ -219,6 +219,23 @@ return ( ) ``` +## Dark offcanvas + +Change the appearance of offcanvases with utilities to better match them to different contexts like dark navbars. Here we add `.text-bg-dark` to the `` and `white` property to `` for proper styling with a dark offcanvas. + +```jsx preview className="docs-example-offcanvas bg-body-secondary p-0 overflow-hidden" + + + Offcanvas + + + + Content for the offcanvas goes here. You can place just about any React component or custom + elements here. + + +``` + ## Responsive Responsive offcanvas properties hide content outside the viewport from a specified breakpoint and down. @@ -290,7 +307,7 @@ export const PlacementTopExample = () => { setVisible(false)} /> - Content for the offcanvas goes here. You can place just about any Bootstrap component or + Content for the offcanvas goes here. You can place just about any React component or custom elements here. @@ -313,7 +330,7 @@ return ( setVisible(false)} /> - Content for the offcanvas goes here. You can place just about any Bootstrap component or + Content for the offcanvas goes here. You can place just about any React component or custom elements here. @@ -332,7 +349,7 @@ export const PlacementRightExample = () => { setVisible(false)} /> - Content for the offcanvas goes here. You can place just about any Bootstrap component or + Content for the offcanvas goes here. You can place just about any React component or custom elements here. @@ -355,7 +372,7 @@ return ( setVisible(false)} /> - Content for the offcanvas goes here. You can place just about any Bootstrap component or + Content for the offcanvas goes here. You can place just about any React component or custom elements here. @@ -374,7 +391,7 @@ export const PlacementBottomExample = () => { setVisible(false)} /> - Content for the offcanvas goes here. You can place just about any Bootstrap component or + Content for the offcanvas goes here. You can place just about any React component or custom elements here. @@ -397,7 +414,7 @@ return ( setVisible(false)} /> - Content for the offcanvas goes here. You can place just about any Bootstrap component or + Content for the offcanvas goes here. You can place just about any React component or custom elements here. diff --git a/packages/docs/content/components/spinner.mdx b/packages/docs/content/components/spinner.mdx index 152eae3a..c4321079 100644 --- a/packages/docs/content/components/spinner.mdx +++ b/packages/docs/content/components/spinner.mdx @@ -19,10 +19,8 @@ For accessibility purposes, each loader here includes `role="status"` and a nest Use the border spinners for a lightweight loading indicator. -## Basic usage - ```jsx preview - + ``` ### Colors @@ -30,14 +28,14 @@ Use the border spinners for a lightweight loading indicator. The border spinner uses `currentColor` for its `border-color`. You can use any of our text color utilities on the standard spinner. ```jsx preview - - - - - - - - + + + + + + + + ``` ## Growing spinner @@ -45,29 +43,83 @@ The border spinner uses `currentColor` for its `border-color`. You can use any o If you don't fancy a border spinner, switch to the grow spinner. While it doesn't technically spin, it does repeatedly grow! ```jsx preview - + ``` Once again, this spinner is built with `currentColor`, so you can easily change its appearance. Here it is in blue, along with the supported variants. ```jsx preview - - - - - - - - + + + + + + + + +``` + +## Alignment + +CoreUI React spinners are built with `rems`, `currentColor`, and `display: inline-flex`. This means they can easily be resized, recolored, and quickly aligned. + +### Margin + +Use [margin utilities](https://coreui.io/docs/utilities/spacing/#margin-and-padding) like `.m-5` for easy spacing. + +```jsx preview + +``` + +### Placement + +Use [flexbox utilities][https://coreui.io/docs/utilities/flex/], [float utilities][https://coreui.io/docs/utilities/float/], or [text alignment][https://coreui.io/docs/utilities/text/] utilities to place spinners exactly where you need them in any situation. + +#### Flex + +```jsx preview +
+ +
+``` + +```jsx preview +
+ Loading... + +
+``` + +#### Floats + +```jsx preview +
+ +
+``` + +#### Text align + +```jsx preview +
+ +
``` ## Size -Add `size="sm"`property` to make a smaller spinner that can quickly be used within other components. +Add `size="sm"` property to make a smaller spinner that can quickly be used within other components. ```jsx preview - - + + +``` + +Or, use custom CSS or inline styles to change the dimensions as needed. + +```jsx preview + + ``` ## Buttons @@ -75,23 +127,23 @@ Add `size="sm"`property` to make a smaller spinner that can quickly be used with Use spinners within buttons to indicate an action is currently processing or taking place. You may also swap the text out of the spinner element and utilize button text as needed. ```jsx preview - - - - + + + + ``` ```jsx preview - - - - + + + + ``` ## Customizing @@ -102,15 +154,15 @@ React spinners use local CSS variables on `.spinner-border` and `.spinner-grow` Border spinner variables: - + Growing spinner variables: - + For both spinners, small spinner modifier classes are used to update the values of these CSS variables as needed. For example, the `.spinner-border-sm` class does the following: - + #### How to use CSS variables @@ -124,10 +176,18 @@ return ... ### SASS variables - + + +### Keyframes + +Used for creating the CSS animations for our spinners. Included in `_spinners.scss`. + + + + ## API ### CSpinner -`markdown:CSpinner.api.mdx` \ No newline at end of file +`markdown:CSpinner.api.mdx` diff --git a/packages/docs/content/forms/validation.mdx b/packages/docs/content/forms/validation.mdx index 17020582..615c89d0 100644 --- a/packages/docs/content/forms/validation.mdx +++ b/packages/docs/content/forms/validation.mdx @@ -680,18 +680,6 @@ return ( ## Customizing -### CSS variables - -CoreUI for React components use local CSS variables for validation for enhanced real-time customization. Values for the CSS variables are set via Sass, so Sass customization is still supported, too. - - - -These variables are also color mode adaptive, meaning they change color while in dark mode. - ### SASS variables - - - - diff --git a/packages/docs/content/layout/gutters.mdx b/packages/docs/content/layout/gutters.mdx index ea0afe94..0674a963 100644 --- a/packages/docs/content/layout/gutters.mdx +++ b/packages/docs/content/layout/gutters.mdx @@ -20,14 +20,14 @@ import { CCol, CContainer, CRow } from '@coreui/react/src/index' `{breakpoint}={{ gutterX: * }}` props can be used to control the horizontal gutter widths. The `` or `` parent may need to be adjusted if larger gutters are used too to avoid unwanted overflow, using a matching padding utility. For example, in the following example we've increased the padding with `.px-4`: -```jsx preview +```jsx preview className="docs-example m-0 border-0 docs-example-cols" -
Custom column padding
+
Custom column padding
-
Custom column padding
+
Custom column padding
@@ -35,14 +35,14 @@ import { CCol, CContainer, CRow } from '@coreui/react/src/index' An alternative solution is to add a wrapper around the `` with the `.overflow-hidden` class: -```jsx preview +```jsx preview className="docs-example m-0 border-0 docs-example-cols" -
Custom column padding
+
Custom column padding
-
Custom column padding
+
Custom column padding
@@ -52,20 +52,20 @@ An alternative solution is to add a wrapper around the `` with the `.overf `{breakpoint}={{ gutterY: * }}` props can be used to control the vertical gutter widths. Like the horizontal gutters, the vertical gutters can cause some overflow below the `` at the end of a page. If this occurs, you add a wrapper around `` with the `.overflow-hidden` class: -```jsx preview +```jsx preview className="docs-example m-0 border-0 docs-example-cols" -
Custom column padding
+
Custom column padding
-
Custom column padding
+
Custom column padding
-
Custom column padding
+
Custom column padding
-
Custom column padding
+
Custom column padding
@@ -75,20 +75,20 @@ An alternative solution is to add a wrapper around the `` with the `.overf `{breakpoint}={{ gutter: * }}` props can be used to control the horizontal gutter widths, for the following example we use a smaller gutter width, so there won't be a need to add the `.overflow-hidden` wrapper class. -```jsx preview +```jsx preview className="docs-example m-0 border-0 docs-example-cols" -
Custom column padding
+
Custom column padding
-
Custom column padding
+
Custom column padding
-
Custom column padding
+
Custom column padding
-
Custom column padding
+
Custom column padding
@@ -98,38 +98,38 @@ An alternative solution is to add a wrapper around the `` with the `.overf Gutter props can also be added to [row columns](../layout/grid#row-columns). In the following example, we use responsive row columns and responsive gutter props. -```jsx preview +```jsx preview className="docs-example m-0 border-0 docs-example-cols" -
Row column
+
Row column
-
Row column
+
Row column
-
Row column
+
Row column
-
Row column
+
Row column
-
Row column
+
Row column
-
Row column
+
Row column
-
Row column
+
Row column
-
Row column
+
Row column
-
Row column
+
Row column
-
Row column
+
Row column
@@ -143,13 +143,27 @@ The gutters between columns in our predefined grid props can be removed with `{b In practice, here's how it looks. Note you can continue to use this with all other predefined grid props (including column widths, responsive tiers, reorders, and more). -```jsx preview -
- - .col-sm-6 .col-md-8 - .col-6 .col-md-4 - -
+```jsx preview className="docs-example m-0 border-0 docs-example-row" + + .col-sm-6 .col-md-8 + .col-6 .col-md-4 + +``` + +## Change the gutters + +Classes are built from the `$gutters` Sass map which is inherited from the `$spacers` Sass map. + +```scss +$grid-gutter-width: 1.5rem; +$gutters: ( + 0: 0, + 1: $spacer * .25, + 2: $spacer * .5, + 3: $spacer, + 4: $spacer * 1.5, + 5: $spacer * 3, +); ``` ## API diff --git a/packages/docs/content/templates/admin-dashboard.mdx b/packages/docs/content/templates/admin-dashboard.mdx index b99d6c0c..42153487 100644 --- a/packages/docs/content/templates/admin-dashboard.mdx +++ b/packages/docs/content/templates/admin-dashboard.mdx @@ -27,7 +27,7 @@ Check out the fully-featured, ready-to-use admin dashboard templates built using Free React Admin Template - Default Theme + Default Theme @@ -38,7 +38,7 @@ Check out the fully-featured, ready-to-use admin dashboard templates built using React Dashboard Template - Default Theme v3 + Default Theme v3 @@ -49,7 +49,7 @@ Check out the fully-featured, ready-to-use admin dashboard templates built using React Dashboard Template - Light Theme v3 + Light Theme v3 @@ -60,7 +60,7 @@ Check out the fully-featured, ready-to-use admin dashboard templates built using React Dashboard Template - Default Theme + Default Theme @@ -71,7 +71,7 @@ Check out the fully-featured, ready-to-use admin dashboard templates built using React Dashboard Template - Light Theme + Light Theme @@ -82,7 +82,7 @@ Check out the fully-featured, ready-to-use admin dashboard templates built using React Dashboard Template - Dark Theme + Dark Theme diff --git a/packages/docs/package.json b/packages/docs/package.json index 7964b705..5a6d2ae5 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "@coreui/react-docs", - "version": "4.9.0-beta.2", + "version": "4.11.1", "private": true, "description": "", "homepage": "https://coreui.io/react/", @@ -25,9 +25,9 @@ }, "dependencies": { "@coreui/chartjs": "^3.1.2", - "@coreui/coreui": "4.3.0-beta.0", + "@coreui/coreui": "4.3.0", "@coreui/icons": "^3.0.1", - "@coreui/icons-react": "^2.1.0", + "@coreui/icons-react": "^2.2.1", "@coreui/react-chartjs": "^2.1.3", "@coreui/utils": "^2.0.2", "@docsearch/css": "^3.5.1", @@ -50,16 +50,16 @@ "gatsby-transformer-sharp": "^5.11.0", "glob": "^7.2.0", "globby": "^11.1.0", - "prism-react-renderer": "^2.0.5", + "prism-react-renderer": "^2.0.6", "prismjs": "^1.29.0", "prop-types": "^15.8.1", "react": "^18.2.0", "react-docgen-typescript": "^2.2.2", "react-dom": "^18.2.0", "react-helmet": "^6.1.0", - "react-imask": "^7.0.1", + "react-imask": "^7.1.3", "rimraf": "^5.0.1", - "sass": "^1.63.4" + "sass": "^1.64.2" }, "devDependencies": { "npm-run-all": "^4.1.5" diff --git a/packages/docs/src/components/CodeBlock.tsx b/packages/docs/src/components/CodeBlock.tsx index 82959d0d..d7fd2422 100644 --- a/packages/docs/src/components/CodeBlock.tsx +++ b/packages/docs/src/components/CodeBlock.tsx @@ -1,11 +1,15 @@ import React, { FC } from 'react' -import { Highlight } from 'prism-react-renderer' +import { Highlight, Prism } from 'prism-react-renderer' interface CodeBlockProps { children: any // eslint-disable-line @typescript-eslint/no-explicit-any } const CodeBlock: FC = ({ children }) => { + ;(typeof global === 'undefined' ? window : global).Prism = Prism + // eslint-disable-next-line unicorn/prefer-module + require('prismjs/components/prism-bash') + require('prismjs/components/prism-scss') const _children = children && children.props.children const language = children.props.className ? children.props.className.replace(/language-/, '') diff --git a/packages/docs/src/components/Header.tsx b/packages/docs/src/components/Header.tsx index 4404f1aa..70e4e58a 100644 --- a/packages/docs/src/components/Header.tsx +++ b/packages/docs/src/components/Header.tsx @@ -7,28 +7,13 @@ import { cibTwitter, cilCloudDownload, cilMenu, - cilSun, - cilMoon, - cilContrast, cilHandshake, } from '@coreui/icons' -import { - CButton, - CDropdown, - CDropdownItem, - CDropdownMenu, - CDropdownToggle, - CHeader, - CHeaderNav, - CHeaderToggler, - CNavItem, - useColorModes, -} from '@coreui/react/src' +import { CButton, CHeader, CHeaderNav, CHeaderToggler, CNavItem } from '@coreui/react/src' import { AppContext } from './../AppContext' const Header: FC = () => { - const { colorMode, setColorMode } = useColorModes('coreui-react-docs-theme') return ( <> @@ -63,50 +48,6 @@ const Header: FC = () => {

- - - {colorMode === 'dark' ? ( - - ) : (colorMode === 'auto' ? ( - - ) : ( - - ))} - - - setColorMode('light')} - > - Light - - setColorMode('dark')} - > - Dark - - setColorMode('auto')} - > - Auto - - - -
  • -
    -
    -
  • { const captureEnd = `// scss-docs-end ${capture}` const re = new RegExp(`${captureStart}((?:.|\n)*)${captureEnd}`) const captured = re.exec(_file.node.internal.content) - const code = captured && captured[1].trim() + const code = captured ? captured[1].trim() : undefined + + if (code === undefined) { + console.error(`Can't find "${capture}" in ${_file.node.relativePath}`) + } return ( code && ( diff --git a/packages/docs/src/components/Toc.tsx b/packages/docs/src/components/Toc.tsx index 53905068..27cedff2 100644 --- a/packages/docs/src/components/Toc.tsx +++ b/packages/docs/src/components/Toc.tsx @@ -34,7 +34,7 @@ const Toc: FC = ({ items }) => { } return ( -
    +
    On this page
      {toc(items)}
    diff --git a/packages/docs/src/nav.tsx b/packages/docs/src/nav.tsx index 4d5418ba..d9ea08ee 100644 --- a/packages/docs/src/nav.tsx +++ b/packages/docs/src/nav.tsx @@ -88,7 +88,7 @@ const nav = [ to: '/layout/columns/', }, { - name: 'Gutter', + name: 'Gutters', to: '/layout/gutters/', }, ], diff --git a/packages/docs/src/styles/_component-examples.scss b/packages/docs/src/styles/_component-examples.scss index 32eff0d7..84abfc90 100644 --- a/packages/docs/src/styles/_component-examples.scss +++ b/packages/docs/src/styles/_component-examples.scss @@ -109,6 +109,14 @@ .pagination { margin-bottom: 0; } + + // Spinners + > .spinner-grow + .spinner-border, + > .spinner-border + .spinner-grow, + > .spinner-border + .spinner-border, + > .spinner-grow + .spinner-grow { + margin-left: .25rem; + } } // @@ -385,7 +393,8 @@ } .docs-example ~ .highlight { - border: 0; + border: solid var(--cui-border-color); + border-width: 1px 0 0; @include border-top-radius(0); } @@ -411,10 +420,6 @@ } } -.docs-example { - border-bottom-width: 0; -} - .docs-example + .highlight { border-top-width: 0; @include border-top-radius(0); diff --git a/packages/docs/src/styles/_prism.scss b/packages/docs/src/styles/_prism.scss index 6145e4b6..21bfa2c9 100644 --- a/packages/docs/src/styles/_prism.scss +++ b/packages/docs/src/styles/_prism.scss @@ -6,8 +6,7 @@ https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+cli * @author Rose Pritchard */ -:root, -[data-coreui-theme="light"] { +:root { // --base00: #fff; // --base01: #f5f5f5; --base02: #c8c8fa; @@ -24,32 +23,6 @@ https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+cli --base0D: #{$purple-500}; // #795da3 --base0E: #{$pink-600}; // #a71d5d --base0F: #333; - } - -@include color-mode(dark, true) { - // --base00: #282c34; - // --base01: #353b45; - --base02: #3e4451; - --base03: #868e96; - --base04: #868e96; - --base05: #abb2bf; - --base06: #b6bdca; - --base07: #{$orange-300}; // #d19a66 - --base08: #{$cyan-300}; - --base09: #{$orange-300}; // #d19a66 - --base0A: #{$yellow-200}; // #e5c07b - --base0B: #{$teal-300}; // #98c379 - --base0C: #{$teal-300}; // #56b6c2 - --base0D: #{$blue-300}; // #61afef - --base0E: #{$indigo-200}; // #c678dd - --base0F: #{$red-300}; // #be5046 - - .language-diff .gd { - color: $red-400; - } - .language-diff .gi { - color: $green-400; - } } code[class*='language-'], diff --git a/packages/docs/src/styles/_search.scss b/packages/docs/src/styles/_search.scss index c487e640..8b19708c 100644 --- a/packages/docs/src/styles/_search.scss +++ b/packages/docs/src/styles/_search.scss @@ -5,26 +5,6 @@ --docsearch-logo-color: var(--cui-primary); } -@include color-mode(dark, true) { - // From here, the values are copied from https://cdn.jsdelivr.net/npm/@docsearch/css@3 - // in html[data-theme="dark"] selector - // and are slightly modified for formatting purpose - --docsearch-text-color: #f5f6f7; - --docsearch-container-background: rgba(9, 10, 17, .8); - --docsearch-modal-background: #15172a; - --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309; - --docsearch-searchbox-background: #090a11; - --docsearch-searchbox-focus-background: #000; - --docsearch-hit-color: #bec3c9; - --docsearch-hit-shadow: none; - --docsearch-hit-background: #090a11; - --docsearch-key-gradient: linear-gradient(-26.5deg, #565872, #31355b); - --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3); - --docsearch-footer-background: #1e2136; - --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2); - --docsearch-muted-color: #7f8497; -} - .DocSearch-Container { --docsearch-muted-color: var(--cui-secondary-color); --docsearch-hit-shadow: none; diff --git a/packages/docs/src/styles/_syntax.scss b/packages/docs/src/styles/_syntax.scss deleted file mode 100644 index ac38381a..00000000 --- a/packages/docs/src/styles/_syntax.scss +++ /dev/null @@ -1,142 +0,0 @@ -:root, -[data-coreui-theme="light"] { - // --base00: #fff; - // --base01: #f5f5f5; - --base02: #c8c8fa; - --base03: #565c64; - --base04: #666; - --base05: #333; - --base06: #fff; - --base07: #{$teal-700}; // #9a6700 - --base08: #{mix($red-500, $red-600, 50%)}; // #bc4c00 - --base09: #{$cyan-700}; // #087990 - --base0A: #{$purple-500}; // #795da3 - --base0B: #{$blue-700}; // #183691 - --base0C: #{$blue-700}; // #183691 - --base0D: #{$purple-500}; // #795da3 - --base0E: #{$pink-600}; // #a71d5d - --base0F: #333; -} - -@include color-mode(dark, true) { - // --base00: #282c34; - // --base01: #353b45; - --base02: #3e4451; - --base03: #868e96; - --base04: #868e96; - --base05: #abb2bf; - --base06: #b6bdca; - --base07: #{$orange-300}; // #d19a66 - --base08: #{$cyan-300}; - --base09: #{$orange-300}; // #d19a66 - --base0A: #{$yellow-200}; // #e5c07b - --base0B: #{$teal-300}; // #98c379 - --base0C: #{$teal-300}; // #56b6c2 - --base0D: #{$blue-300}; // #61afef - --base0E: #{$indigo-200}; // #c678dd - --base0F: #{$red-300}; // #be5046 - - .language-diff .gd { - color: $red-400; - } - .language-diff .gi { - color: $green-400; - } -} - -.hl { background-color: var(--base02); } -.c { color: var(--base03); } -.err { color: var(--base08); } -.k { color: var(--base0E); } -.l { color: var(----base09); } -.n { color: var(--base08); } -.o { color: var(--base05); } -.p { color: var(--base05); } -.cm { color: var(--base04); } -.cp { color: var(--base08); } -.c1 { color: var(--base03); } -.cs { color: var(--base04); } -.gd { color: var(--base08); } -.ge { font-style: italic; } -.gh { - font-weight: 600; - color: var(--base0A); -} -.gi { color: var(--cui-success); } -.gp { - font-weight: 600; - color: var(--base04); -} -.gs { font-weight: 600; } -.gu { - font-weight: 600; - color: var(--base0C); -} -.kc { color: var(--base0E); } -.kd { color: var(--base0E); } -.kn { color: var(--base0C); } -.kp { color: var(--base0E); } -.kr { color: var(--base0E); } -.kt { color: var(--base0A); } -.ld { color: var(--base0C); } -.m { color: var(--base09); } -.s { color: var(--base0C); } -.na { color: var(--base0A); } -.nb { color: var(--base05); } -.nc { color: var(--base07); } -.no { color: var(--base08); } -.nd { color: var(--base07); } -.ni { color: var(--base08); } -.ne { color: var(--base08); } -.nf { color: var(--base0B); } -.nl { color: var(--base05); } -.nn { color: var(--base0A); } -.nx { color: var(--base0A); } -.py { color: var(--base08); } -.nt { color: var(--base08); } -.nv { color: var(--base08); } -.ow { color: var(--base0C); } -.w { color: #fff; } -.mf { color: var(--base09); } -.mh { color: var(--base09); } -.mi { color: var(--base09); } -.mo { color: var(--base09); } -.sb { color: var(--base0C); } -.sc { color: #fff; } -.sd { color: var(--base04); } -.s2 { color: var(--base0C); } -.se { color: var(--base09); } -.sh { color: var(--base0C); } -.si { color: var(--base09); } -.sx { color: var(--base0C); } -.sr { color: var(--base0C); } -.s1 { color: var(--base0C); } -.ss { color: var(--base0C); } -.bp { color: var(--base05); } -.vc { color: var(--base08); } -.vg { color: var(--base08); } -.vi { color: var(--base08); } -.il { color: var(--base09); } - -// Color commas in rgba() values -.m + .o { color: var(--base03); } - -// Fix bash -.language-sh .c { color: var(--base03); } - -.chroma { - .language-bash, - .language-sh { - .line::before { - color: var(--base03); - content: "$ "; - user-select: none; - } - } - - .language-powershell::before { - color: var(--base0C); - content: "PM> "; - user-select: none; - } -} diff --git a/packages/docs/src/styles/_variables.scss b/packages/docs/src/styles/_variables.scss index b53b7754..f60159f9 100644 --- a/packages/docs/src/styles/_variables.scss +++ b/packages/docs/src/styles/_variables.scss @@ -9,6 +9,7 @@ $cd-gutter-x: 3rem; $cd-callout-variants: info, warning, danger !default; :root { + --cui-tertiary-bg: #f0f4f7; --cd-purple: #{$cd-purple}; --cd-violet: #{$cd-violet}; --cd-accent: #{$cd-accent};