diff --git a/docs/src/examples/components/Avatar/Rtl/AvatarExample.rtl.tsx b/docs/src/examples/components/Avatar/Rtl/AvatarExample.rtl.tsx index 286430c532..a3fc35767c 100644 --- a/docs/src/examples/components/Avatar/Rtl/AvatarExample.rtl.tsx +++ b/docs/src/examples/components/Avatar/Rtl/AvatarExample.rtl.tsx @@ -5,7 +5,7 @@ const AvatarExampleRtl = () => ( diff --git a/docs/src/examples/components/Avatar/Usage/AvatarUsageExample.shorthand.tsx b/docs/src/examples/components/Avatar/Usage/AvatarUsageExample.shorthand.tsx index 61e477af92..c1fa9c8d6d 100644 --- a/docs/src/examples/components/Avatar/Usage/AvatarUsageExample.shorthand.tsx +++ b/docs/src/examples/components/Avatar/Usage/AvatarUsageExample.shorthand.tsx @@ -1,7 +1,11 @@ import * as React from 'react' import { Avatar } from '@stardust-ui/react' -const status = { color: 'green', icon: 'stardust-checkmark', title: 'Available' } +const status = { + styles: { backgroundColor: 'green' }, + icon: 'stardust-checkmark', + title: 'Available', +} const AvatarUsageExampleShorthand = () => (
diff --git a/docs/src/examples/components/Avatar/Variations/AvatarExampleExcludedInitials.shorthand.tsx b/docs/src/examples/components/Avatar/Variations/AvatarExampleExcludedInitials.shorthand.tsx index 4725e8ac32..a6fd5f80f8 100644 --- a/docs/src/examples/components/Avatar/Variations/AvatarExampleExcludedInitials.shorthand.tsx +++ b/docs/src/examples/components/Avatar/Variations/AvatarExampleExcludedInitials.shorthand.tsx @@ -1,7 +1,11 @@ import * as React from 'react' import { Avatar } from '@stardust-ui/react' -const status = { color: 'green', icon: 'stardust-checkmark', title: 'Available' } +const status = { + styles: { backgroundColor: 'green' }, + icon: 'stardust-checkmark', + title: 'Available', +} const AvatarExampleExcludedInitialsShorthand = () => (
diff --git a/docs/src/examples/components/Avatar/Variations/AvatarExampleGetInitials.shorthand.tsx b/docs/src/examples/components/Avatar/Variations/AvatarExampleGetInitials.shorthand.tsx index acdf9169b0..8809ae769d 100644 --- a/docs/src/examples/components/Avatar/Variations/AvatarExampleGetInitials.shorthand.tsx +++ b/docs/src/examples/components/Avatar/Variations/AvatarExampleGetInitials.shorthand.tsx @@ -7,7 +7,11 @@ const AvatarExampleGetInitialsShorthand = () => ( ) diff --git a/docs/src/examples/components/Avatar/Variations/AvatarExampleImageCustomization.shorthand.tsx b/docs/src/examples/components/Avatar/Variations/AvatarExampleImageCustomization.shorthand.tsx index adf8fa9f30..b6976d27a1 100644 --- a/docs/src/examples/components/Avatar/Variations/AvatarExampleImageCustomization.shorthand.tsx +++ b/docs/src/examples/components/Avatar/Variations/AvatarExampleImageCustomization.shorthand.tsx @@ -5,12 +5,20 @@ const AvatarExampleImageCustomizationShorthand = () => ( <> ( /> )) } - status={{ color: 'green', icon: 'stardust-checkmark', title: 'Available' }} + status={{ + styles: { backgroundColor: 'green' }, + icon: 'stardust-checkmark', + title: 'Available', + }} /> ) diff --git a/docs/src/examples/components/Avatar/Variations/AvatarExampleName.shorthand.tsx b/docs/src/examples/components/Avatar/Variations/AvatarExampleName.shorthand.tsx index f576f17ba0..e980efb51d 100644 --- a/docs/src/examples/components/Avatar/Variations/AvatarExampleName.shorthand.tsx +++ b/docs/src/examples/components/Avatar/Variations/AvatarExampleName.shorthand.tsx @@ -5,7 +5,7 @@ const AvatarExampleNameShorthand = () => ( (
  - +
diff --git a/docs/src/examples/components/Avatar/Variations/AvatarExampleStatusCustomization.shorthand.tsx b/docs/src/examples/components/Avatar/Variations/AvatarExampleStatusCustomization.shorthand.tsx index e9b1a537ef..08a92a0b66 100644 --- a/docs/src/examples/components/Avatar/Variations/AvatarExampleStatusCustomization.shorthand.tsx +++ b/docs/src/examples/components/Avatar/Variations/AvatarExampleStatusCustomization.shorthand.tsx @@ -5,7 +5,7 @@ const defaultAvatar = ( ( ( image={{ src: 'public/images/avatar/small/matt.jpg', alt: 'Profile picture of Matt' }} size="larger" status={{ - color: 'green', + styles: { backgroundColor: 'green' }, icon: 'stardust-checkmark', title: 'Available', }} @@ -44,7 +44,7 @@ const AvatarExampleStatusCustomizationShorthand = () => ( ( image={{ src: 'public/images/avatar/small/matt.jpg', alt: 'Profile picture of Matt' }} size="larger" status={{ - color: 'green', + styles: { backgroundColor: 'green' }, icon: 'stardust-checkmark', title: 'Available', size: 'medium', diff --git a/docs/src/examples/components/Chat/Performance/Chat.perf.tsx b/docs/src/examples/components/Chat/Performance/Chat.perf.tsx index 444354ab77..eb5d099a83 100644 --- a/docs/src/examples/components/Chat/Performance/Chat.perf.tsx +++ b/docs/src/examples/components/Chat/Performance/Chat.perf.tsx @@ -9,7 +9,7 @@ const avatars = { const janeAvatar = { image: `data:image/jpeg;base64,${avatars.ade}`, - status: { color: 'green', icon: 'stardust-checkmark' }, + status: { styles: { backgroundColor: 'green' }, icon: 'stardust-checkmark' }, } const ChatExample = () => { diff --git a/docs/src/examples/components/Chat/Performance/ChatWithPopover.perf.tsx b/docs/src/examples/components/Chat/Performance/ChatWithPopover.perf.tsx index dd5bc529e3..4799d30faf 100644 --- a/docs/src/examples/components/Chat/Performance/ChatWithPopover.perf.tsx +++ b/docs/src/examples/components/Chat/Performance/ChatWithPopover.perf.tsx @@ -17,7 +17,7 @@ const avatars = { const janeAvatar = { image: `data:image/jpeg;base64,${avatars.ade}`, - status: { color: 'green', icon: 'stardust-checkmark' }, + status: { styles: { backgroundColor: 'green' }, icon: 'stardust-checkmark' }, } export interface PopoverProps { diff --git a/docs/src/examples/components/Chat/Rtl/ChatExample.rtl.tsx b/docs/src/examples/components/Chat/Rtl/ChatExample.rtl.tsx index 113f6c801d..601001957d 100644 --- a/docs/src/examples/components/Chat/Rtl/ChatExample.rtl.tsx +++ b/docs/src/examples/components/Chat/Rtl/ChatExample.rtl.tsx @@ -18,7 +18,7 @@ const items: ShorthandCollection = [ gutter: ( ), message: ( diff --git a/docs/src/examples/components/Chat/Types/ChatExample.shorthand.tsx b/docs/src/examples/components/Chat/Types/ChatExample.shorthand.tsx index ea34b578d4..f437132548 100644 --- a/docs/src/examples/components/Chat/Types/ChatExample.shorthand.tsx +++ b/docs/src/examples/components/Chat/Types/ChatExample.shorthand.tsx @@ -3,7 +3,7 @@ import { Avatar, Chat, ChatItemProps, Divider, ShorthandCollection } from '@star const janeAvatar = { image: 'public/images/avatar/small/ade.jpg', - status: { color: 'green', icon: 'stardust-checkmark' }, + status: { styles: { backgroundColor: 'green' }, icon: 'stardust-checkmark' }, } const items: ShorthandCollection = [ diff --git a/docs/src/examples/components/Chat/Types/ChatExampleContentPosition.shorthand.tsx b/docs/src/examples/components/Chat/Types/ChatExampleContentPosition.shorthand.tsx index 6f24597424..bb1a9f076c 100644 --- a/docs/src/examples/components/Chat/Types/ChatExampleContentPosition.shorthand.tsx +++ b/docs/src/examples/components/Chat/Types/ChatExampleContentPosition.shorthand.tsx @@ -6,7 +6,7 @@ const [janeAvatar, johnAvatar] = [ 'public/images/avatar/small/joe.jpg', ].map(src => ({ image: src, - status: { color: 'green', icon: 'stardust-checkmark' }, + status: { styles: { backgroundColor: 'green' }, icon: 'stardust-checkmark' }, })) const items: ShorthandCollection = [ diff --git a/docs/src/examples/components/Chat/Types/ChatMessageExampleBadge.shorthand.tsx b/docs/src/examples/components/Chat/Types/ChatMessageExampleBadge.shorthand.tsx index b805aa30e8..81b8982231 100644 --- a/docs/src/examples/components/Chat/Types/ChatMessageExampleBadge.shorthand.tsx +++ b/docs/src/examples/components/Chat/Types/ChatMessageExampleBadge.shorthand.tsx @@ -3,7 +3,7 @@ import { Avatar, Chat, ChatItemProps, ShorthandCollection } from '@stardust-ui/r const janeAvatar = { image: 'public/images/avatar/small/ade.jpg', - status: { color: 'green', icon: 'stardust-checkmark' }, + status: { styles: { backgroundColor: 'green' }, icon: 'stardust-checkmark' }, } const items: ShorthandCollection = [ diff --git a/docs/src/examples/components/Chat/Types/ChatMessageExampleStyled.shorthand.tsx b/docs/src/examples/components/Chat/Types/ChatMessageExampleStyled.shorthand.tsx index f3863f0719..08f6b3fd8f 100644 --- a/docs/src/examples/components/Chat/Types/ChatMessageExampleStyled.shorthand.tsx +++ b/docs/src/examples/components/Chat/Types/ChatMessageExampleStyled.shorthand.tsx @@ -15,7 +15,7 @@ const reactions: ShorthandCollection = [ const janeAvatar: AvatarProps = { image: 'public/images/avatar/small/ade.jpg', - status: { color: 'green', icon: 'stardust-checkmark' }, + status: { styles: { backgroundColor: 'green' }, icon: 'stardust-checkmark' }, } const content = ( diff --git a/docs/src/examples/components/Menu/Types/MenuExample.perf.tsx b/docs/src/examples/components/Menu/Types/MenuExample.perf.tsx new file mode 100644 index 0000000000..30c7b1b07b --- /dev/null +++ b/docs/src/examples/components/Menu/Types/MenuExample.perf.tsx @@ -0,0 +1,389 @@ +import * as React from 'react' +import { Menu, menuAsToolbarBehavior, MenuItemProps, ShorthandCollection } from '@stardust-ui/react' + +const items = [ + { key: 'editorials', content: 'Editorials' }, + { key: 'review', content: 'Reviews' }, + { key: 'events', content: 'Upcoming Events' }, +] + +const MenuExample = () => + +const MenuExamplePointing = () => ( +
+ +
+ +
+) + +const MenuExampleUnderlined = () => + +const itemsWithSubmenu = [ + { + key: 'editorials', + content: 'Editorials', + menu: { + items: [ + { key: '1', content: 'item1' }, + { + key: '2', + content: 'item2', + menu: [{ key: '1', content: 'item2.1' }, { key: '2', content: 'item2.2' }], + }, + { + key: '3', + content: 'item3', + menu: [{ key: '1', content: 'item3.1' }, { key: '2', content: 'item3.2' }], + }, + ], + }, + }, + { + key: 'review', + content: 'Reviews', + menu: { + items: [ + { key: '1', content: 'item1' }, + { + key: '2', + icon: 'stardust-circle', + content: 'item2 non augue tortor mollis', + menu: [ + { key: '1', icon: 'stardust-circle', content: 'item2.1' }, + { key: '2', content: 'item2.2' }, + ], + }, + { + key: '3', + icon: 'stardust-circle', + content: 'item3 elementum urna varius augue ultrices gravida malesuada fames', + menu: [ + { key: '1', icon: 'stardust-circle', content: 'item3.1' }, + { key: '2', content: 'item3.2' }, + ], + }, + ], + }, + }, + { key: 'events', content: 'Upcoming Events' }, +] + +const MenuExampleWithSubMenu = () => + +const itemsToolbar: ShorthandCollection = [ + { + key: 'format', + icon: { + name: 'format', + outline: true, + }, + 'aria-label': 'Format Tool', + }, + { + key: 'paperclip', + icon: { + name: 'paperclip', + outline: true, + }, + 'aria-label': 'Paperclip Tool', + }, + { + key: 'emoji', + icon: { + name: 'emoji', + outline: true, + }, + disabled: true, + 'aria-label': 'Emoji Tool', + }, + { + key: 'giphy', + icon: { + name: 'giphy', + outline: true, + }, + 'aria-label': 'Giphy tool', + }, + { + key: 'sticker', + icon: { + name: 'sticker', + outline: true, + }, + 'aria-label': 'Sticker Tool', + }, + { + key: 'meetup', + icon: { + name: 'video-camera-emphasis', + outline: true, + }, + 'aria-label': 'Meetup Tool', + }, + { + key: 'settings', + icon: { + name: 'settings', + outline: true, + }, + 'aria-label': 'Settings', + }, + { + key: 'menuButton2', + icon: { + name: 'more', + outline: true, + }, + 'aria-label': 'More options', + indicator: false, + menu: { + items: [ + { + key: '5', + content: 'item1', + icon: { + name: 'bookmark', + outline: true, + }, + }, + { + key: 'divider', + kind: 'divider', + }, + { + key: '6', + content: 'item2', + icon: { + name: 'mark-as-unread', + outline: true, + }, + }, + { + key: '7', + content: 'item3', + disabled: true, + icon: { + name: 'translation', + outline: true, + }, + }, + { + key: 'divider2', + kind: 'divider', + }, + { + key: '8', + content: 'item3', + icon: { + name: 'trash-can', + outline: true, + }, + }, + ], + }, + }, +] + +const MenuExampleToolbarShorthand = () => ( + +) + +const MenuExampleVerticalPointing = () => ( + +) + +const AllExamples = () => { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default AllExamples diff --git a/docs/src/examples/components/Menu/Types/MenuExample.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExample.shorthand.tsx index 9e5b4550b0..1e34ca70bc 100644 --- a/docs/src/examples/components/Menu/Types/MenuExample.shorthand.tsx +++ b/docs/src/examples/components/Menu/Types/MenuExample.shorthand.tsx @@ -1,12 +1,152 @@ import * as React from 'react' -import { Menu } from '@stardust-ui/react' +import { Menu, Provider } from '@stardust-ui/react' const items = [ - { key: 'editorials', content: 'Editorials' }, - { key: 'review', content: 'Reviews' }, - { key: 'events', content: 'Upcoming Events' }, + { + key: 'editorials', + content: 'Editorials', + }, + { + key: 'review', + content: 'Reviews', + }, + { + key: 'events', + content: 'Upcoming Events', + }, ] -const MenuExample = () => +const itemsSubmenu = [ + { + key: 'editorials', + content: 'Editorials', + icon: { + name: 'bookmark', + outline: true, + }, + }, + { + key: 'review', + content: 'Reviews', + icon: { + name: 'word', + }, + }, + { + key: 'events', + content: 'Upcoming Events', + }, + { + key: 'moreevents', + content: 'View full calendar with content so long that it wraps', + icon: { + name: 'calendar', + }, + menu: { + items: [ + { + key: '1', + content: 'item1', + }, + { + key: '2', + content: 'item2', + }, + { + key: '3', + content: 'item3', + }, + ], + }, + }, +] + +const MenuExampleSubmenu = () => + +const MenuInstance = props => + +const MenuExample = ({ prefix = '' }) => ( + + + + + ({}), + MenuDivider: () => ({}), + MenuItemWrapper: () => ({}), + MenuItem: v => ({ + root: [ + [ + null, + { + border: '3px solid blue', + }, + ], + ], + }), + }, + }} + > + + + ({}), + MenuDivider: () => ({}), + MenuItemWrapper: () => ({}), + MenuItem: v => ({ + root: [ + [ + null, + { + border: '1px solid green', + }, + ], + ], + }), + }, + }} + > + + + + + + + + +) + +const Test = () => ( + <> + +

RTL

+ + + + +) -export default MenuExample +export default Test diff --git a/docs/src/examples/components/Menu/Types/MenuExamplePointing.shorthand.tsx b/docs/src/examples/components/Menu/Types/MenuExamplePointing.shorthand.tsx index 162f46a62f..58739052b3 100644 --- a/docs/src/examples/components/Menu/Types/MenuExamplePointing.shorthand.tsx +++ b/docs/src/examples/components/Menu/Types/MenuExamplePointing.shorthand.tsx @@ -9,7 +9,7 @@ const items = [ const MenuExamplePointing = () => (
- +
diff --git a/docs/src/examples/components/Status/Usage/StatusColorExample.shorthand.tsx b/docs/src/examples/components/Status/Usage/StatusColorExample.shorthand.tsx new file mode 100644 index 0000000000..b5f6dc5e5d --- /dev/null +++ b/docs/src/examples/components/Status/Usage/StatusColorExample.shorthand.tsx @@ -0,0 +1,20 @@ +import * as React from 'react' +import { Status } from '@stardust-ui/react' + +const StatusColorExampleShorthand = () => ( +
+ +   + +   + +   + +   + +   + +
+) + +export default StatusColorExampleShorthand diff --git a/docs/src/examples/components/Status/Usage/StatusCustomExample.shorthand.tsx b/docs/src/examples/components/Status/Usage/StatusCustomExample.shorthand.tsx new file mode 100644 index 0000000000..979ea92ed1 --- /dev/null +++ b/docs/src/examples/components/Status/Usage/StatusCustomExample.shorthand.tsx @@ -0,0 +1,14 @@ +import * as React from 'react' +import { Status } from '@stardust-ui/react' + +const StatusCustomExampleShorthand = () => ( +
+ +   + +   + +
+) + +export default StatusCustomExampleShorthand diff --git a/docs/src/examples/components/Status/Variations/StatusIconExample.shorthand.tsx b/docs/src/examples/components/Status/Usage/StatusIconExample.shorthand.tsx similarity index 100% rename from docs/src/examples/components/Status/Variations/StatusIconExample.shorthand.tsx rename to docs/src/examples/components/Status/Usage/StatusIconExample.shorthand.tsx diff --git a/docs/src/examples/components/Status/Variations/index.tsx b/docs/src/examples/components/Status/Usage/index.tsx similarity index 64% rename from docs/src/examples/components/Status/Variations/index.tsx rename to docs/src/examples/components/Status/Usage/index.tsx index 80da2896d4..816ca8727c 100644 --- a/docs/src/examples/components/Status/Variations/index.tsx +++ b/docs/src/examples/components/Status/Usage/index.tsx @@ -2,24 +2,24 @@ import * as React from 'react' import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample' import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' -const Variations = () => ( - +const Usage = () => ( + ) -export default Variations +export default Usage diff --git a/docs/src/examples/components/Status/Variations/StatusColorExample.shorthand.tsx b/docs/src/examples/components/Status/Variations/StatusColorExample.shorthand.tsx deleted file mode 100644 index e0d00ee078..0000000000 --- a/docs/src/examples/components/Status/Variations/StatusColorExample.shorthand.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import * as React from 'react' -import { Status } from '@stardust-ui/react' - -const StatusColorExampleShorthand = () => ( -
- -   - -   - -   - -   - -   - -
-) - -export default StatusColorExampleShorthand diff --git a/docs/src/examples/components/Status/Variations/StatusCustomExample.shorthand.tsx b/docs/src/examples/components/Status/Variations/StatusCustomExample.shorthand.tsx deleted file mode 100644 index 474de80e99..0000000000 --- a/docs/src/examples/components/Status/Variations/StatusCustomExample.shorthand.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react' -import { Status } from '@stardust-ui/react' - -const StatusCustomExampleShorthand = () => ( -
- -   - -   - -
-) - -export default StatusCustomExampleShorthand diff --git a/docs/src/examples/components/Status/index.tsx b/docs/src/examples/components/Status/index.tsx index 6178c34bb5..f2c2a645db 100644 --- a/docs/src/examples/components/Status/index.tsx +++ b/docs/src/examples/components/Status/index.tsx @@ -1,12 +1,12 @@ import * as React from 'react' import Types from './Types' -import Variations from './Variations' +import Usage from './Usage' const StatusExamples = () => (
- +
) diff --git a/packages/react/package.json b/packages/react/package.json index a83926fbdb..bb83786a42 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -16,6 +16,7 @@ "downshift": "3.2.14", "fast-memoize": "^2.5.1", "fela": "^10.6.1", + "fela-monolithic": "^10.8.2", "fela-plugin-embedded": "^10.6.1", "fela-plugin-fallback-value": "^10.6.1", "fela-plugin-placeholder-prefixer": "^10.6.1", diff --git a/packages/react/src/components/Avatar/Avatar.tsx b/packages/react/src/components/Avatar/Avatar.tsx index 766adeed6d..48faf66d11 100644 --- a/packages/react/src/components/Avatar/Avatar.tsx +++ b/packages/react/src/components/Avatar/Avatar.tsx @@ -111,10 +111,6 @@ class Avatar extends UIComponent, any> { defaultProps: () => ({ size, styles: styles.status, - variables: { - borderColor: variables.statusBorderColor, - borderWidth: variables.statusBorderWidth, - }, }), })} diff --git a/packages/react/src/components/Menu/Menu.tsx b/packages/react/src/components/Menu/Menu.tsx index c66af2c734..a6debbec75 100644 --- a/packages/react/src/components/Menu/Menu.tsx +++ b/packages/react/src/components/Menu/Menu.tsx @@ -3,6 +3,8 @@ import * as customPropTypes from '@stardust-ui/react-proptypes' import * as _ from 'lodash' import * as PropTypes from 'prop-types' import * as React from 'react' +import { useValueAndKey, useKeyOnly } from '../../lib/classNameBuilders' +import cx from 'classnames' import { AutoControlledComponent, @@ -19,7 +21,7 @@ import { mergeComponentVariables } from '../../lib/mergeThemes' import MenuItem, { MenuItemProps } from './MenuItem' import { ReactAccessibilityBehavior } from '../../lib/accessibility/reactTypes' -import { ComponentVariablesObject, ComponentSlotStylesPrepared } from '../../themes/types' +import { ComponentVariablesObject } from '../../themes/types' import { WithAsProp, ShorthandCollection, @@ -29,6 +31,7 @@ import { } from '../../types' import MenuDivider from './MenuDivider' import { IconProps } from '../Icon/Icon' +import MenuItemWrapper from './MenuItemWrapper' export type MenuShorthandKinds = 'divider' | 'item' @@ -140,6 +143,7 @@ class Menu extends AutoControlledComponent, MenuState> { static Item = MenuItem static Divider = MenuDivider + static ItemWrapper = MenuItemWrapper handleItemOverrides = variables => predefinedProps => ({ onClick: (e, itemProps) => { @@ -167,7 +171,7 @@ class Menu extends AutoControlledComponent, MenuState> { }) renderItems = ( - styles: ComponentSlotStylesPrepared, + propsClasses: string, variables: ComponentVariablesObject, accessibility: ReactAccessibilityBehavior, ) => { @@ -198,11 +202,10 @@ class Menu extends AutoControlledComponent, MenuState> { if (kind === 'divider') { return MenuDivider.create(item, { defaultProps: () => ({ - className: Menu.slotClassNames.divider, + className: cx(Menu.slotClassNames.divider, propsClasses), primary, secondary, vertical, - styles: styles.divider, inSubmenu: submenu, accessibility: accessibility.childBehaviors ? accessibility.childBehaviors.divider @@ -216,7 +219,7 @@ class Menu extends AutoControlledComponent, MenuState> { return MenuItem.create(item, { defaultProps: () => ({ - className: Menu.slotClassNames.item, + className: cx(Menu.slotClassNames.item, propsClasses), iconOnly, pills, pointing, @@ -239,16 +242,29 @@ class Menu extends AutoControlledComponent, MenuState> { }) } - renderComponent({ ElementType, classes, accessibility, styles, variables, unhandledProps }) { - const { children } = this.props + renderComponent({ ElementType, classes, accessibility, variables, unhandledProps, theme }) { + const { children, primary, iconOnly, vertical, pills, pointing, underlined } = this.props + + const propClasses = cx( + theme.name, // UGLY + useKeyOnly(primary, 'primary'), + useKeyOnly(iconOnly, 'iconOnly'), + useKeyOnly(vertical, 'vertical'), + useKeyOnly(pills, 'pills'), + useKeyOnly(underlined, 'underlined'), + useValueAndKey(pointing, 'pointing'), + ) + return ( - {childrenExist(children) ? children : this.renderItems(styles, variables, accessibility)} + {childrenExist(children) + ? children + : this.renderItems(propClasses, variables, accessibility)} ) } diff --git a/packages/react/src/components/Menu/MenuDivider.tsx b/packages/react/src/components/Menu/MenuDivider.tsx index dc689a0332..b0620d51c1 100644 --- a/packages/react/src/components/Menu/MenuDivider.tsx +++ b/packages/react/src/components/Menu/MenuDivider.tsx @@ -1,6 +1,8 @@ import { Accessibility, menuDividerBehavior } from '@stardust-ui/accessibility' import * as React from 'react' import * as PropTypes from 'prop-types' +import cx from 'classnames' +import { useKeyOnly } from '../../lib/classNameBuilders' import { createShorthandFactory, @@ -48,15 +50,24 @@ class MenuDivider extends UIComponent> { inSubmenu: PropTypes.bool, } - renderComponent({ ElementType, classes, unhandledProps, accessibility }) { - const { children, content } = this.props + renderComponent({ ElementType, classes, unhandledProps, accessibility, theme }) { + const { children, content, primary, secondary, vertical, inSubmenu } = this.props + + const propClasses = cx( + theme.name, + useKeyOnly(primary, 'primary'), + useKeyOnly(secondary, 'secondary'), + useKeyOnly(vertical, 'vertical'), + useKeyOnly(inSubmenu, 'inSubmenu'), + useKeyOnly(content, 'content'), + ) return ( {childrenExist(children) ? children : content} diff --git a/packages/react/src/components/Menu/MenuItem.tsx b/packages/react/src/components/Menu/MenuItem.tsx index 9e2d9adc36..3ed14ead07 100644 --- a/packages/react/src/components/Menu/MenuItem.tsx +++ b/packages/react/src/components/Menu/MenuItem.tsx @@ -7,6 +7,7 @@ import * as _ from 'lodash' import cx from 'classnames' import * as PropTypes from 'prop-types' import * as React from 'react' +import { useValueAndKey, useKeyOnly } from '../../lib/classNameBuilders' import { AutoControlledComponent, @@ -23,7 +24,7 @@ import { } from '../../lib' import Icon, { IconProps } from '../Icon/Icon' import Menu, { MenuProps, MenuShorthandKinds } from './Menu' -import Box, { BoxProps } from '../Box/Box' +import Box from '../Box/Box' import { ComponentEventHandler, WithAsProp, @@ -32,10 +33,14 @@ import { withSafeTypeForAs, } from '../../types' import { Popper } from '../../lib/positioner' +import { MenuItemWrapperProps } from './MenuItemWrapper' export interface MenuItemSlotClassNames { wrapper: string submenu: string + indicator: string + icon: string + content: string } export interface MenuItemProps @@ -113,7 +118,7 @@ export interface MenuItemProps vertical?: boolean /** Shorthand for the wrapper component. */ - wrapper?: ShorthandValue + wrapper?: ShorthandValue /** Shorthand for the submenu. */ menu?: ShorthandValue | ShorthandCollection @@ -154,6 +159,9 @@ class MenuItem extends AutoControlledComponent, MenuIt static slotClassNames: MenuItemSlotClassNames = { submenu: `${MenuItem.className}__submenu`, wrapper: `${MenuItem.className}__wrapper`, + indicator: `${MenuItem.className}__indicator`, + icon: `${MenuItem.className}__icon`, + content: `${MenuItem.className}__content`, } static create: ShorthandFactory @@ -189,7 +197,7 @@ class MenuItem extends AutoControlledComponent, MenuIt static defaultProps = { as: 'a', accessibility: menuItemBehavior as Accessibility, - wrapper: { as: 'li' }, + wrapper: {}, } static autoControlledProps = ['menuOpen'] @@ -197,19 +205,24 @@ class MenuItem extends AutoControlledComponent, MenuIt menuRef = React.createRef() itemRef = React.createRef() - renderComponent({ ElementType, classes, accessibility, unhandledProps, styles, rtl }) { + renderComponent({ ElementType, classes, accessibility, unhandledProps, rtl, theme }) { const { children, content, - icon, + icon, // wrapper, menu, - primary, + primary, // secondary, - active, - vertical, + active, // + vertical, // indicator, - disabled, + disabled, // + iconOnly, // + pointing, + pills, + underlined, + inSubmenu, } = this.props const { menuOpen } = this.state @@ -217,12 +230,25 @@ class MenuItem extends AutoControlledComponent, MenuIt const indicatorWithDefaults = indicator === undefined ? defaultIndicator : indicator const targetRef = toRefObject(this.context.target) + const propClasses = cx( + theme.name, // UGLY + useKeyOnly(primary, 'primary'), + useKeyOnly(iconOnly, 'iconOnly'), + useKeyOnly(disabled, 'disabled'), + useKeyOnly(vertical, 'vertical'), + useKeyOnly(active, 'active'), + useKeyOnly(pills, 'pills'), + useKeyOnly(underlined, 'underlined'), + useKeyOnly(inSubmenu, 'inSubmenu'), + useValueAndKey(pointing, 'pointing'), + ) + const menuItemInner = childrenExist(children) ? ( children ) : ( , MenuIt {Icon.create(icon, { defaultProps: () => ({ xSpacing: !!content ? 'after' : 'none', - styles: styles.icon, + className: MenuItem.slotClassNames.icon, }), })} {Box.create(content, { - defaultProps: () => ({ as: 'span', styles: styles.content }), + defaultProps: () => ({ + as: 'span', + className: MenuItem.slotClassNames.content, + }), })} {menu && Icon.create(indicatorWithDefaults, { defaultProps: () => ({ + className: MenuItem.slotClassNames.indicator, name: vertical ? 'stardust-menu-arrow-end' : 'stardust-menu-arrow-down', - styles: styles.indicator, }), })} @@ -267,7 +296,6 @@ class MenuItem extends AutoControlledComponent, MenuIt vertical: true, primary, secondary, - styles: styles.menu, submenu: true, indicator, }), @@ -279,9 +307,18 @@ class MenuItem extends AutoControlledComponent, MenuIt ) : null if (wrapper) { - return Box.create(wrapper, { + return Menu.ItemWrapper.create(wrapper, { defaultProps: () => ({ - className: cx(MenuItem.slotClassNames.wrapper, classes.wrapper), + active, + disabled, + iconOnly, + pills, + pointing, + primary, + secondary, + underlined, + vertical, + inSubmenu, ...accessibility.attributes.wrapper, ...applyAccessibilityKeyHandlers(accessibility.keyHandlers.wrapper, wrapper), }), diff --git a/packages/react/src/components/Menu/MenuItemWrapper.tsx b/packages/react/src/components/Menu/MenuItemWrapper.tsx new file mode 100644 index 0000000000..a349f4a25e --- /dev/null +++ b/packages/react/src/components/Menu/MenuItemWrapper.tsx @@ -0,0 +1,154 @@ +import { Accessibility } from '@stardust-ui/accessibility' +import * as React from 'react' + +import { + childrenExist, + createShorthandFactory, + UIComponent, + UIComponentProps, + ChildrenComponentProps, + ContentComponentProps, + commonPropTypes, + ColorComponentProps, + rtlTextContainer, + ShorthandFactory, +} from '../../lib' + +import { ComponentEventHandler, WithAsProp, withSafeTypeForAs } from '../../types' +import MenuItem from './MenuItem' +import cx from 'classnames' +import { useKeyOnly, useValueAndKey } from '../../lib/classNameBuilders' + +export interface MenuItemWrapperProps + extends UIComponentProps, + ChildrenComponentProps, + ContentComponentProps, + ColorComponentProps { + /** + * Accessibility behavior if overridden by the user. + */ + accessibility?: Accessibility + + /** A menu item can be active. */ + active?: boolean + + /** A menu item can show it is currently unable to be interacted with. */ + disabled?: boolean + + /** A menu may have just icons. */ + iconOnly?: boolean + + /** + * Called on click. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onClick?: ComponentEventHandler + + /** + * Called after user's focus. + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onFocus?: ComponentEventHandler + + /** + * Called after item blur. + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onBlur?: ComponentEventHandler + + /** A menu can adjust its appearance to de-emphasize its contents. */ + pills?: boolean + + /** + * A menu can point to show its relationship to nearby content. + * For vertical menu, it can point to the start of the item or to the end. + */ + pointing?: boolean | 'start' | 'end' + + /** The menu item can have primary type. */ + primary?: boolean + + /** The menu item can have secondary type. */ + secondary?: boolean + + /** Menu items can by highlighted using underline. */ + underlined?: boolean + + /** A vertical menu displays elements vertically. */ + vertical?: boolean + + /** Indicates whether the menu item is part of submenu. */ + inSubmenu?: boolean +} + +class MenuItemWrapper extends UIComponent, any> { + static create: ShorthandFactory + + static className = `${MenuItem.className}__wrapper` + + static displayName = 'MenuItemWrapper' + + static propTypes = { + ...commonPropTypes.createCommon({ color: true }), + } + + static defaultProps = { + as: 'li', + } + + renderComponent({ accessibility, ElementType, classes, unhandledProps, theme }) { + const { + children, + content, + primary, + iconOnly, + disabled, + vertical, + active, + pills, + underlined, + inSubmenu, + pointing, + } = this.props + + const propClasses = cx( + theme.name, // UGLY + useKeyOnly(primary, 'primary'), + useKeyOnly(iconOnly, 'iconOnly'), + useKeyOnly(disabled, 'disabled'), + useKeyOnly(vertical, 'vertical'), + useKeyOnly(active, 'active'), + useKeyOnly(pills, 'pills'), + useKeyOnly(underlined, 'underlined'), + useKeyOnly(inSubmenu, 'inSubmenu'), + useValueAndKey(pointing, 'pointing'), + ) + + return ( + + {childrenExist(children) ? children : content} + + ) + } +} + +MenuItemWrapper.create = createShorthandFactory({ + Component: MenuItemWrapper, + mappedProp: 'content', +}) + +/** + * A MenuItemWrapper provides more detailed information about the Header. + */ +export default withSafeTypeForAs( + MenuItemWrapper, +) diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 032da0a33a..4f91122658 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -130,6 +130,8 @@ export * from './components/Menu/MenuItem' export { default as MenuItem } from './components/Menu/MenuItem' export * from './components/Menu/MenuDivider' export { default as MenuDivider } from './components/Menu/MenuDivider' +export * from './components/Menu/MenuItemWrapper' +export { default as MenuItemWrapper } from './components/Menu/MenuItemWrapper' export * from './components/Popup/Popup' export { default as Popup } from './components/Popup/Popup' diff --git a/packages/react/src/lib/classNameBuilders.ts b/packages/react/src/lib/classNameBuilders.ts new file mode 100644 index 0000000000..5bf36c1f32 --- /dev/null +++ b/packages/react/src/lib/classNameBuilders.ts @@ -0,0 +1,21 @@ +/** + * Props where only the prop key is used in the className. + * @param {*} val A props value + * @param {string} key A props key + * + * @example + *