@greypan/web-ui 8.0.0 → 8.1.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.CN.md +36 -18
- package/README.md +82 -63
- package/dist/assets/menu-portal.js +1 -1
- package/dist/assets/menu-portal.js.map +1 -1
- package/dist/assets/selection-control.js +1 -1
- package/dist/assets/selection-control.js.map +1 -1
- package/dist/components/autocomplete/index.js +6 -6
- package/dist/components/autocomplete/style.js +1 -1
- package/dist/components/autocomplete/style.js.map +1 -1
- package/dist/components/badge/style.js.map +1 -1
- package/dist/components/button/index.d.ts +368 -1
- package/dist/components/button/index.js +56 -36
- package/dist/components/button/index.js.map +1 -1
- package/dist/components/button/style.js +1 -1
- package/dist/components/button/style.js.map +1 -1
- package/dist/components/checkbox/index.d.ts +3 -0
- package/dist/components/checkbox/index.js +33 -35
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/checkbox/style.js +1 -1
- package/dist/components/checkbox/style.js.map +1 -1
- package/dist/components/checkbox-group/index.js +8 -8
- package/dist/components/dropdown-item/style.js +1 -1
- package/dist/components/dropdown-item/style.js.map +1 -1
- package/dist/components/editable-text/index.d.ts +2 -0
- package/dist/components/editable-text/index.js +50 -30
- package/dist/components/editable-text/index.js.map +1 -1
- package/dist/components/icon/index.js +7 -7
- package/dist/components/icon/index.js.map +1 -1
- package/dist/components/input/index.d.ts +7 -0
- package/dist/components/input/index.js +34 -29
- package/dist/components/input/index.js.map +1 -1
- package/dist/components/input-number/index.d.ts +1 -0
- package/dist/components/input-number/index.js +28 -26
- package/dist/components/input-number/index.js.map +1 -1
- package/dist/components/layout/index.js +1 -1
- package/dist/components/layout/index.js.map +1 -1
- package/dist/components/layout/style.js +1 -1
- package/dist/components/layout/style.js.map +1 -1
- package/dist/components/option/style.js +1 -1
- package/dist/components/option/style.js.map +1 -1
- package/dist/components/popover/style.js +1 -1
- package/dist/components/popover/style.js.map +1 -1
- package/dist/components/radio/index.d.ts +3 -0
- package/dist/components/radio/index.js +23 -19
- package/dist/components/radio/index.js.map +1 -1
- package/dist/components/radio-group/index.js +8 -8
- package/dist/components/segmented/index.js +8 -8
- package/dist/components/select/style.js +1 -1
- package/dist/components/select/style.js.map +1 -1
- package/dist/components/slider/index.js +9 -9
- package/dist/components/switch/index.d.ts +1 -0
- package/dist/components/switch/index.js +26 -24
- package/dist/components/switch/index.js.map +1 -1
- package/dist/components/textarea/index.d.ts +7 -1
- package/dist/components/textarea/index.js +44 -42
- package/dist/components/textarea/index.js.map +1 -1
- package/dist/components/textarea/style.js.map +1 -1
- package/dist/components/theme/index.d.ts +9 -0
- package/dist/components/theme/index.js +128 -85
- package/dist/components/theme/index.js.map +1 -1
- package/dist/components/theme/style.js +1 -1
- package/dist/components/theme/style.js.map +1 -1
- package/dist/components/toast/toast.js +8 -8
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/tooltip/style.js +1 -1
- package/dist/components/tooltip/style.js.map +1 -1
- package/dist/icons/generated/akar-icons-check.d.ts +6 -0
- package/dist/icons/generated/akar-icons-check.js +10 -0
- package/dist/icons/generated/akar-icons-check.js.map +1 -0
- package/dist/icons/generated/bi-check.d.ts +6 -0
- package/dist/icons/generated/bi-check.js +10 -0
- package/dist/icons/generated/bi-check.js.map +1 -0
- package/dist/icons/index.d.ts +2 -0
- package/dist/icons/index.js +3 -1
- package/dist/shared/form-association/field-id.js +9 -0
- package/dist/shared/form-association/field-id.js.map +1 -0
- package/dist/shared/form-association/index.js +9 -1
- package/dist/shared/form-association/index.js.map +1 -1
- package/dist/shared/label-emptiness/index.js +45 -0
- package/dist/shared/label-emptiness/index.js.map +1 -0
- package/dist/shared/theme/root-sync.js +24 -18
- package/dist/shared/theme/root-sync.js.map +1 -1
- package/package.json +3 -1
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
|
|
2
2
|
import { FormAssociated as t, FormAssociationController as n, defineFormAssociation as r } from "../../shared/form-association/index.js";
|
|
3
|
-
import i from "
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { createFieldId as i } from "../../shared/form-association/field-id.js";
|
|
4
|
+
import a from "./style.js";
|
|
5
|
+
import { LitElement as o, html as s, unsafeCSS as c } from "lit";
|
|
6
|
+
import { customElement as l, property as u, query as d, state as f } from "lit/decorators.js";
|
|
7
|
+
import { ifDefined as p } from "lit/directives/if-defined.js";
|
|
8
|
+
import { classMap as m } from "lit/directives/class-map.js";
|
|
8
9
|
//#region src/components/editable-text/index.ts
|
|
9
|
-
var
|
|
10
|
-
function
|
|
10
|
+
var h = "", g = 1;
|
|
11
|
+
function _(e) {
|
|
11
12
|
for (let t of e.childNodes) if (t instanceof Text) return t;
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
14
|
-
function
|
|
15
|
-
let r =
|
|
15
|
+
function v(e, t, n) {
|
|
16
|
+
let r = _(e);
|
|
16
17
|
if (!r) return 0;
|
|
17
18
|
let i = r.data.length, a = document.createRange(), o = (e) => (a.setStart(r, e), a.setEnd(r, e), a.getBoundingClientRect()), s = [];
|
|
18
|
-
for (let t of e.getClientRects()) s.some((e) => Math.abs(e.top - t.top) <=
|
|
19
|
+
for (let t of e.getClientRects()) s.some((e) => Math.abs(e.top - t.top) <= g) || s.push(t);
|
|
19
20
|
if (s.length === 0) return i;
|
|
20
21
|
let c = s[0], l = Infinity;
|
|
21
22
|
for (let e of s) {
|
|
@@ -25,23 +26,23 @@ function _(e, t, n) {
|
|
|
25
26
|
let u = 0, d = i;
|
|
26
27
|
for (; u < d;) {
|
|
27
28
|
let e = u + d >> 1;
|
|
28
|
-
o(e).top >= c.top -
|
|
29
|
+
o(e).top >= c.top - g ? d = e : u = e + 1;
|
|
29
30
|
}
|
|
30
31
|
let f = u;
|
|
31
32
|
for (u = f, d = i; u < d;) {
|
|
32
33
|
let e = u + d + 1 >> 1;
|
|
33
|
-
o(e).top <= c.top +
|
|
34
|
+
o(e).top <= c.top + g ? u = e : d = e - 1;
|
|
34
35
|
}
|
|
35
36
|
let p = Math.max(u, f);
|
|
36
37
|
for (u = f, d = p; u < d;) {
|
|
37
38
|
let e = u + d + 1 >> 1;
|
|
38
|
-
o(e).left <= t +
|
|
39
|
+
o(e).left <= t + g ? u = e : d = e - 1;
|
|
39
40
|
}
|
|
40
41
|
return u;
|
|
41
42
|
}
|
|
42
|
-
var
|
|
43
|
+
var y = class extends t(o) {
|
|
43
44
|
static {
|
|
44
|
-
this.styles = [
|
|
45
|
+
this.styles = [c(a)];
|
|
45
46
|
}
|
|
46
47
|
get value() {
|
|
47
48
|
return this._value;
|
|
@@ -54,7 +55,7 @@ var v = class extends t(a) {
|
|
|
54
55
|
return this.disabled || this._formAssociation.isFormDisabled();
|
|
55
56
|
}
|
|
56
57
|
constructor() {
|
|
57
|
-
super(), this.name = "", this.placeholder = "", this.disabled = !1, this.ariaLabel = null, this._value = "", this._editing = !1, this._editBase = "", this._pendingCaret = null, this._refocusing = !1, this._resizeObserver = null, this._formAssociation = r({
|
|
58
|
+
super(), this.name = "", this.placeholder = "", this.disabled = !1, this.readonly = !1, this.ariaLabel = null, this._value = "", this._editing = !1, this._editBase = "", this._pendingCaret = null, this._refocusing = !1, this._resizeObserver = null, this._fieldId = i("web-ui-editable-text"), this._formAssociation = r({
|
|
58
59
|
host: this,
|
|
59
60
|
getState: () => this._value,
|
|
60
61
|
setState: (e) => {
|
|
@@ -71,7 +72,10 @@ var v = class extends t(a) {
|
|
|
71
72
|
e.preventDefault(), e.stopPropagation(), this._cancelEditing(), this._returnFocusToHost();
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
74
|
-
e.key === "Enter"
|
|
75
|
+
if (e.key === "Enter") {
|
|
76
|
+
if (e.preventDefault(), e.stopPropagation(), this.readonly) return;
|
|
77
|
+
this._commitEditing(!0);
|
|
78
|
+
}
|
|
75
79
|
}
|
|
76
80
|
}, this.addEventListener("focus", this._onHostFocus);
|
|
77
81
|
}
|
|
@@ -92,7 +96,7 @@ var v = class extends t(a) {
|
|
|
92
96
|
this._enterEditing();
|
|
93
97
|
}
|
|
94
98
|
_onPointerDown(e) {
|
|
95
|
-
this._isDisabled || this._editing || !this._textLayer || (this._pendingCaret =
|
|
99
|
+
this._isDisabled || this._editing || !this._textLayer || (this._pendingCaret = v(this._textLayer, e.clientX, e.clientY));
|
|
96
100
|
}
|
|
97
101
|
_onClick() {
|
|
98
102
|
this._isDisabled || this._editing || this._enterEditing();
|
|
@@ -118,10 +122,21 @@ var v = class extends t(a) {
|
|
|
118
122
|
this._editing && (this._editing = !1, this._releaseEditingKeys(), this.toggleAttribute("editing", !1));
|
|
119
123
|
}
|
|
120
124
|
_onInput() {
|
|
121
|
-
|
|
125
|
+
let e = this._editor;
|
|
126
|
+
if (this._isDisabled || this.readonly) {
|
|
127
|
+
e && e.value !== this._value && (e.value = this._value);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
this._value = e?.value ?? "", this._formAssociation.sync(), this._autosizeEditor();
|
|
122
131
|
}
|
|
123
132
|
_onBlur() {
|
|
124
|
-
this._editing && !this._isDisabled
|
|
133
|
+
if (this._editing && !this._isDisabled) {
|
|
134
|
+
if (this.readonly) {
|
|
135
|
+
this._exitEditing();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
this._commitEditing(!1);
|
|
139
|
+
}
|
|
125
140
|
}
|
|
126
141
|
_commitEditing(e) {
|
|
127
142
|
this._editing && (this._exitEditing(), e && this._returnFocusToHost(), this.dispatchEvent(new Event("change", {
|
|
@@ -165,21 +180,23 @@ var v = class extends t(a) {
|
|
|
165
180
|
e.style.height = n > 0 ? `${n}px` : t;
|
|
166
181
|
}
|
|
167
182
|
get _displayText() {
|
|
168
|
-
return this._value === "" ? this.placeholder === "" ?
|
|
183
|
+
return this._value === "" ? this.placeholder === "" ? h : this.placeholder : this._value;
|
|
169
184
|
}
|
|
170
185
|
render() {
|
|
171
|
-
return
|
|
186
|
+
return s`
|
|
172
187
|
<div class="layers" @pointerdown=${this._onPointerDown} @click=${this._onClick}>
|
|
173
|
-
<span class=${
|
|
188
|
+
<span class=${m({
|
|
174
189
|
text: !0,
|
|
175
190
|
placeholder: this._value === ""
|
|
176
191
|
})}>${this._displayText}</span>
|
|
177
192
|
<textarea
|
|
193
|
+
id=${this._fieldId}
|
|
178
194
|
class="editor"
|
|
179
195
|
.value=${this._value}
|
|
180
196
|
.placeholder=${this.placeholder}
|
|
181
197
|
?disabled=${this._isDisabled}
|
|
182
|
-
|
|
198
|
+
?readonly=${this.readonly}
|
|
199
|
+
aria-label=${p(this.ariaLabel)}
|
|
183
200
|
@input=${this._onInput}
|
|
184
201
|
@blur=${this._onBlur}
|
|
185
202
|
></textarea>
|
|
@@ -187,20 +204,23 @@ var v = class extends t(a) {
|
|
|
187
204
|
`;
|
|
188
205
|
}
|
|
189
206
|
};
|
|
190
|
-
e([
|
|
207
|
+
e([u({
|
|
191
208
|
type: String,
|
|
192
209
|
reflect: !0
|
|
193
|
-
})],
|
|
210
|
+
})], y.prototype, "name", void 0), e([u({
|
|
194
211
|
type: String,
|
|
195
212
|
reflect: !0
|
|
196
|
-
})],
|
|
213
|
+
})], y.prototype, "placeholder", void 0), e([u({
|
|
214
|
+
type: Boolean,
|
|
215
|
+
reflect: !0
|
|
216
|
+
})], y.prototype, "disabled", void 0), e([u({
|
|
197
217
|
type: Boolean,
|
|
198
218
|
reflect: !0
|
|
199
|
-
})],
|
|
219
|
+
})], y.prototype, "readonly", void 0), e([u({
|
|
200
220
|
type: String,
|
|
201
221
|
attribute: "aria-label"
|
|
202
|
-
})],
|
|
222
|
+
})], y.prototype, "ariaLabel", void 0), e([f()], y.prototype, "_value", void 0), e([u({ type: String })], y.prototype, "value", null), e([d(".editor")], y.prototype, "_editor", void 0), e([d(".text")], y.prototype, "_textLayer", void 0), y = e([l("web-ui-editable-text")], y);
|
|
203
223
|
//#endregion
|
|
204
|
-
export {
|
|
224
|
+
export { y as WebUiEditableText };
|
|
205
225
|
|
|
206
226
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/editable-text/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\n\nimport style from './style.css?inline'\n\n/** 空值且无 placeholder 时占位,保留一个行盒,避免宿主退化成 0 高度、无法点击进入编辑。 */\nconst BLANK_GLYPH = '\\u200B'\n\n/** 行/字符落点二分的容差(px),吸收次像素与舍入差异。 */\nconst CARET_TOLERANCE = 1\n\n/**\n * 取文本层承载文案的 Text 节点。\n *\n * 绑定表达式位于元素首个位置时,Lit 会在它前面留一个注释标记节点,因此不能用\n * `firstChild`:那会拿到没有布局的注释,Range 量不到任何矩形。\n */\nfunction textNodeOf(layer: HTMLElement): Text | null {\n for (const child of layer.childNodes) {\n if (child instanceof Text) return child\n }\n return null\n}\n\n/**\n * 把视口坐标映射为文本层的字符偏移。\n *\n * `document.caretPositionFromPoint` 不进入 shadow DOM(对 shadow 内容返回 shadow host\n * 之外的节点),因此在文本层上用 Range 空矩形二分:先按 y 选行,再在行内按 x 选字符。\n * 文本层与编辑层同盒同排版,偏移与 textarea 的 selectionStart 一一对应。\n */\nfunction caretOffsetFromPoint(textLayer: HTMLElement, x: number, y: number): number {\n const node = textNodeOf(textLayer)\n if (!node) return 0\n const length = node.data.length\n const range = document.createRange()\n const rectAt = (offset: number): DOMRect => {\n range.setStart(node, offset)\n range.setEnd(node, offset)\n return range.getBoundingClientRect()\n }\n\n // 同一行的盒可能被拆成多个 client rect;按 top 去重得到行列表,取与 y 最近的一行,\n // 距离相等时取更靠下的一行(与浏览器在行间的落点行为一致)。\n const lines: DOMRect[] = []\n for (const rect of textLayer.getClientRects()) {\n if (!lines.some(line => Math.abs(line.top - rect.top) <= CARET_TOLERANCE)) lines.push(rect)\n }\n if (lines.length === 0) return length\n let line = lines[0]\n let nearest = Infinity\n for (const candidate of lines) {\n const distance = Math.abs((candidate.top + candidate.bottom) / 2 - y)\n if (distance <= nearest) {\n nearest = distance\n line = candidate\n }\n }\n\n // 行首:caret top 首次落入本行的最小偏移\n let lo = 0\n let hi = length\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (rectAt(mid).top >= line.top - CARET_TOLERANCE) hi = mid\n else lo = mid + 1\n }\n const lineStart = lo\n // 行尾:caret top 仍落在本行的最大偏移\n lo = lineStart\n hi = length\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (rectAt(mid).top <= line.top + CARET_TOLERANCE) lo = mid\n else hi = mid - 1\n }\n const lineEnd = Math.max(lo, lineStart)\n // 行内:caret left 不超过 x 的最大偏移\n lo = lineStart\n hi = lineEnd\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (rectAt(mid).left <= x + CARET_TOLERANCE) lo = mid\n else hi = mid - 1\n }\n return lo\n}\n\n@customElement('web-ui-editable-text')\nexport class WebUiEditableText extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) name = ''\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n\n @state() private _value = ''\n\n /**\n * `value` attribute 承载 form reset 的初值,不随当前值反射;property 暴露当前值,\n * 与原生 input 的 value / defaultValue 关系一致。\n */\n @property({ type: String })\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n @query('.editor') private _editor?: HTMLTextAreaElement\n @query('.text') private _textLayer?: HTMLElement\n\n private _editing = false\n private _editBase = ''\n private _pendingCaret: number | null = null\n private _refocusing = false\n private _resizeObserver: ResizeObserver | null = null\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n }\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n constructor() {\n super()\n // focus 不冒泡:宿主监听只在焦点从组件外进入时触发,shadow 内聚焦不会误触\n this.addEventListener('focus', this._onHostFocus)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._releaseEditingKeys()\n this._teardownAutosize()\n }\n\n override connectedCallback() {\n super.connectedCallback()\n /*\n * autosize 的 ResizeObserver 在 disconnectedCallback 被拆掉,搭建若只放在\n * firstUpdated(一生一次),元素断开再重连后 RO 永久丢失、autosize 静默失效。\n * connectedCallback 每次连接都跑:_setupAutosize 自带 teardown,重复进入无副作用。\n * 首连接时渲染尚未发生(_editor 为 null),_autosizeEditor 与 RO 回调都有判空守卫。\n */\n this._setupAutosize()\n // 同类缺口:编辑态被断开时 _releaseEditingKeys 撤了 window 监听,重连后 _editing\n // 仍为 true(blur 取消未必先于断开到达),此时补领,避免重连后按键失灵。\n if (this._editing) this._claimEditingKeys()\n }\n\n override updated() {\n // disabled 时移出 tab 序列;编辑态由 editing attribute 表达,二者互斥\n if (this._isDisabled) this.removeAttribute('tabindex')\n else this.setAttribute('tabindex', '0')\n // 禁用态生效时(属性或 fieldset)退出编辑:值保持当前草稿,不派发提交事件\n if (this._editing && this._isDisabled) this._exitEditing()\n this._autosizeEditor()\n }\n\n private _onHostFocus() {\n if (this._refocusing) {\n this._refocusing = false\n return\n }\n this._enterEditing()\n }\n\n /**\n * pointerdown 时文本层仍是命中目标,此时记录落点;浏览器随后的默认聚焦会触发\n * host focus 进入编辑,偏移在那里应用到 textarea。\n */\n private _onPointerDown(e: PointerEvent) {\n if (this._isDisabled || this._editing || !this._textLayer) return\n this._pendingCaret = caretOffsetFromPoint(this._textLayer, e.clientX, e.clientY)\n }\n\n /** mousedown 默认聚焦被拦截等场景由 click 兜底进入编辑。 */\n private _onClick() {\n if (this._isDisabled || this._editing) return\n this._enterEditing()\n }\n\n /**\n * 公共 API:进入编辑态并全选内容;已在编辑态时只重新全选。\n *\n * `disabled` 时不进入编辑,与 `focus()` 一致。\n */\n select() {\n if (this._isDisabled) return\n if (!this._editing) this._enterEditing()\n const editor = this._editor\n // 全选:末端显式取当前文本长度,覆盖 _enterEditing 落下的光标位置\n editor?.setSelectionRange(0, editor.value.length)\n }\n\n private _enterEditing() {\n if (this._isDisabled || this._editing) return\n this._editing = true\n this._editBase = this._value\n this.toggleAttribute('editing', true)\n this._claimEditingKeys()\n const editor = this._editor\n if (!editor) return\n editor.focus()\n // Tab / 编程式聚焦光标到末尾;指针落点用 pointerdown 记下的偏移\n const offset = this._pendingCaret ?? this._value.length\n this._pendingCaret = null\n const clamped = Math.max(0, Math.min(offset, editor.value.length))\n editor.setSelectionRange(clamped, clamped)\n }\n\n private _exitEditing() {\n if (!this._editing) return\n this._editing = false\n this._releaseEditingKeys()\n this.toggleAttribute('editing', false)\n }\n\n private _onInput() {\n // 原生 input 已 composed 冒泡出 shadow root 且 target 重定向到宿主,无需补发\n this._value = this._editor?.value ?? ''\n this._formAssociation.sync()\n this._autosizeEditor()\n }\n\n /**\n * blur 提交:与 Enter 同一条提交路径,草稿成为新值并派发 change。\n *\n * 不交还焦点:焦点已被用户移走,抢回宿主会吞掉消费者的目标焦点。\n */\n private _onBlur() {\n if (!this._editing || this._isDisabled) return\n this._commitEditing(false)\n }\n\n /**\n * 提交:草稿成为新值、退出编辑,并恰好派发一次 change。Enter 与 blur 同走这一条路。\n *\n * `returnFocus` 区分两种来路:Enter 主动结束时把焦点交还宿主(标记防重入编辑);\n * blur 是被动失焦,焦点已被用户移走,再抢回宿主会吞掉消费者的目标焦点。\n * 先退出编辑再动焦点:退出时 textarea 隐藏触发的 blur 看到 _editing 已为 false,\n * 不会把这次提交误判成又一次 blur 提交。\n */\n private _commitEditing(returnFocus: boolean) {\n if (!this._editing) return\n this._exitEditing()\n if (returnFocus) this._returnFocusToHost()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n /**\n * 取消:恢复进入编辑时的值并派发 cancel。仅 Escape 走这一条路径。\n *\n * 调用方(Escape 分支)随后才交还焦点:本方法先摘除编辑态,焦点迁移触发的 blur\n * 到达时 `_editing` 已为 false 而早退,不会把取消误判成 blur 提交。\n */\n private _cancelEditing() {\n if (!this._editing) return\n this._value = this._editBase\n this._formAssociation.sync()\n this._exitEditing()\n /*\n * 重入守卫先于 dispatch 置位:cancel 同步派发,消费者可能在监听器里调\n * el.focus()——那是一次真实的宿主 focus,没有守卫就会重新进入编辑,随后一次\n * blur 便把恢复后的原值当成新草稿提交,用户什么都没改却收到 change。守卫在此\n * 置位后即被该次 focus 消费,与取消后由组件自己交还焦点走同一条早退路径。\n */\n this._refocusing = true\n /*\n * cancel 与原生 <dialog> 的 cancel 同名,而 dialog 的关闭管线就监听这个事件名。\n * 组件被消费方投映进浮层 shadow(drawer/dialog 标题)时,任何冒泡的 cancel——\n * composed 与否都一样,slot 都会把事件带进 shadow 树——都会被那个 dialog 当成\n * 一次关闭请求:issue #159 的实测症状是 drawer 标题聚焦编辑时按 Escape,或点到\n * 抽屉别处触发 blur,抽屉跟着编辑一起关。因此只在宿主上派发,不冒泡不组合;\n * 监听挂在组件本身即可(vue @cancel、React addEventListener 均不受影响)。\n * 手法同 checkbox/radio 受管时的事件策略(见各自 handleClick 注释)。\n */\n this.dispatchEvent(new Event('cancel', { bubbles: false, composed: false }))\n }\n\n /**\n * 焦点交还宿主:先标记再聚焦,宿主 focus 到达时标记已消费,不会再次进入编辑;\n * 标记在微任务里清掉,此后的聚焦照常进入编辑。\n */\n private _returnFocusToHost() {\n this._refocusing = true\n this.focus()\n queueMicrotask(() => {\n this._refocusing = false\n })\n }\n\n /**\n * 编辑态按键归属:Enter 提交、Escape 取消。\n *\n * 监听挂在 window 捕获阶段而不是 shadow 内的 textarea 上:消费方(浮层仲裁者)在\n * document 捕获阶段收 Escape,组件若只在 textarea 上处理,按键先被仲裁者收走,\n * 于是一次 Escape 会同时取消编辑和关闭外层浮层。window 在 document 上游,\n * stopPropagation 只有挂在这里才拦得住仲裁者。\n */\n private _claimEditingKeys() {\n window.addEventListener('keydown', this._onEditingKeydown, true)\n }\n\n private _releaseEditingKeys() {\n window.removeEventListener('keydown', this._onEditingKeydown, true)\n }\n\n private readonly _onEditingKeydown = (e: KeyboardEvent) => {\n // 输入法组合期间的 Enter/Escape 属于组合会话,交给 IME\n if (e.isComposing) return\n if (!e.composedPath().includes(this)) return\n if (e.key === 'Escape') {\n // 按键本身也被编辑层消费:外层浮层不应收到同一次 Escape\n e.preventDefault()\n e.stopPropagation()\n // 先取消(内部摘除编辑态)再交还焦点:focus 触发的 blur 看到 _editing 已为\n // false 而早退,不会把这次取消误判成 blur 提交\n this._cancelEditing()\n this._returnFocusToHost()\n return\n }\n if (e.key === 'Enter') {\n /*\n * 与 Escape 同套消费策略:preventDefault 压掉 textarea 的换行默认行为,\n * stopPropagation 让按键不外泄。取舍是明说的:编辑态 Enter 属于提交,外层\n * 表单的隐式提交与浮层监听不应收到同一次按键——正如 Escape 属于取消。\n * 此前只 preventDefault 不对称,Enter 会漏进外层监听。\n */\n e.preventDefault()\n e.stopPropagation()\n this._commitEditing(true)\n }\n }\n\n private _setupAutosize() {\n this._teardownAutosize()\n // jsdom 等无 ResizeObserver 的环境退化为按渲染 autosize,够用且不抛错\n if (typeof ResizeObserver === 'undefined') {\n this._autosizeEditor()\n return\n }\n this._resizeObserver = new ResizeObserver(() => this._autosizeEditor())\n this._resizeObserver.observe(this)\n this._autosizeEditor()\n }\n\n private _teardownAutosize() {\n this._resizeObserver?.disconnect()\n this._resizeObserver = null\n this._editor?.style.removeProperty('height')\n }\n\n /**\n * 编辑层高度始终跟随自身内容,不依赖文本层折出的行盒。\n *\n * 编辑层绝对定位并用 top/bottom 撑高,`height: auto` 量到的是包含块高度而不是内容高;\n * 先归零再读 scrollHeight,拿到的才是自身内容的高度(含 1px 内边距)。\n * 观察宿主而非编辑层:编辑层在文档流外,改它的高度不会反过来改动宿主尺寸。\n */\n private _autosizeEditor() {\n const editor = this._editor\n if (!editor) return\n const previous = editor.style.height\n editor.style.height = '0px'\n const height = editor.scrollHeight\n editor.style.height = height > 0 ? `${height}px` : previous\n }\n\n private get _displayText(): string {\n if (this._value !== '') return this._value\n if (this.placeholder !== '') return this.placeholder\n return BLANK_GLYPH\n }\n\n override render() {\n return html`\n <div class=\"layers\" @pointerdown=${this._onPointerDown} @click=${this._onClick}>\n <span class=${classMap({ text: true, placeholder: this._value === '' })}>${this._displayText}</span>\n <textarea\n class=\"editor\"\n .value=${this._value}\n .placeholder=${this.placeholder}\n ?disabled=${this._isDisabled}\n aria-label=${ifDefined(this.ariaLabel)}\n @input=${this._onInput}\n @blur=${this._onBlur}\n ></textarea>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n cancel: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-editable-text': WebUiEditableText\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/editable-text/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\n\nimport style from './style.css?inline'\n\n/** 空值且无 placeholder 时占位,保留一个行盒,避免宿主退化成 0 高度、无法点击进入编辑。 */\nconst BLANK_GLYPH = '\\u200B'\n\n/** 行/字符落点二分的容差(px),吸收次像素与舍入差异。 */\nconst CARET_TOLERANCE = 1\n\n/**\n * 取文本层承载文案的 Text 节点。\n *\n * 绑定表达式位于元素首个位置时,Lit 会在它前面留一个注释标记节点,因此不能用\n * `firstChild`:那会拿到没有布局的注释,Range 量不到任何矩形。\n */\nfunction textNodeOf(layer: HTMLElement): Text | null {\n for (const child of layer.childNodes) {\n if (child instanceof Text) return child\n }\n return null\n}\n\n/**\n * 把视口坐标映射为文本层的字符偏移。\n *\n * `document.caretPositionFromPoint` 不进入 shadow DOM(对 shadow 内容返回 shadow host\n * 之外的节点),因此在文本层上用 Range 空矩形二分:先按 y 选行,再在行内按 x 选字符。\n * 文本层与编辑层同盒同排版,偏移与 textarea 的 selectionStart 一一对应。\n */\nfunction caretOffsetFromPoint(textLayer: HTMLElement, x: number, y: number): number {\n const node = textNodeOf(textLayer)\n if (!node) return 0\n const length = node.data.length\n const range = document.createRange()\n const rectAt = (offset: number): DOMRect => {\n range.setStart(node, offset)\n range.setEnd(node, offset)\n return range.getBoundingClientRect()\n }\n\n // 同一行的盒可能被拆成多个 client rect;按 top 去重得到行列表,取与 y 最近的一行,\n // 距离相等时取更靠下的一行(与浏览器在行间的落点行为一致)。\n const lines: DOMRect[] = []\n for (const rect of textLayer.getClientRects()) {\n if (!lines.some(line => Math.abs(line.top - rect.top) <= CARET_TOLERANCE)) lines.push(rect)\n }\n if (lines.length === 0) return length\n let line = lines[0]\n let nearest = Infinity\n for (const candidate of lines) {\n const distance = Math.abs((candidate.top + candidate.bottom) / 2 - y)\n if (distance <= nearest) {\n nearest = distance\n line = candidate\n }\n }\n\n // 行首:caret top 首次落入本行的最小偏移\n let lo = 0\n let hi = length\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (rectAt(mid).top >= line.top - CARET_TOLERANCE) hi = mid\n else lo = mid + 1\n }\n const lineStart = lo\n // 行尾:caret top 仍落在本行的最大偏移\n lo = lineStart\n hi = length\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (rectAt(mid).top <= line.top + CARET_TOLERANCE) lo = mid\n else hi = mid - 1\n }\n const lineEnd = Math.max(lo, lineStart)\n // 行内:caret left 不超过 x 的最大偏移\n lo = lineStart\n hi = lineEnd\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (rectAt(mid).left <= x + CARET_TOLERANCE) lo = mid\n else hi = mid - 1\n }\n return lo\n}\n\n@customElement('web-ui-editable-text')\nexport class WebUiEditableText extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) name = ''\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n\n @state() private _value = ''\n\n /**\n * `value` attribute 承载 form reset 的初值,不随当前值反射;property 暴露当前值,\n * 与原生 input 的 value / defaultValue 关系一致。\n */\n @property({ type: String })\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n @query('.editor') private _editor?: HTMLTextAreaElement\n @query('.text') private _textLayer?: HTMLElement\n\n private _editing = false\n private _editBase = ''\n private _pendingCaret: number | null = null\n private _refocusing = false\n private _resizeObserver: ResizeObserver | null = null\n private readonly _fieldId = createFieldId('web-ui-editable-text')\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n }\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n constructor() {\n super()\n // focus 不冒泡:宿主监听只在焦点从组件外进入时触发,shadow 内聚焦不会误触\n this.addEventListener('focus', this._onHostFocus)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._releaseEditingKeys()\n this._teardownAutosize()\n }\n\n override connectedCallback() {\n super.connectedCallback()\n /*\n * autosize 的 ResizeObserver 在 disconnectedCallback 被拆掉,搭建若只放在\n * firstUpdated(一生一次),元素断开再重连后 RO 永久丢失、autosize 静默失效。\n * connectedCallback 每次连接都跑:_setupAutosize 自带 teardown,重复进入无副作用。\n * 首连接时渲染尚未发生(_editor 为 null),_autosizeEditor 与 RO 回调都有判空守卫。\n */\n this._setupAutosize()\n // 同类缺口:编辑态被断开时 _releaseEditingKeys 撤了 window 监听,重连后 _editing\n // 仍为 true(blur 取消未必先于断开到达),此时补领,避免重连后按键失灵。\n if (this._editing) this._claimEditingKeys()\n }\n\n override updated() {\n // disabled 时移出 tab 序列;编辑态由 editing attribute 表达,二者互斥\n if (this._isDisabled) this.removeAttribute('tabindex')\n else this.setAttribute('tabindex', '0')\n // 禁用态生效时(属性或 fieldset)退出编辑:值保持当前草稿,不派发提交事件\n if (this._editing && this._isDisabled) this._exitEditing()\n this._autosizeEditor()\n }\n\n private _onHostFocus() {\n if (this._refocusing) {\n this._refocusing = false\n return\n }\n this._enterEditing()\n }\n\n /**\n * pointerdown 时文本层仍是命中目标,此时记录落点;浏览器随后的默认聚焦会触发\n * host focus 进入编辑,偏移在那里应用到 textarea。\n */\n private _onPointerDown(e: PointerEvent) {\n if (this._isDisabled || this._editing || !this._textLayer) return\n this._pendingCaret = caretOffsetFromPoint(this._textLayer, e.clientX, e.clientY)\n }\n\n /** mousedown 默认聚焦被拦截等场景由 click 兜底进入编辑。 */\n private _onClick() {\n if (this._isDisabled || this._editing) return\n this._enterEditing()\n }\n\n /**\n * 公共 API:进入编辑态并全选内容;已在编辑态时只重新全选。\n *\n * `disabled` 时不进入编辑,与 `focus()` 一致。\n */\n select() {\n if (this._isDisabled) return\n if (!this._editing) this._enterEditing()\n const editor = this._editor\n // 全选:末端显式取当前文本长度,覆盖 _enterEditing 落下的光标位置\n editor?.setSelectionRange(0, editor.value.length)\n }\n\n private _enterEditing() {\n if (this._isDisabled || this._editing) return\n this._editing = true\n this._editBase = this._value\n this.toggleAttribute('editing', true)\n this._claimEditingKeys()\n const editor = this._editor\n if (!editor) return\n editor.focus()\n // Tab / 编程式聚焦光标到末尾;指针落点用 pointerdown 记下的偏移\n const offset = this._pendingCaret ?? this._value.length\n this._pendingCaret = null\n const clamped = Math.max(0, Math.min(offset, editor.value.length))\n editor.setSelectionRange(clamped, clamped)\n }\n\n private _exitEditing() {\n if (!this._editing) return\n this._editing = false\n this._releaseEditingKeys()\n this.toggleAttribute('editing', false)\n }\n\n private _onInput() {\n // 原生 input 已 composed 冒泡出 shadow root 且 target 重定向到宿主,无需补发\n const editor = this._editor\n if (this._isDisabled || this.readonly) {\n if (editor && editor.value !== this._value) editor.value = this._value\n return\n }\n this._value = editor?.value ?? ''\n this._formAssociation.sync()\n this._autosizeEditor()\n }\n\n /**\n * blur 提交:与 Enter 同一条提交路径,草稿成为新值并派发 change。\n *\n * 不交还焦点:焦点已被用户移走,抢回宿主会吞掉消费者的目标焦点。\n */\n private _onBlur() {\n if (!this._editing || this._isDisabled) return\n if (this.readonly) {\n this._exitEditing()\n return\n }\n this._commitEditing(false)\n }\n\n /**\n * 提交:草稿成为新值、退出编辑,并恰好派发一次 change。Enter 与 blur 同走这一条路。\n *\n * `returnFocus` 区分两种来路:Enter 主动结束时把焦点交还宿主(标记防重入编辑);\n * blur 是被动失焦,焦点已被用户移走,再抢回宿主会吞掉消费者的目标焦点。\n * 先退出编辑再动焦点:退出时 textarea 隐藏触发的 blur 看到 _editing 已为 false,\n * 不会把这次提交误判成又一次 blur 提交。\n */\n private _commitEditing(returnFocus: boolean) {\n if (!this._editing) return\n this._exitEditing()\n if (returnFocus) this._returnFocusToHost()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n /**\n * 取消:恢复进入编辑时的值并派发 cancel。仅 Escape 走这一条路径。\n *\n * 调用方(Escape 分支)随后才交还焦点:本方法先摘除编辑态,焦点迁移触发的 blur\n * 到达时 `_editing` 已为 false 而早退,不会把取消误判成 blur 提交。\n */\n private _cancelEditing() {\n if (!this._editing) return\n this._value = this._editBase\n this._formAssociation.sync()\n this._exitEditing()\n /*\n * 重入守卫先于 dispatch 置位:cancel 同步派发,消费者可能在监听器里调\n * el.focus()——那是一次真实的宿主 focus,没有守卫就会重新进入编辑,随后一次\n * blur 便把恢复后的原值当成新草稿提交,用户什么都没改却收到 change。守卫在此\n * 置位后即被该次 focus 消费,与取消后由组件自己交还焦点走同一条早退路径。\n */\n this._refocusing = true\n /*\n * cancel 与原生 <dialog> 的 cancel 同名,而 dialog 的关闭管线就监听这个事件名。\n * 组件被消费方投映进浮层 shadow(drawer/dialog 标题)时,任何冒泡的 cancel——\n * composed 与否都一样,slot 都会把事件带进 shadow 树——都会被那个 dialog 当成\n * 一次关闭请求:issue #159 的实测症状是 drawer 标题聚焦编辑时按 Escape,或点到\n * 抽屉别处触发 blur,抽屉跟着编辑一起关。因此只在宿主上派发,不冒泡不组合;\n * 监听挂在组件本身即可(vue @cancel、React addEventListener 均不受影响)。\n * 手法同 checkbox/radio 受管时的事件策略(见各自 handleClick 注释)。\n */\n this.dispatchEvent(new Event('cancel', { bubbles: false, composed: false }))\n }\n\n /**\n * 焦点交还宿主:先标记再聚焦,宿主 focus 到达时标记已消费,不会再次进入编辑;\n * 标记在微任务里清掉,此后的聚焦照常进入编辑。\n */\n private _returnFocusToHost() {\n this._refocusing = true\n this.focus()\n queueMicrotask(() => {\n this._refocusing = false\n })\n }\n\n /**\n * 编辑态按键归属:Enter 提交、Escape 取消。\n *\n * 监听挂在 window 捕获阶段而不是 shadow 内的 textarea 上:消费方(浮层仲裁者)在\n * document 捕获阶段收 Escape,组件若只在 textarea 上处理,按键先被仲裁者收走,\n * 于是一次 Escape 会同时取消编辑和关闭外层浮层。window 在 document 上游,\n * stopPropagation 只有挂在这里才拦得住仲裁者。\n */\n private _claimEditingKeys() {\n window.addEventListener('keydown', this._onEditingKeydown, true)\n }\n\n private _releaseEditingKeys() {\n window.removeEventListener('keydown', this._onEditingKeydown, true)\n }\n\n private readonly _onEditingKeydown = (e: KeyboardEvent) => {\n // 输入法组合期间的 Enter/Escape 属于组合会话,交给 IME\n if (e.isComposing) return\n if (!e.composedPath().includes(this)) return\n if (e.key === 'Escape') {\n // 按键本身也被编辑层消费:外层浮层不应收到同一次 Escape\n e.preventDefault()\n e.stopPropagation()\n // 先取消(内部摘除编辑态)再交还焦点:focus 触发的 blur 看到 _editing 已为\n // false 而早退,不会把这次取消误判成 blur 提交\n this._cancelEditing()\n this._returnFocusToHost()\n return\n }\n if (e.key === 'Enter') {\n /*\n * Enter 无论是否只读都属于编辑层,消费策略与 Escape 对称:preventDefault\n * 压掉默认行为,stopPropagation 阻止外层表单隐式提交或浮层监听收到按键。\n * 只读态只是没有草稿可提交,因此消费后直接返回,不退出编辑、不派发 change。\n */\n e.preventDefault()\n e.stopPropagation()\n if (this.readonly) return\n /*\n * 可编辑态的 Enter 随后提交草稿;外层表单的隐式提交与浮层监听不应收到\n * 同一次按键——正如 Escape 属于取消。\n */\n this._commitEditing(true)\n }\n }\n\n private _setupAutosize() {\n this._teardownAutosize()\n // jsdom 等无 ResizeObserver 的环境退化为按渲染 autosize,够用且不抛错\n if (typeof ResizeObserver === 'undefined') {\n this._autosizeEditor()\n return\n }\n this._resizeObserver = new ResizeObserver(() => this._autosizeEditor())\n this._resizeObserver.observe(this)\n this._autosizeEditor()\n }\n\n private _teardownAutosize() {\n this._resizeObserver?.disconnect()\n this._resizeObserver = null\n this._editor?.style.removeProperty('height')\n }\n\n /**\n * 编辑层高度始终跟随自身内容,不依赖文本层折出的行盒。\n *\n * 编辑层绝对定位并用 top/bottom 撑高,`height: auto` 量到的是包含块高度而不是内容高;\n * 先归零再读 scrollHeight,拿到的才是自身内容的高度(含 1px 内边距)。\n * 观察宿主而非编辑层:编辑层在文档流外,改它的高度不会反过来改动宿主尺寸。\n */\n private _autosizeEditor() {\n const editor = this._editor\n if (!editor) return\n const previous = editor.style.height\n editor.style.height = '0px'\n const height = editor.scrollHeight\n editor.style.height = height > 0 ? `${height}px` : previous\n }\n\n private get _displayText(): string {\n if (this._value !== '') return this._value\n if (this.placeholder !== '') return this.placeholder\n return BLANK_GLYPH\n }\n\n override render() {\n return html`\n <div class=\"layers\" @pointerdown=${this._onPointerDown} @click=${this._onClick}>\n <span class=${classMap({ text: true, placeholder: this._value === '' })}>${this._displayText}</span>\n <textarea\n id=${this._fieldId}\n class=\"editor\"\n .value=${this._value}\n .placeholder=${this.placeholder}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n aria-label=${ifDefined(this.ariaLabel)}\n @input=${this._onInput}\n @blur=${this._onBlur}\n ></textarea>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n cancel: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-editable-text': WebUiEditableText\n }\n}\n"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { customElement as o, property as s } from "lit/decorators.js";
|
|
|
5
5
|
import { classMap as c } from "lit/directives/class-map.js";
|
|
6
6
|
import { unsafeSVG as l } from "lit/directives/unsafe-svg.js";
|
|
7
7
|
//#region src/components/icon/index.ts
|
|
8
|
-
var u = class extends n {
|
|
8
|
+
var u = 16, d = class extends n {
|
|
9
9
|
constructor(...e) {
|
|
10
10
|
super(...e), this.spin = !1, this.size = 18;
|
|
11
11
|
}
|
|
@@ -14,7 +14,7 @@ var u = class extends n {
|
|
|
14
14
|
}
|
|
15
15
|
render() {
|
|
16
16
|
if (!this.icon) return i;
|
|
17
|
-
let e = this.color ? `color: ${this.color}` : "", t = `${this.icon.left ?? 0} ${this.icon.top ?? 0} ${this.icon.width ??
|
|
17
|
+
let e = this.color ? `color: ${this.color}` : "", t = `${this.icon.left ?? 0} ${this.icon.top ?? 0} ${this.icon.width ?? u} ${this.icon.height ?? u}`;
|
|
18
18
|
return r`
|
|
19
19
|
<svg
|
|
20
20
|
class=${c({ spin: this.spin })}
|
|
@@ -29,17 +29,17 @@ var u = class extends n {
|
|
|
29
29
|
`;
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
-
t([s({ attribute: !1 })],
|
|
32
|
+
t([s({ attribute: !1 })], d.prototype, "icon", void 0), t([s({
|
|
33
33
|
type: Boolean,
|
|
34
34
|
reflect: !0
|
|
35
|
-
})],
|
|
35
|
+
})], d.prototype, "spin", void 0), t([s({
|
|
36
36
|
type: Number,
|
|
37
37
|
reflect: !0
|
|
38
|
-
})],
|
|
38
|
+
})], d.prototype, "size", void 0), t([s({
|
|
39
39
|
type: String,
|
|
40
40
|
reflect: !0
|
|
41
|
-
})],
|
|
41
|
+
})], d.prototype, "color", void 0), d = t([o("web-ui-icon")], d);
|
|
42
42
|
//#endregion
|
|
43
|
-
export {
|
|
43
|
+
export { d as WebUiIcon };
|
|
44
44
|
|
|
45
45
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/icon/index.ts"],"sourcesContent":["import type { IconifyIcon } from '@iconify/types'\nimport { html, LitElement, nothing, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { unsafeSVG } from 'lit/directives/unsafe-svg.js'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-icon')\nexport class WebUiIcon extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ attribute: false }) icon?: IconifyIcon\n @property({ type: Boolean, reflect: true }) spin = false\n @property({ type: Number, reflect: true }) size: number = 18\n @property({ type: String, reflect: true }) color?: string\n\n override render() {\n if (!this.icon) return nothing\n\n const svgStyle = this.color ? `color: ${this.color}` : ''\n const viewBox = `${this.icon.left ?? 0} ${this.icon.top ?? 0} ${this.icon.width ??
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/icon/index.ts"],"sourcesContent":["import type { IconifyIcon } from '@iconify/types'\nimport { html, LitElement, nothing, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { unsafeSVG } from 'lit/directives/unsafe-svg.js'\n\nimport style from './style.css?inline'\n\n/*\n * Iconify 规范里 width/height 缺省即 16(见 @iconify/types README),这里必须跟规范一致:\n * 猜成别的值不会报错,只会把图形按错误比例缩放并偏到画布一角。生成资产已自带尺寸,\n * 这条兜底服务的是直接喂进来的原始 IconifyIcon 对象。\n */\nconst ICONIFY_DEFAULT_CANVAS = 16\n\n@customElement('web-ui-icon')\nexport class WebUiIcon extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ attribute: false }) icon?: IconifyIcon\n @property({ type: Boolean, reflect: true }) spin = false\n @property({ type: Number, reflect: true }) size: number = 18\n @property({ type: String, reflect: true }) color?: string\n\n override render() {\n if (!this.icon) return nothing\n\n const svgStyle = this.color ? `color: ${this.color}` : ''\n const viewBox = `${this.icon.left ?? 0} ${this.icon.top ?? 0} ${this.icon.width ?? ICONIFY_DEFAULT_CANVAS} ${this.icon.height ?? ICONIFY_DEFAULT_CANVAS}`\n\n return html`\n <svg\n class=${classMap({ spin: this.spin })}\n style=${svgStyle}\n viewBox=\"${viewBox}\"\n width=\"${this.size}\"\n height=\"${this.size}\"\n aria-hidden=\"true\"\n >\n ${unsafeSVG(this.icon.body)}\n </svg>\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-icon': WebUiIcon\n }\n}\n"],"mappings":";;;;;;;AAaA,IAAM,IAAyB,IAGlB,IAAN,cAAwB,EAAW;;EAKkB,aADP,KAAA,OAAA,IACO,KAAA,OAAA;;;EAJjC,KAAA,SAAA,EAAU,CAAK;;CAOxC,SAAkB;EAChB,IAAI,CAAC,KAAK,MAAM,OAAO;EAEvB,IAAM,IAAW,KAAK,QAAQ,UAAU,KAAK,UAAU,IACjD,IAAU,GAAG,KAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,EAAE,GAAG,KAAK,KAAK,SAAS,EAAuB,GAAG,KAAK,KAAK,UAAU;EAEjI,OAAO,CAAI;;gBAEC,EAAS,EAAE,MAAM,KAAK,KAAK,CAAC,EAAE;gBAC9B,EAAS;mBACN,EAAQ;iBACV,KAAK,KAAK;kBACT,KAAK,KAAK;;;UAGlB,EAAU,KAAK,KAAK,IAAI,EAAE;;;CAGlC;AACF;AAxBG,EAAA,CAAA,EAAS,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GAC7B,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GACxC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,SAAA,KAAA,CAAA,GAP1C,IAAA,EAAA,CAAA,EAAc,aAAa,CAAA,GAAA,CAAA"}
|
|
@@ -379,6 +379,7 @@ export declare class WebUiInput extends WebUiInput_base {
|
|
|
379
379
|
private _focused;
|
|
380
380
|
private _hasPrefix;
|
|
381
381
|
private _hasSuffix;
|
|
382
|
+
private readonly _fieldId;
|
|
382
383
|
get value(): string;
|
|
383
384
|
set value(v: string);
|
|
384
385
|
private get _isDisabled();
|
|
@@ -396,6 +397,12 @@ export declare class WebUiInput extends WebUiInput_base {
|
|
|
396
397
|
private focusInput;
|
|
397
398
|
focus(options?: FocusOptions): void;
|
|
398
399
|
blur(): void;
|
|
400
|
+
/**
|
|
401
|
+
* 公共 API:全选当前值。
|
|
402
|
+
*
|
|
403
|
+
* `disabled` 时与 `focus()` 一致不产生效果;原生控件未渲染时安全 no-op。
|
|
404
|
+
*/
|
|
405
|
+
select(): void;
|
|
399
406
|
render(): import('lit').TemplateResult<1>;
|
|
400
407
|
readonly $events: {
|
|
401
408
|
input: Event;
|
|
@@ -2,17 +2,18 @@ import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate
|
|
|
2
2
|
import "../icon/index.js";
|
|
3
3
|
import t from "../../assets/glass.js";
|
|
4
4
|
import n from "../../icons/generated/jam-close-circle-f.js";
|
|
5
|
-
import "../button/index.js";
|
|
6
5
|
import { FormAssociated as r, FormAssociationController as i, defineFormAssociation as a, forwardInputValidity as o } from "../../shared/form-association/index.js";
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
6
|
+
import "../button/index.js";
|
|
7
|
+
import { createFieldId as s } from "../../shared/form-association/field-id.js";
|
|
8
|
+
import c from "./style.js";
|
|
9
|
+
import { LitElement as l, html as u, unsafeCSS as d } from "lit";
|
|
10
|
+
import { customElement as f, property as p, state as m } from "lit/decorators.js";
|
|
11
|
+
import { ifDefined as h } from "lit/directives/if-defined.js";
|
|
12
|
+
import { classMap as g } from "lit/directives/class-map.js";
|
|
12
13
|
//#region src/components/input/index.ts
|
|
13
|
-
var
|
|
14
|
+
var _ = class extends r(l) {
|
|
14
15
|
constructor(...e) {
|
|
15
|
-
super(...e), this.type = "text", this.placeholder = "", this.name = "", this.disabled = !1, this.readonly = !1, this.required = !1, this.clearable = !1, this.full = !1, this.borderless = !1, this.ariaLabel = null, this._value = "", this._focused = !1, this._hasPrefix = !1, this._hasSuffix = !1, this._formAssociation = a({
|
|
16
|
+
super(...e), this.type = "text", this.placeholder = "", this.name = "", this.disabled = !1, this.readonly = !1, this.required = !1, this.clearable = !1, this.full = !1, this.borderless = !1, this.ariaLabel = null, this._value = "", this._focused = !1, this._hasPrefix = !1, this._hasSuffix = !1, this._fieldId = s("web-ui-input"), this._formAssociation = a({
|
|
16
17
|
host: this,
|
|
17
18
|
getState: () => this._value,
|
|
18
19
|
setState: (e) => {
|
|
@@ -27,7 +28,7 @@ var g = class extends r(c) {
|
|
|
27
28
|
}).make(), this._formAssociationController = new i(this, this._formAssociation);
|
|
28
29
|
}
|
|
29
30
|
static {
|
|
30
|
-
this.styles = [
|
|
31
|
+
this.styles = [d(t), d(c)];
|
|
31
32
|
}
|
|
32
33
|
get value() {
|
|
33
34
|
return this._value;
|
|
@@ -65,7 +66,7 @@ var g = class extends r(c) {
|
|
|
65
66
|
this._isDisabled || this.readonly || e.target instanceof HTMLInputElement && (this._value = e.target.value, this._formAssociation.sync());
|
|
66
67
|
}
|
|
67
68
|
handleNativeChange(e) {
|
|
68
|
-
this.readonly || e.target instanceof HTMLInputElement && (this._value = e.target.value, this._formAssociation.sync(), this.dispatchEvent(new Event("change", {
|
|
69
|
+
this._isDisabled || this.readonly || e.target instanceof HTMLInputElement && (this._value = e.target.value, this._formAssociation.sync(), this.dispatchEvent(new Event("change", {
|
|
69
70
|
bubbles: !0,
|
|
70
71
|
composed: !0
|
|
71
72
|
})));
|
|
@@ -89,21 +90,25 @@ var g = class extends r(c) {
|
|
|
89
90
|
this._isDisabled || this.shadowRoot?.querySelector("input")?.focus();
|
|
90
91
|
}
|
|
91
92
|
focus(e) {
|
|
92
|
-
this.shadowRoot?.querySelector("input")?.focus(e);
|
|
93
|
+
this._isDisabled || this.shadowRoot?.querySelector("input")?.focus(e);
|
|
93
94
|
}
|
|
94
95
|
blur() {
|
|
95
96
|
this.shadowRoot?.querySelector("input")?.blur();
|
|
96
97
|
}
|
|
98
|
+
select() {
|
|
99
|
+
this._isDisabled || this.shadowRoot?.querySelector("input")?.select();
|
|
100
|
+
}
|
|
97
101
|
render() {
|
|
98
102
|
let e = this.clearable && this._value && !this.readonly;
|
|
99
|
-
return
|
|
103
|
+
return u`
|
|
100
104
|
<div class="wui-glass wui-input-inner" @click=${this.focusInput}>
|
|
101
|
-
<slot name="prefix" class=${
|
|
105
|
+
<slot name="prefix" class=${g({ empty: !this._hasPrefix })} @slotchange=${this._onSlotChange}></slot>
|
|
102
106
|
<input
|
|
107
|
+
id=${this._fieldId}
|
|
103
108
|
type=${this.type}
|
|
104
109
|
placeholder=${this.placeholder}
|
|
105
110
|
name=${this.name}
|
|
106
|
-
aria-label=${
|
|
111
|
+
aria-label=${h(this.ariaLabel)}
|
|
107
112
|
?disabled=${this._isDisabled}
|
|
108
113
|
?readonly=${this.readonly}
|
|
109
114
|
?required=${this.required}
|
|
@@ -113,7 +118,7 @@ var g = class extends r(c) {
|
|
|
113
118
|
@focus=${this.handleFocus}
|
|
114
119
|
@blur=${this.handleBlur}
|
|
115
120
|
/>
|
|
116
|
-
${e ?
|
|
121
|
+
${e ? u`<web-ui-button
|
|
117
122
|
icon
|
|
118
123
|
variant="ghost"
|
|
119
124
|
size="20"
|
|
@@ -123,46 +128,46 @@ var g = class extends r(c) {
|
|
|
123
128
|
>
|
|
124
129
|
<web-ui-icon .icon=${n} size="14"></web-ui-icon>
|
|
125
130
|
</web-ui-button>` : ""}
|
|
126
|
-
<slot name="suffix" class=${
|
|
131
|
+
<slot name="suffix" class=${g({ empty: !this._hasSuffix })} @slotchange=${this._onSlotChange}></slot>
|
|
127
132
|
</div>
|
|
128
133
|
`;
|
|
129
134
|
}
|
|
130
135
|
};
|
|
131
|
-
e([
|
|
136
|
+
e([p({
|
|
132
137
|
type: String,
|
|
133
138
|
reflect: !0
|
|
134
|
-
})],
|
|
139
|
+
})], _.prototype, "type", void 0), e([p({
|
|
135
140
|
type: String,
|
|
136
141
|
reflect: !0
|
|
137
|
-
})],
|
|
142
|
+
})], _.prototype, "placeholder", void 0), e([p({
|
|
138
143
|
type: String,
|
|
139
144
|
reflect: !0
|
|
140
|
-
})],
|
|
145
|
+
})], _.prototype, "name", void 0), e([p({
|
|
141
146
|
type: Boolean,
|
|
142
147
|
reflect: !0
|
|
143
|
-
})],
|
|
148
|
+
})], _.prototype, "disabled", void 0), e([p({
|
|
144
149
|
type: Boolean,
|
|
145
150
|
reflect: !0
|
|
146
|
-
})],
|
|
151
|
+
})], _.prototype, "readonly", void 0), e([p({
|
|
147
152
|
type: Boolean,
|
|
148
153
|
reflect: !0
|
|
149
|
-
})],
|
|
154
|
+
})], _.prototype, "required", void 0), e([p({
|
|
150
155
|
type: Boolean,
|
|
151
156
|
reflect: !0
|
|
152
|
-
})],
|
|
157
|
+
})], _.prototype, "clearable", void 0), e([p({
|
|
153
158
|
type: Boolean,
|
|
154
159
|
reflect: !0
|
|
155
|
-
})],
|
|
160
|
+
})], _.prototype, "full", void 0), e([p({
|
|
156
161
|
type: Boolean,
|
|
157
162
|
reflect: !0
|
|
158
|
-
})],
|
|
163
|
+
})], _.prototype, "borderless", void 0), e([p({
|
|
159
164
|
type: String,
|
|
160
165
|
attribute: "aria-label"
|
|
161
|
-
})],
|
|
166
|
+
})], _.prototype, "ariaLabel", void 0), e([m()], _.prototype, "_value", void 0), e([m()], _.prototype, "_focused", void 0), e([m()], _.prototype, "_hasPrefix", void 0), e([m()], _.prototype, "_hasSuffix", void 0), e([p({
|
|
162
167
|
type: String,
|
|
163
168
|
reflect: !0
|
|
164
|
-
})],
|
|
169
|
+
})], _.prototype, "value", null), _ = e([f("web-ui-input")], _);
|
|
165
170
|
//#endregion
|
|
166
|
-
export {
|
|
171
|
+
export { _ as WebUiInput };
|
|
167
172
|
|
|
168
173
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/input/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport '@/components/icon'\nimport '@/components/button'\nimport glass from '@/assets/glass.css?inline'\nimport { jamCloseCircleF } from '@/icons'\nimport {\n FormAssociated,\n defineFormAssociation,\n forwardInputValidity,\n FormAssociationController\n} from '@/shared/form-association'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-input')\nexport class WebUiInput extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) type = 'text'\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) clearable = false\n @property({ type: Boolean, reflect: true }) full = false\n @property({ type: Boolean, reflect: true }) borderless = false\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n\n @state() private _value = ''\n @state() private _focused = false\n @state() private _hasPrefix = false\n @state() private _hasSuffix = false\n\n @property({ type: String, reflect: true })\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_value')) {\n const input = this.shadowRoot?.querySelector('input')\n if (input && input.value !== this._value) {\n input.value = this._value\n }\n }\n this._syncValidity()\n this.toggleAttribute('focused', this._focused)\n }\n\n private _syncValidity() {\n const input = this.shadowRoot?.querySelector('input')\n const internals = this._formAssociation.getInternals()\n if (!internals || !input || typeof internals.setValidity !== 'function') return\n forwardInputValidity(\n internals,\n input,\n ['valueMissing', 'typeMismatch', 'patternMismatch', 'tooLong', 'tooShort'],\n this._isDisabled\n )\n }\n\n private _onSlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n const slot = e.target\n const hasContent = slot.assignedElements().length > 0\n if (slot.name === 'prefix') this._hasPrefix = hasContent\n if (slot.name === 'suffix') this._hasSuffix = hasContent\n }\n\n private handleInput(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n }\n\n // 原生 change 不 composed,被 shadow root 挡住;这里补发 composed 事件,\n // 兑现 $events/README 声明的公共 change 契约。\n private handleNativeChange(e: Event) {\n if (this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleFocus() {\n if (!this._isDisabled) this._focused = true\n }\n\n private handleBlur() {\n this._focused = false\n }\n\n private handleClear() {\n if (this._isDisabled || this.readonly) return\n this._value = ''\n this._formAssociation.sync()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n }\n\n private preventMouseDownBlur(e: Event) {\n e.preventDefault()\n }\n\n private focusInput() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.focus()\n }\n\n // 公共 focus/blur 与 textarea 对齐:宿主自身没有 tab 位(不可聚焦),\n // 不重定向到内部原生控件的话,调用方拿到的是一次空操作\n override focus(options?: FocusOptions) {\n this.shadowRoot?.querySelector('input')?.focus(options)\n }\n\n override blur() {\n this.shadowRoot?.querySelector('input')?.blur()\n }\n\n override render() {\n // readonly 下不可清除:清空按钮会修改值,与只读语义冲突\n const showClear = this.clearable && this._value && !this.readonly\n\n return html`\n <div class=\"wui-glass wui-input-inner\" @click=${this.focusInput}>\n <slot name=\"prefix\" class=${classMap({ empty: !this._hasPrefix })} @slotchange=${this._onSlotChange}></slot>\n <input\n type=${this.type}\n placeholder=${this.placeholder}\n name=${this.name}\n aria-label=${ifDefined(this.ariaLabel)}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n .value=${this._value}\n @input=${this.handleInput}\n @change=${this.handleNativeChange}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n />\n ${\n showClear\n ? html`<web-ui-button\n icon\n variant=\"ghost\"\n size=\"20\"\n aria-label=\"清除\"\n @pointerdown=${this.preventMouseDownBlur}\n @click=${this.handleClear}\n >\n <web-ui-icon .icon=${jamCloseCircleF} size=\"14\"></web-ui-icon>\n </web-ui-button>`\n : ''\n }\n <slot name=\"suffix\" class=${classMap({ empty: !this._hasSuffix })} @slotchange=${this._onSlotChange}></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n focus: FocusEvent\n blur: FocusEvent\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-input': WebUiInput\n }\n}\n"],"mappings":";;;;;;;;;;;;AAmBO,IAAM,IAAN,cAAyB,EAAe,CAAU,CAAC,CAAC;;EAiDX,aA9CI,KAAA,OAAA,QACO,KAAA,cAAA,IACP,KAAA,OAAA,IACK,KAAA,WAAA,IACA,KAAA,WAAA,IACA,KAAA,WAAA,IACC,KAAA,YAAA,IACL,KAAA,OAAA,IACM,KAAA,aAAA,IACgC,KAAA,YAAA,MAE/D,KAAA,SAAA,IACE,KAAA,WAAA,IACE,KAAA,aAAA,IACA,KAAA,aAAA,IAkBM,KAAA,mBAAA,EAA8B;GAChE,MAAM;GACN,gBAAgB,KAAK;GACrB,WAAU,MAAS;IACjB,KAAK,QAAQ;GACf;GACA,oBAAoB,KAAK;GACzB,oBAAoB,KAAK;GACzB,eAAc,MAAS;IACrB,AAAI,OAAO,KAAU,aAAU,KAAK,QAAQ;GAC9C;GACA,oBAAoB,KAAK,cAAc;EACzC,CAAC,CAAC,CAAC,KAAK,GAEsC,KAAA,6BAAA,IAAI,EAA0B,MAAM,KAAK,gBAAgB;;;EAhD9E,KAAA,SAAA,CAAC,EAAU,CAAK,GAAG,EAAU,CAAK,CAAC;;CAkB5D,IACI,QAAgB;EAClB,OAAO,KAAK;CACd;CAEA,IAAI,MAAM,GAAW;EACnB,IAAM,IAAM,KAAK;EAGjB,AAFA,KAAK,SAAS,GACd,KAAK,iBAAiB,KAAK,GAC3B,KAAK,cAAc,SAAS,CAAG;CACjC;CAEA,IAAY,cAAuB;EACjC,OAAO,KAAK,YAAY,KAAK,iBAAiB,eAAe;CAC/D;CAkBA,QAAiB,GAA+B;EAC9C,IAAI,EAAQ,IAAI,QAAQ,GAAG;GACzB,IAAM,IAAQ,KAAK,YAAY,cAAc,OAAO;GACpD,AAAI,KAAS,EAAM,UAAU,KAAK,WAChC,EAAM,QAAQ,KAAK;EAEvB;EAEA,AADA,KAAK,cAAc,GACnB,KAAK,gBAAgB,WAAW,KAAK,QAAQ;CAC/C;CAEA,gBAAwB;EACtB,IAAM,IAAQ,KAAK,YAAY,cAAc,OAAO,GAC9C,IAAY,KAAK,iBAAiB,aAAa;EACjD,AAAC,KAAc,KAAS,OAAO,EAAU,eAAgB,cAC7D,EACE,GACA,GACA;GAAC;GAAgB;GAAgB;GAAmB;GAAW;EAAU,GACzE,KAAK,WACP;CACF;CAEA,cAAsB,GAAU;EAC9B,IAAI,EAAE,EAAE,kBAAkB,kBAAkB;EAC5C,IAAM,IAAO,EAAE,QACT,IAAa,EAAK,iBAAiB,CAAC,CAAC,SAAS;EAEpD,AADI,EAAK,SAAS,aAAU,KAAK,aAAa,IAC1C,EAAK,SAAS,aAAU,KAAK,aAAa;CAChD;CAEA,YAAoB,GAAU;EACxB,KAAK,eAAe,KAAK,YACvB,EAAE,kBAAkB,qBAC1B,KAAK,SAAS,EAAE,OAAO,OACvB,KAAK,iBAAiB,KAAK;CAC7B;CAIA,mBAA2B,GAAU;EAC/B,KAAK,YACH,EAAE,kBAAkB,qBAC1B,KAAK,SAAS,EAAE,OAAO,OACvB,KAAK,iBAAiB,KAAK,GAC3B,KAAK,cAAc,IAAI,MAAM,UAAU;GAAE,SAAS;GAAM,UAAU;EAAK,CAAC,CAAC;CAC3E;CAEA,cAAsB;EACpB,AAAK,KAAK,gBAAa,KAAK,WAAW;CACzC;CAEA,aAAqB;EACnB,KAAK,WAAW;CAClB;CAEA,cAAsB;EAChB,KAAK,eAAe,KAAK,aAC7B,KAAK,SAAS,IACd,KAAK,iBAAiB,KAAK,GAC3B,KAAK,cAAc,IAAI,MAAM,SAAS;GAAE,SAAS;GAAM,UAAU;EAAK,CAAC,CAAC;CAC1E;CAEA,qBAA6B,GAAU;EACrC,EAAE,eAAe;CACnB;CAEA,aAAqB;EACf,KAAK,eACT,KAAK,YAAY,cAAc,OAAO,CAAC,EAAE,MAAM;CACjD;CAIA,MAAe,GAAwB;EACrC,KAAK,YAAY,cAAc,OAAO,CAAC,EAAE,MAAM,CAAO;CACxD;CAEA,OAAgB;EACd,KAAK,YAAY,cAAc,OAAO,CAAC,EAAE,KAAK;CAChD;CAEA,SAAkB;EAEhB,IAAM,IAAY,KAAK,aAAa,KAAK,UAAU,CAAC,KAAK;EAEzD,OAAO,CAAI;sDACuC,KAAK,WAAW;oCAClC,EAAS,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC,EAAE,eAAe,KAAK,cAAc;;iBAE3F,KAAK,KAAK;wBACH,KAAK,YAAY;iBACxB,KAAK,KAAK;uBACJ,EAAU,KAAK,SAAS,EAAE;sBAC3B,KAAK,YAAY;sBACjB,KAAK,SAAS;sBACd,KAAK,SAAS;mBACjB,KAAK,OAAO;mBACZ,KAAK,YAAY;oBAChB,KAAK,mBAAmB;mBACzB,KAAK,YAAY;kBAClB,KAAK,WAAW;;UAGxB,IACI,CAAI;;;;;+BAKa,KAAK,qBAAqB;yBAChC,KAAK,YAAY;;qCAEL,EAAgB;kCAEvC,GACL;oCAC2B,EAAS,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC,EAAE,eAAe,KAAK,cAAc;;;CAG1G;AAQF;AAhLG,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GACxC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,eAAA,KAAA,CAAA,GACxC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GACxC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,aAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAa,CAAC,CAAA,GAAA,EAAA,WAAA,aAAA,KAAA,CAAA,GAElD,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,GACN,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GACN,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,GACN,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,GAEN,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,SAAA,IAAA,GApB1C,IAAA,EAAA,CAAA,EAAc,cAAc,CAAA,GAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/input/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport '@/components/icon'\nimport '@/components/button'\nimport glass from '@/assets/glass.css?inline'\nimport { jamCloseCircleF } from '@/icons'\nimport {\n FormAssociated,\n defineFormAssociation,\n forwardInputValidity,\n FormAssociationController\n} from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-input')\nexport class WebUiInput extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) type = 'text'\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) clearable = false\n @property({ type: Boolean, reflect: true }) full = false\n @property({ type: Boolean, reflect: true }) borderless = false\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n\n @state() private _value = ''\n @state() private _focused = false\n @state() private _hasPrefix = false\n @state() private _hasSuffix = false\n\n private readonly _fieldId = createFieldId('web-ui-input')\n\n @property({ type: String, reflect: true })\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_value')) {\n const input = this.shadowRoot?.querySelector('input')\n if (input && input.value !== this._value) {\n input.value = this._value\n }\n }\n this._syncValidity()\n this.toggleAttribute('focused', this._focused)\n }\n\n private _syncValidity() {\n const input = this.shadowRoot?.querySelector('input')\n const internals = this._formAssociation.getInternals()\n if (!internals || !input || typeof internals.setValidity !== 'function') return\n forwardInputValidity(\n internals,\n input,\n ['valueMissing', 'typeMismatch', 'patternMismatch', 'tooLong', 'tooShort'],\n this._isDisabled\n )\n }\n\n private _onSlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n const slot = e.target\n const hasContent = slot.assignedElements().length > 0\n if (slot.name === 'prefix') this._hasPrefix = hasContent\n if (slot.name === 'suffix') this._hasSuffix = hasContent\n }\n\n private handleInput(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n }\n\n // 原生 change 不 composed,被 shadow root 挡住;这里补发 composed 事件,\n // 兑现 $events/README 声明的公共 change 契约。\n private handleNativeChange(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleFocus() {\n if (!this._isDisabled) this._focused = true\n }\n\n private handleBlur() {\n this._focused = false\n }\n\n private handleClear() {\n if (this._isDisabled || this.readonly) return\n this._value = ''\n this._formAssociation.sync()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n }\n\n private preventMouseDownBlur(e: Event) {\n e.preventDefault()\n }\n\n private focusInput() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.focus()\n }\n\n // 公共 focus/blur 与 textarea 对齐:宿主自身没有 tab 位(不可聚焦),\n // 不重定向到内部原生控件的话,调用方拿到的是一次空操作\n override focus(options?: FocusOptions) {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.focus(options)\n }\n\n override blur() {\n this.shadowRoot?.querySelector('input')?.blur()\n }\n\n /**\n * 公共 API:全选当前值。\n *\n * `disabled` 时与 `focus()` 一致不产生效果;原生控件未渲染时安全 no-op。\n */\n select() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.select()\n }\n\n override render() {\n // readonly 下不可清除:清空按钮会修改值,与只读语义冲突\n const showClear = this.clearable && this._value && !this.readonly\n\n return html`\n <div class=\"wui-glass wui-input-inner\" @click=${this.focusInput}>\n <slot name=\"prefix\" class=${classMap({ empty: !this._hasPrefix })} @slotchange=${this._onSlotChange}></slot>\n <input\n id=${this._fieldId}\n type=${this.type}\n placeholder=${this.placeholder}\n name=${this.name}\n aria-label=${ifDefined(this.ariaLabel)}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n .value=${this._value}\n @input=${this.handleInput}\n @change=${this.handleNativeChange}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n />\n ${\n showClear\n ? html`<web-ui-button\n icon\n variant=\"ghost\"\n size=\"20\"\n aria-label=\"清除\"\n @pointerdown=${this.preventMouseDownBlur}\n @click=${this.handleClear}\n >\n <web-ui-icon .icon=${jamCloseCircleF} size=\"14\"></web-ui-icon>\n </web-ui-button>`\n : ''\n }\n <slot name=\"suffix\" class=${classMap({ empty: !this._hasSuffix })} @slotchange=${this._onSlotChange}></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n focus: FocusEvent\n blur: FocusEvent\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-input': WebUiInput\n }\n}\n"],"mappings":"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"}
|