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 +``` ----------------------------------------------