diff --git a/angular/projects/catalyst/src/lib/directives/proxies.ts b/angular/projects/catalyst/src/lib/directives/proxies.ts
index 4ccea2c5b..9b6c93644 100644
--- a/angular/projects/catalyst/src/lib/directives/proxies.ts
+++ b/angular/projects/catalyst/src/lib/directives/proxies.ts
@@ -1791,14 +1791,34 @@ export declare interface CatToggle extends Components.CatToggle {
}
@ProxyCmp({
- inputs: ['content', 'disabled', 'hideDelay', 'longTouchDuration', 'placement', 'round', 'showDelay', 'size']
+ inputs: [
+ 'content',
+ 'disabled',
+ 'hideDelay',
+ 'longTouchDuration',
+ 'placement',
+ 'round',
+ 'showDelay',
+ 'size',
+ 'trigger'
+ ]
})
@Component({
selector: 'cat-tooltip',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['content', 'disabled', 'hideDelay', 'longTouchDuration', 'placement', 'round', 'showDelay', 'size'],
+ inputs: [
+ 'content',
+ 'disabled',
+ 'hideDelay',
+ 'longTouchDuration',
+ 'placement',
+ 'round',
+ 'showDelay',
+ 'size',
+ 'trigger'
+ ],
standalone: false
})
export class CatTooltip {
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index 07946a816..c7c49227e 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -2179,6 +2179,11 @@ export namespace Components {
* @default 'm'
*/
"size": 's' | 'm' | 'l';
+ /**
+ * The trigger event(s) that show and hide the tooltip. - 'hover-focus': show on both mouse hover and keyboard focus (default) - 'hover': show on mouse hover only - 'focus': show on keyboard focus only
+ * @default 'hover-focus'
+ */
+ "trigger": 'hover' | 'focus' | 'hover-focus';
}
}
export interface CatButtonCustomEvent extends CustomEvent {
@@ -5080,6 +5085,11 @@ declare namespace LocalJSX {
* @default 'm'
*/
"size"?: 's' | 'm' | 'l';
+ /**
+ * The trigger event(s) that show and hide the tooltip. - 'hover-focus': show on both mouse hover and keyboard focus (default) - 'hover': show on mouse hover only - 'focus': show on keyboard focus only
+ * @default 'hover-focus'
+ */
+ "trigger"?: 'hover' | 'focus' | 'hover-focus';
}
interface CatAlertAttributes {
@@ -5513,6 +5523,7 @@ declare namespace LocalJSX {
"showDelay": number;
"hideDelay": number;
"longTouchDuration": number;
+ "trigger": 'hover' | 'focus' | 'hover-focus';
}
interface IntrinsicElements {
diff --git a/core/src/components/cat-select-demo/readme.md b/core/src/components/cat-select-demo/readme.md
index 17a5cbab6..d5dff87f2 100644
--- a/core/src/components/cat-select-demo/readme.md
+++ b/core/src/components/cat-select-demo/readme.md
@@ -19,6 +19,7 @@ graph TD;
cat-select-demo --> cat-select
cat-select-demo --> cat-dropdown
cat-select-demo --> cat-button
+ cat-select --> cat-tooltip
cat-select --> cat-avatar
cat-select --> cat-button
cat-select --> cat-spinner
diff --git a/core/src/components/cat-select/cat-select.spec.tsx b/core/src/components/cat-select/cat-select.spec.tsx
index 30b96da39..fafdc515b 100644
--- a/core/src/components/cat-select/cat-select.spec.tsx
+++ b/core/src/components/cat-select/cat-select.spec.tsx
@@ -240,4 +240,167 @@ describe('cat-select', () => {
expect(updatedOptions[0].render.description).toBe('Updated description');
});
});
+
+ describe('truncation tooltips', () => {
+ describe('option label truncation', () => {
+ it('should mark an option as truncated when scrollHeight exceeds clientHeight', async () => {
+ const { root, waitForChanges, instance } = await render();
+ await instance.connect(stringArrayConnector(['option1', 'option2']));
+
+ const trigger = root?.shadowRoot?.querySelector('.select-wrapper');
+ trigger?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ await waitForChanges();
+
+ const labelEl = root?.shadowRoot?.querySelector('.select-option-label[data-option-key="option1"]');
+ if (labelEl) {
+ Object.defineProperty(labelEl, 'scrollHeight', { value: 50, configurable: true });
+ Object.defineProperty(labelEl, 'clientHeight', { value: 24, configurable: true });
+ }
+
+ instance['checkOptionsTruncation']();
+ await waitForChanges();
+
+ expect(instance['truncatedOptionKeys'].has('option1')).toBe(true);
+ expect(instance['truncatedOptionKeys'].has('option2')).toBe(false);
+ });
+
+ it('should not mark an option as truncated when scrollHeight equals clientHeight', async () => {
+ const { root, waitForChanges, instance } = await render();
+ await instance.connect(stringArrayConnector(['option1']));
+
+ const trigger = root?.shadowRoot?.querySelector('.select-wrapper');
+ trigger?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ await waitForChanges();
+
+ const labelEl = root?.shadowRoot?.querySelector('.select-option-label[data-option-key="option1"]');
+ if (labelEl) {
+ Object.defineProperty(labelEl, 'scrollHeight', { value: 24, configurable: true });
+ Object.defineProperty(labelEl, 'clientHeight', { value: 24, configurable: true });
+ }
+
+ instance['checkOptionsTruncation']();
+ await waitForChanges();
+
+ expect(instance['truncatedOptionKeys'].has('option1')).toBe(false);
+ });
+
+ it('should clear truncatedOptionKeys when dropdown closes', async () => {
+ const { root, waitForChanges, instance } = await render();
+ await instance.connect(stringArrayConnector(['option1']));
+
+ instance['truncatedOptionKeys'] = new Set(['option1']);
+
+ const trigger = root?.shadowRoot?.querySelector('.select-wrapper');
+ trigger?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ await waitForChanges();
+ trigger?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ await waitForChanges();
+
+ expect(instance['truncatedOptionKeys'].size).toBe(0);
+ });
+ });
+
+ describe('pill label truncation', () => {
+ it('should mark a pill as truncated when scrollWidth exceeds clientWidth', async () => {
+ const { root, waitForChanges, instance } = await render();
+ await instance.connect(stringArrayConnector(['option1', 'option2']));
+
+ instance['patchState']({
+ selection: [
+ { item: { id: 'option1' }, render: { label: 'option1' } },
+ { item: { id: 'option2' }, render: { label: 'option2' } }
+ ],
+ tempSelection: []
+ });
+ await waitForChanges();
+
+ const pillSpan = root?.shadowRoot?.querySelector('span[data-pill-index="0"]');
+ if (pillSpan) {
+ Object.defineProperty(pillSpan, 'scrollWidth', { value: 100, configurable: true });
+ Object.defineProperty(pillSpan, 'clientWidth', { value: 60, configurable: true });
+ }
+
+ instance['checkInputTruncation']();
+ await waitForChanges();
+
+ expect(instance['truncatedPillIndices'].has(0)).toBe(true);
+ expect(instance['truncatedPillIndices'].has(1)).toBe(false);
+ });
+
+ it('should not mark a pill as truncated when scrollWidth equals clientWidth', async () => {
+ const { root, waitForChanges, instance } = await render();
+ await instance.connect(stringArrayConnector(['option1']));
+
+ instance['patchState']({
+ selection: [{ item: { id: 'option1' }, render: { label: 'option1' } }],
+ tempSelection: []
+ });
+ await waitForChanges();
+
+ const pillSpan = root?.shadowRoot?.querySelector('span[data-pill-index="0"]');
+ if (pillSpan) {
+ Object.defineProperty(pillSpan, 'scrollWidth', { value: 60, configurable: true });
+ Object.defineProperty(pillSpan, 'clientWidth', { value: 60, configurable: true });
+ }
+
+ instance['checkInputTruncation']();
+ await waitForChanges();
+
+ expect(instance['truncatedPillIndices'].has(0)).toBe(false);
+ });
+ });
+
+ describe('single-select input truncation', () => {
+ it('should mark input as truncated when scrollWidth exceeds clientWidth', async () => {
+ const { root, waitForChanges, instance } = await render();
+ await instance.connect(stringArrayConnector(['option1']));
+ await waitForChanges();
+
+ const input = root?.shadowRoot?.querySelector('input.select-input');
+ if (input) {
+ Object.defineProperty(input, 'scrollWidth', { value: 200, configurable: true });
+ Object.defineProperty(input, 'clientWidth', { value: 80, configurable: true });
+ }
+
+ instance['checkInputTruncation']();
+ await waitForChanges();
+
+ expect(instance['singleValueTruncated']).toBe(true);
+ });
+
+ it('should not mark input as truncated when scrollWidth equals clientWidth', async () => {
+ const { root, waitForChanges, instance } = await render();
+ await instance.connect(stringArrayConnector(['option1']));
+ await waitForChanges();
+
+ const input = root?.shadowRoot?.querySelector('input.select-input');
+ if (input) {
+ Object.defineProperty(input, 'scrollWidth', { value: 80, configurable: true });
+ Object.defineProperty(input, 'clientWidth', { value: 80, configurable: true });
+ }
+
+ instance['checkInputTruncation']();
+ await waitForChanges();
+
+ expect(instance['singleValueTruncated']).toBe(false);
+ });
+
+ it('should not set singleValueTruncated in multiple mode', async () => {
+ const { root, waitForChanges, instance } = await render();
+ await instance.connect(stringArrayConnector(['option1']));
+ await waitForChanges();
+
+ const input = root?.shadowRoot?.querySelector('input.select-input');
+ if (input) {
+ Object.defineProperty(input, 'scrollWidth', { value: 200, configurable: true });
+ Object.defineProperty(input, 'clientWidth', { value: 80, configurable: true });
+ }
+
+ instance['checkInputTruncation']();
+ await waitForChanges();
+
+ expect(instance['singleValueTruncated']).toBe(false);
+ });
+ });
+ });
});
diff --git a/core/src/components/cat-select/cat-select.tsx b/core/src/components/cat-select/cat-select.tsx
index 173aadbe0..19f231887 100644
--- a/core/src/components/cat-select/cat-select.tsx
+++ b/core/src/components/cat-select/cat-select.tsx
@@ -137,6 +137,9 @@ export class CatSelect {
private more$: Subject = new Subject();
private valueChangedBySelection = false;
private cleanupFloatingUi?: () => void;
+ private needsOptionsTruncationCheck = false;
+ private needsInputTruncationCheck = false;
+ private resizeObserver?: ResizeObserver;
@Element() hostElement!: HTMLElement;
@@ -150,6 +153,12 @@ export class CatSelect {
@State() errorMap?: ErrorMap | true;
+ @State() truncatedOptionKeys = new Set();
+
+ @State() truncatedPillIndices = new Set();
+
+ @State() singleValueTruncated = false;
+
/**
* Whether the label need a marker to shown if the select is required or optional.
*/
@@ -316,6 +325,12 @@ export class CatSelect {
const changed = (key: keyof CatSelectState) => newState[key] !== oldState[key];
if (changed('isOpen')) {
this.update();
+ if (!newState.isOpen) {
+ this.truncatedOptionKeys = new Set();
+ }
+ }
+ if (changed('options') && newState.isOpen) {
+ this.needsOptionsTruncationCheck = true;
}
if (changed('activeOptionIndex') && this.state.activeOptionIndex >= 0) {
this.dropdown
@@ -376,10 +391,29 @@ export class CatSelect {
*/
@Event() catBlur!: EventEmitter;
+ componentDidRender(): void {
+ if (this.needsOptionsTruncationCheck) {
+ this.needsOptionsTruncationCheck = false;
+ requestAnimationFrame(() => this.checkOptionsTruncation());
+ }
+ if (this.needsInputTruncationCheck) {
+ this.needsInputTruncationCheck = false;
+ requestAnimationFrame(() => this.checkInputTruncation());
+ }
+ }
+
componentDidLoad(): void {
if (this.input) {
autosizeInput(this.input, { minWidth: true });
}
+ requestAnimationFrame(() => this.checkInputTruncation());
+ this.resizeObserver = new ResizeObserver(() => {
+ this.checkInputTruncation();
+ if (this.state.isOpen) {
+ this.checkOptionsTruncation();
+ }
+ });
+ this.resizeObserver.observe(this.hostElement);
}
componentWillLoad(): void {
@@ -391,6 +425,10 @@ export class CatSelect {
this.hasSlottedHint = !!this.hostElement.querySelector('[slot="hint"]');
}
+ disconnectedCallback(): void {
+ this.resizeObserver?.disconnect();
+ }
+
@Listen('blur')
onBlur(event: FocusEvent): void {
if (!this.multiple && this.state.activeOptionIndex >= 0) {
@@ -659,39 +697,45 @@ export class CatSelect {
class="select-pills"
>
{this.state.selection.map((item, i) => (
-
- {item.render.avatar ? (
-
- ) : null}
- {item.render.label}
- {!this.disabled && (
- this.deselect(item.item.id)}
- tabIndex={-1}
- data-dropdown-no-close
- >
- )}
-
+
+ {item.render.avatar ? (
+
+ ) : null}
+ {item.render.label}
+ {!this.disabled && (
+ this.deselect(item.item.id)}
+ tabIndex={-1}
+ data-dropdown-no-close
+ >
+ )}
+
+
))}
) : this.state.selection.length && this.state.selection[0].render.avatar ? (
@@ -703,26 +747,33 @@ export class CatSelect {
initials={this.state.selection[0].render.avatar.initials ?? ''}
>
) : null}
- (this.input = el)}
- autocomplete={this.autoComplete}
- data-1p-ignore={this.autoComplete === 'off' ? '' : undefined}
- aria-controls={this.isPillboxActive() ? `select-pillbox-${this.id}` : `select-listbox-${this.id}`}
- aria-activedescendant={this.activeDescendant}
- aria-invalid={this.invalid ? 'true' : undefined}
- aria-describedby={this.hasHint ? this.id + '-hint' : undefined}
- aria-autocomplete="list"
- onInput={this.onInput.bind(this)}
- value={!this.multiple ? this.state.term : undefined}
- placeholder={this.placeholder}
- disabled={this.disabled || this.state.isResolving}
- >
+
+ (this.input = el)}
+ autocomplete={this.autoComplete}
+ data-1p-ignore={this.autoComplete === 'off' ? '' : undefined}
+ aria-controls={this.isPillboxActive() ? `select-pillbox-${this.id}` : `select-listbox-${this.id}`}
+ aria-activedescendant={this.activeDescendant}
+ aria-invalid={this.invalid ? 'true' : undefined}
+ aria-describedby={this.hasHint ? this.id + '-hint' : undefined}
+ aria-autocomplete="list"
+ onInput={this.onInput.bind(this)}
+ value={!this.multiple ? this.state.term : undefined}
+ placeholder={this.placeholder}
+ disabled={this.disabled || this.state.isResolving}
+ >
+
{this.state.isResolving && }
{this.invalid && (
@@ -832,12 +883,14 @@ export class CatSelect {
return item.render.label;
};
+ // key in element is a stencil dom directive, it prevents stale checked state when options reload
return (
{this.multiple ? (
) : null}
- {getLabel()}
+
+
+ {getLabel()}
+
+
{item.render.description}
@@ -888,7 +949,15 @@ export class CatSelect {
>
) : null}
- {getLabel()}
+
+
+ {getLabel()}
+
+
{item.render.description}
@@ -927,6 +996,7 @@ export class CatSelect {
this.patchState({ isResolving: false, selection, term, activeOptionIndex: -1 });
this.term$.next(term);
this.input && (this.input.value = term);
+ this.needsInputTruncationCheck = true;
});
}
@@ -989,6 +1059,7 @@ export class CatSelect {
}
}
this.setTransparentCaret();
+ this.needsInputTruncationCheck = true;
}
private deselect(id: string) {
@@ -998,6 +1069,7 @@ export class CatSelect {
activeSelectionIndex: -1
});
}
+ this.needsInputTruncationCheck = true;
}
private rerenderOptions() {
@@ -1089,6 +1161,29 @@ export class CatSelect {
this.state = { ...this.state, ...update };
}
+ private checkOptionsTruncation(): void {
+ const keys = new Set();
+ this.dropdown?.querySelectorAll('.select-option-label[data-option-key]').forEach(el => {
+ if (el.scrollHeight > el.clientHeight) {
+ keys.add(el.dataset.optionKey!);
+ }
+ });
+ this.truncatedOptionKeys = keys;
+ }
+
+ private checkInputTruncation(): void {
+ const indices = new Set();
+ this.trigger?.querySelectorAll('span[data-pill-index]').forEach(el => {
+ if (el.scrollWidth > el.clientWidth) {
+ indices.add(Number(el.dataset.pillIndex));
+ }
+ });
+ this.truncatedPillIndices = indices;
+ if (!this.multiple && this.input) {
+ this.singleValueTruncated = this.input.scrollWidth > this.input.clientWidth;
+ }
+ }
+
private isPillboxActive() {
return this.state.activeSelectionIndex >= 0;
}
diff --git a/core/src/components/cat-select/readme.md b/core/src/components/cat-select/readme.md
index 8fe5921f8..3633e97aa 100644
--- a/core/src/components/cat-select/readme.md
+++ b/core/src/components/cat-select/readme.md
@@ -131,6 +131,7 @@ Type: `Promise`
### Depends on
+- [cat-tooltip](../cat-tooltip)
- [cat-avatar](../cat-avatar)
- [cat-button](../cat-button)
- [cat-spinner](../cat-spinner)
@@ -142,6 +143,7 @@ Type: `Promise`
### Graph
```mermaid
graph TD;
+ cat-select --> cat-tooltip
cat-select --> cat-avatar
cat-select --> cat-button
cat-select --> cat-spinner
diff --git a/core/src/components/cat-tooltip/cat-tooltip.spec.tsx b/core/src/components/cat-tooltip/cat-tooltip.spec.tsx
index 1bfe18df0..a6acd772e 100644
--- a/core/src/components/cat-tooltip/cat-tooltip.spec.tsx
+++ b/core/src/components/cat-tooltip/cat-tooltip.spec.tsx
@@ -1,9 +1,22 @@
-import { describe, it, expect } from 'vitest';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render } from '@stencil/vitest';
import { h } from '@stencil/core';
+
+vi.mock('@floating-ui/dom', () => ({
+ autoUpdate: vi.fn(() => vi.fn()),
+ computePosition: vi.fn(() => Promise.resolve({ x: 0, y: 0 })),
+ flip: vi.fn(() => ({})),
+ offset: vi.fn(() => ({})),
+ shift: vi.fn(() => ({}))
+}));
+
import './cat-tooltip';
describe('cat-tooltip', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
it('renders', async () => {
const { root } = await render(
@@ -18,4 +31,86 @@ describe('cat-tooltip', () => {
`);
});
+
+ describe('trigger prop', () => {
+ const waitForTimer = () => new Promise(resolve => setTimeout(resolve, 10));
+
+ describe('default (hover-focus)', () => {
+ it('should show on mouseenter', async () => {
+ const { root, waitForChanges, instance } = await render(
+
+ Trigger
+
+ );
+ root.querySelector('p')!.dispatchEvent(new MouseEvent('mouseenter'));
+ await waitForTimer();
+ await waitForChanges();
+ expect(instance.open).toBe(true);
+ });
+
+ it('should show on focusin', async () => {
+ const { root, waitForChanges, instance } = await render(
+
+ Trigger
+
+ );
+ root.querySelector('p')!.dispatchEvent(new FocusEvent('focusin'));
+ await waitForTimer();
+ await waitForChanges();
+ expect(instance.open).toBe(true);
+ });
+ });
+
+ describe('trigger="hover"', () => {
+ it('should show on mouseenter', async () => {
+ const { root, waitForChanges, instance } = await render(
+
+ Trigger
+
+ );
+ root.querySelector('p')!.dispatchEvent(new MouseEvent('mouseenter'));
+ await waitForTimer();
+ await waitForChanges();
+ expect(instance.open).toBe(true);
+ });
+
+ it('should not show on focusin', async () => {
+ const { root, waitForChanges, instance } = await render(
+
+ Trigger
+
+ );
+ root.querySelector('p')!.dispatchEvent(new FocusEvent('focusin'));
+ await waitForTimer();
+ await waitForChanges();
+ expect(instance.open).toBe(false);
+ });
+ });
+
+ describe('trigger="focus"', () => {
+ it('should show on focusin', async () => {
+ const { root, waitForChanges, instance } = await render(
+
+ Trigger
+
+ );
+ root.querySelector('p')!.dispatchEvent(new FocusEvent('focusin'));
+ await waitForTimer();
+ await waitForChanges();
+ expect(instance.open).toBe(true);
+ });
+
+ it('should not show on mouseenter', async () => {
+ const { root, waitForChanges, instance } = await render(
+
+ Trigger
+
+ );
+ root.querySelector('p')!.dispatchEvent(new MouseEvent('mouseenter'));
+ await waitForTimer();
+ await waitForChanges();
+ expect(instance.open).toBe(false);
+ });
+ });
+ });
});
diff --git a/core/src/components/cat-tooltip/cat-tooltip.tsx b/core/src/components/cat-tooltip/cat-tooltip.tsx
index 9247b2e87..a25e4274e 100644
--- a/core/src/components/cat-tooltip/cat-tooltip.tsx
+++ b/core/src/components/cat-tooltip/cat-tooltip.tsx
@@ -22,7 +22,7 @@ export class CatTooltip {
private static readonly SHIFT_PADDING = 4;
private readonly id = `cat-tooltip-${nextUniqueId++}`;
private tooltip?: HTMLElement;
- private trigger?: Element;
+ private triggerEl?: Element;
private showTimeout?: number;
private hideTimeout?: number;
private touchTimeout?: number;
@@ -88,6 +88,14 @@ export class CatTooltip {
*/
@Prop() longTouchDuration = 1000;
+ /**
+ * The trigger event(s) that show and hide the tooltip.
+ * - 'hover-focus': show on both mouse hover and keyboard focus (default)
+ * - 'hover': show on mouse hover only
+ * - 'focus': show on keyboard focus only
+ */
+ @Prop() trigger: 'hover' | 'focus' | 'hover-focus' = 'hover-focus';
+
@Listen('keydown', { target: 'window' })
handleKeyDown({ key }: KeyboardEvent) {
key === 'Escape' && this.hideTooltip();
@@ -95,9 +103,9 @@ export class CatTooltip {
componentDidLoad(): void {
const slot = this.hostElement.shadowRoot?.querySelector('slot');
- this.trigger = slot?.assignedElements?.()?.[0];
- if (this.trigger && !this.trigger.hasAttribute('aria-describedby')) {
- this.trigger.setAttribute('aria-describedby', this.id);
+ this.triggerEl = slot?.assignedElements?.()?.[0];
+ if (this.triggerEl && !this.triggerEl.hasAttribute('aria-describedby')) {
+ this.triggerEl.setAttribute('aria-describedby', this.id);
}
this.addListeners();
@@ -143,34 +151,38 @@ export class CatTooltip {
}
private addListeners() {
- this.trigger?.addEventListener('focusin', this.boundShowListener);
- this.trigger?.addEventListener('focusout', this.boundHideListener);
- this.trigger?.addEventListener('mouseenter', this.boundShowListener);
- this.trigger?.addEventListener('mouseleave', this.boundHideListener);
+ if (this.trigger !== 'hover') {
+ this.triggerEl?.addEventListener('focusin', this.boundShowListener);
+ this.triggerEl?.addEventListener('focusout', this.boundHideListener);
+ }
+ if (this.trigger !== 'focus') {
+ this.triggerEl?.addEventListener('mouseenter', this.boundShowListener);
+ this.triggerEl?.addEventListener('mouseleave', this.boundHideListener);
+ }
if (isTouchScreen) {
window.addEventListener('touchstart', this.boundWindowTouchStartListener);
- this.trigger?.addEventListener('touchstart', this.boundTouchStartListener);
- this.trigger?.addEventListener('touchend', this.boundTouchEndListener);
+ this.triggerEl?.addEventListener('touchstart', this.boundTouchStartListener);
+ this.triggerEl?.addEventListener('touchend', this.boundTouchEndListener);
}
}
private removeListeners() {
- this.trigger?.removeEventListener('mouseenter', this.boundShowListener);
- this.trigger?.removeEventListener('mouseleave', this.boundHideListener);
- this.trigger?.removeEventListener('focusin', this.boundShowListener);
- this.trigger?.removeEventListener('focusout', this.boundHideListener);
+ this.triggerEl?.removeEventListener('focusin', this.boundShowListener);
+ this.triggerEl?.removeEventListener('focusout', this.boundHideListener);
+ this.triggerEl?.removeEventListener('mouseenter', this.boundShowListener);
+ this.triggerEl?.removeEventListener('mouseleave', this.boundHideListener);
if (isTouchScreen) {
window.removeEventListener('touchstart', this.boundWindowTouchStartListener);
- this.trigger?.removeEventListener('touchstart', this.boundTouchStartListener);
- this.trigger?.removeEventListener('touchend', this.boundTouchEndListener);
+ this.triggerEl?.removeEventListener('touchstart', this.boundTouchStartListener);
+ this.triggerEl?.removeEventListener('touchend', this.boundTouchEndListener);
}
}
private async update() {
- if (this.trigger && this.tooltip) {
- await computePosition(this.trigger, this.tooltip, {
+ if (this.triggerEl && this.tooltip) {
+ await computePosition(this.triggerEl, this.tooltip, {
strategy: 'fixed',
placement: this.placement,
middleware: [
@@ -233,8 +245,8 @@ export class CatTooltip {
private showTooltip() {
if (!this.inactive) {
- if (this.trigger && this.tooltip) {
- this.cleanupFloatingUi = autoUpdate(this.trigger, this.tooltip, () => this.update());
+ if (this.triggerEl && this.tooltip) {
+ this.cleanupFloatingUi = autoUpdate(this.triggerEl, this.tooltip, () => this.update());
}
this.open = true;
this.tooltip?.classList.add('tooltip-show');
diff --git a/core/src/components/cat-tooltip/readme.md b/core/src/components/cat-tooltip/readme.md
index 0b84ce632..732857cf0 100644
--- a/core/src/components/cat-tooltip/readme.md
+++ b/core/src/components/cat-tooltip/readme.md
@@ -13,17 +13,31 @@ different placements, sizes, and styles.
## Properties
-| Property | Attribute | Description | Type | Default |
-| ------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
-| `content` | `content` | The content of the tooltip. | `string` | `''` |
-| `disabled` | `disabled` | Specifies that the tooltip should be disabled. A disabled tooltip is unusable, and invisible. Corresponds with the native HTML disabled attribute. | `boolean` | `false` |
-| `hideDelay` | `hide-delay` | The delay time for hiding tooltip in ms. | `number` | `0` |
-| `longTouchDuration` | `long-touch-duration` | The duration of tap to show the tooltip. | `number` | `1000` |
-| `placement` | `placement` | The placement of the tooltip. | `"bottom" \| "bottom-end" \| "bottom-start" \| "left" \| "left-end" \| "left-start" \| "right" \| "right-end" \| "right-start" \| "top" \| "top-end" \| "top-start"` | `'top'` |
-| `round` | `round` | Use round tooltip edges. | `boolean` | `false` |
-| `showDelay` | `show-delay` | The delay time for showing tooltip in ms. | `number` | `250` |
-| `size` | `size` | The size of the tooltip. | `"l" \| "m" \| "s"` | `'m'` |
-
+| Property | Attribute | Description | Type | Default |
+| ------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
+| `content` | `content` | The content of the tooltip. | `string` | `''` |
+| `disabled` | `disabled` | Specifies that the tooltip should be disabled. A disabled tooltip is unusable, and invisible. Corresponds with the native HTML disabled attribute. | `boolean` | `false` |
+| `hideDelay` | `hide-delay` | The delay time for hiding tooltip in ms. | `number` | `0` |
+| `longTouchDuration` | `long-touch-duration` | The duration of tap to show the tooltip. | `number` | `1000` |
+| `placement` | `placement` | The placement of the tooltip. | `"bottom" \| "bottom-end" \| "bottom-start" \| "left" \| "left-end" \| "left-start" \| "right" \| "right-end" \| "right-start" \| "top" \| "top-end" \| "top-start"` | `'top'` |
+| `round` | `round` | Use round tooltip edges. | `boolean` | `false` |
+| `showDelay` | `show-delay` | The delay time for showing tooltip in ms. | `number` | `250` |
+| `size` | `size` | The size of the tooltip. | `"l" \| "m" \| "s"` | `'m'` |
+| `trigger` | `trigger` | The trigger event(s) that show and hide the tooltip. - 'hover-focus': show on both mouse hover and keyboard focus (default) - 'hover': show on mouse hover only - 'focus': show on keyboard focus only | `"focus" \| "hover" \| "hover-focus"` | `'hover-focus'` |
+
+
+## Dependencies
+
+### Used by
+
+ - [cat-select](../cat-select)
+
+### Graph
+```mermaid
+graph TD;
+ cat-select --> cat-tooltip
+ style cat-tooltip fill:#f9f,stroke:#333,stroke-width:4px
+```
----------------------------------------------