Skip to content

Commit

Permalink
docs: add Popover documentation and live examples (#3587)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Sep 4, 2024
1 parent 6b6b6d5 commit fd2e1e3
Show file tree
Hide file tree
Showing 29 changed files with 2,546 additions and 0 deletions.
565 changes: 565 additions & 0 deletions articles/components/popover/index.adoc

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions articles/components/popover/styling.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: Styling
description: Styling API reference for the Popover component.
order: 50
---
= Styling


include::../_styling-section-theming-props.adoc[tag=style-properties]

[cols="1,2,2"]
|===
| Feature | Property | Default Value

|Arrow size
|`--vaadin-popover-arrow-size`
|`0.5rem`

|Top offset
|`--vaadin-popover-offset-top`
|`var(--lumo-space-xs)`

|Bottom offset
|`--vaadin-popover-offset-bottom`
|`var(--lumo-space-xs)`

|Start offset
|`--vaadin-popover-offset-start`
|`var(--lumo-space-xs)`

|End offset
|`--vaadin-popover-offset-end`
|`var(--lumo-space-xs)`

|===


include::../_styling-section-intros.adoc[tag=selectors]


Root element:: `vaadin-popover-overlay`


=== States

Non-modal:: `vaadin-popover-overlay+++<wbr>+++**[modeless]**`


=== Parts

Modality curtain (backdrop):: `vaadin-popover-overlay+++<wbr>+++**::part(backdrop)**`
Popover surface:: `vaadin-popover-overlay+++<wbr>+++**::part(overlay)**`
Content wrapper:: `vaadin-popover-overlay+++<wbr>+++**::part(content)**`
Arrow element:: `vaadin-popover-overlay+++<wbr>+++**::part(arrow)**`

=== Style Variants

Arrow:: `vaadin-popover+++<wbr>+++**[theme~="arrow"]**`
125 changes: 125 additions & 0 deletions frontend/demo/component/popover/popover-anchored-dialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import 'Frontend/demo/init'; // hidden-source-line

import { html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import '@vaadin/button';
import '@vaadin/checkbox-group';
import '@vaadin/grid';
import '@vaadin/horizontal-layout';
import '@vaadin/icon';
import '@vaadin/icons';
import '@vaadin/popover';
import type { CheckboxChangeEvent } from '@vaadin/checkbox';
import { popoverRenderer } from '@vaadin/popover/lit.js';
import { getPeople } from 'Frontend/demo/domain/DataService';
import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person';
import { applyTheme } from 'Frontend/generated/theme';

const DEFAULT_COLUMNS = [
{ label: 'First name', key: 'firstName', visible: true },
{ label: 'Last name', key: 'lastName', visible: true },
{ label: 'Email', key: 'email', visible: true },
{ label: 'Phone', key: 'address.phone', visible: false },
{ label: 'Birthday', key: 'birthday', visible: false },
{ label: 'Profession', key: 'profession', visible: true },
];

@customElement('popover-anchored-dialog')
export class Example extends LitElement {
protected override createRenderRoot() {
const root = super.createRenderRoot();
// Apply custom theme (only supported if your app uses one)
applyTheme(root);
return root;
}

// tag::snippet[]
@state()
private items: Person[] = [];

@state()
private gridColumns = [...DEFAULT_COLUMNS];

protected override async firstUpdated() {
const { people } = await getPeople();
this.items = people;
}

protected override render() {
return html`
<!-- tag::snippet[] -->
<vaadin-horizontal-layout style="align-items: baseline">
<strong style="flex: 1;">Employees</strong>
<vaadin-button id="toggle-columns" theme="icon" aria-label="Show / hide columns">
<vaadin-icon icon="vaadin:grid-h"></vaadin-icon>
</vaadin-button>
</vaadin-horizontal-layout>
<vaadin-popover
for="toggle-columns"
modal
with-backdrop
position="bottom-end"
${popoverRenderer(this.popoverRenderer, [this.gridColumns])}
></vaadin-popover>
<!-- end::snippet[] -->
<vaadin-grid .items="${this.items}">
${this.gridColumns.map(
(column) => html`
<vaadin-grid-column
path="${column.key}"
.hidden="${!column.visible}"
></vaadin-grid-column>
`
)}
</vaadin-grid>
`;
}

// tag::snippet[]
popoverRenderer() {
const visibleColumns = this.gridColumns
.filter((column) => column.visible)
.map((column) => column.key);

return html`
<h4 style="margin: 0">Configure columns</h4>
<vaadin-checkbox-group theme="vertical" .value="${visibleColumns}">
${this.gridColumns.map(
(column) => html`
<vaadin-checkbox
.label="${column.label}"
.value="${column.key}"
@change="${this.onCheckboxChange}"
></vaadin-checkbox>
`
)}
</vaadin-checkbox-group>
<vaadin-horizontal-layout theme="spacing-xs">
<vaadin-button @click="${this.showAllColumns}">Show all</vaadin-button>
<vaadin-button @click="${this.resetColumns}">Reset</vaadin-button>
</vaadin-horizontal-layout>
`;
}
// end::snippet[]

onCheckboxChange(event: CheckboxChangeEvent) {
const idx = this.gridColumns.findIndex(({ key }) => key === event.target.value);
this.gridColumns = this.gridColumns.map((column, index) => ({
...column,
visible: idx === index ? event.target.checked : column.visible,
}));
}

showAllColumns() {
this.gridColumns = this.gridColumns.map((column) => ({ ...column, visible: true }));
}

resetColumns() {
this.gridColumns = this.gridColumns.map((column, idx) => ({
...column,
visible: DEFAULT_COLUMNS[idx].visible,
}));
}
}
39 changes: 39 additions & 0 deletions frontend/demo/component/popover/popover-arrow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'Frontend/demo/init'; // hidden-source-line

import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '@vaadin/button';
import '@vaadin/icon';
import '@vaadin/popover';
import '@vaadin/vaadin-lumo-styles/vaadin-iconset.js';
import { popoverRenderer } from '@vaadin/popover/lit.js';
import { applyTheme } from 'Frontend/generated/theme';

@customElement('popover-arrow')
export class Example extends LitElement {
protected override createRenderRoot() {
const root = super.createRenderRoot();
// Apply custom theme (only supported if your app uses one)
applyTheme(root);
return root;
}

protected override render() {
return html`
<vaadin-button id="target" aria-label="notifications" theme="icon">
<vaadin-icon icon="lumo:bell"></vaadin-icon>
</vaadin-button>
<!-- tag::snippet[] -->
<vaadin-popover
for="target"
theme="arrow"
${popoverRenderer(this.popoverRenderer)}
></vaadin-popover>
<!-- end::snippet[] -->
`;
}

popoverRenderer() {
return html`<div>No new notifications</div>`;
}
}
157 changes: 157 additions & 0 deletions frontend/demo/component/popover/popover-dropdown-field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import 'Frontend/demo/init'; // hidden-source-line

import { html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import '@vaadin/date-picker';
import '@vaadin/horizontal-layout';
import '@vaadin/icon';
import '@vaadin/popover';
import '@vaadin/select';
import '@vaadin/text-field';
import '@vaadin/vaadin-lumo-styles/vaadin-iconset.js';
import type { DatePickerChangeEvent } from '@vaadin/date-picker';
import type { PopoverOpenedChangedEvent, PopoverTrigger } from '@vaadin/popover';
import type { SelectChangeEvent } from '@vaadin/select';
import { popoverRenderer } from '@vaadin/popover/lit.js';
import { applyTheme } from 'Frontend/generated/theme';
import { formatISO, subMonths, subWeeks, subYears } from 'date-fns';

@customElement('popover-dropdown-field')
export class Example extends LitElement {
protected override createRenderRoot() {
const root = super.createRenderRoot();
// Apply custom theme (only supported if your app uses one)
applyTheme(root);
return root;
}

@state()
range = '';

@state()
from = '';

@state()
to = '';

@state()
opened = false;

@state()
trigger: PopoverTrigger[] = ['click', 'focus'];

@state()
presets = [
{ label: 'Today', value: 'today' },
{ label: 'Last week', value: 'last-week' },
{ label: 'Last month', value: 'last-month' },
{ label: 'Year to date', value: 'year-to-date' },
{ label: 'Last year', value: 'last-year' },
{ label: 'Past 5 years', value: 'past-5-years' },
];

protected override render() {
return html`
<vaadin-text-field
id="range-field"
label="Search date range"
style="width: 340px"
.value="${this.from && this.to ? `${this.from}${this.to}` : ''}"
>
<vaadin-icon icon="lumo:dropdown" slot="suffix"></vaadin-icon>
</vaadin-text-field>
<!-- tag::snippet[] -->
<vaadin-popover
for="range-field"
.trigger="${this.trigger}"
focus-delay="0"
modal
content-width="325px"
position="bottom-start"
accessible-name="Select a date range"
.opened="${this.opened}"
@opened-changed="${this.onOpenedChanged}"
${popoverRenderer(this.popoverRenderer, [this.from, this.to])}
></vaadin-popover>
<!-- end::snippet[] -->
`;
}

// tag::snippet[]
popoverRenderer() {
return html`
<vaadin-select
label="Common ranges"
.items="${this.presets}"
placeholder="Select preset"
style="width: 100%"
.value="${this.range}"
@change="${this.onRangeChange}"
></vaadin-select>
<vaadin-horizontal-layout theme="spacing-s" style="align-items: baseline">
<vaadin-date-picker
label="From"
style="width: 150px"
.value="${this.from}"
@change="${this.onFromChange}"
></vaadin-date-picker>
<div></div>
<vaadin-date-picker
label="To"
style="width: 150px"
.value="${this.to}"
@change="${this.onToChange}"
></vaadin-date-picker>
</vaadin-horizontal-layout>
`;
}
// end::snippet[]

onFromChange(event: DatePickerChangeEvent) {
this.range = '';
this.from = event.target.value;
}

onToChange(event: DatePickerChangeEvent) {
this.range = '';
this.to = event.target.value;
}

onOpenedChanged(event: PopoverOpenedChangedEvent) {
this.opened = event.detail.value;
}

onRangeChange(event: SelectChangeEvent) {
this.range = event.target.value;
this.to = this.formatDate(new Date());

switch (event.target.value) {
case 'today':
this.from = this.formatDate(new Date());
break;
case 'last-week':
this.from = this.formatDate(subWeeks(new Date(), 1));
break;
case 'last-month':
this.from = this.formatDate(subMonths(new Date(), 1));
break;
case 'year-to-date':
this.from = this.formatDate(new Date(new Date().getFullYear(), 0, 1));
break;
case 'last-year':
this.from = this.formatDate(subYears(new Date(), 1));
break;
case 'past-5-years':
this.from = this.formatDate(subYears(new Date(), 5));
break;
default:
// Do nothing
}

this.opened = false;
}

formatDate(date: Date) {
return formatISO(date, { representation: 'date' });
}
}
Loading

0 comments on commit fd2e1e3

Please sign in to comment.