Skip to content

Commit

Permalink
feat: add middle and end slots to horizontal layout
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan committed Jan 15, 2025
1 parent e34b3ba commit 578a43c
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 2 deletions.
16 changes: 16 additions & 0 deletions packages/horizontal-layout/src/vaadin-horizontal-layout-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,20 @@ export const horizontalLayoutStyles = css`
:host([theme~='spacing']) {
gap: 1em;
}
:host([has-end]:not([has-middle])) ::slotted([last-start-child]) {
margin-inline-end: auto;
}
::slotted([first-middle-child]) {
margin-inline-start: auto;
}
::slotted([last-middle-child]) {
margin-inline-end: auto;
}
:host([has-start]:not([has-middle])) ::slotted([first-end-child]) {
margin-inline-start: auto;
}
`;
72 changes: 71 additions & 1 deletion packages/horizontal-layout/src/vaadin-horizontal-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
*/
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { isEmptyTextNode } from '@vaadin/component-base/src/dom-utils.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { SlotObserver } from '@vaadin/component-base/src/slot-observer.js';
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { horizontalLayoutStyles } from './vaadin-horizontal-layout-styles.js';

Expand Down Expand Up @@ -39,12 +41,80 @@ registerStyles('vaadin-horizontal-layout', horizontalLayoutStyles, { moduleId: '
*/
class HorizontalLayout extends ElementMixin(ThemableMixin(PolymerElement)) {
static get template() {
return html`<slot></slot>`;
return html`
<slot></slot>
<slot name="middle"></slot>
<slot name="end"></slot>
`;
}

static get is() {
return 'vaadin-horizontal-layout';
}

/** @protected */
ready() {
super.ready();

const startSlot = this.shadowRoot.querySelector('slot:not([name])');
this.__startSlotObserver = new SlotObserver(startSlot, ({ currentNodes, removedNodes }) => {
if (removedNodes.length) {
const last = removedNodes.find(
(node) => node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('last-start-child'),
);
if (last) {
last.removeAttribute('last-start-child');
}
}

const children = currentNodes.filter((node) => node.nodeType === Node.ELEMENT_NODE);

if (children.length) {
children[children.length - 1].setAttribute('last-start-child', '');
}

const nodes = currentNodes.filter((node) => !isEmptyTextNode(node));
this.toggleAttribute('has-start', nodes.length > 0);
});

const endSlot = this.shadowRoot.querySelector('[name="end"]');
this.__endSlotObserver = new SlotObserver(endSlot, ({ currentNodes, removedNodes }) => {
if (removedNodes.length) {
const first = removedNodes.find((el) => el.hasAttribute('first-end-child'));
if (first) {
first.removeAttribute('first-end-child');
}
}

if (currentNodes.length) {
currentNodes[0].setAttribute('first-end-child', '');
}

this.toggleAttribute('has-end', currentNodes.length > 0);
});

const middleSlot = this.shadowRoot.querySelector('[name="middle"]');
this.__middleSlotObserver = new SlotObserver(middleSlot, ({ currentNodes, removedNodes }) => {
if (removedNodes.length) {
const first = removedNodes.find((el) => el.hasAttribute('first-middle-child'));
if (first) {
first.removeAttribute('first-middle-child');
}

const last = removedNodes.find((el) => el.hasAttribute('last-middle-child'));
if (last) {
last.removeAttribute('last-middle-child');
}
}

if (currentNodes.length) {
currentNodes[0].setAttribute('first-middle-child', '');
currentNodes[currentNodes.length - 1].setAttribute('last-middle-child', '');
}

this.toggleAttribute('has-middle', currentNodes.length > 0);
});
}
}

defineCustomElement(HorizontalLayout);
Expand Down
17 changes: 16 additions & 1 deletion packages/horizontal-layout/src/vaadin-lit-horizontal-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { html, LitElement } from 'lit';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { SlotObserver } from '@vaadin/component-base/src/slot-observer.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { horizontalLayoutStyles } from './vaadin-horizontal-layout-styles.js';

Expand All @@ -30,7 +31,21 @@ class HorizontalLayout extends ThemableMixin(ElementMixin(PolylitMixin(LitElemen

/** @protected */
render() {
return html`<slot></slot>`;
return html`
<slot></slot>
<slot name="middle"></slot>
<slot name="end"></slot>
`;
}

/** @protected */
ready() {
super.ready();

const endSlot = this.shadowRoot.querySelector('[name="end"]');
this.__endSlotObserver = new SlotObserver(endSlot, ({ currentNodes }) => {
this.toggleAttribute('has-end-children', currentNodes.length > 0);
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,82 @@ describe('horizontal-layout', () => {
element.style.width = '100px';
await visualDiff(div, 'theme-wrap');
});

describe('slots', () => {
describe('all', () => {
beforeEach(() => {
element = fixtureSync(
`
<vaadin-horizontal-layout style="border: solid 1px blue; width: 600px;" theme="spacing wrap">
<div style="background: #90ee90; width: 100px">Start</div>
<div style="background: #90ee90; width: 100px">Start</div>
<div slot="middle" style="background: #ffd700;">Middle</div>
<div slot="end" style="background: #f08080; width: 100px">End</div>
</vaadin-horizontal-layout>
`,
);
});

it('default', async () => {
await visualDiff(element, 'slots-all');
});

it('wrap end', async () => {
element.style.width = '350px';
await visualDiff(element, 'slots-all-wrap-end');
});

it('wrap middle', async () => {
element.style.width = '350px';
element.firstElementChild.style.width = '200px';
await visualDiff(element, 'slots-all-wrap-middle');
});
});

describe('without end', () => {
beforeEach(() => {
element = fixtureSync(
`
<vaadin-horizontal-layout style="border: solid 1px blue; width: 400px;" theme="spacing wrap">
<div style="background: #90ee90; width: 100px">Start</div>
<div style="background: #90ee90; width: 100px">Start</div>
<div slot="middle" style="background: #ffd700;">Middle</div>
</vaadin-horizontal-layout>
`,
);
});

it('default', async () => {
await visualDiff(element, 'slots-without-end');
});

it('wrap', async () => {
element.style.width = '250px';
await visualDiff(element, 'slots-without-end-wrap');
});
});

describe('without start', () => {
beforeEach(() => {
element = fixtureSync(
`
<vaadin-horizontal-layout style="border: solid 1px blue; width: 400px;" theme="spacing wrap">
<div slot="middle" style="background: #ffd700;">Middle</div>
<div slot="end" style="background: #f08080; width: 100px">End</div>
<div slot="end" style="background: #f08080; width: 100px">End</div>
</vaadin-horizontal-layout>
`,
);
});

it('default', async () => {
await visualDiff(element, 'slots-without-start');
});

it('wrap', async () => {
element.style.width = '250px';
await visualDiff(element, 'slots-without-start-wrap');
});
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 578a43c

Please sign in to comment.