@nectary/components 5.7.0 → 5.8.1

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,49 @@ 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
+ this.dispatchEvent(
5306
+ new CustomEvent("-change", {
5307
+ detail: min
5308
+ })
5309
+ );
5310
+ }
5311
+ }
5312
+ break;
5313
+ }
5314
+ case "End": {
5315
+ if (this.type === "number") {
5316
+ const max = getAttribute(this, "max");
5317
+ if (max !== null && !isNaN(parseFloat(max))) {
5318
+ this.#$input.value = max;
5319
+ setFormValue(this.#internals, max);
5320
+ this.dispatchEvent(
5321
+ new CustomEvent("-change", {
5322
+ detail: max
5323
+ })
5324
+ );
5325
+ }
5326
+ }
5292
5327
  }
5293
5328
  }
5294
5329
  };
@@ -5306,6 +5341,7 @@ class Input extends NectaryElement {
5306
5341
  "size",
5307
5342
  "step",
5308
5343
  "autocomplete",
5344
+ "readonly",
5309
5345
  "autofocus",
5310
5346
  "data-size",
5311
5347
  "aria-label",
@@ -5325,6 +5361,11 @@ class Input extends NectaryElement {
5325
5361
  case "type": {
5326
5362
  updateLiteralAttribute(this.#$input, inputTypes, "type", newVal);
5327
5363
  updateAttribute(this.#$input, "spellcheck", newVal === "password" ? "false" : null);
5364
+ const role = newVal === "number" ? "spinbutton" : "textbox";
5365
+ this.#setRole(role);
5366
+ if (newVal === "number") {
5367
+ this.#resetAriaPlaceholder();
5368
+ }
5328
5369
  break;
5329
5370
  }
5330
5371
  case "value": {
@@ -5403,6 +5444,7 @@ class Input extends NectaryElement {
5403
5444
  case "autocomplete":
5404
5445
  case "maxlength":
5405
5446
  case "required":
5447
+ case "readonly":
5406
5448
  case "max":
5407
5449
  case "min":
5408
5450
  case "step": {
@@ -5807,14 +5849,19 @@ class Input extends NectaryElement {
5807
5849
  if (this.#maskSymbols === null) {
5808
5850
  const value = this.placeholder;
5809
5851
  this.#$input.placeholder = value ?? "";
5810
- this.#internals.ariaPlaceholder = value ?? "";
5811
- updateAttribute(this, "aria-placeholder", value);
5852
+ if (this.type !== "number") {
5853
+ this.#internals.ariaPlaceholder = value ?? "";
5854
+ updateAttribute(this, "aria-placeholder", value);
5855
+ }
5812
5856
  } else {
5813
- updateAttribute(this, "aria-placeholder", null);
5814
5857
  this.#$input.placeholder = "";
5815
- this.#internals.ariaPlaceholder = "";
5858
+ this.#resetAriaPlaceholder();
5816
5859
  }
5817
5860
  }
5861
+ #resetAriaPlaceholder() {
5862
+ updateAttribute(this, "aria-placeholder", null);
5863
+ this.#internals.ariaPlaceholder = "";
5864
+ }
5818
5865
  #onIconSlotChange = () => {
5819
5866
  const isEmpty = this.#$iconSlot.assignedElements().length === 0;
5820
5867
  setClass(this.#$iconWrapper, "empty", isEmpty);
@@ -5862,6 +5909,10 @@ class Input extends NectaryElement {
5862
5909
  #onWheelReactHandler = (e) => {
5863
5910
  getReactEventHandler(this, "on-wheel")?.(e);
5864
5911
  };
5912
+ #setRole = (role) => {
5913
+ this.setAttribute("role", role);
5914
+ this.#internals.role = role;
5915
+ };
5865
5916
  }
5866
5917
  defineCustomElement("sinch-input", Input);
5867
5918
  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,49 @@ 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
+ this.dispatchEvent(
140
+ new CustomEvent("-change", {
141
+ detail: min
142
+ })
143
+ );
144
+ }
145
+ }
146
+ break;
147
+ }
148
+ case "End": {
149
+ if (this.type === "number") {
150
+ const max = getAttribute(this, "max");
151
+ if (max !== null && !isNaN(parseFloat(max))) {
152
+ this.#$input.value = max;
153
+ setFormValue(this.#internals, max);
154
+ this.dispatchEvent(
155
+ new CustomEvent("-change", {
156
+ detail: max
157
+ })
158
+ );
159
+ }
160
+ }
126
161
  }
127
162
  }
128
163
  };
@@ -140,6 +175,7 @@ class Input extends NectaryElement {
140
175
  "size",
141
176
  "step",
142
177
  "autocomplete",
178
+ "readonly",
143
179
  "autofocus",
144
180
  "data-size",
145
181
  "aria-label",
@@ -159,6 +195,11 @@ class Input extends NectaryElement {
159
195
  case "type": {
160
196
  updateLiteralAttribute(this.#$input, inputTypes, "type", newVal);
161
197
  updateAttribute(this.#$input, "spellcheck", newVal === "password" ? "false" : null);
198
+ const role = newVal === "number" ? "spinbutton" : "textbox";
199
+ this.#setRole(role);
200
+ if (newVal === "number") {
201
+ this.#resetAriaPlaceholder();
202
+ }
162
203
  break;
163
204
  }
164
205
  case "value": {
@@ -237,6 +278,7 @@ class Input extends NectaryElement {
237
278
  case "autocomplete":
238
279
  case "maxlength":
239
280
  case "required":
281
+ case "readonly":
240
282
  case "max":
241
283
  case "min":
242
284
  case "step": {
@@ -641,14 +683,19 @@ class Input extends NectaryElement {
641
683
  if (this.#maskSymbols === null) {
642
684
  const value = this.placeholder;
643
685
  this.#$input.placeholder = value ?? "";
644
- this.#internals.ariaPlaceholder = value ?? "";
645
- updateAttribute(this, "aria-placeholder", value);
686
+ if (this.type !== "number") {
687
+ this.#internals.ariaPlaceholder = value ?? "";
688
+ updateAttribute(this, "aria-placeholder", value);
689
+ }
646
690
  } else {
647
- updateAttribute(this, "aria-placeholder", null);
648
691
  this.#$input.placeholder = "";
649
- this.#internals.ariaPlaceholder = "";
692
+ this.#resetAriaPlaceholder();
650
693
  }
651
694
  }
695
+ #resetAriaPlaceholder() {
696
+ updateAttribute(this, "aria-placeholder", null);
697
+ this.#internals.ariaPlaceholder = "";
698
+ }
652
699
  #onIconSlotChange = () => {
653
700
  const isEmpty = this.#$iconSlot.assignedElements().length === 0;
654
701
  setClass(this.#$iconWrapper, "empty", isEmpty);
@@ -696,6 +743,10 @@ class Input extends NectaryElement {
696
743
  #onWheelReactHandler = (e) => {
697
744
  getReactEventHandler(this, "on-wheel")?.(e);
698
745
  };
746
+ #setRole = (role) => {
747
+ this.setAttribute("role", role);
748
+ this.#internals.role = role;
749
+ };
699
750
  }
700
751
  defineCustomElement("sinch-input", Input);
701
752
  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.1",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",