diff --git a/CHANGELOG.md b/CHANGELOG.md
index d19a28ab38..da25f1c2d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add accessibility behavior description @kolaps33 ([#74](https://github.com/stardust-ui/react/pull/74))
- Add strict null checks for generated TS types @smykhailov ([#108](https://github.com/stardust-ui/react/pull/108))
- Export themes at `@stardust-ui/react/themes` @levithomason ([#145](https://github.com/stardust-ui/react/pull/145))
+- Add support for Menu `vertical pointing` prop @miroslavstastny ([#123](https://github.com/stardust-ui/react/pull/123))
### Documentation
- Add a Quick Start guide @levithomason ([#145](https://github.com/stardust-ui/react/pull/145))
diff --git a/docs/src/examples/components/Menu/Variations/MenuExamplePointingStart.shorthand.tsx b/docs/src/examples/components/Menu/Variations/MenuExamplePointingStart.shorthand.tsx
new file mode 100644
index 0000000000..a24680cea7
--- /dev/null
+++ b/docs/src/examples/components/Menu/Variations/MenuExamplePointingStart.shorthand.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import { Menu } from '@stardust-ui/react'
+
+const items = [
+ { key: 'editorials', content: 'Editorials' },
+ { key: 'review', content: 'Reviews' },
+ { key: 'events', content: 'Upcoming Events' },
+]
+
+const MenuExamplePointingStart = () => (
+
+)
+
+export default MenuExamplePointingStart
diff --git a/docs/src/examples/components/Menu/Variations/MenuExamplePointingStartPrimary.shorthand.tsx b/docs/src/examples/components/Menu/Variations/MenuExamplePointingStartPrimary.shorthand.tsx
new file mode 100644
index 0000000000..c01ec542f5
--- /dev/null
+++ b/docs/src/examples/components/Menu/Variations/MenuExamplePointingStartPrimary.shorthand.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import { Menu } from '@stardust-ui/react'
+
+const items = [
+ { key: 'editorials', content: 'Editorials' },
+ { key: 'review', content: 'Reviews' },
+ { key: 'events', content: 'Upcoming Events' },
+]
+
+const MenuExamplePointingStartPrimary = () => (
+
+)
+
+export default MenuExamplePointingStartPrimary
diff --git a/docs/src/examples/components/Menu/Variations/MenuExampleVerticalPointing.shorthand.tsx b/docs/src/examples/components/Menu/Variations/MenuExampleVerticalPointing.shorthand.tsx
new file mode 100644
index 0000000000..a9bf96ca97
--- /dev/null
+++ b/docs/src/examples/components/Menu/Variations/MenuExampleVerticalPointing.shorthand.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import { Menu } from '@stardust-ui/react'
+
+const items = [
+ { key: 'editorials', content: 'Editorials' },
+ { key: 'review', content: 'Reviews' },
+ { key: 'events', content: 'Upcoming Events' },
+]
+
+const MenuExampleVerticalPointing = () => (
+
+)
+
+export default MenuExampleVerticalPointing
diff --git a/docs/src/examples/components/Menu/Variations/MenuExampleVerticalPointingEnd.shorthand.tsx b/docs/src/examples/components/Menu/Variations/MenuExampleVerticalPointingEnd.shorthand.tsx
new file mode 100644
index 0000000000..090b26e170
--- /dev/null
+++ b/docs/src/examples/components/Menu/Variations/MenuExampleVerticalPointingEnd.shorthand.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import { Menu } from '@stardust-ui/react'
+
+const items = [
+ { key: 'editorials', content: 'Editorials' },
+ { key: 'review', content: 'Reviews' },
+ { key: 'events', content: 'Upcoming Events' },
+]
+
+const MenuExampleVerticalPointingEnd = () => (
+
+)
+
+export default MenuExampleVerticalPointingEnd
diff --git a/docs/src/examples/components/Menu/Variations/index.tsx b/docs/src/examples/components/Menu/Variations/index.tsx
index 9aa04b73fe..7cb269c989 100644
--- a/docs/src/examples/components/Menu/Variations/index.tsx
+++ b/docs/src/examples/components/Menu/Variations/index.tsx
@@ -34,6 +34,26 @@ const Variations = () => (
description="A menu can point to show its relationship to nearby content."
examplePath="components/Menu/Variations/MenuExamplePointingPrimary"
/>
+
+
+
+
{
/** A menu can adjust its appearance to de-emphasize its contents. */
pills: PropTypes.bool,
- /** A menu can point to show its relationship to nearby content. */
- pointing: PropTypes.bool,
+ /**
+ * 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: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['start', 'end'])]),
/** The menu can have primary or secondary type */
type: PropTypes.oneOf(['primary', 'secondary']),
diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx
index dc39e76a0f..a388662f32 100644
--- a/src/components/Menu/MenuItem.tsx
+++ b/src/components/Menu/MenuItem.tsx
@@ -52,8 +52,11 @@ class MenuItem extends UIComponent {
/** A menu can adjust its appearance to de-emphasize its contents. */
pills: PropTypes.bool,
- /** A menu can point to show its relationship to nearby content. */
- pointing: PropTypes.bool,
+ /**
+ * 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: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['start', 'end'])]),
/** The menu can have primary or secondary type */
type: PropTypes.oneOf(['primary', 'secondary']),
diff --git a/src/themes/teams/components/Menu/menuItemStyles.ts b/src/themes/teams/components/Menu/menuItemStyles.ts
index 813e4081cd..0288152536 100644
--- a/src/themes/teams/components/Menu/menuItemStyles.ts
+++ b/src/themes/teams/components/Menu/menuItemStyles.ts
@@ -8,10 +8,11 @@ const underlinedItem = (color): ICSSInJSStyle => ({
})
const itemSeparator = ({ props, variables }: { props: any; variables }): ICSSInJSStyle => {
- const { active, iconOnly, pills, type, underlined, vertical } = props
+ const { active, iconOnly, pointing, pills, type, underlined, vertical } = props
return {
...(!pills &&
!underlined &&
+ !(pointing && vertical) &&
!iconOnly && {
'::before': {
position: 'absolute',
@@ -35,6 +36,56 @@ const itemSeparator = ({ props, variables }: { props: any; variables }): ICSSInJ
}
}
+const pointingBeak = ({ props, variables }: { props: any; variables }): ICSSInJSStyle => {
+ const { pointing, type } = props
+
+ let backgroundColor: string
+ let borderColor: string
+ let top: string
+ let borders: ICSSInJSStyle
+
+ if (type === 'primary') {
+ backgroundColor = variables.typePrimaryActiveBackgroundColor
+ borderColor = variables.typePrimaryBorderColor
+ } else {
+ backgroundColor = variables.defaultActiveBackgroundColor
+ borderColor = variables.defaultBorderColor
+ }
+
+ if (pointing === 'start') {
+ borders = {
+ borderTop: `1px solid ${borderColor}`,
+ borderLeft: `1px solid ${borderColor}`,
+ }
+ top = '-1px' // 1px for the border
+ } else {
+ borders = {
+ borderBottom: `1px solid ${borderColor}`,
+ borderRight: `1px solid ${borderColor}`,
+ }
+ top = '100%'
+ }
+
+ return {
+ '::after': {
+ visibility: 'visible',
+ background: backgroundColor,
+ position: 'absolute',
+ content: '""',
+ top,
+ left: '50%',
+ transform: 'translateX(-50%) translateY(-50%) rotate(45deg)',
+ margin: '.5px 0 0',
+ width: pxToRem(10),
+ height: pxToRem(10),
+ border: 'none',
+ ...borders,
+ zIndex: 2,
+ transition: 'background .1s ease',
+ },
+ }
+}
+
const menuItemStyles = {
root: ({ props, variables }: { props: any; variables: IMenuVariables }): ICSSInJSStyle => {
const { active, iconOnly, pills, pointing, type, underlined, vertical } = props
@@ -67,6 +118,16 @@ const menuItemStyles = {
color: variables.defaultColor,
}),
...itemSeparator({ props, variables }),
+ ...(pointing &&
+ vertical && {
+ border: '1px solid transparent',
+ borderTopLeftRadius: `${pxToRem(3)}`,
+ borderTopRightRadius: `${pxToRem(3)}`,
+ ...(pointing === 'end'
+ ? { borderRight: `${pxToRem(3)} solid transparent` }
+ : { borderLeft: `${pxToRem(3)} solid transparent` }),
+ marginBottom: `${pxToRem(12)}`,
+ }),
':hover': {
color: variables.defaultActiveColor,
@@ -96,36 +157,19 @@ const menuItemStyles = {
}),
}),
},
- ...(pointing && {
- '::after': {
- visibility: 'visible',
- background: variables.defaultActiveBackgroundColor,
- position: 'absolute',
- content: '""',
- top: '100%',
- left: '50%',
- transform: 'translateX(-50%) translateY(-50%) rotate(45deg)',
- margin: '.5px 0 0',
- width: pxToRem(10),
- height: pxToRem(10),
- border: 'none',
- borderBottom: `1px solid ${variables.defaultBorderColor}`,
- borderRight: `1px solid ${variables.defaultBorderColor}`,
- zIndex: 2,
- transition: 'background .1s ease',
- ...(type === 'primary' && {
- background: variables.typePrimaryActiveBackgroundColor,
- borderBottom: `1px solid ${variables.typePrimaryBorderColor}`,
- borderRight: `1px solid ${variables.typePrimaryBorderColor}`,
- }),
- },
- }),
+ ...(pointing && !vertical && pointingBeak({ props, variables })),
+ ...(pointing &&
+ vertical && {
+ ...(pointing === 'end'
+ ? { borderRight: `${pxToRem(3)} solid ${variables.typePrimaryActiveColor}` }
+ : { borderLeft: `${pxToRem(3)} solid ${variables.typePrimaryActiveColor}` }),
+ }),
}),
}
},
anchor: ({ props, variables }): ICSSInJSStyle => {
- const { active, iconOnly, type, underlined } = props
+ const { active, iconOnly, pointing, type, underlined, vertical } = props
const { iconsMenuItemSize } = variables
return {
@@ -133,7 +177,9 @@ const menuItemStyles = {
display: 'block',
...(underlined
? { padding: `0 0 ${pxToRem(8)} 0` }
- : { padding: `${pxToRem(14)} ${pxToRem(18)}` }),
+ : pointing && vertical
+ ? { padding: `${pxToRem(8)} ${pxToRem(18)}` }
+ : { padding: `${pxToRem(14)} ${pxToRem(18)}` }),
cursor: 'pointer',
...(iconOnly && {
diff --git a/src/themes/teams/components/Menu/menuStyles.ts b/src/themes/teams/components/Menu/menuStyles.ts
index 67558a2eed..e003ffc23c 100644
--- a/src/themes/teams/components/Menu/menuStyles.ts
+++ b/src/themes/teams/components/Menu/menuStyles.ts
@@ -7,7 +7,7 @@ const solidBorder = (color: string) => ({
export default {
root: ({ props, variables }): ICSSInJSStyle => {
- const { iconOnly, fluid, pills, type, underlined, vertical } = props
+ const { iconOnly, fluid, pointing, pills, type, underlined, vertical } = props
return {
display: 'flex',
...(vertical && {
@@ -20,6 +20,7 @@ export default {
}),
...(!pills &&
!iconOnly &&
+ !(pointing && vertical) &&
!underlined && {
...solidBorder(variables.defaultBorderColor),
...(type === 'primary' && {