Skip to content

Commit 02cacf9

Browse files
WestbrookWestbrook Johnson
authored andcommitted
fix(overlay): allow overlay-trigger to declaratively open overlay content
1 parent b7feca4 commit 02cacf9

File tree

7 files changed

+341
-119
lines changed

7 files changed

+341
-119
lines changed

packages/color-area/src/spectrum-color-area.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ THIS FILE IS MACHINE GENERATED. DO NOT EDIT */
108108
)
109109
var(--spectrum-colorarea-border-color);
110110
}
111+
.gradient {
112+
/* .spectrum-ColorArea-gradient,
113+
* .spectrum-ColorHandle-color */
114+
forced-color-adjust: none;
115+
}
111116
:host([disabled]) {
112117
/* .spectrum-ColorArea.is-disabled */
113118
background: var(
@@ -131,3 +136,11 @@ THIS FILE IS MACHINE GENERATED. DO NOT EDIT */
131136
/* .spectrum-ColorArea.is-disabled .spectrum-ColorArea-gradient */
132137
display: none;
133138
}
139+
@media (forced-colors: active) {
140+
:host {
141+
--spectrum-colorarea-fill-color-disabled: GrayText;
142+
}
143+
:host([disabled]) {
144+
forced-color-adjust: none;
145+
}
146+
}

packages/overlay/src/OverlayTrigger.ts

Lines changed: 121 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
import { openOverlay } from './loader.js';
2929
import overlayTriggerStyles from './overlay-trigger.css.js';
3030

31+
export type OverlayContentTypes = 'click' | 'hover' | 'longpress';
32+
3133
/**
3234
* A overlay trigger component for displaying overlays relative to other content.
3335
* @element overlay-trigger
@@ -42,6 +44,7 @@ import overlayTriggerStyles from './overlay-trigger.css.js';
4244
*/
4345
export class OverlayTrigger extends LitElement {
4446
private closeClickOverlay?: () => void;
47+
private closeLongpressOverlay?: () => void;
4548
private closeHoverOverlay?: () => void;
4649

4750
public static get styles(): CSSResultArray {
@@ -61,6 +64,9 @@ export class OverlayTrigger extends LitElement {
6164
@property({ type: Number, reflect: true })
6265
public offset = 6;
6366

67+
@property({ reflect: true })
68+
public open?: OverlayContentTypes;
69+
6470
@property({ type: Boolean, reflect: true })
6571
public disabled = false;
6672

@@ -69,8 +75,8 @@ export class OverlayTrigger extends LitElement {
6975
private hoverContent?: HTMLElement;
7076
private targetContent?: HTMLElement;
7177

72-
public overloadTarget(target: HTMLElement): void {
73-
this.targetContent = target;
78+
private handleClose(): void {
79+
this.removeAttribute('open');
7480
}
7581

7682
protected render(): TemplateResult {
@@ -85,6 +91,7 @@ export class OverlayTrigger extends LitElement {
8591
@mouseleave=${this.onTrigger}
8692
@focusin=${this.onTrigger}
8793
@focusout=${this.onTrigger}
94+
@sp-closed=${this.handleClose}
8895
>
8996
<slot
9097
@slotchange=${this.onTargetSlotChange}
@@ -118,6 +125,50 @@ export class OverlayTrigger extends LitElement {
118125
) {
119126
this.closeClickOverlay();
120127
}
128+
if (changes.has('open')) {
129+
this.manageOpen(changes.get('open') as OverlayContentTypes);
130+
}
131+
}
132+
133+
private manageOpen(previous?: OverlayContentTypes): void {
134+
switch (this.open) {
135+
case 'click':
136+
if (!this.closeClickOverlay) {
137+
this.onTriggerClick();
138+
}
139+
break;
140+
case 'hover':
141+
if (!this.closeHoverOverlay) {
142+
this.onTriggerMouseEnter();
143+
}
144+
break;
145+
case 'longpress':
146+
if (!this.closeLongpressOverlay) {
147+
this.onTriggerLongpress();
148+
}
149+
break;
150+
default:
151+
switch (previous) {
152+
case 'click':
153+
if (this.closeClickOverlay) {
154+
this.closeClickOverlay();
155+
delete this.closeClickOverlay;
156+
}
157+
break;
158+
case 'longpress':
159+
if (this.closeLongpressOverlay) {
160+
this.closeLongpressOverlay();
161+
delete this.closeLongpressOverlay;
162+
}
163+
break;
164+
case 'hover':
165+
this.onTriggerMouseLeave();
166+
break;
167+
default:
168+
break;
169+
}
170+
break;
171+
}
121172
}
122173

123174
public static openOverlay = async (
@@ -143,61 +194,69 @@ export class OverlayTrigger extends LitElement {
143194
return;
144195
}
145196
switch (event.type) {
146-
case 'click':
147-
this.onTriggerClick();
148-
return;
149197
case 'mouseenter':
150198
case 'focusin':
151-
this.onTriggerMouseEnter();
199+
if (this.hoverContent) {
200+
this.open = 'hover';
201+
}
152202
return;
153203
case 'mouseleave':
154204
case 'focusout':
155-
this.onTriggerMouseLeave();
205+
if (this.open === 'hover') {
206+
this.handleClose();
207+
}
208+
return;
209+
case 'click':
210+
if (this.clickContent) {
211+
this.open = event.type;
212+
}
156213
return;
157214
case 'longpress':
158-
this.onTriggerLongpress(event);
215+
if (this.longpressContent) {
216+
this.open = event.type;
217+
}
159218
return;
160219
}
161220
}
162221

222+
private prepareToFocusOverlayContent(overlayContent: HTMLElement): void {
223+
if (this.type !== 'modal') {
224+
return;
225+
}
226+
const firstFocusable = overlayContent.querySelector(
227+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [focusable]'
228+
) as HTMLElement;
229+
if (!firstFocusable) {
230+
overlayContent.tabIndex = 0;
231+
}
232+
}
233+
163234
public async onTriggerClick(): Promise<void> {
164-
if (this.targetContent && this.clickContent) {
165-
if (this.type === 'modal') {
166-
const firstFocusable = this.querySelector(
167-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [focusable]'
168-
) as HTMLElement;
169-
if (!firstFocusable) {
170-
this.clickContent.tabIndex = 0;
171-
}
172-
}
173-
const { targetContent, clickContent } = this;
174-
this.closeClickOverlay = await OverlayTrigger.openOverlay(
175-
targetContent,
176-
this.type ? this.type : 'click',
177-
clickContent,
178-
this.overlayOptions
179-
);
235+
if (!this.targetContent || !this.clickContent) {
236+
return;
180237
}
238+
const { targetContent, clickContent } = this;
239+
this.prepareToFocusOverlayContent(clickContent);
240+
this.closeClickOverlay = await OverlayTrigger.openOverlay(
241+
targetContent,
242+
this.type ? this.type : 'click',
243+
clickContent,
244+
this.overlayOptions
245+
);
181246
}
182247

183248
private async onTriggerLongpress(
184-
event: CustomEvent<LongpressEvent>
249+
event?: CustomEvent<LongpressEvent>
185250
): Promise<void> {
186251
if (!this.targetContent || !this.longpressContent) {
187252
return;
188253
}
189254
const { targetContent, longpressContent } = this;
190-
if (this.type === 'modal') {
191-
const firstFocusable = this.querySelector(
192-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [focusable]'
193-
) as HTMLElement;
194-
if (!firstFocusable) {
195-
longpressContent.tabIndex = 0;
196-
}
197-
}
198-
const type = event.detail.source === 'keyboard' ? 'click' : 'longpress';
255+
this.prepareToFocusOverlayContent(longpressContent);
256+
const type =
257+
event && event.detail.source === 'keyboard' ? 'click' : 'longpress';
199258
const interaction = this.type ? this.type : type || 'longpress';
200-
this.closeClickOverlay = await OverlayTrigger.openOverlay(
259+
this.closeLongpressOverlay = await OverlayTrigger.openOverlay(
201260
targetContent,
202261
interaction,
203262
longpressContent,
@@ -211,22 +270,23 @@ export class OverlayTrigger extends LitElement {
211270
private hoverOverlayReady = Promise.resolve();
212271

213272
public async onTriggerMouseEnter(): Promise<void> {
214-
if (this.targetContent && this.hoverContent) {
215-
let overlayReady: () => void = () => {
216-
return;
217-
};
218-
this.hoverOverlayReady = new Promise((res) => {
219-
overlayReady = res;
220-
});
221-
const { targetContent, hoverContent } = this;
222-
this.closeHoverOverlay = await OverlayTrigger.openOverlay(
223-
targetContent,
224-
'hover',
225-
hoverContent,
226-
this.overlayOptions
227-
);
228-
overlayReady();
273+
if (!this.targetContent || !this.hoverContent) {
274+
return;
229275
}
276+
let overlayReady: () => void = () => {
277+
return;
278+
};
279+
this.hoverOverlayReady = new Promise((res) => {
280+
overlayReady = res;
281+
});
282+
const { targetContent, hoverContent } = this;
283+
this.closeHoverOverlay = await OverlayTrigger.openOverlay(
284+
targetContent,
285+
'hover',
286+
hoverContent,
287+
this.overlayOptions
288+
);
289+
overlayReady();
230290
}
231291

232292
public async onTriggerMouseLeave(): Promise<void> {
@@ -240,29 +300,28 @@ export class OverlayTrigger extends LitElement {
240300
private onClickSlotChange(
241301
event: Event & { target: HTMLSlotElement }
242302
): void {
243-
const content = this.extractSlotContentFromEvent(event);
244-
this.clickContent = content;
303+
this.clickContent = this.extractSlotContentFromEvent(event);
304+
this.manageOpen();
245305
}
246306

247307
private onLongpressSlotChange(
248308
event: Event & { target: HTMLSlotElement }
249309
): void {
250-
const content = this.extractSlotContentFromEvent(event);
251-
this.longpressContent = content;
310+
this.longpressContent = this.extractSlotContentFromEvent(event);
311+
this.manageOpen();
252312
}
253313

254314
private onHoverSlotChange(
255315
event: Event & { target: HTMLSlotElement }
256316
): void {
257-
const content = this.extractSlotContentFromEvent(event);
258-
this.hoverContent = content;
317+
this.hoverContent = this.extractSlotContentFromEvent(event);
318+
this.manageOpen();
259319
}
260320

261321
private onTargetSlotChange(
262322
event: Event & { target: HTMLSlotElement }
263323
): void {
264-
const content = this.extractSlotContentFromEvent(event);
265-
this.targetContent = content;
324+
this.targetContent = this.extractSlotContentFromEvent(event);
266325
}
267326

268327
private extractSlotContentFromEvent(event: Event): HTMLElement | undefined {
@@ -276,6 +335,10 @@ export class OverlayTrigger extends LitElement {
276335
this.closeClickOverlay();
277336
delete this.closeClickOverlay;
278337
}
338+
if (this.closeHoverOverlay) {
339+
this.closeHoverOverlay();
340+
delete this.closeClickOverlay;
341+
}
279342
super.disconnectedCallback();
280343
}
281344
}

0 commit comments

Comments
 (0)