@aquera/nile-elements 0.1.32-beta-1.2 → 0.1.32-beta-1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -23
- package/demo/index.html +263 -24
- package/dist/axe.min-2b379f29.cjs.js +12 -0
- package/dist/axe.min-2b379f29.cjs.js.map +1 -0
- package/dist/axe.min-c2cd8733.esm.js +12 -0
- package/dist/{fixture-3acb409b.cjs.js → fixture-d5b55278.cjs.js} +3 -3
- package/dist/fixture-d5b55278.cjs.js.map +1 -0
- package/dist/{fixture-db35a8ae.esm.js → fixture-df8b52d7.esm.js} +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/nile-accordion/nile-accordian.test.cjs.js +1 -1
- package/dist/nile-accordion/nile-accordian.test.esm.js +1 -1
- package/dist/nile-accordion/nile-accordion.cjs.js +1 -1
- package/dist/nile-accordion/nile-accordion.cjs.js.map +1 -1
- package/dist/nile-accordion/nile-accordion.css.cjs.js +1 -1
- package/dist/nile-accordion/nile-accordion.css.cjs.js.map +1 -1
- package/dist/nile-accordion/nile-accordion.css.esm.js +1 -1
- package/dist/nile-accordion/nile-accordion.esm.js +4 -4
- package/dist/nile-auto-complete/nile-auto-complete.test.cjs.js +1 -1
- package/dist/nile-auto-complete/nile-auto-complete.test.esm.js +1 -1
- package/dist/nile-avatar/nile-avatar.test.cjs.js +1 -1
- package/dist/nile-avatar/nile-avatar.test.esm.js +1 -1
- package/dist/nile-badge/nile-badge.test.cjs.js +1 -1
- package/dist/nile-badge/nile-badge.test.esm.js +1 -1
- package/dist/nile-button/nile-button.test.cjs.js +1 -1
- package/dist/nile-button/nile-button.test.esm.js +1 -1
- package/dist/nile-button-toggle-group/nile-button-toggle-group.test.cjs.js +1 -1
- package/dist/nile-button-toggle-group/nile-button-toggle-group.test.esm.js +1 -1
- package/dist/nile-calendar/nile-calendar.test.cjs.js +1 -1
- package/dist/nile-calendar/nile-calendar.test.esm.js +1 -1
- package/dist/nile-card/nile-card.test.cjs.js +1 -1
- package/dist/nile-card/nile-card.test.esm.js +1 -1
- package/dist/nile-checkbox/nile-checkbox.test.cjs.js +1 -1
- package/dist/nile-checkbox/nile-checkbox.test.esm.js +1 -1
- package/dist/nile-chip/nile-chip.test.cjs.js +1 -1
- package/dist/nile-chip/nile-chip.test.esm.js +1 -1
- package/dist/nile-circular-progressbar/nile-circular-progressbar.css.cjs.js +1 -1
- package/dist/nile-circular-progressbar/nile-circular-progressbar.css.cjs.js.map +1 -1
- package/dist/nile-circular-progressbar/nile-circular-progressbar.css.esm.js +49 -49
- package/dist/nile-code-editor/extensionSetup.cjs.js +7 -7
- package/dist/nile-code-editor/extensionSetup.cjs.js.map +1 -1
- package/dist/nile-code-editor/extensionSetup.esm.js +1 -1
- package/dist/nile-code-editor/nile-code-editor.cjs.js +2 -2
- package/dist/nile-code-editor/nile-code-editor.cjs.js.map +1 -1
- package/dist/nile-code-editor/nile-code-editor.esm.js +2 -2
- package/dist/nile-dialog/nile-dialog.test.cjs.js +1 -1
- package/dist/nile-dialog/nile-dialog.test.esm.js +1 -1
- package/dist/nile-drawer/nile-drawer.test.cjs.js +1 -1
- package/dist/nile-drawer/nile-drawer.test.esm.js +1 -1
- package/dist/nile-dropdown/nile-dropdown.test.cjs.js +1 -1
- package/dist/nile-dropdown/nile-dropdown.test.esm.js +1 -1
- package/dist/nile-empty-state/nile-empty-state.test.cjs.js +1 -1
- package/dist/nile-empty-state/nile-empty-state.test.esm.js +1 -1
- package/dist/nile-error-message/nile-error-message.test.cjs.js +1 -1
- package/dist/nile-error-message/nile-error-message.test.esm.js +1 -1
- package/dist/nile-form-group/nile-form-group.test.cjs.js +1 -1
- package/dist/nile-form-group/nile-form-group.test.esm.js +1 -1
- package/dist/nile-form-help-text/nile-form-help-text.test.cjs.js +1 -1
- package/dist/nile-form-help-text/nile-form-help-text.test.esm.js +1 -1
- package/dist/nile-hero/nile-hero.test.cjs.js +1 -1
- package/dist/nile-hero/nile-hero.test.esm.js +1 -1
- package/dist/nile-icon/nile-icon.test.cjs.js +1 -1
- package/dist/nile-icon/nile-icon.test.esm.js +1 -1
- package/dist/nile-input/nile-input.css.cjs.js +1 -1
- package/dist/nile-input/nile-input.css.cjs.js.map +1 -1
- package/dist/nile-input/nile-input.css.esm.js +8 -0
- package/dist/nile-input/nile-input.test.cjs.js +1 -1
- package/dist/nile-input/nile-input.test.esm.js +1 -1
- package/dist/nile-link/nile-link.test.cjs.js +1 -1
- package/dist/nile-link/nile-link.test.esm.js +1 -1
- package/dist/nile-loader/nile-loader.test.cjs.js +1 -1
- package/dist/nile-loader/nile-loader.test.esm.js +1 -1
- package/dist/nile-popover/nile-popover.test.cjs.js +1 -1
- package/dist/nile-popover/nile-popover.test.esm.js +1 -1
- package/dist/nile-popup/nile-popup.test.cjs.js +1 -1
- package/dist/nile-popup/nile-popup.test.esm.js +1 -1
- package/dist/nile-progress-bar/nile-progress-bar.test.cjs.js +1 -1
- package/dist/nile-progress-bar/nile-progress-bar.test.esm.js +1 -1
- package/dist/nile-radio/nile-radio.test.cjs.js +1 -1
- package/dist/nile-radio/nile-radio.test.esm.js +1 -1
- package/dist/nile-radio-group/nile-radio-group.test.cjs.js +1 -1
- package/dist/nile-radio-group/nile-radio-group.test.esm.js +1 -1
- package/dist/nile-select/nile-select.test.cjs.js +1 -1
- package/dist/nile-select/nile-select.test.esm.js +1 -1
- package/dist/nile-slide-toggle/nile-slide-toggle.test.cjs.js +1 -1
- package/dist/nile-slide-toggle/nile-slide-toggle.test.esm.js +1 -1
- package/dist/nile-tab-group/nile-tab-group.test.cjs.js +1 -1
- package/dist/nile-tab-group/nile-tab-group.test.esm.js +1 -1
- package/dist/nile-table-body/nile-table-body.cjs.js +1 -1
- package/dist/nile-table-body/nile-table-body.cjs.js.map +1 -1
- package/dist/nile-table-body/nile-table-body.esm.js +2 -2
- package/dist/nile-table-cell-item/nile-table-cell-item.css.cjs.js +1 -1
- package/dist/nile-table-cell-item/nile-table-cell-item.css.cjs.js.map +1 -1
- package/dist/nile-table-cell-item/nile-table-cell-item.css.esm.js +4 -8
- package/dist/nile-table-header-item/nile-table-header-item.cjs.js +1 -1
- package/dist/nile-table-header-item/nile-table-header-item.cjs.js.map +1 -1
- package/dist/nile-table-header-item/nile-table-header-item.css.cjs.js +1 -1
- package/dist/nile-table-header-item/nile-table-header-item.css.cjs.js.map +1 -1
- package/dist/nile-table-header-item/nile-table-header-item.css.esm.js +0 -5
- package/dist/nile-table-header-item/nile-table-header-item.esm.js +9 -9
- package/dist/nile-table-row/nile-table-row.cjs.js.map +1 -1
- package/dist/nile-textarea/nile-textarea.test.cjs.js +1 -1
- package/dist/nile-textarea/nile-textarea.test.esm.js +1 -1
- package/dist/nile-tooltip/index.cjs.js +1 -1
- package/dist/nile-tooltip/index.esm.js +1 -1
- package/dist/nile-tooltip/nile-tooltip-utils.cjs.js +2 -0
- package/dist/nile-tooltip/nile-tooltip-utils.cjs.js.map +1 -0
- package/dist/nile-tooltip/nile-tooltip-utils.esm.js +1 -0
- package/dist/nile-tooltip/nile-tooltip.cjs.js +1 -1
- package/dist/nile-tooltip/nile-tooltip.cjs.js.map +1 -1
- package/dist/nile-tooltip/nile-tooltip.css.cjs.js +1 -1
- package/dist/nile-tooltip/nile-tooltip.css.cjs.js.map +1 -1
- package/dist/nile-tooltip/nile-tooltip.css.esm.js +20 -76
- package/dist/nile-tooltip/nile-tooltip.esm.js +20 -8
- package/dist/nile-tooltip/nile-tooltip.test.cjs.js +2 -0
- package/dist/nile-tooltip/nile-tooltip.test.cjs.js.map +1 -0
- package/dist/nile-tooltip/nile-tooltip.test.esm.js +51 -0
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/nile-accordion/nile-accordion.css.js +1 -1
- package/dist/src/nile-accordion/nile-accordion.css.js.map +1 -1
- package/dist/src/nile-accordion/nile-accordion.d.ts +5 -6
- package/dist/src/nile-accordion/nile-accordion.js +8 -21
- package/dist/src/nile-accordion/nile-accordion.js.map +1 -1
- package/dist/src/nile-circular-progressbar/nile-circular-progressbar.css.js +49 -49
- package/dist/src/nile-circular-progressbar/nile-circular-progressbar.css.js.map +1 -1
- package/dist/src/nile-input/nile-input.css.js +8 -0
- package/dist/src/nile-input/nile-input.css.js.map +1 -1
- package/dist/src/nile-table-body/nile-table-body.d.ts +1 -3
- package/dist/src/nile-table-body/nile-table-body.js +1 -9
- package/dist/src/nile-table-body/nile-table-body.js.map +1 -1
- package/dist/src/nile-table-cell-item/nile-table-cell-item.css.js +2 -6
- package/dist/src/nile-table-cell-item/nile-table-cell-item.css.js.map +1 -1
- package/dist/src/nile-table-header-item/nile-table-header-item.css.js +0 -5
- package/dist/src/nile-table-header-item/nile-table-header-item.css.js.map +1 -1
- package/dist/src/nile-table-header-item/nile-table-header-item.js +1 -1
- package/dist/src/nile-table-header-item/nile-table-header-item.js.map +1 -1
- package/dist/src/nile-table-row/nile-table-row.js.map +1 -1
- package/dist/src/nile-tooltip/nile-tooltip-utils.d.ts +18 -0
- package/dist/src/nile-tooltip/nile-tooltip-utils.js +151 -0
- package/dist/src/nile-tooltip/nile-tooltip-utils.js.map +1 -0
- package/dist/src/nile-tooltip/nile-tooltip.css.js +19 -75
- package/dist/src/nile-tooltip/nile-tooltip.css.js.map +1 -1
- package/dist/src/nile-tooltip/nile-tooltip.d.ts +28 -9
- package/dist/src/nile-tooltip/nile-tooltip.js +111 -117
- package/dist/src/nile-tooltip/nile-tooltip.js.map +1 -1
- package/dist/src/nile-tooltip/nile-tooltip.test.d.ts +1 -0
- package/dist/src/nile-tooltip/nile-tooltip.test.js +158 -0
- package/dist/src/nile-tooltip/nile-tooltip.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -2
- package/src/nile-accordion/nile-accordion.css.ts +1 -1
- package/src/nile-accordion/nile-accordion.ts +4 -15
- package/src/nile-circular-progressbar/nile-circular-progressbar.css.ts +56 -55
- package/src/nile-input/nile-input.css.ts +8 -0
- package/src/nile-table-body/nile-table-body.ts +2 -8
- package/src/nile-table-cell-item/nile-table-cell-item.css.ts +2 -6
- package/src/nile-table-header-item/nile-table-header-item.css.ts +0 -5
- package/src/nile-table-header-item/nile-table-header-item.ts +1 -1
- package/src/nile-table-row/nile-table-row.ts +2 -1
- package/src/nile-tooltip/nile-tooltip-utils.ts +190 -0
- package/src/nile-tooltip/nile-tooltip.css.ts +20 -76
- package/src/nile-tooltip/nile-tooltip.test.ts +178 -0
- package/src/nile-tooltip/nile-tooltip.ts +142 -162
- package/vscode-html-custom-data.json +72 -90
- package/dist/axe.min-5bf06036.esm.js +0 -12
- package/dist/axe.min-ff35bfba.cjs.js +0 -12
- package/dist/axe.min-ff35bfba.cjs.js.map +0 -1
- package/dist/fixture-3acb409b.cjs.js.map +0 -1
- package/dist/nile-table/index.cjs.js +0 -2
- package/dist/nile-table/index.cjs.js.map +0 -1
- package/dist/nile-table/index.esm.js +0 -1
- package/dist/nile-table/nile-table.cjs.js +0 -2
- package/dist/nile-table/nile-table.cjs.js.map +0 -1
- package/dist/nile-table/nile-table.css.cjs.js +0 -2
- package/dist/nile-table/nile-table.css.cjs.js.map +0 -1
- package/dist/nile-table/nile-table.css.esm.js +0 -6
- package/dist/nile-table/nile-table.esm.js +0 -3
- package/dist/src/nile-table/index.d.ts +0 -1
- package/dist/src/nile-table/index.js +0 -2
- package/dist/src/nile-table/index.js.map +0 -1
- package/dist/src/nile-table/nile-table.css.d.ts +0 -12
- package/dist/src/nile-table/nile-table.css.js +0 -18
- package/dist/src/nile-table/nile-table.css.js.map +0 -1
- package/dist/src/nile-table/nile-table.d.ts +0 -48
- package/dist/src/nile-table/nile-table.js +0 -143
- package/dist/src/nile-table/nile-table.js.map +0 -1
- package/src/nile-table/index.ts +0 -1
- package/src/nile-table/nile-table.css.ts +0 -20
- package/src/nile-table/nile-table.ts +0 -161
@@ -0,0 +1,178 @@
|
|
1
|
+
import { fixture, html, assert, oneEvent, waitUntil } from '@open-wc/testing';
|
2
|
+
import './nile-tooltip';
|
3
|
+
import { NileTooltip } from './nile-tooltip';
|
4
|
+
|
5
|
+
describe('NileTooltip', () => {
|
6
|
+
|
7
|
+
it('renders with default properties', async () => {
|
8
|
+
const el = await fixture<NileTooltip>(html`<nile-tooltip content="Hello"><button>Hover me</button></nile-tooltip>`);
|
9
|
+
assert.equal(el.content, 'Hello');
|
10
|
+
assert.equal(el.placement, 'bottom');
|
11
|
+
assert.equal(el.size, 'small');
|
12
|
+
assert.equal(el.disabled, false);
|
13
|
+
assert.equal(el.open, false);
|
14
|
+
});
|
15
|
+
|
16
|
+
it('renders text content when no slot is used', async () => {
|
17
|
+
const el = await fixture<NileTooltip>(html`<nile-tooltip content="Fallback"><button>Trigger</button></nile-tooltip>`);
|
18
|
+
const content = el.shadowRoot!.querySelector('.tooltip-content')!;
|
19
|
+
assert.include(content.textContent!, 'Fallback');
|
20
|
+
});
|
21
|
+
|
22
|
+
it('uses slotted content when provided', async () => {
|
23
|
+
const el = await fixture<NileTooltip>(html`
|
24
|
+
<nile-tooltip>
|
25
|
+
<div slot="content">Slot Content</div>
|
26
|
+
<button>Trigger</button>
|
27
|
+
</nile-tooltip>
|
28
|
+
`);
|
29
|
+
|
30
|
+
const slot = el.shadowRoot!.querySelector('slot[name="content"]') as HTMLSlotElement;
|
31
|
+
const assigned = slot.assignedNodes({ flatten: true });
|
32
|
+
assert.isAbove(assigned.length, 0);
|
33
|
+
assert.include(assigned[0].textContent ?? '', 'Slot Content');
|
34
|
+
});
|
35
|
+
|
36
|
+
it('shows tooltip on hover when trigger includes "hover"', async () => {
|
37
|
+
const el = await fixture<NileTooltip>(html`
|
38
|
+
<nile-tooltip content="Tooltip" trigger="hover">
|
39
|
+
<button>Hover me</button>
|
40
|
+
</nile-tooltip>
|
41
|
+
`);
|
42
|
+
const trigger = el.shadowRoot!.querySelector('.trigger-container')!;
|
43
|
+
trigger.dispatchEvent(new Event('mouseover', { bubbles: true }));
|
44
|
+
await waitUntil(() => el.open === true);
|
45
|
+
assert.isTrue(el.open);
|
46
|
+
});
|
47
|
+
|
48
|
+
it('hides tooltip on mouseout when trigger includes "hover"', async () => {
|
49
|
+
const el = await fixture<NileTooltip>(html`
|
50
|
+
<nile-tooltip content="Tooltip" trigger="hover">
|
51
|
+
<button>Hover me</button>
|
52
|
+
</nile-tooltip>
|
53
|
+
`);
|
54
|
+
const trigger = el.shadowRoot!.querySelector('.trigger-container')!;
|
55
|
+
trigger.dispatchEvent(new Event('mouseover', { bubbles: true }));
|
56
|
+
await waitUntil(() => el.open === true);
|
57
|
+
trigger.dispatchEvent(new Event('mouseout', { bubbles: true }));
|
58
|
+
await waitUntil(() => el.open === false);
|
59
|
+
});
|
60
|
+
|
61
|
+
it('toggles tooltip on click when trigger includes "click"', async () => {
|
62
|
+
const el = await fixture<NileTooltip>(html`
|
63
|
+
<nile-tooltip content="Click me" trigger="click">
|
64
|
+
<button>Click</button>
|
65
|
+
</nile-tooltip>
|
66
|
+
`);
|
67
|
+
const trigger = el.shadowRoot!.querySelector('.trigger-container') as HTMLElement;
|
68
|
+
trigger.click();
|
69
|
+
await waitUntil(() => el.open === true);
|
70
|
+
trigger.click();
|
71
|
+
await waitUntil(() => el.open === false);
|
72
|
+
});
|
73
|
+
|
74
|
+
it('shows and hides tooltip on focus/blur when trigger includes "focus"', async () => {
|
75
|
+
const el = await fixture<NileTooltip>(html`
|
76
|
+
<nile-tooltip content="Focus" trigger="focus">
|
77
|
+
<button>Focus</button>
|
78
|
+
</nile-tooltip>
|
79
|
+
`);
|
80
|
+
const trigger = el.shadowRoot!.querySelector('.trigger-container')!;
|
81
|
+
trigger.dispatchEvent(new Event('focusin'));
|
82
|
+
await waitUntil(() => el.open === true);
|
83
|
+
trigger.dispatchEvent(new Event('focusout'));
|
84
|
+
await waitUntil(() => el.open === false);
|
85
|
+
});
|
86
|
+
|
87
|
+
it('does not show tooltip when disabled', async () => {
|
88
|
+
const el = await fixture<NileTooltip>(html`
|
89
|
+
<nile-tooltip content="Disabled" disabled trigger="hover">
|
90
|
+
<button>Hover me</button>
|
91
|
+
</nile-tooltip>
|
92
|
+
`);
|
93
|
+
const trigger = el.shadowRoot!.querySelector('.trigger-container')!;
|
94
|
+
trigger.dispatchEvent(new Event('mouseover'));
|
95
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
96
|
+
assert.isFalse(el.open);
|
97
|
+
});
|
98
|
+
|
99
|
+
it('applies size class correctly', async () => {
|
100
|
+
const el = await fixture<NileTooltip>(html`
|
101
|
+
<nile-tooltip content="Tooltip" size="large">
|
102
|
+
<button>Trigger</button>
|
103
|
+
</nile-tooltip>
|
104
|
+
`);
|
105
|
+
const tooltip = el.shadowRoot!.querySelector('.tooltip')!;
|
106
|
+
assert.isTrue(tooltip.classList.contains('tooltip__body--large'));
|
107
|
+
});
|
108
|
+
|
109
|
+
it('emits nile-show and nile-after-show events', async () => {
|
110
|
+
const el = await fixture<NileTooltip>(html`
|
111
|
+
<nile-tooltip content="Event test" trigger="click">
|
112
|
+
<button>Click</button>
|
113
|
+
</nile-tooltip>
|
114
|
+
`);
|
115
|
+
const trigger = el.shadowRoot!.querySelector('.trigger-container') as HTMLElement;
|
116
|
+
setTimeout(() => trigger.click());
|
117
|
+
const showEvent = await oneEvent(el, 'nile-show');
|
118
|
+
assert.ok(showEvent);
|
119
|
+
const afterShow = await oneEvent(el, 'nile-after-show');
|
120
|
+
assert.ok(afterShow);
|
121
|
+
});
|
122
|
+
|
123
|
+
it('emits nile-hide and nile-after-hide events', async () => {
|
124
|
+
const el = await fixture<NileTooltip>(html`
|
125
|
+
<nile-tooltip content="Event test" trigger="click">
|
126
|
+
<button>Click</button>
|
127
|
+
</nile-tooltip>
|
128
|
+
`);
|
129
|
+
const trigger = el.shadowRoot!.querySelector('.trigger-container') as HTMLElement;
|
130
|
+
trigger.click();
|
131
|
+
await waitUntil(() => el.open === true);
|
132
|
+
setTimeout(() => trigger.click());
|
133
|
+
const hideEvent = await oneEvent(el, 'nile-hide');
|
134
|
+
assert.ok(hideEvent);
|
135
|
+
const afterHide = await oneEvent(el, 'nile-after-hide');
|
136
|
+
assert.ok(afterHide);
|
137
|
+
});
|
138
|
+
|
139
|
+
it('defaults to "bottom" placement when given invalid value', async () => {
|
140
|
+
const el = await fixture<NileTooltip>(html`
|
141
|
+
<nile-tooltip content="Fallback" placement=${'invalid' as unknown as NileTooltip['placement']} >
|
142
|
+
<button>Trigger</button>
|
143
|
+
</nile-tooltip>
|
144
|
+
`);
|
145
|
+
await el.updateComplete;
|
146
|
+
assert.equal(el.placement, 'bottom');
|
147
|
+
});
|
148
|
+
|
149
|
+
|
150
|
+
it('updates hasTooltipSlot on slot change', async () => {
|
151
|
+
const el = await fixture<NileTooltip>(html`
|
152
|
+
<nile-tooltip>
|
153
|
+
<div slot="content">Initial slot</div>
|
154
|
+
<button>Trigger</button>
|
155
|
+
</nile-tooltip>
|
156
|
+
`);
|
157
|
+
|
158
|
+
const slot = el.shadowRoot!.querySelector('slot[name="content"]') as HTMLSlotElement;
|
159
|
+
const newSlotNode = document.createElement('div');
|
160
|
+
newSlotNode.slot = 'content';
|
161
|
+
newSlotNode.textContent = 'Updated slot';
|
162
|
+
el.appendChild(newSlotNode);
|
163
|
+
|
164
|
+
slot.dispatchEvent(new Event('slotchange'));
|
165
|
+
await el.updateComplete;
|
166
|
+
|
167
|
+
assert.isTrue((el as any).hasTooltipSlot);
|
168
|
+
});
|
169
|
+
|
170
|
+
it('respects hoist attribute', async () => {
|
171
|
+
const el = await fixture<NileTooltip>(html`
|
172
|
+
<nile-tooltip hoist content="Hoisted">
|
173
|
+
<button>Hoist</button>
|
174
|
+
</nile-tooltip>
|
175
|
+
`);
|
176
|
+
assert.isTrue(el.hoist);
|
177
|
+
});
|
178
|
+
});
|
@@ -1,198 +1,171 @@
|
|
1
|
-
|
1
|
+
/**
|
2
|
+
* Copyright Aquera Inc 2023
|
3
|
+
*
|
4
|
+
* This source code is licensed under the BSD-3-Clause license found in the
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
6
|
+
*/
|
7
|
+
import { LitElement, html, css, CSSResultArray } from 'lit';
|
2
8
|
import { customElement, property, query } from 'lit/decorators.js';
|
3
9
|
import { classMap } from 'lit/directives/class-map.js';
|
4
10
|
import { styles } from './nile-tooltip.css';
|
5
11
|
import NileElement from '../internal/nile-element';
|
12
|
+
import { isInViewport, getValidTooltipPosition, getCaretPosition } from './nile-tooltip-utils';
|
13
|
+
/**
|
14
|
+
* Nile icon component.
|
15
|
+
*
|
16
|
+
* @tag nile-tooltip
|
17
|
+
*
|
18
|
+
*/
|
6
19
|
|
7
|
-
type TooltipPlacement =
|
8
|
-
| 'top' | 'top-start' | 'top-end'
|
9
|
-
| 'right' | 'right-start' | 'right-end'
|
10
|
-
| 'bottom' | 'bottom-start' | 'bottom-end'
|
11
|
-
| 'left' | 'left-start' | 'left-end';
|
12
|
-
|
13
|
-
// CSS Anchor positiong
|
14
20
|
@customElement('nile-tooltip')
|
15
21
|
export class NileTooltip extends NileElement {
|
16
22
|
@property({ type: String }) content = '';
|
17
23
|
@property({ reflect: true }) size: 'small' | 'large' = 'small';
|
18
|
-
@property({ type: String })
|
24
|
+
@property({ type: String })
|
25
|
+
placement:
|
26
|
+
| 'top'
|
27
|
+
| 'top-start'
|
28
|
+
| 'top-end'
|
29
|
+
| 'right'
|
30
|
+
| 'right-start'
|
31
|
+
| 'right-end'
|
32
|
+
| 'bottom'
|
33
|
+
| 'bottom-start'
|
34
|
+
| 'bottom-end'
|
35
|
+
| 'left'
|
36
|
+
| 'left-start'
|
37
|
+
| 'left-end' = 'bottom';
|
19
38
|
@property({ type: Boolean, reflect: true }) disabled = false;
|
20
39
|
@property({ type: Boolean, reflect: true }) open = false;
|
21
|
-
|
40
|
+
/**
|
41
|
+
* Controls how the tooltip is activated. Possible options include `click`, `hover`, `focus`, and `manual`. Multiple
|
42
|
+
* options can be passed by separating them with a space. When manual is used, the tooltip must be activated
|
43
|
+
* programmatically.
|
44
|
+
*/
|
45
|
+
@property() trigger = 'hover focus';
|
22
46
|
@property({ type: Number }) distance = 8;
|
47
|
+
private readonly SHIFT_OFFSET = 16;
|
48
|
+
/** The distance in pixels from which to offset the tooltip along its target. */
|
23
49
|
@property({ type: Number }) skidding = 0;
|
24
|
-
@property({ type: Boolean }) hoist = false;
|
50
|
+
@property({ type: Boolean, reflect: true }) hoist = false;
|
25
51
|
|
26
52
|
@query('.tooltip') tooltip!: HTMLElement;
|
27
|
-
@query('
|
53
|
+
@query('.trigger-container') triggerContainer!: HTMLElement;
|
54
|
+
@query('.tooltip-caret') caret!: HTMLElement;
|
55
|
+
@query('slot[name="content"]') tooltipSlot!: HTMLSlotElement;
|
28
56
|
|
29
|
-
private
|
57
|
+
private hasTooltipSlot = false;
|
58
|
+
private hoverTimeout: number = 0;
|
59
|
+
private caretSize = 6;
|
60
|
+
private originalPlacement: string = this.placement;
|
30
61
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
62
|
+
public static get styles(): CSSResultArray {
|
63
|
+
return [styles];
|
64
|
+
}
|
35
65
|
|
36
|
-
|
37
|
-
|
38
66
|
connectedCallback() {
|
39
67
|
super.connectedCallback();
|
68
|
+
this.originalPlacement = this.placement;
|
40
69
|
window.addEventListener('resize', this.updateTooltipPosition);
|
41
70
|
window.addEventListener('scroll', this.updateTooltipPosition, true);
|
42
71
|
}
|
43
72
|
|
73
|
+
updated(changedProps: Map<string, unknown>) {
|
74
|
+
super.updated?.(changedProps);
|
75
|
+
|
76
|
+
const validPlacements = [
|
77
|
+
'top', 'top-start', 'top-end',
|
78
|
+
'right', 'right-start', 'right-end',
|
79
|
+
'bottom', 'bottom-start', 'bottom-end',
|
80
|
+
'left', 'left-start', 'left-end'
|
81
|
+
];
|
82
|
+
|
83
|
+
if (!validPlacements.includes(this.placement)) {
|
84
|
+
console.warn(`[nile-tooltip] Invalid placement "${this.placement}", defaulting to "bottom".`);
|
85
|
+
this.placement = 'bottom';
|
86
|
+
}
|
87
|
+
|
88
|
+
if (!validPlacements.includes(this.originalPlacement)) {
|
89
|
+
this.originalPlacement = 'bottom';
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
44
93
|
disconnectedCallback() {
|
45
94
|
super.disconnectedCallback();
|
46
95
|
window.removeEventListener('resize', this.updateTooltipPosition);
|
47
96
|
window.removeEventListener('scroll', this.updateTooltipPosition, true);
|
48
97
|
}
|
49
98
|
|
50
|
-
private
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
private getAlignmentModifier(placement: TooltipPlacement): 'start' | 'end' | null {
|
55
|
-
const parts = placement.split('-');
|
56
|
-
return parts.length > 1 ? parts[1] as 'start' | 'end' : null;
|
99
|
+
private handleTooltipSlotChange() {
|
100
|
+
const nodes = this.tooltipSlot.assignedNodes({ flatten: true });
|
101
|
+
this.hasTooltipSlot = nodes.length > 0;
|
102
|
+
this.requestUpdate();
|
57
103
|
}
|
58
104
|
|
59
105
|
private updateTooltipPosition = () => {
|
60
|
-
if (!this.
|
61
|
-
|
62
|
-
|
63
|
-
|
106
|
+
if (!isInViewport(this.triggerContainer)) {
|
107
|
+
this.open = false;
|
108
|
+
return;
|
109
|
+
}
|
64
110
|
|
65
|
-
|
66
|
-
|
67
|
-
const triggerRect = triggerEl.getBoundingClientRect();
|
111
|
+
const triggerRect = this.triggerContainer.getBoundingClientRect();
|
68
112
|
const tooltipRect = this.tooltip.getBoundingClientRect();
|
69
113
|
const viewportWidth = window.innerWidth;
|
70
114
|
const viewportHeight = window.innerHeight;
|
71
|
-
|
72
|
-
let top = 0, left = 0;
|
73
|
-
let finalPlacement = this.placement;
|
74
|
-
const basePlacement = this.getBasePlacement(this.placement);
|
75
|
-
const alignment = this.getAlignmentModifier(this.placement);
|
76
|
-
|
77
|
-
|
78
|
-
const fitsLeft = triggerRect.left - tooltipRect.width - this.distance > 0;
|
79
|
-
const fitsRight = triggerRect.right + tooltipRect.width + this.distance < viewportWidth;
|
80
|
-
const fitsBottom = triggerRect.bottom + tooltipRect.height + this.distance < viewportHeight;
|
81
|
-
const fitsTop = triggerRect.top - tooltipRect.height - this.distance > 0;
|
82
|
-
|
83
|
-
|
84
|
-
let finalBasePlacement = basePlacement;
|
85
|
-
|
86
|
-
|
87
|
-
if ((basePlacement === 'left' && !fitsLeft) || (basePlacement === 'right' && !fitsRight)) {
|
88
|
-
finalBasePlacement = fitsBottom ? 'bottom' : (fitsTop ? 'top' : 'bottom');
|
89
|
-
}
|
90
|
-
|
91
|
-
else if (basePlacement === 'top' && !fitsTop) {
|
92
|
-
finalBasePlacement = fitsBottom ? 'bottom' : (fitsLeft ? 'left' : 'right');
|
93
|
-
} else if (basePlacement === 'bottom' && !fitsBottom) {
|
94
|
-
finalBasePlacement = fitsTop ? 'top' : (fitsLeft ? 'left' : 'right');
|
95
|
-
}
|
96
|
-
|
97
|
-
|
98
|
-
let finalAlignment = alignment;
|
99
|
-
|
100
|
-
|
101
|
-
if ((basePlacement === 'left' || basePlacement === 'right') &&
|
102
|
-
(finalBasePlacement === 'top' || finalBasePlacement === 'bottom')) {
|
103
|
-
|
104
|
-
if (alignment === 'start') {
|
105
|
-
finalAlignment = 'start';
|
106
|
-
} else if (alignment === 'end') {
|
107
|
-
finalAlignment = 'end';
|
108
|
-
}
|
109
|
-
}
|
110
|
-
|
111
|
-
|
112
|
-
switch (finalBasePlacement) {
|
113
|
-
case 'left':
|
114
|
-
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2 + this.skidding;
|
115
|
-
left = triggerRect.left - tooltipRect.width - this.distance;
|
116
|
-
break;
|
117
|
-
case 'right':
|
118
|
-
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2 + this.skidding;
|
119
|
-
left = triggerRect.right + this.distance;
|
120
|
-
break;
|
121
|
-
case 'top':
|
122
|
-
top = triggerRect.top - tooltipRect.height - this.distance;
|
123
|
-
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2 + this.skidding;
|
124
|
-
break;
|
125
|
-
case 'bottom':
|
126
|
-
top = triggerRect.bottom + this.distance;
|
127
|
-
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2 + this.skidding;
|
128
|
-
break;
|
129
|
-
}
|
130
|
-
|
131
|
-
|
132
|
-
if ((finalBasePlacement === 'top' || finalBasePlacement === 'bottom') && finalAlignment) {
|
133
|
-
if (finalAlignment === 'start') {
|
134
|
-
left = triggerRect.left + this.skidding;
|
135
|
-
} else if (finalAlignment === 'end') {
|
136
|
-
left = triggerRect.right - tooltipRect.width + this.skidding;
|
137
|
-
}
|
138
|
-
}
|
139
|
-
|
140
|
-
|
141
|
-
if ((finalBasePlacement === 'left' || finalBasePlacement === 'right') && finalAlignment) {
|
142
|
-
if (finalAlignment === 'start') {
|
143
|
-
top = triggerRect.top + this.skidding;
|
144
|
-
} else if (finalAlignment === 'end') {
|
145
|
-
top = triggerRect.bottom - tooltipRect.height + this.skidding;
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
|
150
|
-
finalPlacement = finalAlignment ? `${finalBasePlacement}-${finalAlignment}` : finalBasePlacement;
|
151
|
-
|
152
|
-
|
153
|
-
if (left < 0) left = 5;
|
154
|
-
if (left + tooltipRect.width > viewportWidth) left = viewportWidth - tooltipRect.width - 5;
|
155
|
-
if (top < 0) top = 5;
|
156
|
-
if (top + tooltipRect.height > viewportHeight) top = viewportHeight - tooltipRect.height - 5;
|
157
115
|
|
158
|
-
|
116
|
+
const { top, left, placement } = getValidTooltipPosition(
|
117
|
+
triggerRect,
|
118
|
+
tooltipRect,
|
119
|
+
this.originalPlacement,
|
120
|
+
this.distance,
|
121
|
+
this.skidding,
|
122
|
+
this.caretSize,
|
123
|
+
viewportWidth,
|
124
|
+
viewportHeight
|
125
|
+
);
|
126
|
+
|
127
|
+
this.setAttribute('placement', placement);
|
159
128
|
this.tooltip.style.top = `${top}px`;
|
160
129
|
this.tooltip.style.left = `${left}px`;
|
161
|
-
|
130
|
+
|
131
|
+
const { caretLeft, caretTop } = getCaretPosition({
|
132
|
+
placement,
|
133
|
+
tooltipRect,
|
134
|
+
triggerRect,
|
135
|
+
caretSize: this.caretSize,
|
136
|
+
left,
|
137
|
+
top
|
138
|
+
});
|
139
|
+
|
140
|
+
this.caret.style.left = `${caretLeft}px`;
|
141
|
+
this.caret.style.top = `${caretTop}px`;
|
162
142
|
};
|
163
143
|
|
164
144
|
private showTooltip = () => {
|
165
|
-
|
166
|
-
|
167
|
-
|
145
|
+
const trimmedContent = this.content.trim();
|
146
|
+
if (!trimmedContent && !this.hasTooltipSlot) {
|
147
|
+
return;
|
148
|
+
}
|
149
|
+
if (!this.disabled && isInViewport(this.triggerContainer)) {
|
168
150
|
this.emit('nile-show');
|
151
|
+
this.open = true;
|
169
152
|
this.updateComplete.then(() => {
|
170
153
|
this.updateTooltipPosition();
|
171
|
-
|
172
|
-
|
173
|
-
this.tooltip.addEventListener('transitionend', this.handleAfterShow, { once: true });
|
154
|
+
this.emit('nile-after-show');
|
174
155
|
});
|
156
|
+
} else {
|
157
|
+
this.open = false;
|
175
158
|
}
|
176
159
|
};
|
177
|
-
|
178
|
-
private handleAfterShow = () => {
|
179
|
-
this.emit('nile-after-show');
|
180
|
-
};
|
181
|
-
|
182
160
|
|
183
161
|
private hideTooltip = () => {
|
184
|
-
if (!this.open) return;
|
185
|
-
|
186
162
|
this.emit('nile-hide');
|
187
163
|
this.open = false;
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
private handleAfterHide = () => {
|
193
|
-
this.emit('nile-after-hide');
|
164
|
+
setTimeout(() => {
|
165
|
+
this.emit('nile-after-hide');
|
166
|
+
}, 200);
|
194
167
|
};
|
195
|
-
|
168
|
+
|
196
169
|
private handleMouseOver = () => {
|
197
170
|
if (this.trigger.includes('hover')) {
|
198
171
|
clearTimeout(this.hoverTimeout);
|
@@ -209,22 +182,13 @@ export class NileTooltip extends NileElement {
|
|
209
182
|
|
210
183
|
private handleClick = () => {
|
211
184
|
if (this.trigger.includes('click')) {
|
212
|
-
this.open
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
};
|
217
|
-
|
218
|
-
updated(changedProperties: Map<string, any>) {
|
219
|
-
super.updated(changedProperties);
|
220
|
-
|
221
|
-
|
222
|
-
if (changedProperties.has('open') || changedProperties.has('hoist')) {
|
223
|
-
if (this.open && this.hoist) {
|
224
|
-
|
185
|
+
if (!this.open && isInViewport(this.triggerContainer)) {
|
186
|
+
this.showTooltip();
|
187
|
+
} else {
|
188
|
+
this.hideTooltip();
|
225
189
|
}
|
226
190
|
}
|
227
|
-
}
|
191
|
+
};
|
228
192
|
|
229
193
|
private handleFocus = () => {
|
230
194
|
if (this.trigger.includes('focus')) {
|
@@ -240,17 +204,32 @@ export class NileTooltip extends NileElement {
|
|
240
204
|
|
241
205
|
render() {
|
242
206
|
return html`
|
243
|
-
<div
|
244
|
-
|
207
|
+
<div
|
208
|
+
class=${classMap({
|
209
|
+
tooltip: true,
|
210
|
+
'tooltip__body--large': this.size === 'large',
|
211
|
+
})}
|
212
|
+
id="tooltip"
|
213
|
+
>
|
214
|
+
<div class="tooltip-content" part="content">
|
215
|
+
<slot name="content" @slotchange=${this.handleTooltipSlotChange}></slot>
|
216
|
+
${!this.hasTooltipSlot ? html`${this.content}` : null}
|
217
|
+
</div>
|
218
|
+
<div class="tooltip-caret" style="--caret-size: ${this.caretSize}px;"></div>
|
245
219
|
</div>
|
246
|
-
|
220
|
+
|
221
|
+
<div
|
222
|
+
class="trigger-container"
|
223
|
+
tabindex="0"
|
247
224
|
@mouseover=${this.handleMouseOver}
|
248
225
|
@mouseout=${this.handleMouseOut}
|
249
226
|
@click=${this.handleClick}
|
250
|
-
@
|
251
|
-
@
|
227
|
+
@focusin=${this.handleFocus}
|
228
|
+
@focusout=${this.handleBlur}
|
252
229
|
aria-describedby="tooltip"
|
253
|
-
|
230
|
+
>
|
231
|
+
<slot></slot>
|
232
|
+
</div>
|
254
233
|
`;
|
255
234
|
}
|
256
235
|
}
|
@@ -259,4 +238,5 @@ declare global {
|
|
259
238
|
interface HTMLElementTagNameMap {
|
260
239
|
'nile-tooltip': NileTooltip;
|
261
240
|
}
|
262
|
-
}
|
241
|
+
}
|
242
|
+
|