From 468b0b9e66dbb7e3f2bc853c4af9a864b46e719c Mon Sep 17 00:00:00 2001 From: Mitch Ray <5735900+mitchray@users.noreply.github.com> Date: Fri, 3 Nov 2023 07:49:18 +1100 Subject: [PATCH 1/3] Add loading attribute to menu-item --- docs/pages/components/menu-item.md | 33 +++++++++++++++++-- .../menu-item/menu-item.component.ts | 11 ++++++- src/components/menu-item/menu-item.styles.ts | 26 +++++++++++++++ src/components/menu-item/menu-item.test.ts | 8 +++++ 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/docs/pages/components/menu-item.md b/docs/pages/components/menu-item.md index ef80dfbb..5fa0b207 100644 --- a/docs/pages/components/menu-item.md +++ b/docs/pages/components/menu-item.md @@ -9,7 +9,7 @@ layout: component Option 1 Option 2 - Option 3 + Option 3 Checkbox Disabled @@ -37,7 +37,7 @@ const App = () => ( Option 1 Option 2 - Option 3 + Option 3 Checkbox @@ -60,6 +60,35 @@ const App = () => ( ## Examples +### Loading + +Use the `loading` attribute to make a menu item busy. Clicks will be suppressed until the loading state is removed. + +```html:preview + + Option 1 + Option 2 + Option 3 + +``` + +{% raw %} + +```jsx:react +import SlMenu from '@shoelace-style/shoelace/dist/react/menu'; +import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item'; + +const App = () => ( + + Option 1 + Option 2 + Option 3 + +); +``` + +{% endraw %} + ### Disabled Add the `disabled` attribute to disable the menu item so it cannot be selected. diff --git a/src/components/menu-item/menu-item.component.ts b/src/components/menu-item/menu-item.component.ts index ebb92eef..b9e3c60e 100644 --- a/src/components/menu-item/menu-item.component.ts +++ b/src/components/menu-item/menu-item.component.ts @@ -8,6 +8,7 @@ import { watch } from '../../internal/watch.js'; import ShoelaceElement from '../../internal/shoelace-element.js'; import SlIcon from '../icon/icon.component.js'; import SlPopup from '../popup/popup.component.js'; +import SlSpinner from '../spinner/spinner.component.js'; import styles from './menu-item.styles.js'; import type { CSSResultGroup } from 'lit'; @@ -19,6 +20,7 @@ import type { CSSResultGroup } from 'lit'; * * @dependency sl-icon * @dependency sl-popup + * @dependency sl-spinner * * @slot - The menu item's label. * @slot prefix - Used to prepend an icon or similar element to the menu item. @@ -30,6 +32,7 @@ import type { CSSResultGroup } from 'lit'; * @csspart prefix - The prefix container. * @csspart label - The menu item label. * @csspart suffix - The suffix container. + * @csspart spinner - The spinner that shows when the menu item is in the loading state. * @csspart submenu-icon - The submenu icon, visible only when the menu item has a submenu (not yet implemented). * * @cssproperty [--submenu-offset=-2px] - The distance submenus shift to overlap the parent menu. @@ -38,7 +41,8 @@ export default class SlMenuItem extends ShoelaceElement { static styles: CSSResultGroup = styles; static dependencies = { 'sl-icon': SlIcon, - 'sl-popup': SlPopup + 'sl-popup': SlPopup, + 'sl-spinner': SlSpinner }; private cachedTextLabel: string; @@ -55,6 +59,9 @@ export default class SlMenuItem extends ShoelaceElement { /** A unique value to store in the menu item. This can be used as a way to identify menu items when selected. */ @property() value = ''; + /** Draws the menu item in a loading state. */ + @property({ type: Boolean, reflect: true }) loading = false; + /** Draws the menu item in a disabled state, preventing selection. */ @property({ type: Boolean, reflect: true }) disabled = false; @@ -158,6 +165,7 @@ export default class SlMenuItem extends ShoelaceElement { 'menu-item--rtl': isRtl, 'menu-item--checked': this.checked, 'menu-item--disabled': this.disabled, + 'menu-item--loading': this.loading, 'menu-item--has-submenu': this.isSubmenu(), 'menu-item--submenu-expanded': isSubmenuExpanded })} @@ -179,6 +187,7 @@ export default class SlMenuItem extends ShoelaceElement { ${this.submenuController.renderSubmenu()} + ${this.loading ? html`` : ''} `; } diff --git a/src/components/menu-item/menu-item.styles.ts b/src/components/menu-item/menu-item.styles.ts index 476add68..c96d1297 100644 --- a/src/components/menu-item/menu-item.styles.ts +++ b/src/components/menu-item/menu-item.styles.ts @@ -139,4 +139,30 @@ export default css` outline-offset: -1px; } } + + /* + * Loading modifier + */ + + .menu-item--loading { + position: relative; + cursor: wait; + } + + .menu-item--loading .menu-item__prefix, + .menu-item--loading .menu-item__label, + .menu-item--loading .menu-item__suffix, + .menu-item--loading .menu-item__check { + visibility: hidden; + } + + .menu-item--loading sl-spinner { + --indicator-color: currentColor; + position: absolute; + font-size: 1em; + height: 1em; + width: 1em; + top: calc(50% - 0.5em); + left: calc(50% - 0.5em); + } `; diff --git a/src/components/menu-item/menu-item.test.ts b/src/components/menu-item/menu-item.test.ts index abee95d8..65676e53 100644 --- a/src/components/menu-item/menu-item.test.ts +++ b/src/components/menu-item/menu-item.test.ts @@ -40,6 +40,7 @@ describe('', () => { expect(el.value).to.equal(''); expect(el.disabled).to.be.false; + expect(el.loading).to.equal(false); expect(el.getAttribute('aria-disabled')).to.equal('false'); }); @@ -48,6 +49,13 @@ describe('', () => { expect(el.getAttribute('aria-disabled')).to.equal('true'); }); + describe('when loading', () => { + it('should have a spinner present', async () => { + const el = await fixture(html` Menu Item Label `); + expect(el.shadowRoot!.querySelector('sl-spinner')).to.exist; + }); + }); + it('should return a text label when calling getTextLabel()', async () => { const el = await fixture(html` Test `); expect(el.getTextLabel()).to.equal('Test'); From a2b7816010320522ccdb7090ab2775d43123c133 Mon Sep 17 00:00:00 2001 From: Mitch Ray <5735900+mitchray@users.noreply.github.com> Date: Sat, 4 Nov 2023 10:05:09 +1100 Subject: [PATCH 2/3] Keep text shown --- src/components/menu-item/menu-item.styles.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/menu-item/menu-item.styles.ts b/src/components/menu-item/menu-item.styles.ts index c96d1297..c1fdab4c 100644 --- a/src/components/menu-item/menu-item.styles.ts +++ b/src/components/menu-item/menu-item.styles.ts @@ -153,7 +153,7 @@ export default css` .menu-item--loading .menu-item__label, .menu-item--loading .menu-item__suffix, .menu-item--loading .menu-item__check { - visibility: hidden; + opacity: 0.5; } .menu-item--loading sl-spinner { @@ -163,6 +163,6 @@ export default css` height: 1em; width: 1em; top: calc(50% - 0.5em); - left: calc(50% - 0.5em); + left: 0.45em; } `; From 1a8403b9b25a0ba271a6fd705c385ef4f9c172e5 Mon Sep 17 00:00:00 2001 From: Mitch Ray <5735900+mitchray@users.noreply.github.com> Date: Sat, 25 Nov 2023 09:51:08 +1100 Subject: [PATCH 3/3] Reduce size --- src/components/menu-item/menu-item.styles.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/menu-item/menu-item.styles.ts b/src/components/menu-item/menu-item.styles.ts index c1fdab4c..0ed8682b 100644 --- a/src/components/menu-item/menu-item.styles.ts +++ b/src/components/menu-item/menu-item.styles.ts @@ -158,11 +158,13 @@ export default css` .menu-item--loading sl-spinner { --indicator-color: currentColor; + --track-width: 1px; position: absolute; - font-size: 1em; + font-size: 0.75em; height: 1em; width: 1em; top: calc(50% - 0.5em); - left: 0.45em; + left: calc(1rem - 1px); + transform: translateX(-50%); } `;