Skip to content

Commit

Permalink
feat: support drag-reordering of dashboard items (#7738)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomivirkki authored Sep 5, 2024
1 parent 176a6cf commit b5bc352
Show file tree
Hide file tree
Showing 12 changed files with 1,235 additions and 123 deletions.
16 changes: 15 additions & 1 deletion dev/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,24 @@
</vaadin-dashboard-widget>
`;
};

dashboard.addEventListener('dashboard-item-reorder-start', (e) => {
console.log('dashboard-item-reorder-start');
});

dashboard.addEventListener('dashboard-item-drag-reorder', (e) => {
console.log('dashboard-item-drag-reorder', e.detail);
// e.preventDefault();
});

dashboard.addEventListener('dashboard-item-reorder-end', (e) => {
console.log('dashboard-item-reorder-end');
console.log('items after reorder', e.target.items);
});
</script>
</head>

<body>
<vaadin-dashboard></vaadin-dashboard>
<vaadin-dashboard editable></vaadin-dashboard>
</body>
</html>
95 changes: 52 additions & 43 deletions packages/dashboard/src/vaadin-dashboard-section.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { css } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { TitleController } from './title-controller.js';
import { dashboardWidgetAndSectionStyles, hasWidgetWrappers } from './vaadin-dashboard-styles.js';

/**
* A section component for use with the Dashboard component
Expand All @@ -30,48 +31,54 @@ class DashboardSection extends ControllerMixin(ElementMixin(PolylitMixin(LitElem
}

static get styles() {
return css`
:host {
display: grid;
grid-template-columns: subgrid;
--_vaadin-dashboard-section-column: 1 / calc(var(--_vaadin-dashboard-effective-col-count) + 1);
grid-column: var(--_vaadin-dashboard-section-column) !important;
gap: var(--vaadin-dashboard-gap, 1rem);
/* Dashbaord section header height */
--_vaadin-dashboard-section-header-height: minmax(0, auto);
grid-template-rows: var(--_vaadin-dashboard-section-header-height) repeat(
auto-fill,
var(--_vaadin-dashboard-row-height)
);
grid-auto-rows: var(--_vaadin-dashboard-row-height);
}
:host([hidden]) {
display: none !important;
}
::slotted(*) {
--_vaadin-dashboard-title-level: 3;
--_vaadin-dashboard-item-column: span
min(
var(--vaadin-dashboard-item-colspan, 1),
var(--_vaadin-dashboard-effective-col-count, var(--_vaadin-dashboard-col-count))
);
grid-column: var(--_vaadin-dashboard-item-column);
}
::slotted(vaadin-dashboard-widget-wrapper) {
display: contents;
}
header {
display: flex;
grid-column: var(--_vaadin-dashboard-section-column);
justify-content: space-between;
align-items: center;
}
`;
return [
css`
:host {
display: grid;
position: relative;
grid-template-columns: subgrid;
--_vaadin-dashboard-section-column: 1 / calc(var(--_vaadin-dashboard-effective-col-count) + 1);
grid-column: var(--_vaadin-dashboard-section-column) !important;
gap: var(--vaadin-dashboard-gap, 1rem);
/* Dashbaord section header height */
--_vaadin-dashboard-section-header-height: minmax(0, auto);
grid-template-rows: var(--_vaadin-dashboard-section-header-height) repeat(
auto-fill,
var(--_vaadin-dashboard-row-height)
);
grid-auto-rows: var(--_vaadin-dashboard-row-height);
}
:host([hidden]) {
display: none !important;
}
:host([highlight]) {
background-color: #f5f5f5;
}
::slotted(*) {
--_vaadin-dashboard-title-level: 3;
--_vaadin-dashboard-item-column: span
min(
var(--vaadin-dashboard-item-colspan, 1),
var(--_vaadin-dashboard-effective-col-count, var(--_vaadin-dashboard-col-count))
);
grid-column: var(--_vaadin-dashboard-item-column);
}
header {
grid-column: var(--_vaadin-dashboard-section-column);
}
:host::before {
z-index: 2 !important;
}
`,
hasWidgetWrappers,
dashboardWidgetAndSectionStyles,
];
}

static get properties() {
Expand All @@ -92,7 +99,9 @@ class DashboardSection extends ControllerMixin(ElementMixin(PolylitMixin(LitElem
return html`
<header>
<slot name="title" @slotchange="${this.__onTitleSlotChange}"></slot>
<div id="header-actions"></div>
<div id="header-actions">
<span id="drag-handle" draggable="true" class="drag-handle"></span>
</div>
</header>
<slot></slot>
Expand Down
37 changes: 37 additions & 0 deletions packages/dashboard/src/vaadin-dashboard-styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { css } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

export const hasWidgetWrappers = css`
::slotted(vaadin-dashboard-widget-wrapper) {
display: contents;
}
`;

export const dashboardWidgetAndSectionStyles = css`
/* Placeholder shown while the widget or section is dragged */
:host::before {
content: '';
z-index: 1;
position: absolute;
display: var(--_vaadin-dashboard-item-placeholder-display, none);
inset: 0;
border: 3px dashed black;
border-radius: 5px;
background-color: #fff;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
}
#header-actions {
display: var(--_vaadin-dashboard-widget-actions-display, none);
}
#drag-handle::before {
font-size: 30px;
content: '☰';
cursor: grab;
}
`;
45 changes: 23 additions & 22 deletions packages/dashboard/src/vaadin-dashboard-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { css } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { TitleController } from './title-controller.js';
import { dashboardWidgetAndSectionStyles } from './vaadin-dashboard-styles.js';

/**
* A Widget component for use with the Dashboard component
Expand All @@ -30,27 +31,25 @@ class DashboardWidget extends ControllerMixin(ElementMixin(PolylitMixin(LitEleme
}

static get styles() {
return css`
:host {
display: flex;
flex-direction: column;
grid-column: var(--_vaadin-dashboard-item-column);
}
:host([hidden]) {
display: none !important;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
}
#content {
flex: 1;
}
`;
return [
css`
:host {
display: flex;
flex-direction: column;
grid-column: var(--_vaadin-dashboard-item-column);
position: relative;
}
:host([hidden]) {
display: none !important;
}
#content {
flex: 1;
}
`,
dashboardWidgetAndSectionStyles,
];
}

static get properties() {
Expand All @@ -72,7 +71,9 @@ class DashboardWidget extends ControllerMixin(ElementMixin(PolylitMixin(LitEleme
<header>
<slot name="title" @slotchange="${this.__onTitleSlotChange}"></slot>
<slot name="header"></slot>
<div id="header-actions"></div>
<div id="header-actions">
<span id="drag-handle" draggable="true" class="drag-handle"></span>
</div>
</header>
<div id="content">
Expand Down
47 changes: 46 additions & 1 deletion packages/dashboard/src/vaadin-dashboard.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface DashboardSectionItem<TItem extends DashboardItem> {
/**
* The title of the section
*/
title: string | null | undefined;
title?: string | null;

/**
* The items of the section
Expand All @@ -42,6 +42,34 @@ export type DashboardRenderer<TItem extends DashboardItem> = (
model: DashboardItemModel<TItem>,
) => void;

/**
* Fired when item reordering starts
*/
export type DashboardItemReorderStartEvent = CustomEvent;

/**
* Fired when item reordering ends
*/
export type DashboardItemReorderEndEvent = CustomEvent;

/**
* Fired when an items will be reordered by dragging
*/
export type DashboardItemDragReorderEvent<TItem extends DashboardItem> = CustomEvent<{
item: TItem | DashboardSectionItem<TItem>;
targetIndex: number;
}>;

export interface DashboardCustomEventMap<TItem extends DashboardItem> {
'dashboard-item-reorder-start': DashboardItemReorderStartEvent;

'dashboard-item-reorder-end': DashboardItemReorderEndEvent;

'dashboard-item-drag-reorder': DashboardItemDragReorderEvent<TItem>;
}

export type DashboardEventMap<TItem extends DashboardItem> = DashboardCustomEventMap<TItem> & HTMLElementEventMap;

/**
* A responsive, grid-based dashboard layout component
*/
Expand All @@ -65,6 +93,23 @@ declare class Dashboard<TItem extends DashboardItem = DashboardItem> extends Das
* - `model.item` The item.
*/
renderer: DashboardRenderer<TItem> | null | undefined;

/**
* Whether the dashboard is editable.
*/
editable: boolean;

addEventListener<K extends keyof DashboardEventMap<TItem>>(
type: K,
listener: (this: Dashboard, ev: DashboardEventMap<TItem>[K]) => void,
options?: AddEventListenerOptions | boolean,
): void;

removeEventListener<K extends keyof DashboardEventMap<TItem>>(
type: K,
listener: (this: Dashboard, ev: DashboardEventMap<TItem>[K]) => void,
options?: EventListenerOptions | boolean,
): void;
}

declare global {
Expand Down
Loading

0 comments on commit b5bc352

Please sign in to comment.