diff --git a/examples/minimal-ts-vite/examples/web-components/index.html b/examples/minimal-ts-vite/examples/web-components/index.html index e8ad1e28..7bd65916 100644 --- a/examples/minimal-ts-vite/examples/web-components/index.html +++ b/examples/minimal-ts-vite/examples/web-components/index.html @@ -14,13 +14,16 @@ interactive style="float: right; position: relative; top: 40px; left: -10px" > - + - + test image diff --git a/examples/react-ts-vite/src/App.tsx b/examples/react-ts-vite/src/App.tsx index f2f7847c..51b922bd 100644 --- a/examples/react-ts-vite/src/App.tsx +++ b/examples/react-ts-vite/src/App.tsx @@ -116,11 +116,14 @@ function WebComponents({ return (
- + test image {manifestStore ? (
- + `, }, { pattern: /ethereum/i, - icon: html``, + icon: html``, }, { pattern: /linkedin/i, - icon: html``, + icon: html``, }, ]; diff --git a/packages/c2pa-wc/src/components/Indicator/Indicator.ts b/packages/c2pa-wc/src/components/Indicator/Indicator.ts index 5ecc417b..9c650182 100644 --- a/packages/c2pa-wc/src/components/Indicator/Indicator.ts +++ b/packages/c2pa-wc/src/components/Indicator/Indicator.ts @@ -42,11 +42,17 @@ export class Indicator extends LitElement { display: inline-block; width: var(--cai-indicator-size, 24px); height: var(--cai-indicator-size, 24px); + border-radius: 50% 50% 0 50%; + line-height: 0; } .icon { --cai-icon-width: var(--cai-indicator-size, 24px); --cai-icon-height: var(--cai-indicator-size, 24px); } + :host:focus-visible { + outline-color: var(--cai-focus-ring-color, #1473e6); + outline-offset: 1px; + } `, ]; } diff --git a/packages/c2pa-wc/src/components/Popover/Popover.ts b/packages/c2pa-wc/src/components/Popover/Popover.ts index 894d25a8..7aadb14a 100644 --- a/packages/c2pa-wc/src/components/Popover/Popover.ts +++ b/packages/c2pa-wc/src/components/Popover/Popover.ts @@ -84,7 +84,7 @@ export class Popover extends LitElement { interactive = false; @property({ type: String }) - trigger: string = 'mouseenter:mouseleave focus:blur'; + trigger: string = 'mouseenter:mouseleave click'; @property({ type: Number }) zIndex = 10; @@ -101,6 +101,18 @@ export class Popover extends LitElement { @query('#trigger') triggerElement: HTMLElement | undefined; + private _triggerElementSlot: HTMLSlotElement | undefined; + + private _triggerSlotAssignedNodes: Node[] = []; + + private _triggerElementButton: HTMLElement | undefined; + + private _contentElementSlot: HTMLSlotElement | undefined; + + private _contentSlotAssignedNodes: Node[] = []; + + private _hasTooltipRole = false; + // @TODO: respect updated properties protected updated( _changedProperties: PropertyValueMap | Map, @@ -196,41 +208,58 @@ export class Popover extends LitElement { private _showTooltip() { this._isShown = true; this._updatePosition(); + this.hostElement!.ownerDocument!.addEventListener( + 'keydown', + this._onKeyDownEsc.bind(this), + ); + if (!this._hasTooltipRole) { + this._triggerElementButton?.setAttribute('aria-expanded', 'true'); + } } private _hideTooltip() { this._isShown = false; + this.hostElement!.ownerDocument!.removeEventListener( + 'keydown', + this._onKeyDownEsc.bind(this), + ); + if (!this._hasTooltipRole) { + this._triggerElementButton?.setAttribute('aria-expanded', 'false'); + } } - private _onKeyDown(e: KeyboardEvent) { + private _toogleTooltip() { + if (!this._isShown) { + this._showTooltip(); + } else { + this._hideTooltip(); + } + } + + private _onKeyDownEsc(e: KeyboardEvent) { switch (e.key) { case 'Escape': if (this._isShown) { e.stopPropagation(); e.preventDefault(); + const restoreFocus = this.contains(document.activeElement); this._hideTooltip(); - this.triggerElement!.focus(); - } - break; - case 'Enter': - case ' ': - if ( - e.target !== this.hostElement && - e.composedPath().includes(this.triggerElement as EventTarget) - ) { - e.stopPropagation(); - e.preventDefault(); - this._onClick(); + if (restoreFocus) { + this._triggerElementButton!.focus(); + } } break; } } - private _onClick() { - if (!this._isShown) { - this._showTooltip(); - } else { - this._hideTooltip(); + private _onKeyDownTrigger(e: KeyboardEvent) { + switch (e.key) { + case 'Enter': + case ' ': + e.stopPropagation(); + e.preventDefault(); + (e.target as HTMLElement).click(); + break; } } @@ -239,60 +268,59 @@ export class Popover extends LitElement { const cleanup = this._eventCleanupFns.shift(); cleanup?.(); } - - this.triggerElement!.removeEventListener('click', this._onClick); - - this.hostElement!.removeEventListener('keydown', this._onKeyDown, true); } private _setTriggers() { this._cleanupTriggers(); const triggers = this.trigger.split(/\s+/); + const toggleTooltipFn = this._toogleTooltip.bind(this); + const showTooltipFn = this._showTooltip.bind(this); + const hideTooltipFn = this._hideTooltip.bind(this); + const keydownTriggerFn = this._onKeyDownTrigger.bind(this); this._eventCleanupFns = triggers.map((trigger) => { const [show, hide] = trigger.split(':'); - this.triggerElement!.addEventListener( - show, - this._showTooltip.bind(this), - show === 'focus', - ); - if (this.interactive && hide === 'mouseleave') { - this.hostElement!.addEventListener(hide, this._hideTooltip.bind(this)); + if (show === 'click') { + this.triggerElement!.addEventListener(show, toggleTooltipFn); + this.triggerElement!.addEventListener('keydown', keydownTriggerFn); } else { this.triggerElement!.addEventListener( - hide, - this._hideTooltip.bind(this), - hide === 'blur', - ); - } - return () => { - this.triggerElement!.removeEventListener( show, - this._showTooltip, + showTooltipFn, show === 'focus', ); if (this.interactive && hide === 'mouseleave') { - this.contentElement!.addEventListener( + this.hostElement!.addEventListener(hide, hideTooltipFn); + } else { + this.triggerElement!.addEventListener( hide, - this._hideTooltip.bind(this), + hideTooltipFn, + hide === 'blur', ); + } + } + return () => { + if (show === 'click') { + this.triggerElement!.removeEventListener(show, toggleTooltipFn); + this.triggerElement!.removeEventListener('keydown', keydownTriggerFn); } else { this.triggerElement!.removeEventListener( - hide, - this._hideTooltip, - hide === 'blur', + show, + showTooltipFn, + show === 'focus', ); + if (this.interactive && hide === 'mouseleave') { + this.contentElement!.addEventListener(hide, hideTooltipFn); + } else { + this.triggerElement!.removeEventListener( + hide, + hideTooltipFn, + hide === 'blur', + ); + } } }; }); - - this.triggerElement!.addEventListener('click', this._onClick.bind(this)); - - this.hostElement!.addEventListener( - 'keydown', - this._onKeyDown.bind(this), - true, - ); } private async _updatePosition() { @@ -372,6 +400,29 @@ export class Popover extends LitElement { ); this.contentElement?.classList.add('hidden'); + + this._contentElementSlot = this.contentElement?.querySelector( + 'slot[name="content"]', + ) as HTMLSlotElement; + this._contentSlotAssignedNodes = + this._contentElementSlot?.assignedElements({ flatten: true }) ?? []; + this._hasTooltipRole = this._contentSlotAssignedNodes.some( + (node) => + node instanceof HTMLElement && node.getAttribute('role') === 'tooltip', + ); + + this._triggerElementSlot = this.triggerElement?.querySelector( + 'slot[name="trigger"]', + ) as HTMLSlotElement; + this._triggerSlotAssignedNodes = + this._triggerElementSlot?.assignedElements({ flatten: true }) ?? []; + this._triggerElementButton = this + ._triggerSlotAssignedNodes[0] as HTMLElement; + this._triggerElementButton.setAttribute('role', 'button'); + this._triggerElementButton.setAttribute('tabindex', '0'); + if (!this._hasTooltipRole) { + this._triggerElementButton.setAttribute('aria-expanded', 'false'); + } } disconnectedCallback(): void { @@ -390,6 +441,10 @@ export class Popover extends LitElement { }; return html`
+
+
+ +
${this.arrow ? html`
` : null}
-
-
- -
`; } } diff --git a/packages/c2pa-wc/src/components/Thumbnail/Thumbnail.stories.ts b/packages/c2pa-wc/src/components/Thumbnail/Thumbnail.stories.ts index b73ac2c3..fb2898eb 100644 --- a/packages/c2pa-wc/src/components/Thumbnail/Thumbnail.stories.ts +++ b/packages/c2pa-wc/src/components/Thumbnail/Thumbnail.stories.ts @@ -25,7 +25,7 @@ export default { component: 'cai-thumbnail', argTypes: { src: { - defaultValue: 'https://place-puppy.com/450x300', + defaultValue: 'https://place.dog/450/300', control: { type: 'text', }, diff --git a/packages/c2pa-wc/src/components/Tooltip/Tooltip.ts b/packages/c2pa-wc/src/components/Tooltip/Tooltip.ts index 865a9b6a..6d07978c 100644 --- a/packages/c2pa-wc/src/components/Tooltip/Tooltip.ts +++ b/packages/c2pa-wc/src/components/Tooltip/Tooltip.ts @@ -11,7 +11,6 @@ import { autoPlacement } from '@floating-ui/dom'; import { css, html, LitElement } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; -import { ifDefined } from 'lit/directives/if-defined.js'; import '../../../assets/svg/monochrome/help.svg'; import { defaultStyles } from '../../styles'; import { Configurable } from '../../mixins/configurable'; @@ -109,22 +108,20 @@ export class Tooltip extends Configurable(LitElement, defaultConfig) { ?arrow=${this.arrow} .autoPlacement=${this.autoPlacement} ?interactive=${false} + trigger="mouseenter:mouseleave focus:blur click" >
- +