diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..74d8bc85 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,11 @@ +{ + "permissions": { + "allow": [ + "mcp__ide__getDiagnostics", + "Bash(npm run lint:*)", + "Bash(npm test:*)" + ], + "deny": [], + "ask": [] + } +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..33a8fd20 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,135 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +CoreUI for React is a comprehensive React.js components library built on top of Bootstrap 5 and TypeScript. It's organized as a monorepo using Lerna with multiple packages including the main React component library, icons, charts, and documentation. + +## Repository Structure + +This is a **Lerna monorepo** with the following key packages: +- `packages/coreui-react/` - Main React components library (TypeScript) +- `packages/coreui-icons-react/` - Icon components for React +- `packages/coreui-react-chartjs/` - Chart.js integration for React +- `packages/docs/` - Gatsby-based documentation site + +## Development Commands + +### Root Level Commands +- `npm run lint` - Lint all packages +- `npm run test` - Run tests for all packages +- `npm run test:update` - Update snapshots for all packages + +### Package-Specific Commands (using Lerna) +- `npm run lib:build` - Build main React library +- `npm run lib:test` - Test main React library only +- `npm run lib:test:update` - Update main library test snapshots +- `npm run icons:build` - Build icons package +- `npm run charts:build` - Build charts package +- `npm run docs:dev` - Start documentation dev server +- `npm run docs:build` - Build documentation + +### Working with Individual Packages +Navigate to specific packages to run commands directly: +```bash +cd packages/coreui-react +npm test -- src/components/focus-trap/__tests__/CFocusTrap.spec.tsx # Run specific test +npm run build # Build this package only +``` + +### Running Single Tests +To run a specific test file: +```bash +cd packages/coreui-react +npm test -- path/to/test.spec.tsx +``` + +## Architecture + +### Component Organization +Each component follows a consistent structure: +``` +components/[component-name]/ +├── C[ComponentName].tsx # Main component +├── C[ComponentName]Part.tsx # Sub-components +├── index.ts # Exports +├── types.ts # TypeScript types (if complex) +├── utils.ts # Utility functions (if any) +├── const.ts # Constants (if any) +└── __tests__/ # Tests and snapshots + ├── C[ComponentName].spec.tsx + └── __snapshots__/ +``` + +### Component Development Patterns + +**Props Interface**: All components have well-documented TypeScript interfaces with JSDoc comments focusing on accessibility and SEO benefits. + +**Ref Forwarding**: Components forward refs properly to DOM elements for accessibility and integration. + +**Testing**: Uses React Testing Library with Jest, focusing on behavior over implementation details. Each component has snapshot tests and behavioral tests. + +**Styling**: Components use Bootstrap 5 classes and are compatible with `@coreui/coreui` CSS library. + +### Key Development Principles + +**TypeScript First**: All components are written in TypeScript with proper type definitions. + +**Accessibility Focus**: Components implement WCAG 2.1 standards and include proper ARIA attributes. + +**Bootstrap Compatible**: Components extend Bootstrap 5 functionality while maintaining compatibility. + +**No Extra DOM**: Many components avoid adding wrapper elements, using ref merging instead (see `focus-trap` component). + +**Utility Separation**: Complex components separate utilities into dedicated files (`utils.ts`, `const.ts`). + +## Testing + +### Test Structure +- Snapshot tests for UI consistency +- Behavioral tests for user interactions +- Accessibility tests for focus management +- Props validation tests + +### Test Environment +- Jest with JSDOM environment +- React Testing Library for component testing +- `@testing-library/jest-dom` for DOM assertions + +### Running Tests +Tests are run at the package level. Some complex focus management tests may not work perfectly in JSDOM but will work in real browsers. + +## Build System + +### Rollup Configuration +Each package uses Rollup for building: +- ESM and CommonJS outputs +- TypeScript compilation +- Separate bundles for different environments + +### Package Dependencies +- `@coreui/coreui` - Core CSS library +- `@popperjs/core` - For positioning (tooltips, dropdowns) +- `prop-types` - Runtime type checking +- React 17+ peer dependency + +## Component Development + +### Creating New Components +1. Follow the directory structure pattern +2. Use TypeScript interfaces with comprehensive JSDoc +3. Implement proper ref forwarding +4. Add comprehensive tests (snapshot + behavioral) +5. Export from package index files +6. Consider accessibility from the start + +### Refactoring Components +When refactoring complex components: +1. Separate utilities into `utils.ts` and constants into `const.ts` +2. Maintain backward compatibility with existing props +3. Update tests to match new structure +4. Keep the same export interface + +### Focus Management +For components requiring focus management (modals, dropdowns), use the patterns established in the `focus-trap` component, which implements proper Tab/Shift+Tab cycling and external focus redirection. \ No newline at end of file diff --git a/README.md b/README.md index c0e48a57..edd87fba 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ · Request feature · - Roadmap + Roadmap · Blog

@@ -48,7 +48,7 @@ Several quick start options are available: -- [Download the latest release](https://github.com/coreui/coreui-react/archive/v5.8.0.zip) +- [Download the latest release](https://github.com/coreui/coreui-react/archive/v5.9.0.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 8a0e6ac0..0a49658b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "npmClient": "yarn", "packages": ["packages/*"], - "version": "5.8.0", + "version": "5.9.0", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/package.json b/package.json index 58169718..cdacc619 100644 --- a/package.json +++ b/package.json @@ -22,18 +22,18 @@ "test:update": "npm-run-all charts:test:update icons:test:update lib:test:update" }, "devDependencies": { - "@typescript-eslint/parser": "^8.42.0", - "eslint": "^9.34.0", + "@typescript-eslint/parser": "^8.44.0", + "eslint": "^9.35.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-unicorn": "^60.0.0", - "globals": "^16.3.0", - "lerna": "^8.2.3", + "eslint-plugin-unicorn": "^61.0.2", + "globals": "^16.4.0", + "lerna": "^8.2.4", "npm-run-all": "^4.1.5", "prettier": "^3.6.2", - "typescript-eslint": "^8.42.0" + "typescript-eslint": "^8.44.0" }, "overrides": { "gatsby-remark-external-links": { diff --git a/packages/coreui-react/README.md b/packages/coreui-react/README.md index 269cb3cd..ddfb584b 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/v5.8.0.zip) +- [Download the latest release](https://github.com/coreui/coreui-react/archive/v5.9.0.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 99d28f01..c1e7c2cc 100644 --- a/packages/coreui-react/package.json +++ b/packages/coreui-react/package.json @@ -1,6 +1,6 @@ { "name": "@coreui/react", - "version": "5.8.0", + "version": "5.9.0", "description": "UI Components Library for React.js", "keywords": [ "react", @@ -53,8 +53,8 @@ "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "@types/jest": "^29.5.14", - "@types/prop-types": "15.8.05", - "@types/react": "^19.1.12", + "@types/prop-types": "15.7.15", + "@types/react": "^19.1.13", "@types/react-dom": "^19.1.9", "@types/react-transition-group": "^4.4.12", "classnames": "^2.5.1", @@ -64,8 +64,8 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-transition-group": "^4.4.5", - "rollup": "^4.50.0", - "ts-jest": "^29.4.1", + "rollup": "^4.50.2", + "ts-jest": "^29.4.2", "tslib": "^2.8.1", "typescript": "^5.9.2" }, diff --git a/packages/coreui-react/src/components/dropdown/CDropdown.tsx b/packages/coreui-react/src/components/dropdown/CDropdown.tsx index e32efa65..3c47a20b 100644 --- a/packages/coreui-react/src/components/dropdown/CDropdown.tsx +++ b/packages/coreui-react/src/components/dropdown/CDropdown.tsx @@ -21,7 +21,8 @@ import type { Placements } from '../../types' import { getNextActiveElement, isRTL } from '../../utils' import type { Alignments, Directions } from './types' -import { getPlacement } from './utils' +import { getPlacement, getReferenceElement } from './utils' +import { CFocusTrap } from '../focus-trap' export interface CDropdownProps extends HTMLAttributes { /** @@ -160,6 +161,27 @@ export interface CDropdownProps extends HTMLAttributes + * Toggle dropdown + * + * Action + * Another Action + * + * + * + * @since 5.9.0 + */ + reference?: 'parent' | 'toggle' | HTMLElement | React.RefObject + /** * Defines the visual variant of the React Dropdown */ @@ -203,6 +225,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps> popper = true, popperConfig, portal = false, + reference = 'toggle', variant = 'btn-group', visible = false, ...rest @@ -254,12 +277,20 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps> }, [visible]) useEffect(() => { - const toggleElement = dropdownToggleElement + return () => { + if (_visible) { + handleHide() + } + } + }, []) + + useEffect(() => { + const referenceElement = getReferenceElement(reference, dropdownToggleElement, dropdownRef) const menuElement = dropdownMenuRef.current - if (allowPopperUse && menuElement && toggleElement && _visible) { - initPopper(toggleElement, menuElement, computedPopperConfig) + if (allowPopperUse && menuElement && referenceElement && _visible) { + initPopper(referenceElement, menuElement, computedPopperConfig) } - }, [dropdownToggleElement]) + }, [dropdownToggleElement, reference]) useEffect(() => { if (pendingKeyDownEvent !== null) { @@ -271,24 +302,28 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps> const handleHide = useCallback(() => { setVisible(false) - const toggleElement = dropdownToggleElement const menuElement = dropdownMenuRef.current + const toggleElement = dropdownToggleElement if (allowPopperUse) { destroyPopper() } - toggleElement?.removeEventListener('keydown', handleKeydown) menuElement?.removeEventListener('keydown', handleKeydown) + toggleElement?.removeEventListener('keydown', handleKeydown) - window.removeEventListener('mouseup', handleMouseUp) + window.removeEventListener('click', handleClick) window.removeEventListener('keyup', handleKeyup) onHide?.() - }, [dropdownToggleElement, allowPopperUse, destroyPopper, onHide]) + }, [allowPopperUse, dropdownToggleElement, destroyPopper, onHide]) const handleKeydown = useCallback((event: KeyboardEvent) => { - if (dropdownMenuRef.current && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) { + if (!dropdownMenuRef.current) { + return + } + + if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { event.preventDefault() const target = event.target as HTMLElement const items = [ @@ -311,28 +346,40 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps> dropdownToggleElement?.focus() } }, - [autoClose, handleHide] + [autoClose, dropdownToggleElement, handleHide] ) - const handleMouseUp = useCallback( - (event: Event) => { + const handleClick = useCallback( + (event: MouseEvent) => { if (!dropdownToggleElement || !dropdownMenuRef.current) { return } - if (dropdownToggleElement.contains(event.target as HTMLElement)) { + if ((event as MouseEvent).button === 2) { + return + } + + const composedPath = event.composedPath() + const isOnToggle = composedPath.includes(dropdownToggleElement) + const isOnMenu = composedPath.includes(dropdownMenuRef.current) + + if (isOnToggle) { + return + } + + const target = event.target as HTMLElement | null + const FORM_TAG_RE = /^(input|select|option|textarea|form|button|label)$/i + + if (isOnMenu && target && FORM_TAG_RE.test(target.tagName)) { return } if ( autoClose === true || - (autoClose === 'inside' && - dropdownMenuRef.current.contains(event.target as HTMLElement)) || - (autoClose === 'outside' && - !dropdownMenuRef.current.contains(event.target as HTMLElement)) + (autoClose === 'inside' && isOnMenu) || + (autoClose === 'outside' && !isOnMenu) ) { - setTimeout(() => handleHide(), 1) - return + handleHide() } }, [autoClose, dropdownToggleElement, handleHide] @@ -340,21 +387,22 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps> const handleShow = useCallback( (event?: KeyboardEvent) => { - const toggleElement = dropdownToggleElement const menuElement = dropdownMenuRef.current + const referenceElement = getReferenceElement(reference, dropdownToggleElement, dropdownRef) + const toggleElement = dropdownToggleElement - if (toggleElement && menuElement) { + if (menuElement && referenceElement && toggleElement) { setVisible(true) if (allowPopperUse) { - initPopper(toggleElement, menuElement, computedPopperConfig) + initPopper(referenceElement, menuElement, computedPopperConfig) } toggleElement.focus() toggleElement.addEventListener('keydown', handleKeydown) menuElement.addEventListener('keydown', handleKeydown) - window.addEventListener('mouseup', handleMouseUp) + window.addEventListener('click', handleClick) window.addEventListener('keyup', handleKeyup) if (event && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) { @@ -365,53 +413,71 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps> } }, [ - dropdownToggleElement, allowPopperUse, - initPopper, computedPopperConfig, + dropdownToggleElement, + reference, + handleClick, handleKeydown, - handleMouseUp, handleKeyup, + initPopper, onShow, ] ) - const contextValues = { - alignment, - container, - dark, - dropdownMenuRef, - dropdownToggleRef, - handleHide, - handleShow, - popper: allowPopperUse, - portal, - variant, - visible: _visible, - } + const contextValues = useMemo( + () => ({ + alignment, + container, + dark, + dropdownMenuRef, + dropdownToggleRef, + handleHide, + handleShow, + popper: allowPopperUse, + portal, + variant, + visible: _visible, + }), + [ + alignment, + container, + dark, + dropdownMenuRef, + dropdownToggleRef, + handleHide, + handleShow, + allowPopperUse, + portal, + variant, + _visible, + ] + ) return ( - {variant === 'input-group' ? ( - <>{children} - ) : ( - - {children} - - )} + + {variant === 'input-group' ? ( + <>{children} + ) : ( + + {children} + + )} + ) } diff --git a/packages/coreui-react/src/components/dropdown/CDropdownHeader.tsx b/packages/coreui-react/src/components/dropdown/CDropdownHeader.tsx index 89e53673..1aff66e9 100644 --- a/packages/coreui-react/src/components/dropdown/CDropdownHeader.tsx +++ b/packages/coreui-react/src/components/dropdown/CDropdownHeader.tsx @@ -9,6 +9,7 @@ export interface CDropdownHeaderProps extends HTMLAttributes * Component used for the root node. Either a string to use a HTML element or a component. */ as?: ElementType + /** * A string of all className you want applied to the component. */ diff --git a/packages/coreui-react/src/components/dropdown/CDropdownItemPlain.tsx b/packages/coreui-react/src/components/dropdown/CDropdownItemPlain.tsx index bc4ae0bb..7d489da3 100644 --- a/packages/coreui-react/src/components/dropdown/CDropdownItemPlain.tsx +++ b/packages/coreui-react/src/components/dropdown/CDropdownItemPlain.tsx @@ -9,6 +9,7 @@ export interface CDropdownItemPlainProps extends HTMLAttributes * Component used for the root node. Either a string to use a HTML element or a component. */ as?: ElementType + /** * A string of all className you want applied to the component. */ diff --git a/packages/coreui-react/src/components/dropdown/CDropdownMenu.tsx b/packages/coreui-react/src/components/dropdown/CDropdownMenu.tsx index 59442d63..3ee88ad5 100644 --- a/packages/coreui-react/src/components/dropdown/CDropdownMenu.tsx +++ b/packages/coreui-react/src/components/dropdown/CDropdownMenu.tsx @@ -15,6 +15,7 @@ export interface CDropdownMenuProps extends HTMLAttributes { * Enables pseudo element caret on toggler. */ caret?: boolean + /** * Create a custom toggler which accepts any content. */ custom?: boolean + /** * If a dropdown `variant` is set to `nav-item` then render the toggler as a * link instead of a button. @@ -24,12 +26,22 @@ export interface CDropdownToggleProps extends Omit { * @since 5.0.0 */ navLink?: boolean + /** * Similarly, create split button dropdowns with virtually the same markup as * single button dropdowns, but with the addition of `.dropdown-toggle-split` * className for proper spacing around the dropdown caret. */ split?: boolean + + /** + * Screen reader label for split button dropdown toggle. + * + * @default 'Toggle Dropdown' + * @since 5.9.0 + */ + splitLabel?: string + /** * Sets which event handlers you'd like provided to your toggle prop. You can * specify one trigger or an array of them. @@ -46,6 +58,7 @@ export const CDropdownToggle: FC = ({ className, navLink = true, split, + splitLabel = 'Toggle Dropdown', trigger = 'click', ...rest }) => { @@ -113,7 +126,7 @@ export const CDropdownToggle: FC = ({ return ( {children} - {split && Toggle Dropdown} + {split && {splitLabel}} ) } @@ -124,6 +137,7 @@ CDropdownToggle.propTypes = { className: PropTypes.string, custom: PropTypes.bool, split: PropTypes.bool, + splitLabel: PropTypes.string, trigger: triggerPropType, } diff --git a/packages/coreui-react/src/components/dropdown/utils.ts b/packages/coreui-react/src/components/dropdown/utils.ts index 8b4a213f..171fa0f7 100644 --- a/packages/coreui-react/src/components/dropdown/utils.ts +++ b/packages/coreui-react/src/components/dropdown/utils.ts @@ -1,3 +1,4 @@ +import React from 'react' import type { Placement } from '@popperjs/core' import type { Placements } from '../../types' import type { Alignments, Breakpoints } from './types' @@ -49,3 +50,23 @@ export const getPlacement = ( return _placement } + +export const getReferenceElement = ( + reference: 'parent' | 'toggle' | React.RefObject | HTMLElement, + dropdownToggleElement: HTMLElement | null, + dropdownRef: React.RefObject +): HTMLElement | null => { + if (reference === 'parent') { + return dropdownRef.current + } + + if (reference instanceof HTMLElement) { + return reference + } + + if (reference instanceof Object && 'current' in reference) { + return reference.current + } + + return dropdownToggleElement +} diff --git a/packages/coreui-react/src/components/focus-trap/utils.ts b/packages/coreui-react/src/components/focus-trap/utils.ts index 8f9a76f0..92b4bae3 100644 --- a/packages/coreui-react/src/components/focus-trap/utils.ts +++ b/packages/coreui-react/src/components/focus-trap/utils.ts @@ -56,12 +56,6 @@ export const isElement = (object: unknown): object is Element => { return false } - // Handle jQuery objects - if ('jquery' in object && object.jquery !== undefined) { - const jQueryObject = object as { [key: number]: Element } - return isElement(jQueryObject[0]) - } - return 'nodeType' in object && typeof object.nodeType === 'number' } diff --git a/packages/docs/content/api/CDropdown.api.mdx b/packages/docs/content/api/CDropdown.api.mdx index 765fbd96..ef211307 100644 --- a/packages/docs/content/api/CDropdown.api.mdx +++ b/packages/docs/content/api/CDropdown.api.mdx @@ -190,6 +190,30 @@ const myContainer = document.getElementById('my-container')

Renders the React Dropdown Menu using a React Portal, allowing it to escape the DOM hierarchy for improved positioning.

+ + reference#5.9.0+ + {`toggle`} + {`HTMLElement`}, {`'parent'`}, {`'toggle'`}, {`RefObject\`} + + + +

Sets the reference element for positioning the React Dropdown Menu.

+
    +
  • {`toggle`} - The React Dropdown Toggle button (default).
  • +
  • {`parent`} - The React Dropdown wrapper element.
  • +
  • {`HTMLElement`} - A custom HTML element.
  • +
  • {`React.RefObject`} - A custom reference element.
  • +
+ + Toggle dropdown + + Action + Another Action + +`} /> + + variant# {`btn-group`} diff --git a/packages/docs/content/api/CDropdownToggle.api.mdx b/packages/docs/content/api/CDropdownToggle.api.mdx index 1f6572e1..cd56fb6f 100644 --- a/packages/docs/content/api/CDropdownToggle.api.mdx +++ b/packages/docs/content/api/CDropdownToggle.api.mdx @@ -145,6 +145,16 @@ import CDropdownToggle from '@coreui/react/src/components/dropdown/CDropdownTogg

Similarly, create split button dropdowns with virtually the same markup as single button dropdowns, but with the addition of {`.dropdown-toggle-split`} className for proper spacing around the dropdown caret.

+ + splitLabel#5.9.0+ + {`Toggle Dropdown`} + {`string`} + + + +

Screen reader label for split button dropdown toggle.

+ + trigger# {`click`} diff --git a/packages/docs/content/components/dropdown/examples/DropdownDropendExample.tsx b/packages/docs/content/components/dropdown/examples/DropdownDropendExample.tsx index e9d0656e..a6a1bd68 100644 --- a/packages/docs/content/components/dropdown/examples/DropdownDropendExample.tsx +++ b/packages/docs/content/components/dropdown/examples/DropdownDropendExample.tsx @@ -24,7 +24,7 @@ export const DropdownDropendExample = () => { Small split button - + Action Another action diff --git a/packages/docs/content/components/dropdown/examples/DropdownDropstartExample.tsx b/packages/docs/content/components/dropdown/examples/DropdownDropstartExample.tsx index 276b5cff..195cd05e 100644 --- a/packages/docs/content/components/dropdown/examples/DropdownDropstartExample.tsx +++ b/packages/docs/content/components/dropdown/examples/DropdownDropstartExample.tsx @@ -25,7 +25,7 @@ export const DropdownDropstartExample = () => { - + Action Another action diff --git a/packages/docs/content/components/dropdown/examples/DropdownOptionsAutoCloseBehaviorExample.tsx b/packages/docs/content/components/dropdown/examples/DropdownOptionsAutoCloseBehaviorExample.tsx new file mode 100644 index 00000000..23d90df6 --- /dev/null +++ b/packages/docs/content/components/dropdown/examples/DropdownOptionsAutoCloseBehaviorExample.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import { CDropdown, CDropdownItem, CDropdownMenu, CDropdownToggle } from '@coreui/react' + +export const DropdownOptionsAutoCloseBehaviorExample = () => { + return ( +
+ + Default dropdown + + Action + Another action + Something else here + + + + Clickable inside + + Action + Another action + Something else here + + + + Clickable outside + + Action + Another action + Something else here + + + + Manual close + + Action + Another action + Something else here + + +
+ ) +} diff --git a/packages/docs/content/components/dropdown/examples/DropdownOptionsExample.tsx b/packages/docs/content/components/dropdown/examples/DropdownOptionsExample.tsx new file mode 100644 index 00000000..688797e1 --- /dev/null +++ b/packages/docs/content/components/dropdown/examples/DropdownOptionsExample.tsx @@ -0,0 +1,36 @@ +import React from 'react' +import { CButton, CDropdown, CDropdownItem, CDropdownMenu, CDropdownToggle } from '@coreui/react' + +export const DropdownOptionsExample = () => { + return ( +
+ + Offset + + Action + Another action + Something else here + + + + + Portal + + + Action + Another action + Something else here + + + + Reference + + + Action + Another action + Something else here + + +
+ ) +} diff --git a/packages/docs/content/components/dropdown/index.mdx b/packages/docs/content/components/dropdown/index.mdx index c5890f31..b0e8ba47 100644 --- a/packages/docs/content/components/dropdown/index.mdx +++ b/packages/docs/content/components/dropdown/index.mdx @@ -172,6 +172,22 @@ Put a form within a dropdown menu, or make it into a dropdown menu. +## Dropdown options + +Use `offset` to displace the dropdown from its default position. The value is a string with two numbers separated by a comma, e.g. `offset={[10, 20]}`. Use `portal` property to render dropdowns in `body` instead of the parent element. This helps to avoid any overflow or z-index issues. + + + +### Auto close behavior + +By default, dropdowns are closed when clicking outside of the dropdown menu or the toggle button. You can change this behavior with the `autoClose` property. Set `autoClose` to: + +- `true` - Close on clicks inside or outside of the React.js dropdown menu. +- `false` - Disable auto-close; close manually by setting the `visible={false}` (also not closed by `Escape`). +- `'inside'` - Close only when clicking inside the React.js dropdown menu. +- `'outside'` - Close only when clicking outside the React.js dropdown menu. + + ## API diff --git a/packages/docs/package.json b/packages/docs/package.json index 400d494a..54647ee2 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "@coreui/react-docs", - "version": "5.8.0", + "version": "5.9.0", "private": true, "description": "", "homepage": "https://coreui.io/react/", @@ -30,8 +30,8 @@ "@coreui/icons-react": "^2.3.0", "@coreui/react-chartjs": "^3.0.0", "@coreui/utils": "^2.0.2", - "@docsearch/css": "^3.9.0", - "@docsearch/react": "^3.9.0", + "@docsearch/css": "^4.0.1", + "@docsearch/react": "^4.0.1", "@mdx-js/mdx": "^3.1.1", "@mdx-js/react": "^3.1.1", "@stackblitz/sdk": "^1.11.0", @@ -58,7 +58,7 @@ "react-imask": "^7.6.1", "react-markdown": "^10.1.0", "rimraf": "^6.0.1", - "sass": "^1.91.0", + "sass": "^1.92.1", "showdown": "^2.1.0" }, "devDependencies": { diff --git a/packages/docs/src/components/Seo.tsx b/packages/docs/src/components/Seo.tsx index 1d1763f3..befabf25 100644 --- a/packages/docs/src/components/Seo.tsx +++ b/packages/docs/src/components/Seo.tsx @@ -154,7 +154,7 @@ const SEO = ({ title, description, name, image, article, pro }: SEOProps) => { '@type': 'WebPage', '@id': seo.url.replace('docs//', 'docs/'), }, - version: pro ? '5.17.1' : '5.8.0', + version: pro ? '5.17.1' : '5.9.0', proficiencyLevel: 'Beginner', }, ] diff --git a/packages/docs/src/styles/search.scss b/packages/docs/src/styles/search.scss index fb794847..16fa476c 100644 --- a/packages/docs/src/styles/search.scss +++ b/packages/docs/src/styles/search.scss @@ -19,7 +19,9 @@ // stylelint-disable selector-class-pattern :root { --docsearch-primary-color: var(--cui-primary); + --docsearch-muted-color: var(--cui-secondary-color); --docsearch-logo-color: var(--cui-primary); + --docsearch-key-color: var(--cui-secondary-color); } @include color-mode(dark, true) { @@ -43,7 +45,6 @@ } .DocSearch-Container { - --docsearch-muted-color: var(--cui-secondary-color); --docsearch-hit-shadow: none; position: fixed; @@ -74,7 +75,8 @@ @include box-shadow($input-box-shadow); @include transition($input-transition); - &:focus { + &:focus, + &:hover { color: $input-focus-color; background-color: $input-focus-bg; border-color: $input-focus-border-color; @@ -87,10 +89,6 @@ } } - &:hover:not(:disabled):not([readonly])::file-selector-button { - background-color: $form-file-button-hover-bg; - } - @include media-breakpoint-down(md) { &, &:hover, @@ -109,32 +107,29 @@ .DocSearch-Button-Keys { min-width: 0; padding: 0 .25rem; - background: rgba($black, .125); + background: var(--cui-secondary-bg); @include border-radius(.25rem); } .DocSearch-Button-Key { - top: 0; width: auto; - height: 1.5rem; - padding: 0 .125rem; - margin-right: 0; - font-size: .875rem; + padding: 0; background: none; + border: 0; box-shadow: none; -} -.DocSearch-Commands-Key { - padding-left: 1px; - font-size: .875rem; - background-color: rgba($black, .1); - background-image: none; - box-shadow: none; + &:first-child { + margin-right: 0; + } } -.DocSearch-Form { - @include border-radius(var(--cui-border-radius)); -} +// .DocSearch-Commands-Key { +// padding-left: 1px; +// font-size: .875rem; +// background-color: rgba($black, .1); +// background-image: none; +// box-shadow: none; +// } .DocSearch-Hits { mark {