Skip to content

Commit

Permalink
feat(ld-context-menu): add rightClick prop to trigger menu on right-c…
Browse files Browse the repository at this point in the history
…lick
  • Loading branch information
borisdiakur committed Jun 1, 2023
1 parent 116ab85 commit ef35bbe
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 84 deletions.
1 change: 1 addition & 0 deletions src/docs/components/docs-icon-group/docs-icon-group.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
border-radius: var(--ld-br-l);
display: grid;
grid-template-columns: repeat(2, 1fr);
text-align: center;

@media (width >= 70rem) {
grid-template-columns: repeat(4, 1fr);
Expand Down
45 changes: 27 additions & 18 deletions src/docs/components/docs-icon/docs-icon.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,45 @@
position: relative;
text-decoration: none;
width: 100%;
transition: transform 0.2s ease;

&:hover {
&:hover,
&:focus-visible,
&.docs-icon--active {
background: var(--ld-col-neutral-010);
color: var(--ld-col-neutral-900);
height: calc(100% + var(--ld-sp-16));
margin: calc(var(--ld-sp-8) * -1);
padding: var(--ld-sp-32) var(--ld-sp-16) var(--ld-sp-16);
width: calc(100% + var(--ld-sp-16));
transform: scale(1.05);
z-index: 1;

.docs-icon__player {
filter: none;
}

.docs-icon__identifier {
margin: calc(var(--ld-sp-8) * -1) 0 var(--ld-sp-8);
opacity: 1;
}

.docs-icon__name {
margin-top: 0;
opacity: 0;
top: 3.5rem;
}

.docs-icon__instructions {
top: 0;
opacity: 1;

&--hidden {
opacity: 0;
}
}
}

&.docs-icon--active {
&::before {
content: '';
border-radius: var(--ld-br-l);
inset: 0 0 -1rem;
position: absolute;
z-index: -1;
}
}

Expand Down Expand Up @@ -120,14 +137,12 @@
}
}

&,
&__player,
&__identifier,
&__name,
&__instructions,
&__confirmation {
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
transition: all 100ms ease-in-out;
transition: opacity 0.2s ease-in-out;
}
}

Expand All @@ -141,13 +156,7 @@
line-height: 140%;
}

.docs-icon:hover & {
top: 0;
opacity: 1;
}

&--hidden,
.docs-icon:hover &--hidden {
&--hidden {
opacity: 0;
}

Expand Down
131 changes: 86 additions & 45 deletions src/docs/components/docs-icon/docs-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import '@lottiefiles/lottie-player'
shadow: false,
})
export class DocsIcon {
private aRef: HTMLAnchorElement
private isDownload = false

/** Play the animation back and forth */
@Prop() bounce = false

Expand All @@ -33,9 +36,16 @@ export class DocsIcon {
@Prop() name: string

@State() confirm = false
@State() isMenuOpen = false

private handleMenuOpen = () => {
this.isMenuOpen = true
}
private handleMenuClose = () => {
this.isMenuOpen = false
}

private copyIdentifier = async (event: MouseEvent) => {
event.preventDefault()
private copyIdentifier = async () => {
await copyToClipboard(this.identifier)

this.confirm = true
Expand All @@ -45,6 +55,22 @@ export class DocsIcon {
}, 2000)
}

private handleClick = (ev: MouseEvent) => {
if (!this.isDownload) {
ev.preventDefault()

if (!this.isAnimation) {
this.copyIdentifier()
}
}
}

private handleClickDownload = () => {
this.isDownload = true
this.aRef.click()
this.isDownload = false
}

async componentWillLoad(): Promise<void> {
if (!this.downloadUrl && this.isAnimation) {
const { buildstamp } =
Expand All @@ -57,55 +83,70 @@ export class DocsIcon {

render() {
return (
<a
class="docs-icon"
href={this.downloadUrl}
onContextMenu={this.isAnimation ? undefined : this.copyIdentifier}
slot="trigger"
download={this.identifier}
<ld-context-menu
onLdcontextmenuopen={this.handleMenuOpen}
onLdcontextmenuclose={this.handleMenuClose}
position="bottom center"
rightClick
size="sm"
>
{this.isAnimation ? (
<lottie-player
class="docs-icon__player"
autoplay
loop
mode={this.bounce ? 'bounce' : undefined}
src={this.downloadUrl}
/>
) : (
<ld-icon name={this.identifier} size="lg" />
)}
<p class="docs-icon__name">{this.name}</p>
<p class="docs-icon__identifier">{this.identifier}</p>
<div class="docs-icon__action">
<ld-typo
class={getClassNames([
'docs-icon__instructions',
this.confirm && 'docs-icon__instructions--hidden',
])}
variant="body-xs"
>
<span>Click</span> to download
{!this.isAnimation && (
<>
<br />
<span>Right-click</span> to copy name
</>
)}
</ld-typo>
{!this.isAnimation && (
<a
class={getClassNames([
'docs-icon',
this.isMenuOpen && 'docs-icon--active',
])}
href={this.downloadUrl}
ref={(el) => (this.aRef = el)}
slot="trigger"
download={this.identifier}
onClick={this.handleClick}
>
{this.isAnimation ? (
<lottie-player
class="docs-icon__player"
autoplay
loop
mode={this.bounce ? 'bounce' : undefined}
src={this.downloadUrl}
/>
) : (
<ld-icon name={this.identifier} size="lg" />
)}
<p class="docs-icon__name">{this.name}</p>
<p class="docs-icon__identifier">{this.identifier}</p>
<div class="docs-icon__action">
<ld-typo
class={getClassNames([
'docs-icon__confirmation',
this.confirm && 'docs-icon__confirmation--visible',
'docs-icon__instructions',
this.confirm && 'docs-icon__instructions--hidden',
])}
variant="label-s"
variant="body-xs"
>
Copied! <ld-icon name="checkmark" size="sm" />
{!this.isAnimation && (
<>
<span>Click</span> to copy name
<br />
</>
)}
<span>Right-click</span> to download
</ld-typo>
)}
</div>
</a>
{!this.isAnimation && (
<ld-typo
class={getClassNames([
'docs-icon__confirmation',
this.confirm && 'docs-icon__confirmation--visible',
])}
variant="label-s"
>
Copied! <ld-icon name="checkmark" size="sm" />
</ld-typo>
)}
</div>
</a>
<ld-menuitem onClick={this.handleClickDownload}>
<ld-icon name="download" /> Download
</ld-menuitem>
</ld-context-menu>
)
}
}
5 changes: 3 additions & 2 deletions src/docs/layouts/docs-layout/docs-layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ html {
}

body {
overscroll-behavior: none;
-webkit-text-size-adjust: none; /* stylelint-disable-line plugin/no-unsupported-browser-features */
--docs-header-height: 3.125rem;
--docs-max-width: 90rem;
--docs-nav-width: 19rem;
--docs-toc-top: 7.75rem;
--docs-toc-width: 17rem;
-webkit-text-size-adjust: none; /* stylelint-disable-line plugin/no-unsupported-browser-features */
overscroll-behavior: none;
overflow-x: hidden;

@media (width <= 75rem) {
--docs-toc-width: 13rem;
Expand Down
35 changes: 29 additions & 6 deletions src/liquid/components/ld-context-menu/ld-context-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
Component,
Element,
Event,
EventEmitter,
h,
Method,
Prop,
Expand Down Expand Up @@ -30,6 +32,9 @@ export class LdContextMenu {
/** Position of the context menu relative to the trigger element. */
@Prop() position?: HTMLLdTooltipElement['position'] = 'bottom left'

/** Use right-click. */
@Prop() rightClick? = false

/** Size of the context menu. */
@Prop() size?: 'sm' | 'lg'

Expand All @@ -51,6 +56,16 @@ export class LdContextMenu {
}
}

private focusFirstMenuitem = async (menuInTooltip: HTMLLdMenuElement) => {
const firstMenuItem = await menuInTooltip.getFirstMenuItem()

if (!firstMenuItem) {
return
}

await firstMenuItem.focusInner()
}

private handleKeyDown = async (event: KeyboardEvent) => {
switch (event.key) {
case 'Escape':
Expand All @@ -70,15 +85,22 @@ export class LdContextMenu {
menuInTooltip.addEventListener('keydown', this.handleKeyDown)
this.initialized = true
}
const firstMenuItem = await menuInTooltip.getFirstMenuItem()

if (!firstMenuItem) {
return
}
this.focusFirstMenuitem(menuInTooltip)
this.ldcontextmenuopen.emit()
}

await firstMenuItem.focusInner()
private handleMenuClose = () => {
this.resetFocus()
this.ldcontextmenuclose.emit()
}

/** Emitted when the context menu is opened. */
@Event() ldcontextmenuopen: EventEmitter

/** Emitted when the context menu is closed. */
@Event() ldcontextmenuclose: EventEmitter

/** Show context menu */
@Method()
async showContextMenu() {
Expand Down Expand Up @@ -118,9 +140,10 @@ export class LdContextMenu {
render() {
return (
<ld-tooltip
onLdtooltipclose={this.resetFocus}
onLdtooltipclose={this.handleMenuClose}
onLdtooltipopen={this.handleMenuOpen}
ref={(element: HTMLLdTooltipElement) => (this.tooltipRef = element)}
rightClick={this.rightClick}
part="tooltip"
position={this.position}
preventScreenreader
Expand Down
29 changes: 29 additions & 0 deletions src/liquid/components/ld-context-menu/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,26 @@ You can prevent auto-closing of the context menu on click, by attaching the `pre
</LdContextMenu>
{% endexample %}

### Use right-click

You can configure the context-menu to open on right-click instead of left-click by assigning the `rightClick` prop.

{% example %}
<ld-context-menu right-click>
<ld-button slot="trigger">Open menu</ld-button>
<ld-menuitem>Menu item 1</ld-menuitem>
<ld-menuitem>Menu item 2</ld-menuitem>
</ld-context-menu>

<!-- React component -->

<LdContextMenu rightClick>
<LdButton slot="trigger">Open menu</LdButton>
<LdMenuitem>Menu item 1</LdMenuitem>
<LdMenuitem>Menu item 2</LdMenuitem>
</LdContextMenu>
{% endexample %}

<!-- Auto Generated Below -->


Expand All @@ -221,10 +241,19 @@ You can prevent auto-closing of the context menu on click, by attaching the `pre
| `key` | `key` | for tracking the node's identity when working with lists | `string \| number` | `undefined` |
| `position` | `position` | Position of the context menu relative to the trigger element. | `"bottom center" \| "bottom left" \| "bottom right" \| "left bottom" \| "left middle" \| "left top" \| "right bottom" \| "right middle" \| "right top" \| "top center" \| "top left" \| "top right"` | `'bottom left'` |
| `ref` | `ref` | reference to component | `any` | `undefined` |
| `rightClick` | `right-click` | Use right-click. | `boolean` | `false` |
| `size` | `size` | Size of the context menu. | `"lg" \| "sm"` | `undefined` |
| `tetherOptions` | `tether-options` | Tether options object to be merged with the default options (optionally stringified). | `string \| { attachment?: string; bodyElement?: HTMLElement; classes?: { [className: string]: string \| boolean; }; classPrefix?: string; constraints?: ITetherConstraint[]; element?: any; enabled?: boolean; offset?: string; optimizations?: any; target?: any; targetAttachment?: string; targetOffset?: string; targetModifier?: string; }` | `undefined` |


## Events

| Event | Description | Type |
| -------------------- | ---------------------------------------- | ------------------ |
| `ldcontextmenuclose` | Emitted when the context menu is closed. | `CustomEvent<any>` |
| `ldcontextmenuopen` | Emitted when the context menu is opened. | `CustomEvent<any>` |


## Methods

### `hideContextMenu() => Promise<void>`
Expand Down
Loading

0 comments on commit ef35bbe

Please sign in to comment.