@nectary/components 5.7.0 → 5.8.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
@@ -5214,8 +5214,8 @@ class Input extends NectaryElement {
5214
5214
  }
5215
5215
  connectedCallback() {
5216
5216
  super.connectedCallback();
5217
- this.setAttribute("role", "textbox");
5218
- this.#internals.role = "textbox";
5217
+ const role = this.type === "number" ? "spinbutton" : "textbox";
5218
+ this.#setRole(role);
5219
5219
  if (this.#controller === null) {
5220
5220
  this.#controller = new AbortController();
5221
5221
  }
@@ -5273,7 +5273,7 @@ class Input extends NectaryElement {
5273
5273
  }
5274
5274
  // This handler mimicks the behavior (with some exceptions) of the implicit form submission logic from the HTML spec:
5275
5275
  // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission
5276
- #onKeyDown = (e) => {
5276
+ #formSubmitHandler = () => {
5277
5277
  const form = this.#internals.form;
5278
5278
  if (form === null) {
5279
5279
  return;
@@ -5281,14 +5281,39 @@ class Input extends NectaryElement {
5281
5281
  if (form.disabled === true) {
5282
5282
  return;
5283
5283
  }
5284
- if (e.key === "Enter") {
5285
- const submitSelectors = [
5286
- 'sinch-button[form-type="submit"]'
5287
- ];
5288
- const formSubmitters = Array.from(form.querySelectorAll(submitSelectors.join(",")));
5289
- const formSubmitter = formSubmitters.find((submitter) => !submitter.disabled) ?? null;
5290
- if (formSubmitter !== null) {
5291
- requestSubmitForm(form, formSubmitter);
5284
+ const submitSelectors = [
5285
+ 'sinch-button[form-type="submit"]'
5286
+ ];
5287
+ const formSubmitters = Array.from(form.querySelectorAll(submitSelectors.join(",")));
5288
+ const formSubmitter = formSubmitters.find((submitter) => !submitter.disabled) ?? null;
5289
+ if (formSubmitter !== null) {
5290
+ requestSubmitForm(form, formSubmitter);
5291
+ }
5292
+ };
5293
+ #onKeyDown = (e) => {
5294
+ switch (e.key) {
5295
+ case "Enter": {
5296
+ this.#formSubmitHandler();
5297
+ break;
5298
+ }
5299
+ case "Home": {
5300
+ if (this.type === "number") {
5301
+ const min = getAttribute(this, "min");
5302
+ if (min !== null && !isNaN(parseFloat(min))) {
5303
+ this.#$input.value = min;
5304
+ setFormValue(this.#internals, min);
5305
+ }
5306
+ }
5307
+ break;
5308
+ }
5309
+ case "End": {
5310
+ if (this.type === "number") {
5311
+ const max = getAttribute(this, "max");
5312
+ if (max !== null && !isNaN(parseFloat(max))) {
5313
+ this.#$input.value = max;
5314
+ setFormValue(this.#internals, max);
5315
+ }
5316
+ }
5292
5317
  }
5293
5318
  }
5294
5319
  };
@@ -5306,6 +5331,7 @@ class Input extends NectaryElement {
5306
5331
  "size",
5307
5332
  "step",
5308
5333
  "autocomplete",
5334
+ "readonly",
5309
5335
  "autofocus",
5310
5336
  "data-size",
5311
5337
  "aria-label",
@@ -5325,6 +5351,11 @@ class Input extends NectaryElement {
5325
5351
  case "type": {
5326
5352
  updateLiteralAttribute(this.#$input, inputTypes, "type", newVal);
5327
5353
  updateAttribute(this.#$input, "spellcheck", newVal === "password" ? "false" : null);
5354
+ const role = newVal === "number" ? "spinbutton" : "textbox";
5355
+ this.#setRole(role);
5356
+ if (newVal === "number") {
5357
+ this.#resetAriaPlaceholder();
5358
+ }
5328
5359
  break;
5329
5360
  }
5330
5361
  case "value": {
@@ -5403,6 +5434,7 @@ class Input extends NectaryElement {
5403
5434
  case "autocomplete":
5404
5435
  case "maxlength":
5405
5436
  case "required":
5437
+ case "readonly":
5406
5438
  case "max":
5407
5439
  case "min":
5408
5440
  case "step": {
@@ -5807,14 +5839,19 @@ class Input extends NectaryElement {
5807
5839
  if (this.#maskSymbols === null) {
5808
5840
  const value = this.placeholder;
5809
5841
  this.#$input.placeholder = value ?? "";
5810
- this.#internals.ariaPlaceholder = value ?? "";
5811
- updateAttribute(this, "aria-placeholder", value);
5842
+ if (this.type !== "number") {
5843
+ this.#internals.ariaPlaceholder = value ?? "";
5844
+ updateAttribute(this, "aria-placeholder", value);
5845
+ }
5812
5846
  } else {
5813
- updateAttribute(this, "aria-placeholder", null);
5814
5847
  this.#$input.placeholder = "";
5815
- this.#internals.ariaPlaceholder = "";
5848
+ this.#resetAriaPlaceholder();
5816
5849
  }
5817
5850
  }
5851
+ #resetAriaPlaceholder() {
5852
+ updateAttribute(this, "aria-placeholder", null);
5853
+ this.#internals.ariaPlaceholder = "";
5854
+ }
5818
5855
  #onIconSlotChange = () => {
5819
5856
  const isEmpty = this.#$iconSlot.assignedElements().length === 0;
5820
5857
  setClass(this.#$iconWrapper, "empty", isEmpty);
@@ -5862,6 +5899,10 @@ class Input extends NectaryElement {
5862
5899
  #onWheelReactHandler = (e) => {
5863
5900
  getReactEventHandler(this, "on-wheel")?.(e);
5864
5901
  };
5902
+ #setRole = (role) => {
5903
+ this.setAttribute("role", role);
5904
+ this.#internals.role = role;
5905
+ };
5865
5906
  }
5866
5907
  defineCustomElement("sinch-input", Input);
5867
5908
  const orientationValues = [
package/input/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context, subscribeContext } from "../utils/context.js";
2
- import { getBooleanAttribute, updateAttribute, isAttrEqual, isAttrTrue, updateBooleanAttribute, updateLiteralAttribute, getAttribute, getLiteralAttribute, setClass } from "../utils/dom.js";
2
+ import { getBooleanAttribute, getAttribute, updateAttribute, isAttrEqual, isAttrTrue, updateBooleanAttribute, updateLiteralAttribute, getLiteralAttribute, setClass } from "../utils/dom.js";
3
3
  import { defineCustomElement, NectaryElement } from "../utils/element.js";
4
4
  import { isElementFocused } from "../utils/slot.js";
5
5
  import { getReactEventHandler } from "../utils/get-react-event-handler.js";
@@ -48,8 +48,8 @@ class Input extends NectaryElement {
48
48
  }
49
49
  connectedCallback() {
50
50
  super.connectedCallback();
51
- this.setAttribute("role", "textbox");
52
- this.#internals.role = "textbox";
51
+ const role = this.type === "number" ? "spinbutton" : "textbox";
52
+ this.#setRole(role);
53
53
  if (this.#controller === null) {
54
54
  this.#controller = new AbortController();
55
55
  }
@@ -107,7 +107,7 @@ class Input extends NectaryElement {
107
107
  }
108
108
  // This handler mimicks the behavior (with some exceptions) of the implicit form submission logic from the HTML spec:
109
109
  // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission
110
- #onKeyDown = (e) => {
110
+ #formSubmitHandler = () => {
111
111
  const form = this.#internals.form;
112
112
  if (form === null) {
113
113
  return;
@@ -115,14 +115,39 @@ class Input extends NectaryElement {
115
115
  if (form.disabled === true) {
116
116
  return;
117
117
  }
118
- if (e.key === "Enter") {
119
- const submitSelectors = [
120
- 'sinch-button[form-type="submit"]'
121
- ];
122
- const formSubmitters = Array.from(form.querySelectorAll(submitSelectors.join(",")));
123
- const formSubmitter = formSubmitters.find((submitter) => !submitter.disabled) ?? null;
124
- if (formSubmitter !== null) {
125
- requestSubmitForm(form, formSubmitter);
118
+ const submitSelectors = [
119
+ 'sinch-button[form-type="submit"]'
120
+ ];
121
+ const formSubmitters = Array.from(form.querySelectorAll(submitSelectors.join(",")));
122
+ const formSubmitter = formSubmitters.find((submitter) => !submitter.disabled) ?? null;
123
+ if (formSubmitter !== null) {
124
+ requestSubmitForm(form, formSubmitter);
125
+ }
126
+ };
127
+ #onKeyDown = (e) => {
128
+ switch (e.key) {
129
+ case "Enter": {
130
+ this.#formSubmitHandler();
131
+ break;
132
+ }
133
+ case "Home": {
134
+ if (this.type === "number") {
135
+ const min = getAttribute(this, "min");
136
+ if (min !== null && !isNaN(parseFloat(min))) {
137
+ this.#$input.value = min;
138
+ setFormValue(this.#internals, min);
139
+ }
140
+ }
141
+ break;
142
+ }
143
+ case "End": {
144
+ if (this.type === "number") {
145
+ const max = getAttribute(this, "max");
146
+ if (max !== null && !isNaN(parseFloat(max))) {
147
+ this.#$input.value = max;
148
+ setFormValue(this.#internals, max);
149
+ }
150
+ }
126
151
  }
127
152
  }
128
153
  };
@@ -140,6 +165,7 @@ class Input extends NectaryElement {
140
165
  "size",
141
166
  "step",
142
167
  "autocomplete",
168
+ "readonly",
143
169
  "autofocus",
144
170
  "data-size",
145
171
  "aria-label",
@@ -159,6 +185,11 @@ class Input extends NectaryElement {
159
185
  case "type": {
160
186
  updateLiteralAttribute(this.#$input, inputTypes, "type", newVal);
161
187
  updateAttribute(this.#$input, "spellcheck", newVal === "password" ? "false" : null);
188
+ const role = newVal === "number" ? "spinbutton" : "textbox";
189
+ this.#setRole(role);
190
+ if (newVal === "number") {
191
+ this.#resetAriaPlaceholder();
192
+ }
162
193
  break;
163
194
  }
164
195
  case "value": {
@@ -237,6 +268,7 @@ class Input extends NectaryElement {
237
268
  case "autocomplete":
238
269
  case "maxlength":
239
270
  case "required":
271
+ case "readonly":
240
272
  case "max":
241
273
  case "min":
242
274
  case "step": {
@@ -641,14 +673,19 @@ class Input extends NectaryElement {
641
673
  if (this.#maskSymbols === null) {
642
674
  const value = this.placeholder;
643
675
  this.#$input.placeholder = value ?? "";
644
- this.#internals.ariaPlaceholder = value ?? "";
645
- updateAttribute(this, "aria-placeholder", value);
676
+ if (this.type !== "number") {
677
+ this.#internals.ariaPlaceholder = value ?? "";
678
+ updateAttribute(this, "aria-placeholder", value);
679
+ }
646
680
  } else {
647
- updateAttribute(this, "aria-placeholder", null);
648
681
  this.#$input.placeholder = "";
649
- this.#internals.ariaPlaceholder = "";
682
+ this.#resetAriaPlaceholder();
650
683
  }
651
684
  }
685
+ #resetAriaPlaceholder() {
686
+ updateAttribute(this, "aria-placeholder", null);
687
+ this.#internals.ariaPlaceholder = "";
688
+ }
652
689
  #onIconSlotChange = () => {
653
690
  const isEmpty = this.#$iconSlot.assignedElements().length === 0;
654
691
  setClass(this.#$iconWrapper, "empty", isEmpty);
@@ -696,6 +733,10 @@ class Input extends NectaryElement {
696
733
  #onWheelReactHandler = (e) => {
697
734
  getReactEventHandler(this, "on-wheel")?.(e);
698
735
  };
736
+ #setRole = (role) => {
737
+ this.setAttribute("role", role);
738
+ this.#internals.role = role;
739
+ };
699
740
  }
700
741
  defineCustomElement("sinch-input", Input);
701
742
  export {
package/input/types.d.ts CHANGED
@@ -28,6 +28,18 @@ export type TSinchInputProps = {
28
28
  autofocus?: boolean;
29
29
  /** Size, `m` by default */
30
30
  size?: TSinchSize;
31
+ /** Whether or not the input is in readonly mode **/
32
+ readonly?: boolean;
33
+ /** Whether or not the input is required **/
34
+ required?: boolean;
35
+ /** Maximum length of the input value */
36
+ maxLength?: number | null;
37
+ /** Maximum numeric value for type 'number' */
38
+ max?: number | null;
39
+ /** Minimum numeric value for type 'number' */
40
+ min?: number | null;
41
+ /** Step value for type 'number' */
42
+ step?: number | null;
31
43
  selectionStart?: number | null;
32
44
  selectionEnd?: number | null;
33
45
  selectionDirection?: 'forward' | 'backward' | 'none' | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "5.7.0",
3
+ "version": "5.8.0",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",