@spectrum-web-components/textfield 1.12.0-testing.20260223092154 → 1.12.0
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 +18 -0
- package/custom-elements.json +256 -0
- package/package.json +9 -7
- package/src/Textfield.d.ts +11 -5
- package/src/Textfield.dev.js +29 -3
- package/src/Textfield.dev.js.map +2 -2
- package/src/Textfield.js +27 -26
- package/src/Textfield.js.map +3 -3
- package/src/TruncatedValueTooltipController.d.ts +90 -0
- package/src/TruncatedValueTooltipController.dev.js +163 -0
- package/src/TruncatedValueTooltipController.dev.js.map +7 -0
- package/src/TruncatedValueTooltipController.js +19 -0
- package/src/TruncatedValueTooltipController.js.map +7 -0
- package/src/spectrum-textfield.css.dev.js +1 -1
- package/src/spectrum-textfield.css.dev.js.map +1 -1
- package/src/spectrum-textfield.css.js +1 -1
- package/src/spectrum-textfield.css.js.map +1 -1
- package/src/textfield.css.dev.js +1 -1
- package/src/textfield.css.dev.js.map +1 -1
- package/src/textfield.css.js +1 -1
- package/src/textfield.css.js.map +1 -1
package/src/Textfield.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
"use strict";var c=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var t=(
|
|
1
|
+
"use strict";var c=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var t=(o,u,e,l)=>{for(var r=l>1?void 0:l?m(u,e):u,s=o.length-1,d;s>=0;s--)(d=o[s])&&(r=(l?d(u,e,r):d(r))||r);return l&&r&&c(u,e,r),r};import{html as a,nothing as p,SizedMixin as v}from"@spectrum-web-components/base";import{property as i,query as y,state as h}from"@spectrum-web-components/base/src/decorators.js";import{ifDefined as n,live as b}from"@spectrum-web-components/base/src/directives.js";import{ManageHelpText as g}from"@spectrum-web-components/help-text/src/manage-help-text.js";import f from"@spectrum-web-components/icon/src/spectrum-icon-checkmark.css.js";import{Focusable as $}from"@spectrum-web-components/shared/src/focusable.js";import"@spectrum-web-components/icons-ui/icons/sp-icon-checkmark100.js";import"@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js";import E from"./textfield.css.js";import{TruncatedValueTooltipController as S}from"./TruncatedValueTooltipController.js";export{TruncatedValueTooltipController,truncatedValueTooltipUpdatedSymbol}from"./TruncatedValueTooltipController.js";const T=["text","url","tel","email","password"];export class TextfieldBase extends g(v($,{noDefaultSize:!0})){constructor(){super(...arguments);this.truncatedValueTooltipController=new S(this);this.allowedKeys="";this.focused=!1;this.invalid=!1;this.label="";this.placeholder="";this._type="text";this.grows=!1;this.maxlength=-1;this.minlength=-1;this.multiline=!1;this.readonly=!1;this.truncatedValueTooltipPlacement="bottom";this.rows=-1;this.valid=!1;this._value="";this.quiet=!1;this.required=!1}static get styles(){return[E,f]}set type(e){const l=this._type;this._type=e,this.requestUpdate("type",l)}get type(){var e;return(e=T.find(l=>l===this._type))!=null?e:"text"}set value(e){if(e===this.value)return;const l=this._value;this._value=e,this.requestUpdate("value",l)}get value(){return this._value}get focusElement(){return this.inputElement}setSelectionRange(e,l,r="none"){this.inputElement.setSelectionRange(e,l,r)}select(){this.inputElement.select()}handleInput(e){if(this.allowedKeys&&this.inputElement.value&&!new RegExp(`^[${this.allowedKeys}]*$`,"u").test(this.inputElement.value)){const s=this.inputElement.selectionStart-1;this.inputElement.value=this.value.toString(),this.inputElement.setSelectionRange(s,s);return}this.value=this.inputElement.value}handleChange(){this.dispatchEvent(new Event("change",{bubbles:!0,composed:!0}))}onFocus(){this.focused=!this.readonly&&!0}onBlur(e){this.focused=!this.readonly&&!1}handleInputElementPointerdown(){}renderStateIcons(){return this.invalid?a`
|
|
2
2
|
<sp-icon-alert id="invalid" class="icon"></sp-icon-alert>
|
|
3
|
-
`:this.valid?
|
|
3
|
+
`:this.valid?a`
|
|
4
4
|
<sp-icon-checkmark100
|
|
5
5
|
id="valid"
|
|
6
6
|
class="icon spectrum-UIIcon-Checkmark100"
|
|
7
7
|
></sp-icon-checkmark100>
|
|
8
|
-
`:
|
|
9
|
-
${this.multiline&&this.grows&&this.rows===-1?
|
|
8
|
+
`:p}get displayValue(){return this.value.toString()}get renderMultiline(){return a`
|
|
9
|
+
${this.multiline&&this.grows&&this.rows===-1?a`
|
|
10
10
|
<div id="sizer" class="input" aria-hidden="true">${this.value}​
|
|
11
11
|
</div>
|
|
12
|
-
`:
|
|
12
|
+
`:p}
|
|
13
13
|
<!-- @ts-ignore -->
|
|
14
14
|
<textarea
|
|
15
|
-
name=${
|
|
15
|
+
name=${n(this.name||void 0)}
|
|
16
16
|
aria-describedby=${this.helpTextId}
|
|
17
|
-
aria-label=${this.label||this.appliedLabel||this.placeholder}
|
|
18
|
-
aria-invalid=${
|
|
17
|
+
aria-label=${n(this.label||this.appliedLabel||this.placeholder||void 0)}
|
|
18
|
+
aria-invalid=${n(this.invalid||void 0)}
|
|
19
19
|
class="input"
|
|
20
|
-
maxlength=${
|
|
21
|
-
minlength=${
|
|
22
|
-
title=${this.invalid?"":
|
|
23
|
-
pattern=${
|
|
20
|
+
maxlength=${n(this.maxlength>-1?this.maxlength:void 0)}
|
|
21
|
+
minlength=${n(this.minlength>-1?this.minlength:void 0)}
|
|
22
|
+
title=${this.invalid?"":p}
|
|
23
|
+
pattern=${n(this.pattern)}
|
|
24
24
|
placeholder=${this.placeholder}
|
|
25
25
|
.value=${this.displayValue}
|
|
26
26
|
@change=${this.handleChange}
|
|
@@ -30,22 +30,22 @@
|
|
|
30
30
|
?disabled=${this.disabled}
|
|
31
31
|
?required=${this.required}
|
|
32
32
|
?readonly=${this.readonly}
|
|
33
|
-
rows=${
|
|
34
|
-
autocomplete=${
|
|
33
|
+
rows=${n(this.rows>-1?this.rows:void 0)}
|
|
34
|
+
autocomplete=${n(this.autocomplete)}
|
|
35
35
|
></textarea>
|
|
36
|
-
`}get renderInput(){return
|
|
36
|
+
`}get renderInput(){return a`
|
|
37
37
|
<!-- @ts-ignore -->
|
|
38
38
|
<input
|
|
39
|
-
name=${
|
|
39
|
+
name=${n(this.name||void 0)}
|
|
40
40
|
type=${this.type}
|
|
41
41
|
aria-describedby=${this.helpTextId}
|
|
42
|
-
aria-label=${this.label||this.appliedLabel||this.placeholder}
|
|
43
|
-
aria-invalid=${
|
|
42
|
+
aria-label=${n(this.label||this.appliedLabel||this.placeholder||void 0)}
|
|
43
|
+
aria-invalid=${n(this.invalid||void 0)}
|
|
44
44
|
class="input"
|
|
45
|
-
title=${this.invalid?"":
|
|
46
|
-
maxlength=${
|
|
47
|
-
minlength=${
|
|
48
|
-
pattern=${
|
|
45
|
+
title=${this.invalid?"":p}
|
|
46
|
+
maxlength=${n(this.maxlength>-1?this.maxlength:void 0)}
|
|
47
|
+
minlength=${n(this.minlength>-1?this.minlength:void 0)}
|
|
48
|
+
pattern=${n(this.pattern)}
|
|
49
49
|
placeholder=${this.placeholder}
|
|
50
50
|
.value=${b(this.displayValue)}
|
|
51
51
|
@change=${this.handleChange}
|
|
@@ -56,13 +56,14 @@
|
|
|
56
56
|
?disabled=${this.disabled}
|
|
57
57
|
?required=${this.required}
|
|
58
58
|
?readonly=${this.readonly}
|
|
59
|
-
autocomplete=${
|
|
59
|
+
autocomplete=${n(this.autocomplete)}
|
|
60
60
|
/>
|
|
61
|
-
`}renderField(){return
|
|
61
|
+
`}renderField(){return a`
|
|
62
62
|
${this.renderStateIcons()}
|
|
63
63
|
${this.multiline?this.renderMultiline:this.renderInput}
|
|
64
|
-
`}render(){return
|
|
64
|
+
`}render(){return a`
|
|
65
65
|
<div id="textfield">${this.renderField()}</div>
|
|
66
|
+
${this.truncatedValueTooltipController.render()}
|
|
66
67
|
${this.renderHelpText(this.invalid)}
|
|
67
|
-
`}update(e){(e.has("value")||e.has("required")&&this.required)&&this.updateComplete.then(()=>{this.checkValidity()}),super.update(e)}checkValidity(){let e=this.inputElement.checkValidity();return(this.required||this.value&&this.pattern)&&((this.disabled||this.multiline)&&this.pattern&&(e=new RegExp(`^${this.pattern}$`,"u").test(this.value.toString())),typeof this.minlength!="undefined"&&(e=e&&this.value.toString().length>=this.minlength),this.valid=e,this.invalid=!e),e}}t([
|
|
68
|
+
`}update(e){(e.has("value")||e.has("required")&&this.required)&&this.updateComplete.then(()=>{this.checkValidity(),e.has("value")&&this.truncatedValueTooltipController.refresh()}),super.update(e),e.has("focused")&&!this.focused&&this.truncatedValueTooltipController.refresh()}checkValidity(){let e=this.inputElement.checkValidity();return(this.required||this.value&&this.pattern)&&((this.disabled||this.multiline)&&this.pattern&&(e=new RegExp(`^${this.pattern}$`,"u").test(this.value.toString())),typeof this.minlength!="undefined"&&(e=e&&this.value.toString().length>=this.minlength),this.valid=e,this.invalid=!e),e}}t([h()],TextfieldBase.prototype,"appliedLabel",2),t([i({attribute:"allowed-keys"})],TextfieldBase.prototype,"allowedKeys",2),t([i({type:Boolean,reflect:!0})],TextfieldBase.prototype,"focused",2),t([y(".input:not(#sizer)")],TextfieldBase.prototype,"inputElement",2),t([i({type:Boolean,reflect:!0})],TextfieldBase.prototype,"invalid",2),t([i()],TextfieldBase.prototype,"label",2),t([i({type:String,reflect:!0})],TextfieldBase.prototype,"name",2),t([i()],TextfieldBase.prototype,"placeholder",2),t([h()],TextfieldBase.prototype,"type",1),t([i({attribute:"type",reflect:!0})],TextfieldBase.prototype,"_type",2),t([i()],TextfieldBase.prototype,"pattern",2),t([i({type:Boolean,reflect:!0})],TextfieldBase.prototype,"grows",2),t([i({type:Number})],TextfieldBase.prototype,"maxlength",2),t([i({type:Number})],TextfieldBase.prototype,"minlength",2),t([i({type:Boolean,reflect:!0})],TextfieldBase.prototype,"multiline",2),t([i({type:Boolean,reflect:!0})],TextfieldBase.prototype,"readonly",2),t([i({attribute:"tooltip-placement"})],TextfieldBase.prototype,"truncatedValueTooltipPlacement",2),t([i({type:Number})],TextfieldBase.prototype,"rows",2),t([i({type:Boolean,reflect:!0})],TextfieldBase.prototype,"valid",2),t([i({type:String})],TextfieldBase.prototype,"value",1),t([i({type:Boolean,reflect:!0})],TextfieldBase.prototype,"quiet",2),t([i({type:Boolean,reflect:!0})],TextfieldBase.prototype,"required",2),t([i({type:String,reflect:!0})],TextfieldBase.prototype,"autocomplete",2);export class Textfield extends TextfieldBase{constructor(){super(...arguments);this._value=""}set value(e){if(e===this.value)return;const l=this._value;this._value=e,this.requestUpdate("value",l)}get value(){return this._value}}t([i({type:String})],Textfield.prototype,"value",1);
|
|
68
69
|
//# sourceMappingURL=Textfield.js.map
|
package/src/Textfield.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["Textfield.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {\n CSSResultArray,\n html,\n nothing,\n PropertyValues,\n SizedMixin,\n TemplateResult,\n} from '@spectrum-web-components/base';\nimport {\n property,\n query,\n state,\n} from '@spectrum-web-components/base/src/decorators.js';\nimport {\n ifDefined,\n live,\n} from '@spectrum-web-components/base/src/directives.js';\nimport { ManageHelpText } from '@spectrum-web-components/help-text/src/manage-help-text.js';\nimport checkmarkStyles from '@spectrum-web-components/icon/src/spectrum-icon-checkmark.css.js';\nimport { Focusable } from '@spectrum-web-components/shared/src/focusable.js';\n\nimport '@spectrum-web-components/icons-ui/icons/sp-icon-checkmark100.js';\nimport '@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js';\n\nimport textfieldStyles from './textfield.css.js';\n\nconst textfieldTypes = ['text', 'url', 'tel', 'email', 'password'] as const;\nexport type TextfieldType = (typeof textfieldTypes)[number];\n\n/**\n * @fires input - The value of the element has changed.\n * @fires change - An alteration to the value of the element has been committed by the user.\n */\nexport class TextfieldBase extends ManageHelpText(\n SizedMixin(Focusable, {\n noDefaultSize: true,\n })\n) {\n public static override get styles(): CSSResultArray {\n return [textfieldStyles, checkmarkStyles];\n }\n\n @state()\n protected appliedLabel?: string;\n\n /**\n * A regular expression outlining the keys that will be allowed to update the value of the form control.\n */\n @property({ attribute: 'allowed-keys' })\n allowedKeys = '';\n\n /**\n * @private\n */\n @property({ type: Boolean, reflect: true })\n public focused = false;\n\n @query('.input:not(#sizer)')\n protected inputElement!: HTMLInputElement | HTMLTextAreaElement;\n\n /**\n * Whether the `value` held by the form control is invalid.\n */\n @property({ type: Boolean, reflect: true })\n public invalid = false;\n\n /**\n * A string applied via `aria-label` to the form control when a user visible label is not provided.\n */\n @property()\n public label = '';\n\n /**\n * Name of the form control.\n */\n @property({ type: String, reflect: true })\n public name: string | undefined;\n\n /**\n * Text that appears in the form control when it has no value set\n */\n @property()\n public placeholder = '';\n\n @state()\n set type(val: TextfieldType) {\n const prev = this._type;\n this._type = val;\n this.requestUpdate('type', prev);\n }\n\n get type(): TextfieldType {\n return textfieldTypes.find((t) => t === this._type) ?? 'text';\n }\n\n /**\n * @private\n * This binding allows for invalid value for `type` to still be reflected to the DOM\n */\n @property({ attribute: 'type', reflect: true })\n private _type: TextfieldType = 'text';\n\n /**\n * Pattern the `value` must match to be valid\n */\n @property()\n public pattern?: string;\n\n /**\n * Whether a form control delivered with the `multiline` attribute will change size\n * vertically to accomodate longer input\n */\n @property({ type: Boolean, reflect: true })\n public grows = false;\n\n /**\n * Defines the maximum string length that the user can enter\n */\n @property({ type: Number })\n public maxlength = -1;\n\n /**\n * Defines the minimum string length that the user can enter\n */\n @property({ type: Number })\n public minlength = -1;\n\n /**\n * Whether the form control should accept a value longer than one line\n */\n @property({ type: Boolean, reflect: true })\n public multiline = false;\n\n /**\n * Whether a user can interact with the value of the form control\n */\n @property({ type: Boolean, reflect: true })\n public readonly = false;\n\n /**\n * The specific number of rows the form control should provide in the user interface\n */\n @property({ type: Number })\n public rows = -1;\n\n /**\n * Whether the `value` held by the form control is valid.\n */\n @property({ type: Boolean, reflect: true })\n public valid = false;\n\n /**\n * The value held by the form control\n */\n @property({ type: String })\n public set value(value: string | number) {\n if (value === this.value) {\n return;\n }\n const oldValue = this._value;\n this._value = value;\n this.requestUpdate('value', oldValue);\n }\n\n public get value(): string | number {\n return this._value;\n }\n\n protected _value: string | number = '';\n\n /**\n * Whether to display the form control with no visible background\n */\n @property({ type: Boolean, reflect: true })\n public quiet = false;\n\n /**\n * Whether the form control will be found to be invalid when it holds no `value`\n */\n @property({ type: Boolean, reflect: true })\n public required = false;\n\n /**\n * What form of assistance should be provided when attempting to supply a value to the form control\n *\n * Note: combobox overrides `autocomplete` intentionally with `aria-autocomplete` values, which is\n * why those values (although invalid for native `autocomplete`) are included here to support the\n * combobox accessibility pattern.\n */\n @property({ type: String, reflect: true })\n public autocomplete?:\n | 'list'\n | 'none'\n | HTMLInputElement['autocomplete']\n | HTMLTextAreaElement['autocomplete'];\n\n public override get focusElement(): HTMLInputElement | HTMLTextAreaElement {\n return this.inputElement;\n }\n\n /**\n * Sets the start and end positions of the current selection.\n *\n * @param selectionStart The 0-based index of the first selected character. An index greater than the length of the\n * element's value is treated as pointing to the end of the value.\n * @param selectionEnd The 0-based index of the character after the last selected character. An index greater than\n * the length of the element's value is treated as pointing to the end of the value.\n * @param [selectionDirection=\"none\"] A string indicating the direction in which the selection is considered to\n * have been performed.\n */\n public setSelectionRange(\n selectionStart: number,\n selectionEnd: number,\n selectionDirection: 'forward' | 'backward' | 'none' = 'none'\n ): void {\n this.inputElement.setSelectionRange(\n selectionStart,\n selectionEnd,\n selectionDirection\n );\n }\n\n /**\n * Selects all the text.\n */\n public select(): void {\n this.inputElement.select();\n }\n\n protected handleInput(_event: Event): void {\n if (this.allowedKeys && this.inputElement.value) {\n const regExp = new RegExp(`^[${this.allowedKeys}]*$`, 'u');\n if (!regExp.test(this.inputElement.value)) {\n const selectionStart = this.inputElement.selectionStart as number;\n const nextSelectStart = selectionStart - 1;\n this.inputElement.value = this.value.toString();\n this.inputElement.setSelectionRange(nextSelectStart, nextSelectStart);\n return;\n }\n }\n this.value = this.inputElement.value;\n }\n\n protected handleChange(): void {\n this.dispatchEvent(\n new Event('change', {\n bubbles: true,\n composed: true,\n })\n );\n }\n\n protected onFocus(): void {\n this.focused = !this.readonly && true;\n }\n\n protected onBlur(_event: FocusEvent): void {\n this.focused = !this.readonly && false;\n }\n\n protected handleInputElementPointerdown(): void {}\n\n protected renderStateIcons(): TemplateResult | typeof nothing {\n if (this.invalid) {\n return html`\n <sp-icon-alert id=\"invalid\" class=\"icon\"></sp-icon-alert>\n `;\n } else if (this.valid) {\n return html`\n <sp-icon-checkmark100\n id=\"valid\"\n class=\"icon spectrum-UIIcon-Checkmark100\"\n ></sp-icon-checkmark100>\n `;\n }\n return nothing;\n }\n\n protected get displayValue(): string {\n return this.value.toString();\n }\n\n // prettier-ignore\n private get renderMultiline(): TemplateResult {\n return html`\n ${this.multiline && this.grows && this.rows === -1\n ? html`\n <div id=\"sizer\" class=\"input\" aria-hidden=\"true\">${this.value}​\n </div>\n `\n : nothing}\n <!-- @ts-ignore -->\n <textarea\n name=${ifDefined(this.name || undefined)}\n aria-describedby=${this.helpTextId}\n aria-label=${this.label ||\n this.appliedLabel ||\n this.placeholder}\n aria-invalid=${ifDefined(this.invalid || undefined)}\n class=\"input\"\n maxlength=${ifDefined(\n this.maxlength > -1 ? this.maxlength : undefined\n )}\n minlength=${ifDefined(\n this.minlength > -1 ? this.minlength : undefined\n )}\n title=${this.invalid ? '' : nothing}\n pattern=${ifDefined(this.pattern)}\n placeholder=${this.placeholder}\n .value=${this.displayValue}\n @change=${this.handleChange}\n @input=${this.handleInput}\n @focus=${this.onFocus}\n @blur=${this.onBlur}\n ?disabled=${this.disabled}\n ?required=${this.required}\n ?readonly=${this.readonly}\n rows=${ifDefined(this.rows > -1 ? this.rows : undefined)}\n autocomplete=${ifDefined(this.autocomplete)}\n ></textarea>\n `;\n }\n\n private get renderInput(): TemplateResult {\n return html`\n <!-- @ts-ignore -->\n <input\n name=${ifDefined(this.name || undefined)}\n type=${this.type}\n aria-describedby=${this.helpTextId}\n aria-label=${this.label || this.appliedLabel || this.placeholder}\n aria-invalid=${ifDefined(this.invalid || undefined)}\n class=\"input\"\n title=${this.invalid ? '' : nothing}\n maxlength=${ifDefined(this.maxlength > -1 ? this.maxlength : undefined)}\n minlength=${ifDefined(this.minlength > -1 ? this.minlength : undefined)}\n pattern=${ifDefined(this.pattern)}\n placeholder=${this.placeholder}\n .value=${live(this.displayValue)}\n @change=${this.handleChange}\n @input=${this.handleInput}\n @pointerdown=${this.handleInputElementPointerdown}\n @focus=${this.onFocus}\n @blur=${this.onBlur}\n ?disabled=${this.disabled}\n ?required=${this.required}\n ?readonly=${this.readonly}\n autocomplete=${ifDefined(this.autocomplete)}\n />\n `;\n }\n\n protected renderField(): TemplateResult {\n return html`\n ${this.renderStateIcons()}\n ${this.multiline ? this.renderMultiline : this.renderInput}\n `;\n }\n\n protected override render(): TemplateResult {\n return html`\n <div id=\"textfield\">${this.renderField()}</div>\n ${this.renderHelpText(this.invalid)}\n `;\n }\n\n protected override update(changedProperties: PropertyValues): void {\n if (\n changedProperties.has('value') ||\n (changedProperties.has('required') && this.required)\n ) {\n this.updateComplete.then(() => {\n this.checkValidity();\n });\n }\n super.update(changedProperties);\n }\n\n public checkValidity(): boolean {\n let validity = this.inputElement.checkValidity();\n if (this.required || (this.value && this.pattern)) {\n if ((this.disabled || this.multiline) && this.pattern) {\n const regex = new RegExp(`^${this.pattern}$`, 'u');\n validity = regex.test(this.value.toString());\n }\n if (typeof this.minlength !== 'undefined') {\n validity = validity && this.value.toString().length >= this.minlength;\n }\n this.valid = validity;\n this.invalid = !validity;\n }\n return validity;\n }\n}\n\n/**\n * @element sp-textfield\n * @slot help-text - default or non-negative help text to associate to your form element\n * @slot negative-help-text - negative help text to associate to your form element when `invalid`\n */\nexport class Textfield extends TextfieldBase {\n @property({ type: String })\n public override set value(value: string) {\n if (value === this.value) {\n return;\n }\n const oldValue = this._value;\n this._value = value;\n this.requestUpdate('value', oldValue);\n }\n\n public override get value(): string {\n return this._value;\n }\n\n protected override _value = '';\n}\n"],
|
|
5
|
-
"mappings": "qNAYA,OAEE,QAAAA,EACA,WAAAC,EAEA,cAAAC,MAEK,gCACP,OACE,YAAAC,EACA,SAAAC,EACA,SAAAC,MACK,kDACP,OACE,aAAAC,EACA,QAAAC,MACK,kDACP,OAAS,kBAAAC,MAAsB,6DAC/B,OAAOC,MAAqB,
|
|
6
|
-
"names": ["html", "nothing", "SizedMixin", "property", "query", "state", "ifDefined", "live", "ManageHelpText", "checkmarkStyles", "Focusable", "textfieldStyles", "textfieldTypes", "val", "prev", "_a", "t", "value", "oldValue", "selectionStart", "selectionEnd", "selectionDirection", "_event", "nextSelectStart", "changedProperties", "validity", "__decorateClass"]
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {\n CSSResultArray,\n html,\n nothing,\n PropertyValues,\n SizedMixin,\n TemplateResult,\n} from '@spectrum-web-components/base';\nimport {\n property,\n query,\n state,\n} from '@spectrum-web-components/base/src/decorators.js';\nimport {\n ifDefined,\n live,\n} from '@spectrum-web-components/base/src/directives.js';\nimport { ManageHelpText } from '@spectrum-web-components/help-text/src/manage-help-text.js';\nimport checkmarkStyles from '@spectrum-web-components/icon/src/spectrum-icon-checkmark.css.js';\nimport type { Placement } from '@spectrum-web-components/overlay';\nimport { Focusable } from '@spectrum-web-components/shared/src/focusable.js';\n\nimport '@spectrum-web-components/icons-ui/icons/sp-icon-checkmark100.js';\nimport '@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js';\n\nimport textfieldStyles from './textfield.css.js';\nimport {\n TruncatedValueTooltipController,\n type TruncatedValueTooltipHost,\n} from './TruncatedValueTooltipController.js';\n\nexport type { TruncatedValueTooltipHost } from './TruncatedValueTooltipController.js';\nexport {\n TruncatedValueTooltipController,\n truncatedValueTooltipUpdatedSymbol,\n} from './TruncatedValueTooltipController.js';\n\nconst textfieldTypes = ['text', 'url', 'tel', 'email', 'password'] as const;\nexport type TextfieldType = (typeof textfieldTypes)[number];\n\n/**\n * @fires input - The value of the element has changed.\n * @fires change - An alteration to the value of the element has been committed by the user.\n */\nexport class TextfieldBase extends ManageHelpText(\n SizedMixin(Focusable, {\n noDefaultSize: true,\n })\n) {\n protected truncatedValueTooltipController =\n new TruncatedValueTooltipController(\n this as unknown as TruncatedValueTooltipHost & typeof this\n );\n public static override get styles(): CSSResultArray {\n return [textfieldStyles, checkmarkStyles];\n }\n\n @state()\n protected appliedLabel?: string;\n\n /**\n * A regular expression outlining the keys that will be allowed to update the value of the form control.\n */\n @property({ attribute: 'allowed-keys' })\n allowedKeys = '';\n\n /**\n * @private\n */\n @property({ type: Boolean, reflect: true })\n public focused = false;\n\n @query('.input:not(#sizer)')\n protected inputElement!: HTMLInputElement | HTMLTextAreaElement;\n\n /**\n * Whether the `value` held by the form control is invalid.\n */\n @property({ type: Boolean, reflect: true })\n public invalid = false;\n\n /**\n * A string applied via `aria-label` to the form control when a user visible label is not provided.\n */\n @property()\n public label = '';\n\n /**\n * Name of the form control.\n */\n @property({ type: String, reflect: true })\n public name: string | undefined;\n\n /**\n * Text that appears in the form control when it has no value set\n */\n @property()\n public placeholder = '';\n\n @state()\n set type(val: TextfieldType) {\n const prev = this._type;\n this._type = val;\n this.requestUpdate('type', prev);\n }\n\n get type(): TextfieldType {\n return textfieldTypes.find((t) => t === this._type) ?? 'text';\n }\n\n /**\n * @private\n * This binding allows for invalid value for `type` to still be reflected to the DOM\n */\n @property({ attribute: 'type', reflect: true })\n private _type: TextfieldType = 'text';\n\n /**\n * Pattern the `value` must match to be valid\n */\n @property()\n public pattern?: string;\n\n /**\n * Whether a form control delivered with the `multiline` attribute will change size\n * vertically to accomodate longer input\n */\n @property({ type: Boolean, reflect: true })\n public grows = false;\n\n /**\n * Defines the maximum string length that the user can enter\n */\n @property({ type: Number })\n public maxlength = -1;\n\n /**\n * Defines the minimum string length that the user can enter\n */\n @property({ type: Number })\n public minlength = -1;\n\n /**\n * Whether the form control should accept a value longer than one line\n */\n @property({ type: Boolean, reflect: true })\n public multiline = false;\n\n /**\n * Whether a user can interact with the value of the form control\n */\n @property({ type: Boolean, reflect: true })\n public readonly = false;\n\n /**\n * Placement of the tooltip shown when the value is truncated (e.g. 'bottom', 'top').\n * Defaults to 'bottom' per Spectrum design.\n */\n @property({ attribute: 'tooltip-placement' })\n public truncatedValueTooltipPlacement: Placement = 'bottom';\n\n /**\n * The specific number of rows the form control should provide in the user interface\n */\n @property({ type: Number })\n public rows = -1;\n\n /**\n * Whether the `value` held by the form control is valid.\n */\n @property({ type: Boolean, reflect: true })\n public valid = false;\n\n /**\n * The value held by the form control\n */\n @property({ type: String })\n public set value(value: string | number) {\n if (value === this.value) {\n return;\n }\n const oldValue = this._value;\n this._value = value;\n this.requestUpdate('value', oldValue);\n }\n\n public get value(): string | number {\n return this._value;\n }\n\n protected _value: string | number = '';\n\n /**\n * Whether to display the form control with no visible background\n */\n @property({ type: Boolean, reflect: true })\n public quiet = false;\n\n /**\n * Whether the form control will be found to be invalid when it holds no `value`\n */\n @property({ type: Boolean, reflect: true })\n public required = false;\n\n /**\n * What form of assistance should be provided when attempting to supply a value to the form control\n *\n * Note: combobox overrides `autocomplete` intentionally with `aria-autocomplete` values, which is\n * why those values (although invalid for native `autocomplete`) are included here to support the\n * combobox accessibility pattern.\n */\n @property({ type: String, reflect: true })\n public autocomplete?:\n | 'list'\n | 'none'\n | HTMLInputElement['autocomplete']\n | HTMLTextAreaElement['autocomplete'];\n\n public override get focusElement(): HTMLInputElement | HTMLTextAreaElement {\n return this.inputElement;\n }\n\n /**\n * Sets the start and end positions of the current selection.\n *\n * @param selectionStart The 0-based index of the first selected character. An index greater than the length of the\n * element's value is treated as pointing to the end of the value.\n * @param selectionEnd The 0-based index of the character after the last selected character. An index greater than\n * the length of the element's value is treated as pointing to the end of the value.\n * @param [selectionDirection=\"none\"] A string indicating the direction in which the selection is considered to\n * have been performed.\n */\n public setSelectionRange(\n selectionStart: number,\n selectionEnd: number,\n selectionDirection: 'forward' | 'backward' | 'none' = 'none'\n ): void {\n this.inputElement.setSelectionRange(\n selectionStart,\n selectionEnd,\n selectionDirection\n );\n }\n\n /**\n * Selects all the text.\n */\n public select(): void {\n this.inputElement.select();\n }\n\n protected handleInput(_event: Event): void {\n if (this.allowedKeys && this.inputElement.value) {\n const regExp = new RegExp(`^[${this.allowedKeys}]*$`, 'u');\n if (!regExp.test(this.inputElement.value)) {\n const selectionStart = this.inputElement.selectionStart as number;\n const nextSelectStart = selectionStart - 1;\n this.inputElement.value = this.value.toString();\n this.inputElement.setSelectionRange(nextSelectStart, nextSelectStart);\n return;\n }\n }\n this.value = this.inputElement.value;\n }\n\n protected handleChange(): void {\n this.dispatchEvent(\n new Event('change', {\n bubbles: true,\n composed: true,\n })\n );\n }\n\n protected onFocus(): void {\n this.focused = !this.readonly && true;\n }\n\n protected onBlur(_event: FocusEvent): void {\n this.focused = !this.readonly && false;\n }\n\n protected handleInputElementPointerdown(): void {}\n\n protected renderStateIcons(): TemplateResult | typeof nothing {\n if (this.invalid) {\n return html`\n <sp-icon-alert id=\"invalid\" class=\"icon\"></sp-icon-alert>\n `;\n } else if (this.valid) {\n return html`\n <sp-icon-checkmark100\n id=\"valid\"\n class=\"icon spectrum-UIIcon-Checkmark100\"\n ></sp-icon-checkmark100>\n `;\n }\n return nothing;\n }\n\n protected get displayValue(): string {\n return this.value.toString();\n }\n\n // prettier-ignore\n private get renderMultiline(): TemplateResult {\n return html`\n ${this.multiline && this.grows && this.rows === -1\n ? html`\n <div id=\"sizer\" class=\"input\" aria-hidden=\"true\">${this.value}​\n </div>\n `\n : nothing}\n <!-- @ts-ignore -->\n <textarea\n name=${ifDefined(this.name || undefined)}\n aria-describedby=${this.helpTextId}\n aria-label=${ifDefined(\n this.label || this.appliedLabel || this.placeholder || undefined\n )}\n aria-invalid=${ifDefined(this.invalid || undefined)}\n class=\"input\"\n maxlength=${ifDefined(\n this.maxlength > -1 ? this.maxlength : undefined\n )}\n minlength=${ifDefined(\n this.minlength > -1 ? this.minlength : undefined\n )}\n title=${this.invalid ? '' : nothing}\n pattern=${ifDefined(this.pattern)}\n placeholder=${this.placeholder}\n .value=${this.displayValue}\n @change=${this.handleChange}\n @input=${this.handleInput}\n @focus=${this.onFocus}\n @blur=${this.onBlur}\n ?disabled=${this.disabled}\n ?required=${this.required}\n ?readonly=${this.readonly}\n rows=${ifDefined(this.rows > -1 ? this.rows : undefined)}\n autocomplete=${ifDefined(this.autocomplete)}\n ></textarea>\n `;\n }\n\n private get renderInput(): TemplateResult {\n return html`\n <!-- @ts-ignore -->\n <input\n name=${ifDefined(this.name || undefined)}\n type=${this.type}\n aria-describedby=${this.helpTextId}\n aria-label=${ifDefined(\n this.label || this.appliedLabel || this.placeholder || undefined\n )}\n aria-invalid=${ifDefined(this.invalid || undefined)}\n class=\"input\"\n title=${this.invalid ? '' : nothing}\n maxlength=${ifDefined(this.maxlength > -1 ? this.maxlength : undefined)}\n minlength=${ifDefined(this.minlength > -1 ? this.minlength : undefined)}\n pattern=${ifDefined(this.pattern)}\n placeholder=${this.placeholder}\n .value=${live(this.displayValue)}\n @change=${this.handleChange}\n @input=${this.handleInput}\n @pointerdown=${this.handleInputElementPointerdown}\n @focus=${this.onFocus}\n @blur=${this.onBlur}\n ?disabled=${this.disabled}\n ?required=${this.required}\n ?readonly=${this.readonly}\n autocomplete=${ifDefined(this.autocomplete)}\n />\n `;\n }\n\n protected renderField(): TemplateResult {\n return html`\n ${this.renderStateIcons()}\n ${this.multiline ? this.renderMultiline : this.renderInput}\n `;\n }\n\n protected override render(): TemplateResult {\n return html`\n <div id=\"textfield\">${this.renderField()}</div>\n ${this.truncatedValueTooltipController.render()}\n ${this.renderHelpText(this.invalid)}\n `;\n }\n\n protected override update(changedProperties: PropertyValues): void {\n const valueOrRequiredChanged =\n changedProperties.has('value') ||\n (changedProperties.has('required') && this.required);\n\n if (valueOrRequiredChanged) {\n this.updateComplete.then(() => {\n this.checkValidity();\n if (changedProperties.has('value')) {\n // Truncation tooltip uses an intentional multi-phase update: (1) first render puts value in\n // DOM, (2) after updateComplete we measure and set truncation state, (3) controller defers\n // requestUpdate to a microtask so a second render shows the tooltip. We cannot show the\n // tooltip in the first render because we must measure after layout. This can cause a brief\n // delay before the tooltip appears; that is expected.\n this.truncatedValueTooltipController.refresh();\n }\n });\n }\n\n super.update(changedProperties);\n\n if (changedProperties.has('focused') && !this.focused) {\n this.truncatedValueTooltipController.refresh();\n }\n }\n\n public checkValidity(): boolean {\n let validity = this.inputElement.checkValidity();\n if (this.required || (this.value && this.pattern)) {\n if ((this.disabled || this.multiline) && this.pattern) {\n const regex = new RegExp(`^${this.pattern}$`, 'u');\n validity = regex.test(this.value.toString());\n }\n if (typeof this.minlength !== 'undefined') {\n validity = validity && this.value.toString().length >= this.minlength;\n }\n this.valid = validity;\n this.invalid = !validity;\n }\n return validity;\n }\n}\n\n/**\n * @element sp-textfield\n * @slot help-text - default or non-negative help text to associate to your form element\n * @slot negative-help-text - negative help text to associate to your form element when `invalid`\n */\nexport class Textfield extends TextfieldBase {\n @property({ type: String })\n public override set value(value: string) {\n if (value === this.value) {\n return;\n }\n const oldValue = this._value;\n this._value = value;\n this.requestUpdate('value', oldValue);\n }\n\n public override get value(): string {\n return this._value;\n }\n\n protected override _value = '';\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["html", "nothing", "SizedMixin", "property", "query", "state", "ifDefined", "live", "ManageHelpText", "checkmarkStyles", "Focusable", "textfieldStyles", "TruncatedValueTooltipController", "textfieldTypes", "val", "prev", "_a", "t", "value", "oldValue", "selectionStart", "selectionEnd", "selectionDirection", "_event", "nextSelectStart", "changedProperties", "validity", "__decorateClass"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations in the License.
|
|
11
|
+
*/
|
|
12
|
+
import { nothing, type ReactiveController, type ReactiveElement, type TemplateResult } from '@spectrum-web-components/base';
|
|
13
|
+
import type { Placement } from '@spectrum-web-components/overlay';
|
|
14
|
+
/**
|
|
15
|
+
* Symbol used to request a host re-render when truncation state or tooltip deps change.
|
|
16
|
+
* The host can use this in its update lifecycle to react if needed; the important part
|
|
17
|
+
* is that requestUpdate(symbol, previous) triggers a re-render so controller.render() runs again.
|
|
18
|
+
*/
|
|
19
|
+
export declare const truncatedValueTooltipUpdatedSymbol: unique symbol;
|
|
20
|
+
/**
|
|
21
|
+
* Host interface for TruncatedValueTooltipController. The host must provide these
|
|
22
|
+
* properties (e.g. TextfieldBase). The controller reads them to determine truncation
|
|
23
|
+
* and to render the tooltip content and placement.
|
|
24
|
+
*/
|
|
25
|
+
export interface TruncatedValueTooltipHost {
|
|
26
|
+
inputElement: HTMLInputElement | HTMLTextAreaElement;
|
|
27
|
+
multiline: boolean;
|
|
28
|
+
type: string;
|
|
29
|
+
disabled: boolean;
|
|
30
|
+
focused?: boolean;
|
|
31
|
+
displayValue: string;
|
|
32
|
+
truncatedValueTooltipPlacement: Placement;
|
|
33
|
+
}
|
|
34
|
+
type HostElement = ReactiveElement & TruncatedValueTooltipHost;
|
|
35
|
+
/**
|
|
36
|
+
* A reactive controller that adds truncated-value tooltip behavior: when the visible
|
|
37
|
+
* value is clipped, a tooltip with the full value is shown on hover/focus. Overlay
|
|
38
|
+
* and tooltip are lazy-loaded only when truncation is first detected.
|
|
39
|
+
*
|
|
40
|
+
* The host must implement TruncatedValueTooltipHost (e.g. TextfieldBase). The host
|
|
41
|
+
* includes the controller's render output in its template and may call refresh() when
|
|
42
|
+
* value or focused changes; NumberField also calls syncTooltipText() from handleInput()
|
|
43
|
+
* so the tooltip stays in sync with the live input without re-renders.
|
|
44
|
+
*/
|
|
45
|
+
export declare class TruncatedValueTooltipController implements ReactiveController {
|
|
46
|
+
private host;
|
|
47
|
+
private _isTruncated;
|
|
48
|
+
private _tooltipDepsLoaded;
|
|
49
|
+
private _resizeObserver;
|
|
50
|
+
private _observerInitialized;
|
|
51
|
+
constructor(host: HostElement);
|
|
52
|
+
private get inputElementIsTruncated();
|
|
53
|
+
/**
|
|
54
|
+
* Updates truncation state. Returns true if we just transitioned to truncated
|
|
55
|
+
* (so the host can e.g. schedule syncTooltipText after updateComplete).
|
|
56
|
+
*/
|
|
57
|
+
private refreshTruncationState;
|
|
58
|
+
/**
|
|
59
|
+
* Public API for hosts (e.g. NumberField) to force a re-check of truncation state.
|
|
60
|
+
* Call from handleInput() or when value/focused changes so the tooltip visibility stays in sync.
|
|
61
|
+
* Returns true if we just became truncated (host may schedule syncTooltipText after updateComplete).
|
|
62
|
+
*/
|
|
63
|
+
refresh(): boolean;
|
|
64
|
+
private ensureTooltipDeps;
|
|
65
|
+
/**
|
|
66
|
+
* Returns the tooltip overlay template when truncated; otherwise nothing.
|
|
67
|
+
* The host includes this in its render() (e.g. this.truncatedValueTooltipController.render()).
|
|
68
|
+
*/
|
|
69
|
+
render(): TemplateResult | typeof nothing;
|
|
70
|
+
/**
|
|
71
|
+
* Updates the tooltip label text without requestUpdate. Used by NumberField from
|
|
72
|
+
* handleInput() so the tooltip shows the current input value while typing without
|
|
73
|
+
* triggering re-renders that would affect formatting or selection.
|
|
74
|
+
*
|
|
75
|
+
* We mutate an existing text node under `<sp-tooltip>` instead of assigning
|
|
76
|
+
* `tooltip.textContent`. Clearing `textContent` on the host removes all light-DOM
|
|
77
|
+
* children, which ejects Lit's marker nodes for `${host.displayValue}` in
|
|
78
|
+
* `render()` and causes "ChildPart has no parentNode" on the next update.
|
|
79
|
+
*
|
|
80
|
+
* Trade-off: this couples to our own light-DOM shape: `render()` must keep a
|
|
81
|
+
* direct text child of `<sp-tooltip>` (or update this lookup if we wrap the label
|
|
82
|
+
* in an element, e.g. a dedicated `<span id="…">`). This does not depend on
|
|
83
|
+
* `sp-tooltip`'s internal shadow DOM.
|
|
84
|
+
*/
|
|
85
|
+
syncTooltipText(text: string): void;
|
|
86
|
+
hostConnected(): void;
|
|
87
|
+
hostUpdated(): void;
|
|
88
|
+
hostDisconnected(): void;
|
|
89
|
+
}
|
|
90
|
+
export {};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import {
|
|
3
|
+
html,
|
|
4
|
+
nothing
|
|
5
|
+
} from "@spectrum-web-components/base";
|
|
6
|
+
export const truncatedValueTooltipUpdatedSymbol = Symbol(
|
|
7
|
+
"truncated value tooltip updated"
|
|
8
|
+
);
|
|
9
|
+
export class TruncatedValueTooltipController {
|
|
10
|
+
constructor(host) {
|
|
11
|
+
this._isTruncated = false;
|
|
12
|
+
this._tooltipDepsLoaded = false;
|
|
13
|
+
this._resizeObserver = null;
|
|
14
|
+
this._observerInitialized = false;
|
|
15
|
+
this.host = host;
|
|
16
|
+
this.host.addController(this);
|
|
17
|
+
}
|
|
18
|
+
get inputElementIsTruncated() {
|
|
19
|
+
const host = this.host;
|
|
20
|
+
if (!host.inputElement || host.multiline || host.type === "password") {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return host.inputElement.scrollWidth > host.inputElement.clientWidth + 1;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Updates truncation state. Returns true if we just transitioned to truncated
|
|
27
|
+
* (so the host can e.g. schedule syncTooltipText after updateComplete).
|
|
28
|
+
*/
|
|
29
|
+
refreshTruncationState() {
|
|
30
|
+
const host = this.host;
|
|
31
|
+
const currentlyTruncated = this.inputElementIsTruncated;
|
|
32
|
+
if (host.focused && this._isTruncated && !currentlyTruncated) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (currentlyTruncated === this._isTruncated) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const previous = this._isTruncated;
|
|
39
|
+
this._isTruncated = currentlyTruncated;
|
|
40
|
+
Promise.resolve().then(() => {
|
|
41
|
+
this.host.requestUpdate(truncatedValueTooltipUpdatedSymbol, previous);
|
|
42
|
+
});
|
|
43
|
+
return currentlyTruncated;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Public API for hosts (e.g. NumberField) to force a re-check of truncation state.
|
|
47
|
+
* Call from handleInput() or when value/focused changes so the tooltip visibility stays in sync.
|
|
48
|
+
* Returns true if we just became truncated (host may schedule syncTooltipText after updateComplete).
|
|
49
|
+
*/
|
|
50
|
+
refresh() {
|
|
51
|
+
return this.refreshTruncationState();
|
|
52
|
+
}
|
|
53
|
+
async ensureTooltipDeps() {
|
|
54
|
+
if (this._tooltipDepsLoaded) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await Promise.all([
|
|
58
|
+
import("@spectrum-web-components/overlay/sp-overlay.js"),
|
|
59
|
+
import("@spectrum-web-components/tooltip/sp-tooltip.js")
|
|
60
|
+
]);
|
|
61
|
+
this._tooltipDepsLoaded = true;
|
|
62
|
+
Promise.resolve().then(() => {
|
|
63
|
+
this.host.requestUpdate(truncatedValueTooltipUpdatedSymbol, false);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns the tooltip overlay template when truncated; otherwise nothing.
|
|
68
|
+
* The host includes this in its render() (e.g. this.truncatedValueTooltipController.render()).
|
|
69
|
+
*/
|
|
70
|
+
render() {
|
|
71
|
+
const host = this.host;
|
|
72
|
+
if (!this._isTruncated || host.disabled || !host.inputElement || host.type === "password") {
|
|
73
|
+
return nothing;
|
|
74
|
+
}
|
|
75
|
+
if (!this._tooltipDepsLoaded) {
|
|
76
|
+
this.ensureTooltipDeps();
|
|
77
|
+
return nothing;
|
|
78
|
+
}
|
|
79
|
+
return html`
|
|
80
|
+
<sp-overlay
|
|
81
|
+
id="truncated-value-tooltip"
|
|
82
|
+
aria-hidden="true"
|
|
83
|
+
.describeTrigger=${"none"}
|
|
84
|
+
.triggerElement=${host.inputElement}
|
|
85
|
+
.triggerInteraction=${"hover"}
|
|
86
|
+
type="hint"
|
|
87
|
+
.placement=${host.truncatedValueTooltipPlacement}
|
|
88
|
+
>
|
|
89
|
+
<sp-tooltip
|
|
90
|
+
aria-hidden="true"
|
|
91
|
+
placement=${host.truncatedValueTooltipPlacement}
|
|
92
|
+
>
|
|
93
|
+
${host.displayValue}
|
|
94
|
+
</sp-tooltip>
|
|
95
|
+
</sp-overlay>
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Updates the tooltip label text without requestUpdate. Used by NumberField from
|
|
100
|
+
* handleInput() so the tooltip shows the current input value while typing without
|
|
101
|
+
* triggering re-renders that would affect formatting or selection.
|
|
102
|
+
*
|
|
103
|
+
* We mutate an existing text node under `<sp-tooltip>` instead of assigning
|
|
104
|
+
* `tooltip.textContent`. Clearing `textContent` on the host removes all light-DOM
|
|
105
|
+
* children, which ejects Lit's marker nodes for `${host.displayValue}` in
|
|
106
|
+
* `render()` and causes "ChildPart has no parentNode" on the next update.
|
|
107
|
+
*
|
|
108
|
+
* Trade-off: this couples to our own light-DOM shape: `render()` must keep a
|
|
109
|
+
* direct text child of `<sp-tooltip>` (or update this lookup if we wrap the label
|
|
110
|
+
* in an element, e.g. a dedicated `<span id="…">`). This does not depend on
|
|
111
|
+
* `sp-tooltip`'s internal shadow DOM.
|
|
112
|
+
*/
|
|
113
|
+
syncTooltipText(text) {
|
|
114
|
+
var _a, _b;
|
|
115
|
+
const tooltip = (_a = this.host.shadowRoot) == null ? void 0 : _a.querySelector(
|
|
116
|
+
"#truncated-value-tooltip sp-tooltip"
|
|
117
|
+
);
|
|
118
|
+
if (!tooltip) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const tooltipTextNode = (_b = Array.from(tooltip.childNodes).find(
|
|
122
|
+
(node) => {
|
|
123
|
+
var _a2;
|
|
124
|
+
return node.nodeType === Node.TEXT_NODE && Boolean(((_a2 = node.textContent) != null ? _a2 : "").trim().length);
|
|
125
|
+
}
|
|
126
|
+
)) != null ? _b : Array.from(tooltip.childNodes).find(
|
|
127
|
+
(node) => node.nodeType === Node.TEXT_NODE
|
|
128
|
+
);
|
|
129
|
+
if (tooltipTextNode) {
|
|
130
|
+
tooltipTextNode.textContent = text;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
hostConnected() {
|
|
134
|
+
if (!this._observerInitialized) {
|
|
135
|
+
this.host.requestUpdate();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
hostUpdated() {
|
|
139
|
+
const host = this.host;
|
|
140
|
+
if (host.multiline || this._observerInitialized) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!host.inputElement) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this._observerInitialized = true;
|
|
147
|
+
this._resizeObserver = new ResizeObserver(() => {
|
|
148
|
+
this.refreshTruncationState();
|
|
149
|
+
});
|
|
150
|
+
this._resizeObserver.observe(this.host);
|
|
151
|
+
this._resizeObserver.observe(host.inputElement);
|
|
152
|
+
this.refreshTruncationState();
|
|
153
|
+
}
|
|
154
|
+
hostDisconnected() {
|
|
155
|
+
if (this._resizeObserver) {
|
|
156
|
+
this._resizeObserver.disconnect();
|
|
157
|
+
this._resizeObserver = null;
|
|
158
|
+
}
|
|
159
|
+
this._observerInitialized = false;
|
|
160
|
+
this._isTruncated = false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=TruncatedValueTooltipController.dev.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["TruncatedValueTooltipController.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations in the License.\n */\n\nimport {\n html,\n nothing,\n type ReactiveController,\n type ReactiveElement,\n type TemplateResult,\n} from '@spectrum-web-components/base';\nimport type { Placement } from '@spectrum-web-components/overlay';\n\n/**\n * Symbol used to request a host re-render when truncation state or tooltip deps change.\n * The host can use this in its update lifecycle to react if needed; the important part\n * is that requestUpdate(symbol, previous) triggers a re-render so controller.render() runs again.\n */\nexport const truncatedValueTooltipUpdatedSymbol = Symbol(\n 'truncated value tooltip updated'\n);\n\n/**\n * Host interface for TruncatedValueTooltipController. The host must provide these\n * properties (e.g. TextfieldBase). The controller reads them to determine truncation\n * and to render the tooltip content and placement.\n */\nexport interface TruncatedValueTooltipHost {\n inputElement: HTMLInputElement | HTMLTextAreaElement;\n multiline: boolean;\n type: string;\n disabled: boolean;\n focused?: boolean;\n displayValue: string;\n truncatedValueTooltipPlacement: Placement;\n}\n\ntype HostElement = ReactiveElement & TruncatedValueTooltipHost;\n\n/**\n * A reactive controller that adds truncated-value tooltip behavior: when the visible\n * value is clipped, a tooltip with the full value is shown on hover/focus. Overlay\n * and tooltip are lazy-loaded only when truncation is first detected.\n *\n * The host must implement TruncatedValueTooltipHost (e.g. TextfieldBase). The host\n * includes the controller's render output in its template and may call refresh() when\n * value or focused changes; NumberField also calls syncTooltipText() from handleInput()\n * so the tooltip stays in sync with the live input without re-renders.\n */\nexport class TruncatedValueTooltipController implements ReactiveController {\n private host: HostElement;\n\n private _isTruncated = false;\n\n private _tooltipDepsLoaded = false;\n\n private _resizeObserver: ResizeObserver | null = null;\n\n private _observerInitialized = false;\n\n constructor(host: HostElement) {\n this.host = host;\n this.host.addController(this);\n }\n\n private get inputElementIsTruncated(): boolean {\n const host = this.host;\n if (!host.inputElement || host.multiline || host.type === 'password') {\n return false;\n }\n // Add 1 because Safari sometimes rounds by 1px, breaking the calculation otherwise.\n return host.inputElement.scrollWidth > host.inputElement.clientWidth + 1;\n }\n\n /**\n * Updates truncation state. Returns true if we just transitioned to truncated\n * (so the host can e.g. schedule syncTooltipText after updateComplete).\n */\n private refreshTruncationState(): boolean {\n const host = this.host;\n const currentlyTruncated = this.inputElementIsTruncated;\n if (host.focused && this._isTruncated && !currentlyTruncated) {\n // Keep tooltip mounted through the active focus session once truncation has occurred.\n return false;\n }\n if (currentlyTruncated === this._isTruncated) {\n return false;\n }\n const previous = this._isTruncated;\n this._isTruncated = currentlyTruncated;\n // Defer so we don't schedule an update during the host's update (Lit warning). This creates an\n // intentional multi-phase update: first render (value in DOM) \u2192 measure here \u2192 microtask \u2192\n // requestUpdate \u2192 second render (tooltip in DOM). We need the second render to show the\n // tooltip; the brief delay before it appears is expected.\n Promise.resolve().then(() => {\n this.host.requestUpdate(truncatedValueTooltipUpdatedSymbol, previous);\n });\n return currentlyTruncated;\n }\n\n /**\n * Public API for hosts (e.g. NumberField) to force a re-check of truncation state.\n * Call from handleInput() or when value/focused changes so the tooltip visibility stays in sync.\n * Returns true if we just became truncated (host may schedule syncTooltipText after updateComplete).\n */\n public refresh(): boolean {\n return this.refreshTruncationState();\n }\n\n private async ensureTooltipDeps(): Promise<void> {\n if (this._tooltipDepsLoaded) {\n return;\n }\n await Promise.all([\n import('@spectrum-web-components/overlay/sp-overlay.js'),\n import('@spectrum-web-components/tooltip/sp-tooltip.js'),\n ]);\n this._tooltipDepsLoaded = true;\n // Defer so we don't schedule an update during the host's render (Lit warning).\n Promise.resolve().then(() => {\n this.host.requestUpdate(truncatedValueTooltipUpdatedSymbol, false);\n });\n }\n\n /**\n * Returns the tooltip overlay template when truncated; otherwise nothing.\n * The host includes this in its render() (e.g. this.truncatedValueTooltipController.render()).\n */\n public render(): TemplateResult | typeof nothing {\n const host = this.host;\n if (\n !this._isTruncated ||\n host.disabled ||\n !host.inputElement ||\n host.type === 'password'\n ) {\n return nothing;\n }\n if (!this._tooltipDepsLoaded) {\n this.ensureTooltipDeps();\n return nothing;\n }\n return html`\n <sp-overlay\n id=\"truncated-value-tooltip\"\n aria-hidden=\"true\"\n .describeTrigger=${'none'}\n .triggerElement=${host.inputElement}\n .triggerInteraction=${'hover'}\n type=\"hint\"\n .placement=${host.truncatedValueTooltipPlacement}\n >\n <sp-tooltip\n aria-hidden=\"true\"\n placement=${host.truncatedValueTooltipPlacement}\n >\n ${host.displayValue}\n </sp-tooltip>\n </sp-overlay>\n `;\n }\n\n /**\n * Updates the tooltip label text without requestUpdate. Used by NumberField from\n * handleInput() so the tooltip shows the current input value while typing without\n * triggering re-renders that would affect formatting or selection.\n *\n * We mutate an existing text node under `<sp-tooltip>` instead of assigning\n * `tooltip.textContent`. Clearing `textContent` on the host removes all light-DOM\n * children, which ejects Lit's marker nodes for `${host.displayValue}` in\n * `render()` and causes \"ChildPart has no parentNode\" on the next update.\n *\n * Trade-off: this couples to our own light-DOM shape: `render()` must keep a\n * direct text child of `<sp-tooltip>` (or update this lookup if we wrap the label\n * in an element, e.g. a dedicated `<span id=\"\u2026\">`). This does not depend on\n * `sp-tooltip`'s internal shadow DOM.\n */\n public syncTooltipText(text: string): void {\n const tooltip = this.host.shadowRoot?.querySelector(\n '#truncated-value-tooltip sp-tooltip'\n );\n if (!tooltip) {\n return;\n }\n // Find the Lit-bound text node; do not replace all children (see JSDoc above).\n const tooltipTextNode =\n Array.from(tooltip.childNodes).find(\n (node) =>\n node.nodeType === Node.TEXT_NODE &&\n Boolean((node.textContent ?? '').trim().length)\n ) ??\n Array.from(tooltip.childNodes).find(\n (node) => node.nodeType === Node.TEXT_NODE\n );\n if (tooltipTextNode) {\n tooltipTextNode.textContent = text;\n }\n }\n\n hostConnected(): void {\n // Defer ResizeObserver setup to hostUpdated so inputElement is in the DOM.\n // On reconnection, request an update so hostUpdated runs and we re-attach the observer.\n if (!this._observerInitialized) {\n this.host.requestUpdate();\n }\n }\n\n hostUpdated(): void {\n const host = this.host;\n if (host.multiline || this._observerInitialized) {\n return;\n }\n // Defer full setup until inputElement is in the DOM so we can observe it for truncation.\n if (!host.inputElement) {\n return;\n }\n this._observerInitialized = true;\n this._resizeObserver = new ResizeObserver(() => {\n this.refreshTruncationState();\n });\n this._resizeObserver.observe(this.host as unknown as Element);\n this._resizeObserver.observe(host.inputElement);\n this.refreshTruncationState();\n }\n\n hostDisconnected(): void {\n if (this._resizeObserver) {\n this._resizeObserver.disconnect();\n this._resizeObserver = null;\n }\n this._observerInitialized = false;\n this._isTruncated = false;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["_a"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";import{html as a,nothing as l}from"@spectrum-web-components/base";export const truncatedValueTooltipUpdatedSymbol=Symbol("truncated value tooltip updated");export class TruncatedValueTooltipController{constructor(e){this._isTruncated=!1;this._tooltipDepsLoaded=!1;this._resizeObserver=null;this._observerInitialized=!1;this.host=e,this.host.addController(this)}get inputElementIsTruncated(){const e=this.host;return!e.inputElement||e.multiline||e.type==="password"?!1:e.inputElement.scrollWidth>e.inputElement.clientWidth+1}refreshTruncationState(){const e=this.host,t=this.inputElementIsTruncated;if(e.focused&&this._isTruncated&&!t||t===this._isTruncated)return!1;const i=this._isTruncated;return this._isTruncated=t,Promise.resolve().then(()=>{this.host.requestUpdate(truncatedValueTooltipUpdatedSymbol,i)}),t}refresh(){return this.refreshTruncationState()}async ensureTooltipDeps(){this._tooltipDepsLoaded||(await Promise.all([import("@spectrum-web-components/overlay/sp-overlay.js"),import("@spectrum-web-components/tooltip/sp-tooltip.js")]),this._tooltipDepsLoaded=!0,Promise.resolve().then(()=>{this.host.requestUpdate(truncatedValueTooltipUpdatedSymbol,!1)}))}render(){const e=this.host;return!this._isTruncated||e.disabled||!e.inputElement||e.type==="password"?l:this._tooltipDepsLoaded?a`
|
|
2
|
+
<sp-overlay
|
|
3
|
+
id="truncated-value-tooltip"
|
|
4
|
+
aria-hidden="true"
|
|
5
|
+
.describeTrigger=${"none"}
|
|
6
|
+
.triggerElement=${e.inputElement}
|
|
7
|
+
.triggerInteraction=${"hover"}
|
|
8
|
+
type="hint"
|
|
9
|
+
.placement=${e.truncatedValueTooltipPlacement}
|
|
10
|
+
>
|
|
11
|
+
<sp-tooltip
|
|
12
|
+
aria-hidden="true"
|
|
13
|
+
placement=${e.truncatedValueTooltipPlacement}
|
|
14
|
+
>
|
|
15
|
+
${e.displayValue}
|
|
16
|
+
</sp-tooltip>
|
|
17
|
+
</sp-overlay>
|
|
18
|
+
`:(this.ensureTooltipDeps(),l)}syncTooltipText(e){var o,s;const t=(o=this.host.shadowRoot)==null?void 0:o.querySelector("#truncated-value-tooltip sp-tooltip");if(!t)return;const i=(s=Array.from(t.childNodes).find(r=>{var n;return r.nodeType===Node.TEXT_NODE&&!!((n=r.textContent)!=null?n:"").trim().length}))!=null?s:Array.from(t.childNodes).find(r=>r.nodeType===Node.TEXT_NODE);i&&(i.textContent=e)}hostConnected(){this._observerInitialized||this.host.requestUpdate()}hostUpdated(){const e=this.host;e.multiline||this._observerInitialized||e.inputElement&&(this._observerInitialized=!0,this._resizeObserver=new ResizeObserver(()=>{this.refreshTruncationState()}),this._resizeObserver.observe(this.host),this._resizeObserver.observe(e.inputElement),this.refreshTruncationState())}hostDisconnected(){this._resizeObserver&&(this._resizeObserver.disconnect(),this._resizeObserver=null),this._observerInitialized=!1,this._isTruncated=!1}}
|
|
19
|
+
//# sourceMappingURL=TruncatedValueTooltipController.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["TruncatedValueTooltipController.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations in the License.\n */\n\nimport {\n html,\n nothing,\n type ReactiveController,\n type ReactiveElement,\n type TemplateResult,\n} from '@spectrum-web-components/base';\nimport type { Placement } from '@spectrum-web-components/overlay';\n\n/**\n * Symbol used to request a host re-render when truncation state or tooltip deps change.\n * The host can use this in its update lifecycle to react if needed; the important part\n * is that requestUpdate(symbol, previous) triggers a re-render so controller.render() runs again.\n */\nexport const truncatedValueTooltipUpdatedSymbol = Symbol(\n 'truncated value tooltip updated'\n);\n\n/**\n * Host interface for TruncatedValueTooltipController. The host must provide these\n * properties (e.g. TextfieldBase). The controller reads them to determine truncation\n * and to render the tooltip content and placement.\n */\nexport interface TruncatedValueTooltipHost {\n inputElement: HTMLInputElement | HTMLTextAreaElement;\n multiline: boolean;\n type: string;\n disabled: boolean;\n focused?: boolean;\n displayValue: string;\n truncatedValueTooltipPlacement: Placement;\n}\n\ntype HostElement = ReactiveElement & TruncatedValueTooltipHost;\n\n/**\n * A reactive controller that adds truncated-value tooltip behavior: when the visible\n * value is clipped, a tooltip with the full value is shown on hover/focus. Overlay\n * and tooltip are lazy-loaded only when truncation is first detected.\n *\n * The host must implement TruncatedValueTooltipHost (e.g. TextfieldBase). The host\n * includes the controller's render output in its template and may call refresh() when\n * value or focused changes; NumberField also calls syncTooltipText() from handleInput()\n * so the tooltip stays in sync with the live input without re-renders.\n */\nexport class TruncatedValueTooltipController implements ReactiveController {\n private host: HostElement;\n\n private _isTruncated = false;\n\n private _tooltipDepsLoaded = false;\n\n private _resizeObserver: ResizeObserver | null = null;\n\n private _observerInitialized = false;\n\n constructor(host: HostElement) {\n this.host = host;\n this.host.addController(this);\n }\n\n private get inputElementIsTruncated(): boolean {\n const host = this.host;\n if (!host.inputElement || host.multiline || host.type === 'password') {\n return false;\n }\n // Add 1 because Safari sometimes rounds by 1px, breaking the calculation otherwise.\n return host.inputElement.scrollWidth > host.inputElement.clientWidth + 1;\n }\n\n /**\n * Updates truncation state. Returns true if we just transitioned to truncated\n * (so the host can e.g. schedule syncTooltipText after updateComplete).\n */\n private refreshTruncationState(): boolean {\n const host = this.host;\n const currentlyTruncated = this.inputElementIsTruncated;\n if (host.focused && this._isTruncated && !currentlyTruncated) {\n // Keep tooltip mounted through the active focus session once truncation has occurred.\n return false;\n }\n if (currentlyTruncated === this._isTruncated) {\n return false;\n }\n const previous = this._isTruncated;\n this._isTruncated = currentlyTruncated;\n // Defer so we don't schedule an update during the host's update (Lit warning). This creates an\n // intentional multi-phase update: first render (value in DOM) \u2192 measure here \u2192 microtask \u2192\n // requestUpdate \u2192 second render (tooltip in DOM). We need the second render to show the\n // tooltip; the brief delay before it appears is expected.\n Promise.resolve().then(() => {\n this.host.requestUpdate(truncatedValueTooltipUpdatedSymbol, previous);\n });\n return currentlyTruncated;\n }\n\n /**\n * Public API for hosts (e.g. NumberField) to force a re-check of truncation state.\n * Call from handleInput() or when value/focused changes so the tooltip visibility stays in sync.\n * Returns true if we just became truncated (host may schedule syncTooltipText after updateComplete).\n */\n public refresh(): boolean {\n return this.refreshTruncationState();\n }\n\n private async ensureTooltipDeps(): Promise<void> {\n if (this._tooltipDepsLoaded) {\n return;\n }\n await Promise.all([\n import('@spectrum-web-components/overlay/sp-overlay.js'),\n import('@spectrum-web-components/tooltip/sp-tooltip.js'),\n ]);\n this._tooltipDepsLoaded = true;\n // Defer so we don't schedule an update during the host's render (Lit warning).\n Promise.resolve().then(() => {\n this.host.requestUpdate(truncatedValueTooltipUpdatedSymbol, false);\n });\n }\n\n /**\n * Returns the tooltip overlay template when truncated; otherwise nothing.\n * The host includes this in its render() (e.g. this.truncatedValueTooltipController.render()).\n */\n public render(): TemplateResult | typeof nothing {\n const host = this.host;\n if (\n !this._isTruncated ||\n host.disabled ||\n !host.inputElement ||\n host.type === 'password'\n ) {\n return nothing;\n }\n if (!this._tooltipDepsLoaded) {\n this.ensureTooltipDeps();\n return nothing;\n }\n return html`\n <sp-overlay\n id=\"truncated-value-tooltip\"\n aria-hidden=\"true\"\n .describeTrigger=${'none'}\n .triggerElement=${host.inputElement}\n .triggerInteraction=${'hover'}\n type=\"hint\"\n .placement=${host.truncatedValueTooltipPlacement}\n >\n <sp-tooltip\n aria-hidden=\"true\"\n placement=${host.truncatedValueTooltipPlacement}\n >\n ${host.displayValue}\n </sp-tooltip>\n </sp-overlay>\n `;\n }\n\n /**\n * Updates the tooltip label text without requestUpdate. Used by NumberField from\n * handleInput() so the tooltip shows the current input value while typing without\n * triggering re-renders that would affect formatting or selection.\n *\n * We mutate an existing text node under `<sp-tooltip>` instead of assigning\n * `tooltip.textContent`. Clearing `textContent` on the host removes all light-DOM\n * children, which ejects Lit's marker nodes for `${host.displayValue}` in\n * `render()` and causes \"ChildPart has no parentNode\" on the next update.\n *\n * Trade-off: this couples to our own light-DOM shape: `render()` must keep a\n * direct text child of `<sp-tooltip>` (or update this lookup if we wrap the label\n * in an element, e.g. a dedicated `<span id=\"\u2026\">`). This does not depend on\n * `sp-tooltip`'s internal shadow DOM.\n */\n public syncTooltipText(text: string): void {\n const tooltip = this.host.shadowRoot?.querySelector(\n '#truncated-value-tooltip sp-tooltip'\n );\n if (!tooltip) {\n return;\n }\n // Find the Lit-bound text node; do not replace all children (see JSDoc above).\n const tooltipTextNode =\n Array.from(tooltip.childNodes).find(\n (node) =>\n node.nodeType === Node.TEXT_NODE &&\n Boolean((node.textContent ?? '').trim().length)\n ) ??\n Array.from(tooltip.childNodes).find(\n (node) => node.nodeType === Node.TEXT_NODE\n );\n if (tooltipTextNode) {\n tooltipTextNode.textContent = text;\n }\n }\n\n hostConnected(): void {\n // Defer ResizeObserver setup to hostUpdated so inputElement is in the DOM.\n // On reconnection, request an update so hostUpdated runs and we re-attach the observer.\n if (!this._observerInitialized) {\n this.host.requestUpdate();\n }\n }\n\n hostUpdated(): void {\n const host = this.host;\n if (host.multiline || this._observerInitialized) {\n return;\n }\n // Defer full setup until inputElement is in the DOM so we can observe it for truncation.\n if (!host.inputElement) {\n return;\n }\n this._observerInitialized = true;\n this._resizeObserver = new ResizeObserver(() => {\n this.refreshTruncationState();\n });\n this._resizeObserver.observe(this.host as unknown as Element);\n this._resizeObserver.observe(host.inputElement);\n this.refreshTruncationState();\n }\n\n hostDisconnected(): void {\n if (this._resizeObserver) {\n this._resizeObserver.disconnect();\n this._resizeObserver = null;\n }\n this._observerInitialized = false;\n this._isTruncated = false;\n }\n}\n"],
|
|
5
|
+
"mappings": "aAYA,OACE,QAAAA,EACA,WAAAC,MAIK,gCAQA,aAAM,mCAAqC,OAChD,iCACF,EA6BO,aAAM,+BAA8D,CAWzE,YAAYC,EAAmB,CAR/B,KAAQ,aAAe,GAEvB,KAAQ,mBAAqB,GAE7B,KAAQ,gBAAyC,KAEjD,KAAQ,qBAAuB,GAG7B,KAAK,KAAOA,EACZ,KAAK,KAAK,cAAc,IAAI,CAC9B,CAEA,IAAY,yBAAmC,CAC7C,MAAMA,EAAO,KAAK,KAClB,MAAI,CAACA,EAAK,cAAgBA,EAAK,WAAaA,EAAK,OAAS,WACjD,GAGFA,EAAK,aAAa,YAAcA,EAAK,aAAa,YAAc,CACzE,CAMQ,wBAAkC,CACxC,MAAMA,EAAO,KAAK,KACZC,EAAqB,KAAK,wBAKhC,GAJID,EAAK,SAAW,KAAK,cAAgB,CAACC,GAItCA,IAAuB,KAAK,aAC9B,MAAO,GAET,MAAMC,EAAW,KAAK,aACtB,YAAK,aAAeD,EAKpB,QAAQ,QAAQ,EAAE,KAAK,IAAM,CAC3B,KAAK,KAAK,cAAc,mCAAoCC,CAAQ,CACtE,CAAC,EACMD,CACT,CAOO,SAAmB,CACxB,OAAO,KAAK,uBAAuB,CACrC,CAEA,MAAc,mBAAmC,CAC3C,KAAK,qBAGT,MAAM,QAAQ,IAAI,CAChB,OAAO,gDAAgD,EACvD,OAAO,gDAAgD,CACzD,CAAC,EACD,KAAK,mBAAqB,GAE1B,QAAQ,QAAQ,EAAE,KAAK,IAAM,CAC3B,KAAK,KAAK,cAAc,mCAAoC,EAAK,CACnE,CAAC,EACH,CAMO,QAA0C,CAC/C,MAAMD,EAAO,KAAK,KAClB,MACE,CAAC,KAAK,cACNA,EAAK,UACL,CAACA,EAAK,cACNA,EAAK,OAAS,WAEPD,EAEJ,KAAK,mBAIHD;AAAA;AAAA;AAAA;AAAA,2BAIgB,MAAM;AAAA,0BACPE,EAAK,YAAY;AAAA,8BACb,OAAO;AAAA;AAAA,qBAEhBA,EAAK,8BAA8B;AAAA;AAAA;AAAA;AAAA,sBAIlCA,EAAK,8BAA8B;AAAA;AAAA,YAE7CA,EAAK,YAAY;AAAA;AAAA;AAAA,OAjBvB,KAAK,kBAAkB,EAChBD,EAoBX,CAiBO,gBAAgBI,EAAoB,CAzL7C,IAAAC,EAAAC,EA0LI,MAAMC,GAAUF,EAAA,KAAK,KAAK,aAAV,YAAAA,EAAsB,cACpC,uCAEF,GAAI,CAACE,EACH,OAGF,MAAMC,GACJF,EAAA,MAAM,KAAKC,EAAQ,UAAU,EAAE,KAC5BE,GAAM,CAnMf,IAAAJ,EAoMU,OAAAI,EAAK,WAAa,KAAK,WACvB,IAASJ,EAAAI,EAAK,cAAL,KAAAJ,EAAoB,IAAI,KAAK,EAAE,OAC5C,IAJA,KAAAC,EAKA,MAAM,KAAKC,EAAQ,UAAU,EAAE,KAC5BE,GAASA,EAAK,WAAa,KAAK,SACnC,EACED,IACFA,EAAgB,YAAcJ,EAElC,CAEA,eAAsB,CAGf,KAAK,sBACR,KAAK,KAAK,cAAc,CAE5B,CAEA,aAAoB,CAClB,MAAMH,EAAO,KAAK,KACdA,EAAK,WAAa,KAAK,sBAItBA,EAAK,eAGV,KAAK,qBAAuB,GAC5B,KAAK,gBAAkB,IAAI,eAAe,IAAM,CAC9C,KAAK,uBAAuB,CAC9B,CAAC,EACD,KAAK,gBAAgB,QAAQ,KAAK,IAA0B,EAC5D,KAAK,gBAAgB,QAAQA,EAAK,YAAY,EAC9C,KAAK,uBAAuB,EAC9B,CAEA,kBAAyB,CACnB,KAAK,kBACP,KAAK,gBAAgB,WAAW,EAChC,KAAK,gBAAkB,MAEzB,KAAK,qBAAuB,GAC5B,KAAK,aAAe,EACtB,CACF",
|
|
6
|
+
"names": ["html", "nothing", "host", "currentlyTruncated", "previous", "text", "_a", "_b", "tooltip", "tooltipTextNode", "node"]
|
|
7
|
+
}
|