@nectary/components 5.35.0 → 5.36.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/bundle.js CHANGED
@@ -14844,10 +14844,11 @@ class Textarea extends NectaryElement {
14844
14844
  #$bottomWrapper;
14845
14845
  #$resizeHandle;
14846
14846
  #cursorPos = null;
14847
- #isPendingDk = false;
14847
+ #pendingPostInputCaret = null;
14848
14848
  #controller = null;
14849
14849
  #sizeContext;
14850
14850
  #prevContentHeight = 0;
14851
+ #lastValueForAutoResize;
14851
14852
  #dragStartY = 0;
14852
14853
  #intersectionObserver = null;
14853
14854
  #internals;
@@ -14874,7 +14875,6 @@ class Textarea extends NectaryElement {
14874
14875
  this.ariaMultiLine = "true";
14875
14876
  this.#internals.ariaMultiLine = "true";
14876
14877
  this.#$input.addEventListener("input", this.#onInput, options);
14877
- this.#$input.addEventListener("compositionstart", this.#onCompositionStart, options);
14878
14878
  this.#$input.addEventListener("mousedown", this.#onSelectionChange, options);
14879
14879
  this.#$input.addEventListener("keydown", this.#onSelectionChange, options);
14880
14880
  this.#$input.addEventListener("focus", this.#onInputFocus, options);
@@ -14888,6 +14888,7 @@ class Textarea extends NectaryElement {
14888
14888
  this.#onBottomSlotChange();
14889
14889
  this.#updateMinRows();
14890
14890
  this.#updateMaxRows();
14891
+ this.#lastValueForAutoResize = this.#$input.value;
14891
14892
  this.#onSizeUpdate();
14892
14893
  }
14893
14894
  disconnectedCallback() {
@@ -14905,6 +14906,10 @@ class Textarea extends NectaryElement {
14905
14906
  formResetCallback() {
14906
14907
  this.#$input.value = "";
14907
14908
  setFormValue(this.#internals, "");
14909
+ this.#lastValueForAutoResize = "";
14910
+ if (!this.resizable) {
14911
+ this.#applyAutoResize(true);
14912
+ }
14908
14913
  }
14909
14914
  formStateRestoreCallback(state) {
14910
14915
  if (this.#internals.form === null || getBooleanAttribute(this.#internals.form, "data-form-state-restore") === false) {
@@ -14912,8 +14917,14 @@ class Textarea extends NectaryElement {
14912
14917
  }
14913
14918
  if (state !== null) {
14914
14919
  const value = typeof state === "string" ? state : state.get(this.name);
14915
- this.#$input.value = value?.toString() ?? "";
14916
- setFormValue(this.#internals, value?.toString() ?? "");
14920
+ const next = value?.toString() ?? "";
14921
+ const prevLen = this.#$input.value.length;
14922
+ this.#$input.value = next;
14923
+ setFormValue(this.#internals, next);
14924
+ this.#lastValueForAutoResize = next;
14925
+ if (!this.resizable) {
14926
+ this.#applyAutoResize(next.length < prevLen);
14927
+ }
14917
14928
  }
14918
14929
  }
14919
14930
  static get observedAttributes() {
@@ -14938,20 +14949,18 @@ class Textarea extends NectaryElement {
14938
14949
  const isShrinkingContent = nextVal.length < prevVal.length;
14939
14950
  this.#$input.value = nextVal;
14940
14951
  setFormValue(this.#internals, nextVal);
14952
+ this.#lastValueForAutoResize = nextVal;
14941
14953
  if (!this.resizable) {
14942
- if (isShrinkingContent) {
14943
- this.#$input.style.removeProperty("height");
14944
- }
14945
- const nextContentHeight = this.#$input.scrollHeight;
14946
- if (isShrinkingContent || nextContentHeight !== this.#prevContentHeight) {
14947
- this.#prevContentHeight = nextContentHeight;
14948
- this.#$input.style.setProperty("height", `${this.#prevContentHeight}px`);
14949
- }
14954
+ this.#applyAutoResize(isShrinkingContent);
14950
14955
  }
14951
14956
  if (!isPrevCursorEnd) {
14952
- this.#$input.setSelectionRange(this.#cursorPos, this.#cursorPos);
14957
+ const growing = nextVal.length >= prevVal.length;
14958
+ const rawCaret = growing ? this.#pendingPostInputCaret ?? prevCursorPos ?? this.#cursorPos ?? 0 : this.#cursorPos ?? prevCursorPos ?? 0;
14959
+ const caret = Math.min(Math.max(rawCaret, 0), nextVal.length);
14960
+ this.#$input.setSelectionRange(caret, caret);
14953
14961
  }
14954
14962
  }
14963
+ this.#pendingPostInputCaret = null;
14955
14964
  break;
14956
14965
  }
14957
14966
  case "placeholder": {
@@ -15130,34 +15139,49 @@ class Textarea extends NectaryElement {
15130
15139
  this.#$input.style.setProperty("min-height", `${getRect(this.#$input).height}px`);
15131
15140
  this.#$input.rows = this.rows;
15132
15141
  }
15133
- #onCompositionStart = () => {
15134
- this.#isPendingDk = true;
15135
- };
15136
15142
  #onSelectionChange = () => {
15137
15143
  this.#cursorPos = this.#$input.selectionEnd;
15138
15144
  };
15145
+ #applyAutoResize(isShrinkingContent) {
15146
+ if (isShrinkingContent) {
15147
+ this.#$input.style.removeProperty("height");
15148
+ }
15149
+ const nextContentHeight = this.#$input.scrollHeight;
15150
+ if (isShrinkingContent || nextContentHeight !== this.#prevContentHeight) {
15151
+ this.#prevContentHeight = nextContentHeight;
15152
+ this.#$input.style.setProperty("height", `${this.#prevContentHeight}px`);
15153
+ }
15154
+ }
15139
15155
  #onInput = (e) => {
15140
15156
  e.stopPropagation();
15141
15157
  const nextValue = this.#$input.value;
15142
15158
  const prevValue = this.value;
15159
+ const priorForResize = this.#lastValueForAutoResize;
15160
+ const isShrinkingContent = priorForResize !== void 0 && nextValue.length < priorForResize.length;
15143
15161
  setFormValue(this.#internals, nextValue);
15162
+ if (!this.resizable) {
15163
+ this.#applyAutoResize(isShrinkingContent);
15164
+ }
15165
+ this.#lastValueForAutoResize = nextValue;
15144
15166
  if (prevValue !== nextValue) {
15145
- const nextCursorPos = this.#$input.selectionEnd;
15146
- if (!this.#isPendingDk) {
15147
- this.#$input.value = prevValue;
15148
- const prevCursorPos = this.#cursorPos;
15149
- const isPrevCursorEnd = prevCursorPos === null || prevCursorPos === prevValue.length;
15150
- if (!isPrevCursorEnd) {
15151
- this.#$input.setSelectionRange(prevCursorPos, prevCursorPos);
15152
- }
15167
+ const postInputCaret = this.#$input.selectionEnd;
15168
+ if (postInputCaret !== null) {
15169
+ this.#pendingPostInputCaret = postInputCaret;
15153
15170
  }
15154
- this.#isPendingDk = false;
15155
- this.#cursorPos = nextCursorPos;
15156
15171
  this.dispatchEvent(
15157
15172
  new CustomEvent("-change", {
15158
15173
  detail: nextValue
15159
15174
  })
15160
15175
  );
15176
+ queueMicrotask(() => {
15177
+ if (!this.isConnected) {
15178
+ return;
15179
+ }
15180
+ const end = this.#$input.selectionEnd;
15181
+ if (end !== null) {
15182
+ this.#cursorPos = end;
15183
+ }
15184
+ });
15161
15185
  }
15162
15186
  };
15163
15187
  #onDragStart = (e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "5.35.0",
3
+ "version": "5.36.0",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",
package/textarea/index.js CHANGED
@@ -14,10 +14,11 @@ class Textarea extends NectaryElement {
14
14
  #$bottomWrapper;
15
15
  #$resizeHandle;
16
16
  #cursorPos = null;
17
- #isPendingDk = false;
17
+ #pendingPostInputCaret = null;
18
18
  #controller = null;
19
19
  #sizeContext;
20
20
  #prevContentHeight = 0;
21
+ #lastValueForAutoResize;
21
22
  #dragStartY = 0;
22
23
  #intersectionObserver = null;
23
24
  #internals;
@@ -44,7 +45,6 @@ class Textarea extends NectaryElement {
44
45
  this.ariaMultiLine = "true";
45
46
  this.#internals.ariaMultiLine = "true";
46
47
  this.#$input.addEventListener("input", this.#onInput, options);
47
- this.#$input.addEventListener("compositionstart", this.#onCompositionStart, options);
48
48
  this.#$input.addEventListener("mousedown", this.#onSelectionChange, options);
49
49
  this.#$input.addEventListener("keydown", this.#onSelectionChange, options);
50
50
  this.#$input.addEventListener("focus", this.#onInputFocus, options);
@@ -58,6 +58,7 @@ class Textarea extends NectaryElement {
58
58
  this.#onBottomSlotChange();
59
59
  this.#updateMinRows();
60
60
  this.#updateMaxRows();
61
+ this.#lastValueForAutoResize = this.#$input.value;
61
62
  this.#onSizeUpdate();
62
63
  }
63
64
  disconnectedCallback() {
@@ -75,6 +76,10 @@ class Textarea extends NectaryElement {
75
76
  formResetCallback() {
76
77
  this.#$input.value = "";
77
78
  setFormValue(this.#internals, "");
79
+ this.#lastValueForAutoResize = "";
80
+ if (!this.resizable) {
81
+ this.#applyAutoResize(true);
82
+ }
78
83
  }
79
84
  formStateRestoreCallback(state) {
80
85
  if (this.#internals.form === null || getBooleanAttribute(this.#internals.form, "data-form-state-restore") === false) {
@@ -82,8 +87,14 @@ class Textarea extends NectaryElement {
82
87
  }
83
88
  if (state !== null) {
84
89
  const value = typeof state === "string" ? state : state.get(this.name);
85
- this.#$input.value = value?.toString() ?? "";
86
- setFormValue(this.#internals, value?.toString() ?? "");
90
+ const next = value?.toString() ?? "";
91
+ const prevLen = this.#$input.value.length;
92
+ this.#$input.value = next;
93
+ setFormValue(this.#internals, next);
94
+ this.#lastValueForAutoResize = next;
95
+ if (!this.resizable) {
96
+ this.#applyAutoResize(next.length < prevLen);
97
+ }
87
98
  }
88
99
  }
89
100
  static get observedAttributes() {
@@ -108,20 +119,18 @@ class Textarea extends NectaryElement {
108
119
  const isShrinkingContent = nextVal.length < prevVal.length;
109
120
  this.#$input.value = nextVal;
110
121
  setFormValue(this.#internals, nextVal);
122
+ this.#lastValueForAutoResize = nextVal;
111
123
  if (!this.resizable) {
112
- if (isShrinkingContent) {
113
- this.#$input.style.removeProperty("height");
114
- }
115
- const nextContentHeight = this.#$input.scrollHeight;
116
- if (isShrinkingContent || nextContentHeight !== this.#prevContentHeight) {
117
- this.#prevContentHeight = nextContentHeight;
118
- this.#$input.style.setProperty("height", `${this.#prevContentHeight}px`);
119
- }
124
+ this.#applyAutoResize(isShrinkingContent);
120
125
  }
121
126
  if (!isPrevCursorEnd) {
122
- this.#$input.setSelectionRange(this.#cursorPos, this.#cursorPos);
127
+ const growing = nextVal.length >= prevVal.length;
128
+ const rawCaret = growing ? this.#pendingPostInputCaret ?? prevCursorPos ?? this.#cursorPos ?? 0 : this.#cursorPos ?? prevCursorPos ?? 0;
129
+ const caret = Math.min(Math.max(rawCaret, 0), nextVal.length);
130
+ this.#$input.setSelectionRange(caret, caret);
123
131
  }
124
132
  }
133
+ this.#pendingPostInputCaret = null;
125
134
  break;
126
135
  }
127
136
  case "placeholder": {
@@ -300,34 +309,49 @@ class Textarea extends NectaryElement {
300
309
  this.#$input.style.setProperty("min-height", `${getRect(this.#$input).height}px`);
301
310
  this.#$input.rows = this.rows;
302
311
  }
303
- #onCompositionStart = () => {
304
- this.#isPendingDk = true;
305
- };
306
312
  #onSelectionChange = () => {
307
313
  this.#cursorPos = this.#$input.selectionEnd;
308
314
  };
315
+ #applyAutoResize(isShrinkingContent) {
316
+ if (isShrinkingContent) {
317
+ this.#$input.style.removeProperty("height");
318
+ }
319
+ const nextContentHeight = this.#$input.scrollHeight;
320
+ if (isShrinkingContent || nextContentHeight !== this.#prevContentHeight) {
321
+ this.#prevContentHeight = nextContentHeight;
322
+ this.#$input.style.setProperty("height", `${this.#prevContentHeight}px`);
323
+ }
324
+ }
309
325
  #onInput = (e) => {
310
326
  e.stopPropagation();
311
327
  const nextValue = this.#$input.value;
312
328
  const prevValue = this.value;
329
+ const priorForResize = this.#lastValueForAutoResize;
330
+ const isShrinkingContent = priorForResize !== void 0 && nextValue.length < priorForResize.length;
313
331
  setFormValue(this.#internals, nextValue);
332
+ if (!this.resizable) {
333
+ this.#applyAutoResize(isShrinkingContent);
334
+ }
335
+ this.#lastValueForAutoResize = nextValue;
314
336
  if (prevValue !== nextValue) {
315
- const nextCursorPos = this.#$input.selectionEnd;
316
- if (!this.#isPendingDk) {
317
- this.#$input.value = prevValue;
318
- const prevCursorPos = this.#cursorPos;
319
- const isPrevCursorEnd = prevCursorPos === null || prevCursorPos === prevValue.length;
320
- if (!isPrevCursorEnd) {
321
- this.#$input.setSelectionRange(prevCursorPos, prevCursorPos);
322
- }
337
+ const postInputCaret = this.#$input.selectionEnd;
338
+ if (postInputCaret !== null) {
339
+ this.#pendingPostInputCaret = postInputCaret;
323
340
  }
324
- this.#isPendingDk = false;
325
- this.#cursorPos = nextCursorPos;
326
341
  this.dispatchEvent(
327
342
  new CustomEvent("-change", {
328
343
  detail: nextValue
329
344
  })
330
345
  );
346
+ queueMicrotask(() => {
347
+ if (!this.isConnected) {
348
+ return;
349
+ }
350
+ const end = this.#$input.selectionEnd;
351
+ if (end !== null) {
352
+ this.#cursorPos = end;
353
+ }
354
+ });
331
355
  }
332
356
  };
333
357
  #onDragStart = (e) => {