@nectary/components 5.6.7 → 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
  };
@@ -5299,10 +5324,14 @@ class Input extends NectaryElement {
5299
5324
  "value",
5300
5325
  "placeholder",
5301
5326
  "mask",
5327
+ "max",
5328
+ "min",
5302
5329
  "invalid",
5303
5330
  "disabled",
5304
5331
  "size",
5332
+ "step",
5305
5333
  "autocomplete",
5334
+ "readonly",
5306
5335
  "autofocus",
5307
5336
  "data-size",
5308
5337
  "aria-label",
@@ -5322,6 +5351,11 @@ class Input extends NectaryElement {
5322
5351
  case "type": {
5323
5352
  updateLiteralAttribute(this.#$input, inputTypes, "type", newVal);
5324
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
+ }
5325
5359
  break;
5326
5360
  }
5327
5361
  case "value": {
@@ -5399,7 +5433,11 @@ class Input extends NectaryElement {
5399
5433
  }
5400
5434
  case "autocomplete":
5401
5435
  case "maxlength":
5402
- case "required": {
5436
+ case "required":
5437
+ case "readonly":
5438
+ case "max":
5439
+ case "min":
5440
+ case "step": {
5403
5441
  updateAttribute(this.#$input, name, newVal);
5404
5442
  break;
5405
5443
  }
@@ -5801,14 +5839,19 @@ class Input extends NectaryElement {
5801
5839
  if (this.#maskSymbols === null) {
5802
5840
  const value = this.placeholder;
5803
5841
  this.#$input.placeholder = value ?? "";
5804
- this.#internals.ariaPlaceholder = value ?? "";
5805
- updateAttribute(this, "aria-placeholder", value);
5842
+ if (this.type !== "number") {
5843
+ this.#internals.ariaPlaceholder = value ?? "";
5844
+ updateAttribute(this, "aria-placeholder", value);
5845
+ }
5806
5846
  } else {
5807
- updateAttribute(this, "aria-placeholder", null);
5808
5847
  this.#$input.placeholder = "";
5809
- this.#internals.ariaPlaceholder = "";
5848
+ this.#resetAriaPlaceholder();
5810
5849
  }
5811
5850
  }
5851
+ #resetAriaPlaceholder() {
5852
+ updateAttribute(this, "aria-placeholder", null);
5853
+ this.#internals.ariaPlaceholder = "";
5854
+ }
5812
5855
  #onIconSlotChange = () => {
5813
5856
  const isEmpty = this.#$iconSlot.assignedElements().length === 0;
5814
5857
  setClass(this.#$iconWrapper, "empty", isEmpty);
@@ -5856,6 +5899,10 @@ class Input extends NectaryElement {
5856
5899
  #onWheelReactHandler = (e) => {
5857
5900
  getReactEventHandler(this, "on-wheel")?.(e);
5858
5901
  };
5902
+ #setRole = (role) => {
5903
+ this.setAttribute("role", role);
5904
+ this.#internals.role = role;
5905
+ };
5859
5906
  }
5860
5907
  defineCustomElement("sinch-input", Input);
5861
5908
  const orientationValues = [
@@ -6591,6 +6638,7 @@ class Field extends NectaryElement {
6591
6638
  this.shouldShowTopSection();
6592
6639
  this.#$label.addEventListener("click", this.#onLabelClick, options);
6593
6640
  this.#$tooltipSlot.addEventListener("slotchange", this.#onTooltipSlotChange, options);
6641
+ this.#$inputSlot.addEventListener("slotchange", this.#onInputSlotChange, options);
6594
6642
  }
6595
6643
  disconnectedCallback() {
6596
6644
  this.#controller.abort();
@@ -6674,6 +6722,13 @@ class Field extends NectaryElement {
6674
6722
  #onTooltipSlotChange = () => {
6675
6723
  setClass(this.#$tooltipWrapper, "empty", this.#$tooltipSlot.assignedElements().length === 0);
6676
6724
  };
6725
+ #onInputSlotChange = () => {
6726
+ const inputElement = getFirstSlotElement(this.#$inputSlot);
6727
+ const labelText = this.#$label.textContent;
6728
+ if (inputElement !== null && labelText != null && labelText.length > 0) {
6729
+ inputElement.setAttribute("aria-label", "label");
6730
+ }
6731
+ };
6677
6732
  }
6678
6733
  defineCustomElement("sinch-field", Field);
6679
6734
  const doFilesSatisfySize$1 = (files, size) => {
package/field/index.js CHANGED
@@ -34,6 +34,7 @@ class Field extends NectaryElement {
34
34
  this.shouldShowTopSection();
35
35
  this.#$label.addEventListener("click", this.#onLabelClick, options);
36
36
  this.#$tooltipSlot.addEventListener("slotchange", this.#onTooltipSlotChange, options);
37
+ this.#$inputSlot.addEventListener("slotchange", this.#onInputSlotChange, options);
37
38
  }
38
39
  disconnectedCallback() {
39
40
  this.#controller.abort();
@@ -117,6 +118,13 @@ class Field extends NectaryElement {
117
118
  #onTooltipSlotChange = () => {
118
119
  setClass(this.#$tooltipWrapper, "empty", this.#$tooltipSlot.assignedElements().length === 0);
119
120
  };
121
+ #onInputSlotChange = () => {
122
+ const inputElement = getFirstSlotElement(this.#$inputSlot);
123
+ const labelText = this.#$label.textContent;
124
+ if (inputElement !== null && labelText != null && labelText.length > 0) {
125
+ inputElement.setAttribute("aria-label", "label");
126
+ }
127
+ };
120
128
  }
121
129
  defineCustomElement("sinch-field", Field);
122
130
  export {
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
  };
@@ -133,10 +158,14 @@ class Input extends NectaryElement {
133
158
  "value",
134
159
  "placeholder",
135
160
  "mask",
161
+ "max",
162
+ "min",
136
163
  "invalid",
137
164
  "disabled",
138
165
  "size",
166
+ "step",
139
167
  "autocomplete",
168
+ "readonly",
140
169
  "autofocus",
141
170
  "data-size",
142
171
  "aria-label",
@@ -156,6 +185,11 @@ class Input extends NectaryElement {
156
185
  case "type": {
157
186
  updateLiteralAttribute(this.#$input, inputTypes, "type", newVal);
158
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
+ }
159
193
  break;
160
194
  }
161
195
  case "value": {
@@ -233,7 +267,11 @@ class Input extends NectaryElement {
233
267
  }
234
268
  case "autocomplete":
235
269
  case "maxlength":
236
- case "required": {
270
+ case "required":
271
+ case "readonly":
272
+ case "max":
273
+ case "min":
274
+ case "step": {
237
275
  updateAttribute(this.#$input, name, newVal);
238
276
  break;
239
277
  }
@@ -635,14 +673,19 @@ class Input extends NectaryElement {
635
673
  if (this.#maskSymbols === null) {
636
674
  const value = this.placeholder;
637
675
  this.#$input.placeholder = value ?? "";
638
- this.#internals.ariaPlaceholder = value ?? "";
639
- updateAttribute(this, "aria-placeholder", value);
676
+ if (this.type !== "number") {
677
+ this.#internals.ariaPlaceholder = value ?? "";
678
+ updateAttribute(this, "aria-placeholder", value);
679
+ }
640
680
  } else {
641
- updateAttribute(this, "aria-placeholder", null);
642
681
  this.#$input.placeholder = "";
643
- this.#internals.ariaPlaceholder = "";
682
+ this.#resetAriaPlaceholder();
644
683
  }
645
684
  }
685
+ #resetAriaPlaceholder() {
686
+ updateAttribute(this, "aria-placeholder", null);
687
+ this.#internals.ariaPlaceholder = "";
688
+ }
646
689
  #onIconSlotChange = () => {
647
690
  const isEmpty = this.#$iconSlot.assignedElements().length === 0;
648
691
  setClass(this.#$iconWrapper, "empty", isEmpty);
@@ -690,6 +733,10 @@ class Input extends NectaryElement {
690
733
  #onWheelReactHandler = (e) => {
691
734
  getReactEventHandler(this, "on-wheel")?.(e);
692
735
  };
736
+ #setRole = (role) => {
737
+ this.setAttribute("role", role);
738
+ this.#internals.role = role;
739
+ };
693
740
  }
694
741
  defineCustomElement("sinch-input", Input);
695
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.6.7",
3
+ "version": "5.8.0",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",