@nordhealth/components 5.0.0 → 5.0.2
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/custom-elements.json +26062 -25769
- package/lib/AsideDrawer.js +2 -2
- package/lib/AsideDrawer.js.map +1 -1
- package/lib/AsideTrigger.js +2 -2
- package/lib/AsideTrigger.js.map +1 -1
- package/lib/Autocomplete.js +1 -1
- package/lib/Autocomplete.js.map +1 -1
- package/lib/Button.js +2 -2
- package/lib/Button.js.map +1 -1
- package/lib/Calendar.js +1 -1
- package/lib/Combobox.js +1 -1
- package/lib/Combobox.js.map +1 -1
- package/lib/ComboboxBase.js +1 -1
- package/lib/ComboboxBase.js.map +1 -1
- package/lib/CommandMenu.js +1 -1
- package/lib/CommandMenu.js.map +1 -1
- package/lib/DatePicker.js +1 -1
- package/lib/DatePicker.js.map +1 -1
- package/lib/DateRangePicker.js +1 -1
- package/lib/DateRangePicker.js.map +1 -1
- package/lib/Dropdown.js +1 -1
- package/lib/Dropdown.js.map +1 -1
- package/lib/DropdownSubmenu.js +1 -1
- package/lib/FilterAddButton.js +1 -1
- package/lib/FilterAddButton.js.map +1 -1
- package/lib/FilterDateRange.js +1 -1
- package/lib/FilterDateRange.js.map +1 -1
- package/lib/FilterDropdown.js +1 -1
- package/lib/FilterDropdown.js.map +1 -1
- package/lib/FilterField.js +1 -1
- package/lib/FilterField.js.map +1 -1
- package/lib/FilterInput.js +1 -1
- package/lib/FilterInput.js.map +1 -1
- package/lib/FilterResetButton.js +1 -1
- package/lib/FilterResetButton.js.map +1 -1
- package/lib/Header.js +1 -1
- package/lib/Header.js.map +1 -1
- package/lib/IconManager.js +1 -1
- package/lib/Input.js +1 -1
- package/lib/Item.js +1 -1
- package/lib/Item.js.map +1 -1
- package/lib/Layout.js +1 -1
- package/lib/Layout.js.map +1 -1
- package/lib/LocalizeController.js +1 -1
- package/lib/LocalizeController.js.map +1 -1
- package/lib/Message.js +1 -1
- package/lib/Message.js.map +1 -1
- package/lib/Modal.js +1 -1
- package/lib/Modal.js.map +1 -1
- package/lib/NavGroup.js +1 -1
- package/lib/NavGroup.js.map +1 -1
- package/lib/NavItem.js +1 -1
- package/lib/NavItem.js.map +1 -1
- package/lib/NavToggle-ROsuaJkW.js +2 -0
- package/lib/{NavToggle-BY83xpx0.js.map → NavToggle-ROsuaJkW.js.map} +1 -1
- package/lib/NavToggle.js +1 -1
- package/lib/Navigation.js +1 -1
- package/lib/Navigation.js.map +1 -1
- package/lib/Notification.js +1 -1
- package/lib/Notification.js.map +1 -1
- package/lib/NumberField.js +1 -1
- package/lib/NumberField.js.map +1 -1
- package/lib/OtpField.js +1 -1
- package/lib/OtpField.js.map +1 -1
- package/lib/Outline.js +1 -1
- package/lib/Outline.js.map +1 -1
- package/lib/Pagination.js +1 -1
- package/lib/Pagination.js.map +1 -1
- package/lib/Popout-B6Hirj3y.js +2 -0
- package/lib/{Popout-CFtFoCaA.js.map → Popout-B6Hirj3y.js.map} +1 -1
- package/lib/Popout.js +1 -1
- package/lib/Range.js +1 -1
- package/lib/SegmentedControlItem.js +1 -1
- package/lib/Tag.js +1 -1
- package/lib/Tag.js.map +1 -1
- package/lib/TextField-BYSciLeB.js +2 -0
- package/lib/TextField-BYSciLeB.js.map +1 -0
- package/lib/Textarea.js +1 -1
- package/lib/Textarea.js.map +1 -1
- package/lib/TimePicker.js +1 -1
- package/lib/TimePicker.js.map +1 -1
- package/lib/bundle.js +13 -13
- package/lib/bundle.js.map +1 -1
- package/lib/en-us.js +1 -1
- package/lib/en-us.js.map +1 -1
- package/lib/fi-fi.js +1 -1
- package/lib/fi-fi.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/localization13.js +1 -1
- package/lib/localization13.js.map +1 -1
- package/lib/localization14.js +1 -1
- package/lib/localization14.js.map +1 -1
- package/lib/localization15.js +1 -1
- package/lib/localization15.js.map +1 -1
- package/lib/localization16.js +1 -1
- package/lib/localization16.js.map +1 -1
- package/lib/localization17.js +1 -1
- package/lib/localization17.js.map +1 -1
- package/lib/localization18.js +1 -1
- package/lib/localization18.js.map +1 -1
- package/lib/localization19.js +1 -1
- package/lib/localization19.js.map +1 -1
- package/lib/localization20.js +1 -1
- package/lib/localization20.js.map +1 -1
- package/lib/localization21.js +1 -1
- package/lib/localization21.js.map +1 -1
- package/lib/localization22.js +1 -1
- package/lib/localization22.js.map +1 -1
- package/lib/localization23.js +1 -1
- package/lib/localization23.js.map +1 -1
- package/lib/localization24.js +2 -0
- package/lib/localization24.js.map +1 -0
- package/lib/nav-context.js +2 -0
- package/lib/nav-context.js.map +1 -0
- package/lib/src/aside-trigger/AsideTrigger.d.ts +28 -2
- package/lib/src/button/Button.d.ts +7 -0
- package/lib/src/dropdown/Dropdown.d.ts +3 -0
- package/lib/src/layout/Layout.d.ts +12 -0
- package/lib/src/layout/localization.d.ts +5 -0
- package/lib/src/localization/en-us.d.ts +4 -0
- package/lib/src/localization/translation.d.ts +4 -0
- package/lib/src/nav-group/NavGroup.d.ts +1 -0
- package/lib/src/nav-group/nav-context.d.ts +11 -0
- package/lib/src/nav-item/NavItem.d.ts +45 -0
- package/lib/src/navigation/Navigation.d.ts +18 -0
- package/lib/translation.js +1 -1
- package/lib/translation.js.map +1 -1
- package/package.json +8 -8
- package/lib/NavToggle-BY83xpx0.js +0 -2
- package/lib/Popout-CFtFoCaA.js +0 -2
- package/lib/TextField-CSKvieoG.js +0 -2
- package/lib/TextField-CSKvieoG.js.map +0 -1
package/lib/NumberField.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{_ as e}from"./tslib.es6-CmLYFWVC.js";import{a as t}from"./interface-add-small-B-hv2v9J.js";import{r as i}from"./interface-remove-small-DUaWSs90.js";import{css as n,isServer as r,html as o,nothing as s,LitElement as l}from"lit";import{query as a,property as d,state as u,customElement as c}from"lit/decorators.js";import{classMap as h}from"lit/directives/class-map.js";import{createRef as p,ref as m}from"lit/directives/ref.js";import{F as b}from"./FormDataController-OUt5L5uC.js";import{S as f}from"./StateAttributesController-BIh96sXZ.js";import{N as v}from"./events-Bv6wNHwJ.js";import{i as y}from"./light-dom-styles-DXsbiKU9.js";import{D as g}from"./DraftComponentMixin-4qug5Rky.js";import{F as S}from"./FieldControlMixin-BXNZJXu9.js";import{S as k}from"./SizeMixin-CU9cLbLC.js";import w from"./Icon.js";import{LocalizeController as x}from"./LocalizeController.js";import{resolveCurrencyPlacement as z,getCurrencySymbol as D}from"./currency.js";import{formatNumber as j}from"./format.js";import{NumberFieldChangeEvent as I}from"./NumberFieldEvents.js";import{getNumberLocaleDetails as $,BASE_NON_NUMERIC_SYMBOLS as T,SPACE_SEPARATOR_RE as _,PERCENTAGES as A,PERMILLE as V,PLUS_SIGNS_WITH_ASCII as P,MINUS_SIGNS_WITH_ASCII as N,ARABIC_DETECT_RE as E,PERSIAN_DETECT_RE as C,HAN_DETECT_RE as W,FULLWIDTH_DETECT_RE as L,ANY_MINUS_DETECT_RE as M,ANY_PLUS_DETECT_RE as O,parseNumber as R}from"./parse.js";import{clamp as F,toValidatedNumber as q,hasNumberFormatRoundingOptions as B,removeFloatingPointErrors as U}from"./validate.js";import"./context-consumer-BJVUulGf.js";import"./context-request-event-CCRlXoeR.js";import"./field-context.js";import"./create-context-CJ69ntlY.js";import"./SlotController-Z6eG7LSZ.js";import"./EventController-BBOmvfLa.js";import"./VisuallyHidden.js";import"lit/directives/if-defined.js";import"lit/directives/unsafe-html.js";import"./observe-D0n0zOfU.js";import"./cond-CI1KbneT.js";import"./Component-BJ5O5n8k.js";import"./IconManager.js";import"./translation.js";import"./en-us.js";import"./localization.js";import"./localization2.js";import"./localization3.js";import"./localization4.js";import"./localization5.js";import"./localization6.js";import"./localization7.js";import"./localization8.js";import"./localization9.js";import"./localization10.js";import"./localization11.js";import"./localization12.js";import"./localization13.js";import"./localization14.js";import"./localization15.js";import"./localization16.js";import"./localization17.js";import"./localization18.js";import"./localization19.js";import"./localization20.js";import"./localization21.js";import"./localization22.js";import"./localization23.js";const K=n`nord-number-field,nord-number-field *,nord-number-field :after,nord-number-field :before{box-sizing:border-box}nord-number-field{--n-number-field-control-min-inline-size:8ch;--n-number-field-background:var(--n-input-background,var(--n-color-surface));--n-number-field-border-color:var(--n-input-border-color,var(--n-color-border-strong));--n-number-field-border-radius:var(--n-input-border-radius,var(--n-border-radius-s));--_n-number-field-block-size:var(--n-number-field-block-size,var(--n-space-xl));--_n-number-field-border-color:var(--n-number-field-border-color);--_n-number-field-min-content-block-size:calc(var(--n-line-height-form) + var(--n-space-s)/1.6*2 + 2px);--_n-number-field-padding-block:calc(var(--_n-number-field-block-size)/2 - var(--n-line-height-form)/2 - 1px);--_n-number-field-padding-inline:calc(var(--n-space-s)*1.6);--_n-number-field-font-size:var(--n-font-size-m);--_n-number-field-step-inline-size:var(--n-number-field-step-inline-size,var(--_n-number-field-block-size));display:block}nord-number-field .n-number-field{inline-size:100%}nord-number-field .n-number-field-root{display:contents}nord-number-field .n-scrub{align-items:center;color:var(--n-color-icon);cursor:ew-resize;display:inline-flex;margin-block-end:var(--n-space-xs);touch-action:none;user-select:none}nord-number-field .n-scrub-grip{block-size:var(--n-size-icon-s);display:block;inline-size:auto}nord-number-field.is-scrubbing,nord-number-field.is-scrubbing .n-input,nord-number-field.is-scrubbing .n-scrub{cursor:ew-resize}nord-number-field .n-control{align-items:stretch;background:var(--n-number-field-background);border:1px solid var(--_n-number-field-border-color);border-radius:var(--n-number-field-border-radius);display:flex;inline-size:100%;min-block-size:var(--_n-number-field-block-size);transition:border var(--n-transition-quickly),box-shadow var(--n-transition-quickly),background var(--n-transition-quickly)}nord-number-field .n-control:hover{--_n-number-field-border-color:var(--n-input-border-color,var(--n-color-border-hover))}nord-number-field .n-control:focus-within{--_n-number-field-border-color:var(--n-input-border-color,var(--n-color-accent));box-shadow:0 0 0 1px var(--_n-number-field-border-color)}nord-number-field[disabled] .n-control{--n-number-field-background:var(--n-input-background-inactive,var(--n-color-active));--_n-number-field-border-color:var(--n-input-border-color,var(--n-color-active));cursor:auto}nord-number-field .n-control.is-invalid{--_n-number-field-border-color:var(--n-color-border-danger)}nord-number-field .n-control.is-invalid:focus-within{--_n-number-field-border-color:var(--n-color-status-danger)}nord-number-field .n-input{appearance:none;background:#0000;border:none;color:var(--n-color-text);flex:1;font-family:var(--n-font-family);font-feature-settings:var(--n-font-features);font-size:var(--_n-number-field-font-size);font-variant-numeric:tabular-nums;line-height:var(--n-line-height-form);margin:0;min-inline-size:var(--n-number-field-control-min-inline-size);outline:none;padding-block-end:var(--_n-number-field-padding-block);padding-block-start:var(--_n-number-field-padding-block);padding-inline-end:var(--_n-number-field-padding-inline);padding-inline-start:var(--_n-number-field-padding-inline);text-align:start}nord-number-field .n-control input.n-input:focus,nord-number-field .n-control input.n-input:focus-visible,nord-number-field .n-control input.n-input:focus-within{background:#0000;border:none;box-shadow:none;outline:none}nord-number-field .n-input::placeholder{color:var(--n-color-text-weakest);opacity:1}nord-number-field[disabled] .n-input{color:var(--n-color-text-weakest)}nord-number-field .n-number-field-slot{align-items:center;color:var(--n-color-text-weaker);display:inline-flex;flex:0 0 auto;font-family:var(--n-font-family);font-size:var(--_n-number-field-font-size);gap:var(--n-space-xs);line-height:var(--n-line-height-form);white-space:nowrap}nord-number-field .n-number-field-slot:empty{display:none}nord-number-field .n-number-field-slot-start{padding-inline-start:var(--_n-number-field-padding-inline)}nord-number-field .n-number-field-slot-end{padding-inline-end:var(--_n-number-field-padding-inline)}nord-number-field .n-number-field-slot nord-icon{--_n-icon-size:var(--n-size-icon-s)}nord-number-field[disabled] .n-number-field-slot{color:var(--n-color-text-weakest)}nord-number-field .n-currency-symbol{color:var(--n-color-text-weaker);font-variant-numeric:tabular-nums;user-select:none}nord-number-field .n-step{align-items:center;appearance:none;background:#0000;border:none;color:var(--n-color-icon);cursor:pointer;display:flex;flex-shrink:0;inline-size:var(--_n-number-field-step-inline-size);justify-content:center;margin:0;padding:0;transition:color var(--n-transition-quickly),background var(--n-transition-quickly);user-select:none}nord-number-field .n-step:hover:not([disabled]){background:var(--n-color-button-hover);color:var(--n-color-text)}nord-number-field .n-step:active:not([disabled]){background:var(--n-color-active)}nord-number-field .n-step[disabled]{color:var(--n-color-text-weakest);cursor:auto}nord-number-field .n-decrement{border-inline-end:1px solid var(--_n-number-field-border-color)}nord-number-field .n-increment{border-inline-start:1px solid var(--_n-number-field-border-color)}nord-number-field .n-step nord-icon{--_n-icon-size:var(--n-size-icon-s)}nord-number-field[size=s]{--_n-number-field-block-size:var(--n-number-field-block-size,calc(var(--n-space-l) + var(--n-space-xs)));--_n-number-field-padding-block:calc(var(--_n-number-field-block-size)/2 - var(--n-line-height-form)/2 - 1px);--_n-number-field-padding-inline:var(--n-space-s);--_n-number-field-font-size:var(--n-font-size-s);--_n-number-field-step-inline-size:var(--n-number-field-step-inline-size,var(--_n-number-field-block-size))}nord-number-field[size=s] .n-step nord-icon{--_n-icon-size:var(--n-size-icon-xs)}nord-number-field[size=l]{--_n-number-field-block-size:max(var(--n-number-field-block-size,calc(var(--n-space-xxl) - var(--n-space-l))),var(--_n-number-field-min-content-block-size));--_n-number-field-padding-block:calc(var(--_n-number-field-block-size)/2 - var(--n-line-height-form)/2 - 1px);--_n-number-field-font-size:var(--n-font-size-l);--_n-number-field-step-inline-size:var(--n-number-field-step-inline-size,var(--_n-number-field-block-size))}nord-number-field[size=l] .n-step nord-icon{--_n-icon-size:var(--n-size-icon-m)}`;w.registerIcon(t),w.registerIcon(i);let H=1;function Z(e,t){return Number.isFinite(e)&&e>0?e:t}let G=class extends(g(k(S(l)))){constructor(){super(...arguments),this.numberFieldId="nord-number-field-"+H++,this.inputId=`${this.numberFieldId}-input`,this.localize=new x(this),this.inputRef=p(),this.formData=new b(this,{value:()=>null==this.value?void 0:String(this.value)}),this.stateAttributes=new f(this,(()=>({invalid:this.effectiveInvalid})),{trackFocus:!0}),this.formAncestor=null,this.value=null,this.step=1,this.largeStep=10,this.smallStep=.1,this.snapOnStep=!1,this.allowOutOfRange=!1,this.integer=!1,this.allowWheelScrub=!1,this.scrub=!1,this.disabled=!1,this.readonly=!1,this.invalid=!1,this.hideControls=!1,this.currencyPlacement="auto",this.inputText="",this.scrubbing=!1,this.allowInputSync=!0,this.hasTouchedInput=!1,this.skipNextClick=!1,this.scrubAccumulator=0,this.scrubMoved=!1,this.scrubPointerDownTarget=null,this.onScrubMove=e=>this.handleScrubMove(e),this.onScrubUp=e=>this.handleScrubUp(e),this.onWheel=e=>this.handleWheel(e),this.wheelAttached=!1}get ownInvalid(){return this.invalid||super.ownInvalid}set form(e){const t=this._formId;this._formId=e,this.requestUpdate("form",t)}get form(){if(this._formId){return this.getRootNode().querySelector(`form#${this._formId}`)}return this.formAncestor}get minWithDefault(){var e;return null!==(e=this.min)&&void 0!==e?e:Number.MIN_SAFE_INTEGER}get maxWithDefault(){var e;return null!==(e=this.max)&&void 0!==e?e:Number.MAX_SAFE_INTEGER}get minWithZeroDefault(){var e;return null!==(e=this.min)&&void 0!==e?e:0}get allowsNegative(){return this.minWithDefault<0}get numericStep(){return"any"===this.step?1:Z(this.step,1)}get effectiveLargeStep(){return Z(this.largeStep,10)}get effectiveSmallStep(){return Z(this.smallStep,.1)}formatDisplay(e){return this.formatValue?this.formatValue(e,this.locale,this.format):j(e,this.locale,this.format)}connectedCallback(){var e,t;super.connectedCallback(),y(this,K),this.formAncestor=this.closest("form"),null!==(e=this.ariaObserver)&&void 0!==e||(this.ariaObserver=new MutationObserver((()=>this.requestUpdate()))),this.ariaObserver.observe(this,{attributes:!0,attributeFilter:["aria-label","aria-labelledby","aria-describedby"]}),null!==(t=this.slotObserver)&&void 0!==t||(this.slotObserver=new MutationObserver((()=>this.requestUpdate()))),this.slotObserver.observe(this,{childList:!0}),this.allowInputSync&&(this.inputText=this.formatDisplay(this.value))}disconnectedCallback(){var e,t;super.disconnectedCallback(),null===(e=this.ariaObserver)||void 0===e||e.disconnect(),this.ariaObserver=void 0,null===(t=this.slotObserver)||void 0===t||t.disconnect(),this.slotObserver=void 0,this.formAncestor=null,this.stopHold(),this.detachWheel(),this.removeScrubListeners()}createRenderRoot(){if(r)return this;const e=document.createElement("div");return e.className="n-number-field-root",this.appendChild(e),e}relocateSlots(){if(r)return;const e=(e,t)=>{if(t)for(const i of Array.from(this.children))i.getAttribute("slot")===e&&i.parentElement!==t&&t.appendChild(i)};e("start",this.startZone),e("end",this.endZone)}willUpdate(e){if(e.has("value")&&this.allowInputSync&&!this.allowOutOfRange&&null!=this.value){const e=F(this.value,this.minWithDefault,this.maxWithDefault);e!==this.value&&(this.value=e)}e.has("value")&&this.integer&&null!=this.value&&!Number.isInteger(this.value)&&(this.value=Math.round(this.value)),this.allowInputSync&&(e.has("value")||e.has("format")||e.has("locale"))&&(this.inputText=this.formatDisplay(this.value))}updated(e){var t;super.updated(e),this.relocateSlots();const i=this.allowWheelScrub&&!this.disabled&&!this.readonly;if(i&&!this.wheelAttached?this.attachWheel():!i&&this.wheelAttached&&this.detachWheel(),this.pendingSelection){const[e,i]=this.pendingSelection;this.pendingSelection=void 0,null===(t=this.inputRef.value)||void 0===t||t.setSelectionRange(e,i)}}focus(e){var t;null===(t=this.inputRef.value)||void 0===t||t.focus(e)}isTextReason(e){return"input-change"===e||"input-blur"===e||"input-paste"===e||"input-clear"===e||"none"===e}getStepAmount(e){return(null==e?void 0:e.altKey)?this.effectiveSmallStep:(null==e?void 0:e.shiftKey)?this.effectiveLargeStep:this.numericStep}setValue(e,{reason:t,direction:i,event:n}){var r;const o=!this.allowOutOfRange||!this.isTextReason(t),s=q(e,{step:i?this.getStepAmount(n)*i:void 0,format:this.format,minWithDefault:this.minWithDefault,maxWithDefault:this.maxWithDefault,minWithZeroDefault:this.minWithZeroDefault,snapOnStep:this.snapOnStep,small:null!==(r=null==n?void 0:n.altKey)&&void 0!==r&&r,clamp:o}),l=this.isTextReason(t),a=s!==this.value||l&&(e!==this.value||!1===this.allowInputSync);return a&&(this.value=s,this.dispatchEvent(new v("input"))),this.allowInputSync&&(this.inputText=this.formatDisplay(s)),this.requestUpdate(),a}incrementValue(e,{direction:t,currentValue:i,event:n,reason:r}){var o;const s=i??this.value,l="number"==typeof s?s+e*t:Math.max(0,null!==(o=this.min)&&void 0!==o?o:0);return this.setValue(l,{reason:r,direction:t,event:n})}commit(e){this.dispatchEvent(new I(this.value,e))}getAllowedNonNumericKeys(){var e,t,i;const{decimal:n,group:r,currency:o,literal:s}=$(this.locale,this.format),l=new Set;T.forEach((e=>l.add(e))),n&&l.add(n),r&&(l.add(r),_.test(r)&&l.add(" "));const a=null===(e=this.format)||void 0===e?void 0:e.style,d="percent"===a||"unit"===a&&"percent"===(null===(t=this.format)||void 0===t?void 0:t.unit),u="percent"===a||"unit"===a&&"permille"===(null===(i=this.format)||void 0===i?void 0:i.unit);if(d&&A.forEach((e=>l.add(e))),u&&V.forEach((e=>l.add(e))),"currency"===a&&o&&l.add(o),s&&(Array.from(s).forEach((e=>l.add(e))),_.test(s)&&l.add(" ")),P.forEach((e=>l.add(e))),this.minWithDefault<0&&N.forEach((e=>l.add(e))),this.integer)for(const e of[n,".",".","٫"])e&&e!==r&&l.delete(e);return l}handleKeydown(e){if(e.defaultPrevented||this.readonly||this.disabled)return;this.allowInputSync=!0;if(!new Set(["ArrowUp","ArrowDown","PageUp","PageDown","Home","End"]).has(e.key)){if(this.mask)return;const t=this.getAllowedNonNumericKeys(),i=e.key>="0"&&e.key<="9",n=e.key.length>1;return void(229===e.which||e.ctrlKey||e.metaKey||e.altKey||i||n||t.has(e.key)||E.test(e.key)||C.test(e.key)||W.test(e.key)||L.test(e.key)||this.allowsNegative&&M.test(e.key)||O.test(e.key)||e.preventDefault())}const t=this.parseValue?this.parseValue(this.inputText,this.locale,this.format):R(this.inputText,this.locale,this.format);switch(e.key){case"ArrowUp":e.preventDefault(),this.incrementValue(this.getStepAmount(e),{direction:1,currentValue:t,event:e,reason:"keyboard"}),this.commit("keyboard");break;case"ArrowDown":e.preventDefault(),this.incrementValue(this.getStepAmount(e),{direction:-1,currentValue:t,event:e,reason:"keyboard"}),this.commit("keyboard");break;case"PageUp":e.preventDefault(),this.incrementValue(this.effectiveLargeStep,{direction:1,currentValue:t,reason:"keyboard"}),this.commit("keyboard");break;case"PageDown":e.preventDefault(),this.incrementValue(this.effectiveLargeStep,{direction:-1,currentValue:t,reason:"keyboard"}),this.commit("keyboard");break;case"Home":null!=this.min&&Number.isFinite(this.min)&&(e.preventDefault(),this.setValue(this.min,{reason:"keyboard"}),this.commit("keyboard"));break;case"End":null!=this.max&&Number.isFinite(this.max)&&(e.preventDefault(),this.setValue(this.max,{reason:"keyboard"}),this.commit("keyboard"))}}isValidCharacterString(e){const t=this.getAllowedNonNumericKeys();return Array.from(e).every((e=>e>="0"&&e<="9"||E.test(e)||W.test(e)||C.test(e)||L.test(e)||this.allowsNegative&&M.test(e)||t.has(e)))}handleInput(e){var t,i;e.stopPropagation(),this.allowInputSync=!1;const n=e.target;let r=n.value;const o=this.mask;if(o&&""!==r.trim()){const e=o({value:r,selectionStart:null!==(t=n.selectionStart)&&void 0!==t?t:r.length,selectionEnd:null!==(i=n.selectionEnd)&&void 0!==i?i:r.length},{locale:this.locale,format:this.format});r=e.value,this.pendingSelection=[e.selectionStart,e.selectionEnd]}if(""===r.trim())return this.inputText="",this.pendingSelection=void 0,void this.setValue(null,{reason:"input-clear"});if(!this.mask&&!this.isValidCharacterString(r))return;this.inputText=r;const s=this.parseValue?this.parseValue(r,this.locale,this.format):R(r,this.locale,this.format);null!==s&&this.setValue(s,{reason:"input-change"})}handlePaste(e){var t,i;if(e.defaultPrevented||this.readonly||this.disabled)return;if(this.mask)return;const n=null!==(i=null===(t=e.clipboardData)||void 0===t?void 0:t.getData("text/plain"))&&void 0!==i?i:"";e.preventDefault();const r=this.parseValue?this.parseValue(n,this.locale,this.format):R(n,this.locale,this.format);null!==r&&(this.allowInputSync=!0,this.setValue(r,{reason:"input-paste"}))}handleFocus(e){if(e.defaultPrevented||this.readonly||this.disabled)return;if(this.hasTouchedInput)return;this.hasTouchedInput=!0;const t=e.currentTarget,i=t.value.length;t.setSelectionRange(i,i)}handleBlur(e){if(e.defaultPrevented||this.readonly||this.disabled)return;if(this.allowInputSync=!0,""===this.inputText.trim())return this.setValue(null,{reason:"input-clear"}),void this.commit("input-clear");const t=this.parseValue?this.parseValue(this.inputText,this.locale,this.format):R(this.inputText,this.locale,this.format);if(null===t){const e=this.formatDisplay(this.value);return void(this.inputText!==e&&(this.inputText=e))}const i=B(this.format)?U(t,this.format):t;i!==this.value&&this.setValue(i,{reason:"input-blur"});const n=this.formatDisplay(this.value);this.inputText!==n&&(this.inputText=n),this.commit("input-blur")}commitDirtyInput(e,t){this.allowInputSync=!0;const i=this.parseValue?this.parseValue(this.inputText,this.locale,this.format):R(this.inputText,this.locale,this.format);null!==i&&this.setValue(i,{reason:e,direction:t})}handleStepPointerDown(e,t){var i;const n=!e.button||0===e.button;if(e.defaultPrevented||this.readonly||this.disabled||!n)return;const r=1===t?"increment-press":"decrement-press";this.skipNextClick=!0,this.activePointerId=e.pointerId,this.commitDirtyInput(r,t),"touch"!==e.pointerType&&"pen"!==e.pointerType&&(null===(i=this.inputRef.value)||void 0===i||i.focus()),this.allowInputSync=!0,this.incrementValue(this.getStepAmount(e),{direction:t,event:e,reason:r});const o=e.currentTarget;try{o.setPointerCapture(e.pointerId)}catch(e){}this.holdStartTimer=setTimeout((()=>{this.holdTickTimer=setInterval((()=>{this.allowInputSync=!0,this.incrementValue(this.getStepAmount(e),{direction:t,event:e,reason:r})}),60)}),400)}handleStepPointerStop(e,t){if(void 0!==this.activePointerId&&this.activePointerId!==e.pointerId)return;const i=null!=this.holdStartTimer||null!=this.holdTickTimer;this.stopHold(),this.activePointerId=void 0,(this.skipNextClick||i)&&this.commit(1===t?"increment-press":"decrement-press")}handleStepClick(e,t){const i=1===t?"increment-press":"decrement-press";if(e.defaultPrevented||this.readonly||this.disabled)return;if(this.skipNextClick)return void(this.skipNextClick=!1);this.commitDirtyInput(i,t),this.allowInputSync=!0;this.incrementValue(this.getStepAmount(e),{direction:t,event:e,reason:i})&&this.commit(i)}stopHold(){null!=this.holdStartTimer&&(clearTimeout(this.holdStartTimer),this.holdStartTimer=void 0),null!=this.holdTickTimer&&(clearInterval(this.holdTickTimer),this.holdTickTimer=void 0)}get incrementDisabled(){return this.disabled||this.readonly||null!=this.value&&this.value>=this.maxWithDefault}get decrementDisabled(){return this.disabled||this.readonly||null!=this.value&&this.value<=this.minWithDefault}attachWheel(){const e=this.inputRef.value;e&&(e.addEventListener("wheel",this.onWheel,{passive:!1}),this.wheelAttached=!0)}detachWheel(){const e=this.inputRef.value;null==e||e.removeEventListener("wheel",this.onWheel),this.wheelAttached=!1}handleWheel(e){if(e.ctrlKey||this.inputRef.value!==this.getActiveElement())return;e.preventDefault(),this.allowInputSync=!0;const t=this.getStepAmount(e);this.incrementValue(t,{direction:e.deltaY>0?-1:1,event:e,reason:"wheel"})}getActiveElement(){return this.getRootNode().activeElement}handleScrubPointerDown(e){var t;const i=!e.button||0===e.button;if(e.defaultPrevented||this.readonly||this.disabled||!i)return;"mouse"===e.pointerType&&(e.preventDefault(),null===(t=this.inputRef.value)||void 0===t||t.focus()),this.scrubbing=!0,this.scrubMoved=!1,this.scrubAccumulator=0,this.scrubPointerDownTarget=e.target;const n=this.ownerWindow();n.addEventListener("pointermove",this.onScrubMove,!0),n.addEventListener("pointerup",this.onScrubUp,!0)}handleScrubMove(e){if(this.scrubbing&&(e.preventDefault(),this.scrubAccumulator+=e.movementX,Math.abs(this.scrubAccumulator)>=2)){this.scrubAccumulator=0,this.scrubMoved=!0;const t=e.movementX,i=Math.abs(t*this.getStepAmount(e));0!==i&&(this.allowInputSync=!0,this.incrementValue(i,{direction:t>=0?1:-1,event:e,reason:"scrub"}))}}handleScrubUp(e){this.removeScrubListeners(),this.scrubbing=!1,this.commit("scrub"),!this.scrubMoved&&this.scrubPointerDownTarget&&this.scrubPointerDownTarget.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0})),this.scrubMoved=!1,this.scrubPointerDownTarget=null}removeScrubListeners(){const e=this.ownerWindow();e.removeEventListener("pointermove",this.onScrubMove,!0),e.removeEventListener("pointerup",this.onScrubUp,!0)}handleScrubTouchStart(e){this.disabled||this.readonly||1===e.touches.length&&e.preventDefault()}ownerWindow(){var e;return null!==(e=this.ownerDocument.defaultView)&&void 0!==e?e:window}get computedInputMode(){if(null!=this.min&&this.min<0)return"text";if(this.integer)return"numeric";const e="any"===this.step||Number.isInteger(this.numericStep),t=null==this.min||Number.isInteger(this.min)&&this.min>=0,i=null==this.max||Number.isInteger(this.max);return e&&t&&i?"numeric":"decimal"}get resolvedCurrencyPlacement(){return"start"===this.currencyPlacement||"end"===this.currencyPlacement?this.currencyPlacement:this.currency?z(this.locale,this.currency):"start"}render(){var e,t;const i=this.inputId,n=this.controlAriaLabel,r=this.controlAriaLabelledby,l=this.currency?D(this.locale,this.currency):void 0,a=this.currency?this.resolvedCurrencyPlacement:void 0,d=this.currency?`${i}-currency`:void 0,u=[this.controlAriaDescribedby,d].filter(Boolean).join(" ")||void 0,c=e=>null!=l&&a===e?o`<span id="${d}" class="n-currency-symbol" aria-hidden="true">${l}</span>`:s;return o`${this.renderLabel()}<div class="${h({"n-number-field":!0,"is-scrubbing":this.scrubbing})}">${this.scrub?o`<div class="n-scrub" role="presentation" aria-hidden="true" @pointerdown="${this.handleScrubPointerDown}" @touchstart="${this.handleScrubTouchStart}"><svg class="n-scrub-grip" width="24" height="14" viewBox="0 0 24 14" fill="currentColor" aria-hidden="true"><path d="M19.5 5.5L6.49737 5.51844V2L1 6.9999L6.5 12L6.49737 8.5L19.5 8.5V12L25 6.9999L19.5 2V5.5Z"></path></svg></div>`:s}<div class="${h({"n-control":!0,"is-invalid":this.effectiveInvalid})}">${this.hideControls?s:o`<button class="${h({"n-step":!0,"n-decrement":!0})}" type="button" tabindex="-1" aria-label="${this.localize.term("decreaseLabel")}" aria-controls="${i}" aria-readonly="${this.readonly?"true":s}" ?disabled="${this.decrementDisabled}" @pointerdown="${e=>this.handleStepPointerDown(e,-1)}" @pointerup="${e=>this.handleStepPointerStop(e,-1)}" @pointerleave="${e=>this.handleStepPointerStop(e,-1)}" @pointercancel="${e=>this.handleStepPointerStop(e,-1)}" @click="${e=>this.handleStepClick(e,-1)}"><nord-icon name="interface-remove-small"></nord-icon></button>`} <span class="n-number-field-slot n-number-field-slot-start">${c("start")}</span> <input ${m(this.inputRef)} class="n-input" type="text" id="${i}" inputmode="${this.computedInputMode}" .value="${this.inputText}" placeholder="${null!==(e=this.placeholder)&&void 0!==e?e:s}" ?disabled="${this.effectiveDisabled}" ?readonly="${this.readonly}" ?required="${this.required}" autocomplete="off" autocorrect="off" spellcheck="false" aria-roledescription="${this.localize.term("roleDescription")}" aria-label="${null!=n?n:s}" aria-labelledby="${null!=r?r:s}" aria-describedby="${null!=u?u:s}" aria-invalid="${null!==(t=this.controlAriaInvalid)&&void 0!==t?t:s}" aria-required="${this.required?"true":s}" aria-readonly="${this.readonly?"true":s}" @input="${this.handleInput}" @blur="${this.handleBlur}" @focus="${this.handleFocus}" @keydown="${this.handleKeydown}" @paste="${this.handlePaste}"> <span class="n-number-field-slot n-number-field-slot-end">${c("end")}</span> ${this.hideControls?s:o`<button class="${h({"n-step":!0,"n-increment":!0})}" type="button" tabindex="-1" aria-label="${this.localize.term("increaseLabel")}" aria-controls="${i}" aria-readonly="${this.readonly?"true":s}" ?disabled="${this.incrementDisabled}" @pointerdown="${e=>this.handleStepPointerDown(e,1)}" @pointerup="${e=>this.handleStepPointerStop(e,1)}" @pointerleave="${e=>this.handleStepPointerStop(e,1)}" @pointercancel="${e=>this.handleStepPointerStop(e,1)}" @click="${e=>this.handleStepClick(e,1)}"><nord-icon name="interface-add-small"></nord-icon></button>`}</div></div>${this.renderError()} ${this.isHintBelow?this.renderHint():s}`}};e([a(".n-number-field-slot-start")],G.prototype,"startZone",void 0),e([a(".n-number-field-slot-end")],G.prototype,"endZone",void 0),e([d({converter:function(e){if(null==e||""===e.trim())return null;const t=Number(e);return Number.isNaN(t)?null:t}})],G.prototype,"value",void 0),e([d({reflect:!0,type:Number})],G.prototype,"min",void 0),e([d({reflect:!0,type:Number})],G.prototype,"max",void 0),e([d({reflect:!0,converter:function(e){return null==e?1:"any"===e?"any":Z(Number(e),1)}})],G.prototype,"step",void 0),e([d({reflect:!0,type:Number,attribute:"large-step"})],G.prototype,"largeStep",void 0),e([d({reflect:!0,type:Number,attribute:"small-step"})],G.prototype,"smallStep",void 0),e([d({reflect:!0,type:Boolean,attribute:"snap-on-step"})],G.prototype,"snapOnStep",void 0),e([d({reflect:!0,type:Boolean,attribute:"allow-out-of-range"})],G.prototype,"allowOutOfRange",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"integer",void 0),e([d({reflect:!0,type:Boolean,attribute:"allow-wheel-scrub"})],G.prototype,"allowWheelScrub",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"scrub",void 0),e([d({attribute:!1})],G.prototype,"format",void 0),e([d({attribute:!1})],G.prototype,"formatValue",void 0),e([d({attribute:!1})],G.prototype,"parseValue",void 0),e([d({attribute:!1})],G.prototype,"mask",void 0),e([d({reflect:!0})],G.prototype,"locale",void 0),e([d({reflect:!0})],G.prototype,"placeholder",void 0),e([d({reflect:!0})],G.prototype,"name",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"disabled",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"readonly",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"invalid",void 0),e([d({reflect:!0,type:Boolean,attribute:"hide-controls"})],G.prototype,"hideControls",void 0),e([d({reflect:!0})],G.prototype,"currency",void 0),e([d({reflect:!0,attribute:"currency-placement"})],G.prototype,"currencyPlacement",void 0),e([u()],G.prototype,"inputText",void 0),e([u()],G.prototype,"scrubbing",void 0),e([d()],G.prototype,"form",null),G=e([c("nord-number-field")],G);var X=G;export{I as NumberFieldChangeEvent,X as default,D as getCurrencySymbol,z as resolveCurrencyPlacement};
|
|
1
|
+
import{_ as e}from"./tslib.es6-CmLYFWVC.js";import{a as t}from"./interface-add-small-B-hv2v9J.js";import{r as i}from"./interface-remove-small-DUaWSs90.js";import{css as n,isServer as r,html as o,nothing as s,LitElement as l}from"lit";import{query as a,property as d,state as u,customElement as c}from"lit/decorators.js";import{classMap as h}from"lit/directives/class-map.js";import{createRef as p,ref as m}from"lit/directives/ref.js";import{F as b}from"./FormDataController-OUt5L5uC.js";import{S as f}from"./StateAttributesController-BIh96sXZ.js";import{N as v}from"./events-Bv6wNHwJ.js";import{i as y}from"./light-dom-styles-DXsbiKU9.js";import{D as g}from"./DraftComponentMixin-4qug5Rky.js";import{F as S}from"./FieldControlMixin-BXNZJXu9.js";import{S as k}from"./SizeMixin-CU9cLbLC.js";import w from"./Icon.js";import{LocalizeController as x}from"./LocalizeController.js";import{resolveCurrencyPlacement as z,getCurrencySymbol as D}from"./currency.js";import{formatNumber as j}from"./format.js";import{NumberFieldChangeEvent as I}from"./NumberFieldEvents.js";import{getNumberLocaleDetails as $,BASE_NON_NUMERIC_SYMBOLS as T,SPACE_SEPARATOR_RE as _,PERCENTAGES as A,PERMILLE as V,PLUS_SIGNS_WITH_ASCII as P,MINUS_SIGNS_WITH_ASCII as N,ARABIC_DETECT_RE as E,PERSIAN_DETECT_RE as C,HAN_DETECT_RE as W,FULLWIDTH_DETECT_RE as L,ANY_MINUS_DETECT_RE as M,ANY_PLUS_DETECT_RE as O,parseNumber as R}from"./parse.js";import{clamp as F,toValidatedNumber as q,hasNumberFormatRoundingOptions as B,removeFloatingPointErrors as U}from"./validate.js";import"./context-consumer-BJVUulGf.js";import"./context-request-event-CCRlXoeR.js";import"./field-context.js";import"./create-context-CJ69ntlY.js";import"./SlotController-Z6eG7LSZ.js";import"./EventController-BBOmvfLa.js";import"./VisuallyHidden.js";import"lit/directives/if-defined.js";import"lit/directives/unsafe-html.js";import"./observe-D0n0zOfU.js";import"./cond-CI1KbneT.js";import"./Component-BJ5O5n8k.js";import"./IconManager.js";import"./translation.js";import"./en-us.js";import"./localization.js";import"./localization2.js";import"./localization3.js";import"./localization4.js";import"./localization5.js";import"./localization6.js";import"./localization7.js";import"./localization8.js";import"./localization9.js";import"./localization10.js";import"./localization11.js";import"./localization12.js";import"./localization13.js";import"./localization14.js";import"./localization15.js";import"./localization16.js";import"./localization17.js";import"./localization18.js";import"./localization19.js";import"./localization20.js";import"./localization21.js";import"./localization22.js";import"./localization23.js";import"./localization24.js";const K=n`nord-number-field,nord-number-field *,nord-number-field :after,nord-number-field :before{box-sizing:border-box}nord-number-field{--n-number-field-control-min-inline-size:8ch;--n-number-field-background:var(--n-input-background,var(--n-color-surface));--n-number-field-border-color:var(--n-input-border-color,var(--n-color-border-strong));--n-number-field-border-radius:var(--n-input-border-radius,var(--n-border-radius-s));--_n-number-field-block-size:var(--n-number-field-block-size,var(--n-space-xl));--_n-number-field-border-color:var(--n-number-field-border-color);--_n-number-field-min-content-block-size:calc(var(--n-line-height-form) + var(--n-space-s)/1.6*2 + 2px);--_n-number-field-padding-block:calc(var(--_n-number-field-block-size)/2 - var(--n-line-height-form)/2 - 1px);--_n-number-field-padding-inline:calc(var(--n-space-s)*1.6);--_n-number-field-font-size:var(--n-font-size-m);--_n-number-field-step-inline-size:var(--n-number-field-step-inline-size,var(--_n-number-field-block-size));display:block}nord-number-field .n-number-field{inline-size:100%}nord-number-field .n-number-field-root{display:contents}nord-number-field .n-scrub{align-items:center;color:var(--n-color-icon);cursor:ew-resize;display:inline-flex;margin-block-end:var(--n-space-xs);touch-action:none;user-select:none}nord-number-field .n-scrub-grip{block-size:var(--n-size-icon-s);display:block;inline-size:auto}nord-number-field.is-scrubbing,nord-number-field.is-scrubbing .n-input,nord-number-field.is-scrubbing .n-scrub{cursor:ew-resize}nord-number-field .n-control{align-items:stretch;background:var(--n-number-field-background);border:1px solid var(--_n-number-field-border-color);border-radius:var(--n-number-field-border-radius);display:flex;inline-size:100%;min-block-size:var(--_n-number-field-block-size);transition:border var(--n-transition-quickly),box-shadow var(--n-transition-quickly),background var(--n-transition-quickly)}nord-number-field .n-control:hover{--_n-number-field-border-color:var(--n-input-border-color,var(--n-color-border-hover))}nord-number-field .n-control:focus-within{--_n-number-field-border-color:var(--n-input-border-color,var(--n-color-accent));box-shadow:0 0 0 1px var(--_n-number-field-border-color)}nord-number-field[disabled] .n-control{--n-number-field-background:var(--n-input-background-inactive,var(--n-color-active));--_n-number-field-border-color:var(--n-input-border-color,var(--n-color-active));cursor:auto}nord-number-field .n-control.is-invalid{--_n-number-field-border-color:var(--n-color-border-danger)}nord-number-field .n-control.is-invalid:focus-within{--_n-number-field-border-color:var(--n-color-status-danger)}nord-number-field .n-input{appearance:none;background:#0000;border:none;color:var(--n-color-text);flex:1;font-family:var(--n-font-family);font-feature-settings:var(--n-font-features);font-size:var(--_n-number-field-font-size);font-variant-numeric:tabular-nums;line-height:var(--n-line-height-form);margin:0;min-inline-size:var(--n-number-field-control-min-inline-size);outline:none;padding-block-end:var(--_n-number-field-padding-block);padding-block-start:var(--_n-number-field-padding-block);padding-inline-end:var(--_n-number-field-padding-inline);padding-inline-start:var(--_n-number-field-padding-inline);text-align:start}nord-number-field .n-control input.n-input:focus,nord-number-field .n-control input.n-input:focus-visible,nord-number-field .n-control input.n-input:focus-within{background:#0000;border:none;box-shadow:none;outline:none}nord-number-field .n-input::placeholder{color:var(--n-color-text-weakest);opacity:1}nord-number-field[disabled] .n-input{color:var(--n-color-text-weakest)}nord-number-field .n-number-field-slot{align-items:center;color:var(--n-color-text-weaker);display:inline-flex;flex:0 0 auto;font-family:var(--n-font-family);font-size:var(--_n-number-field-font-size);gap:var(--n-space-xs);line-height:var(--n-line-height-form);white-space:nowrap}nord-number-field .n-number-field-slot:empty{display:none}nord-number-field .n-number-field-slot-start{padding-inline-start:var(--_n-number-field-padding-inline)}nord-number-field .n-number-field-slot-end{padding-inline-end:var(--_n-number-field-padding-inline)}nord-number-field .n-number-field-slot nord-icon{--_n-icon-size:var(--n-size-icon-s)}nord-number-field[disabled] .n-number-field-slot{color:var(--n-color-text-weakest)}nord-number-field .n-currency-symbol{color:var(--n-color-text-weaker);font-variant-numeric:tabular-nums;user-select:none}nord-number-field .n-step{align-items:center;appearance:none;background:#0000;border:none;color:var(--n-color-icon);cursor:pointer;display:flex;flex-shrink:0;inline-size:var(--_n-number-field-step-inline-size);justify-content:center;margin:0;padding:0;transition:color var(--n-transition-quickly),background var(--n-transition-quickly);user-select:none}nord-number-field .n-step:hover:not([disabled]){background:var(--n-color-button-hover);color:var(--n-color-text)}nord-number-field .n-step:active:not([disabled]){background:var(--n-color-active)}nord-number-field .n-step[disabled]{color:var(--n-color-text-weakest);cursor:auto}nord-number-field .n-decrement{border-inline-end:1px solid var(--_n-number-field-border-color)}nord-number-field .n-increment{border-inline-start:1px solid var(--_n-number-field-border-color)}nord-number-field .n-step nord-icon{--_n-icon-size:var(--n-size-icon-s)}nord-number-field[size=s]{--_n-number-field-block-size:var(--n-number-field-block-size,calc(var(--n-space-l) + var(--n-space-xs)));--_n-number-field-padding-block:calc(var(--_n-number-field-block-size)/2 - var(--n-line-height-form)/2 - 1px);--_n-number-field-padding-inline:var(--n-space-s);--_n-number-field-font-size:var(--n-font-size-s);--_n-number-field-step-inline-size:var(--n-number-field-step-inline-size,var(--_n-number-field-block-size))}nord-number-field[size=s] .n-step nord-icon{--_n-icon-size:var(--n-size-icon-xs)}nord-number-field[size=l]{--_n-number-field-block-size:max(var(--n-number-field-block-size,calc(var(--n-space-xxl) - var(--n-space-l))),var(--_n-number-field-min-content-block-size));--_n-number-field-padding-block:calc(var(--_n-number-field-block-size)/2 - var(--n-line-height-form)/2 - 1px);--_n-number-field-font-size:var(--n-font-size-l);--_n-number-field-step-inline-size:var(--n-number-field-step-inline-size,var(--_n-number-field-block-size))}nord-number-field[size=l] .n-step nord-icon{--_n-icon-size:var(--n-size-icon-m)}`;w.registerIcon(t),w.registerIcon(i);let H=1;function Z(e,t){return Number.isFinite(e)&&e>0?e:t}let G=class extends(g(k(S(l)))){constructor(){super(...arguments),this.numberFieldId="nord-number-field-"+H++,this.inputId=`${this.numberFieldId}-input`,this.localize=new x(this),this.inputRef=p(),this.formData=new b(this,{value:()=>null==this.value?void 0:String(this.value)}),this.stateAttributes=new f(this,(()=>({invalid:this.effectiveInvalid})),{trackFocus:!0}),this.formAncestor=null,this.value=null,this.step=1,this.largeStep=10,this.smallStep=.1,this.snapOnStep=!1,this.allowOutOfRange=!1,this.integer=!1,this.allowWheelScrub=!1,this.scrub=!1,this.disabled=!1,this.readonly=!1,this.invalid=!1,this.hideControls=!1,this.currencyPlacement="auto",this.inputText="",this.scrubbing=!1,this.allowInputSync=!0,this.hasTouchedInput=!1,this.skipNextClick=!1,this.scrubAccumulator=0,this.scrubMoved=!1,this.scrubPointerDownTarget=null,this.onScrubMove=e=>this.handleScrubMove(e),this.onScrubUp=e=>this.handleScrubUp(e),this.onWheel=e=>this.handleWheel(e),this.wheelAttached=!1}get ownInvalid(){return this.invalid||super.ownInvalid}set form(e){const t=this._formId;this._formId=e,this.requestUpdate("form",t)}get form(){if(this._formId){return this.getRootNode().querySelector(`form#${this._formId}`)}return this.formAncestor}get minWithDefault(){var e;return null!==(e=this.min)&&void 0!==e?e:Number.MIN_SAFE_INTEGER}get maxWithDefault(){var e;return null!==(e=this.max)&&void 0!==e?e:Number.MAX_SAFE_INTEGER}get minWithZeroDefault(){var e;return null!==(e=this.min)&&void 0!==e?e:0}get allowsNegative(){return this.minWithDefault<0}get numericStep(){return"any"===this.step?1:Z(this.step,1)}get effectiveLargeStep(){return Z(this.largeStep,10)}get effectiveSmallStep(){return Z(this.smallStep,.1)}formatDisplay(e){return this.formatValue?this.formatValue(e,this.locale,this.format):j(e,this.locale,this.format)}connectedCallback(){var e,t;super.connectedCallback(),y(this,K),this.formAncestor=this.closest("form"),null!==(e=this.ariaObserver)&&void 0!==e||(this.ariaObserver=new MutationObserver((()=>this.requestUpdate()))),this.ariaObserver.observe(this,{attributes:!0,attributeFilter:["aria-label","aria-labelledby","aria-describedby"]}),null!==(t=this.slotObserver)&&void 0!==t||(this.slotObserver=new MutationObserver((()=>this.requestUpdate()))),this.slotObserver.observe(this,{childList:!0}),this.allowInputSync&&(this.inputText=this.formatDisplay(this.value))}disconnectedCallback(){var e,t;super.disconnectedCallback(),null===(e=this.ariaObserver)||void 0===e||e.disconnect(),this.ariaObserver=void 0,null===(t=this.slotObserver)||void 0===t||t.disconnect(),this.slotObserver=void 0,this.formAncestor=null,this.stopHold(),this.detachWheel(),this.removeScrubListeners()}createRenderRoot(){if(r)return this;const e=document.createElement("div");return e.className="n-number-field-root",this.appendChild(e),e}relocateSlots(){if(r)return;const e=(e,t)=>{if(t)for(const i of Array.from(this.children))i.getAttribute("slot")===e&&i.parentElement!==t&&t.appendChild(i)};e("start",this.startZone),e("end",this.endZone)}willUpdate(e){if(e.has("value")&&this.allowInputSync&&!this.allowOutOfRange&&null!=this.value){const e=F(this.value,this.minWithDefault,this.maxWithDefault);e!==this.value&&(this.value=e)}e.has("value")&&this.integer&&null!=this.value&&!Number.isInteger(this.value)&&(this.value=Math.round(this.value)),this.allowInputSync&&(e.has("value")||e.has("format")||e.has("locale"))&&(this.inputText=this.formatDisplay(this.value))}updated(e){var t;super.updated(e),this.relocateSlots();const i=this.allowWheelScrub&&!this.disabled&&!this.readonly;if(i&&!this.wheelAttached?this.attachWheel():!i&&this.wheelAttached&&this.detachWheel(),this.pendingSelection){const[e,i]=this.pendingSelection;this.pendingSelection=void 0,null===(t=this.inputRef.value)||void 0===t||t.setSelectionRange(e,i)}}focus(e){var t;null===(t=this.inputRef.value)||void 0===t||t.focus(e)}isTextReason(e){return"input-change"===e||"input-blur"===e||"input-paste"===e||"input-clear"===e||"none"===e}getStepAmount(e){return(null==e?void 0:e.altKey)?this.effectiveSmallStep:(null==e?void 0:e.shiftKey)?this.effectiveLargeStep:this.numericStep}setValue(e,{reason:t,direction:i,event:n}){var r;const o=!this.allowOutOfRange||!this.isTextReason(t),s=q(e,{step:i?this.getStepAmount(n)*i:void 0,format:this.format,minWithDefault:this.minWithDefault,maxWithDefault:this.maxWithDefault,minWithZeroDefault:this.minWithZeroDefault,snapOnStep:this.snapOnStep,small:null!==(r=null==n?void 0:n.altKey)&&void 0!==r&&r,clamp:o}),l=this.isTextReason(t),a=s!==this.value||l&&(e!==this.value||!1===this.allowInputSync);return a&&(this.value=s,this.dispatchEvent(new v("input"))),this.allowInputSync&&(this.inputText=this.formatDisplay(s)),this.requestUpdate(),a}incrementValue(e,{direction:t,currentValue:i,event:n,reason:r}){var o;const s=i??this.value,l="number"==typeof s?s+e*t:Math.max(0,null!==(o=this.min)&&void 0!==o?o:0);return this.setValue(l,{reason:r,direction:t,event:n})}commit(e){this.dispatchEvent(new I(this.value,e))}getAllowedNonNumericKeys(){var e,t,i;const{decimal:n,group:r,currency:o,literal:s}=$(this.locale,this.format),l=new Set;T.forEach((e=>l.add(e))),n&&l.add(n),r&&(l.add(r),_.test(r)&&l.add(" "));const a=null===(e=this.format)||void 0===e?void 0:e.style,d="percent"===a||"unit"===a&&"percent"===(null===(t=this.format)||void 0===t?void 0:t.unit),u="percent"===a||"unit"===a&&"permille"===(null===(i=this.format)||void 0===i?void 0:i.unit);if(d&&A.forEach((e=>l.add(e))),u&&V.forEach((e=>l.add(e))),"currency"===a&&o&&l.add(o),s&&(Array.from(s).forEach((e=>l.add(e))),_.test(s)&&l.add(" ")),P.forEach((e=>l.add(e))),this.minWithDefault<0&&N.forEach((e=>l.add(e))),this.integer)for(const e of[n,".",".","٫"])e&&e!==r&&l.delete(e);return l}handleKeydown(e){if(e.defaultPrevented||this.readonly||this.disabled)return;this.allowInputSync=!0;if(!new Set(["ArrowUp","ArrowDown","PageUp","PageDown","Home","End"]).has(e.key)){if(this.mask)return;const t=this.getAllowedNonNumericKeys(),i=e.key>="0"&&e.key<="9",n=e.key.length>1;return void(229===e.which||e.ctrlKey||e.metaKey||e.altKey||i||n||t.has(e.key)||E.test(e.key)||C.test(e.key)||W.test(e.key)||L.test(e.key)||this.allowsNegative&&M.test(e.key)||O.test(e.key)||e.preventDefault())}const t=this.parseValue?this.parseValue(this.inputText,this.locale,this.format):R(this.inputText,this.locale,this.format);switch(e.key){case"ArrowUp":e.preventDefault(),this.incrementValue(this.getStepAmount(e),{direction:1,currentValue:t,event:e,reason:"keyboard"}),this.commit("keyboard");break;case"ArrowDown":e.preventDefault(),this.incrementValue(this.getStepAmount(e),{direction:-1,currentValue:t,event:e,reason:"keyboard"}),this.commit("keyboard");break;case"PageUp":e.preventDefault(),this.incrementValue(this.effectiveLargeStep,{direction:1,currentValue:t,reason:"keyboard"}),this.commit("keyboard");break;case"PageDown":e.preventDefault(),this.incrementValue(this.effectiveLargeStep,{direction:-1,currentValue:t,reason:"keyboard"}),this.commit("keyboard");break;case"Home":null!=this.min&&Number.isFinite(this.min)&&(e.preventDefault(),this.setValue(this.min,{reason:"keyboard"}),this.commit("keyboard"));break;case"End":null!=this.max&&Number.isFinite(this.max)&&(e.preventDefault(),this.setValue(this.max,{reason:"keyboard"}),this.commit("keyboard"))}}isValidCharacterString(e){const t=this.getAllowedNonNumericKeys();return Array.from(e).every((e=>e>="0"&&e<="9"||E.test(e)||W.test(e)||C.test(e)||L.test(e)||this.allowsNegative&&M.test(e)||t.has(e)))}handleInput(e){var t,i;e.stopPropagation(),this.allowInputSync=!1;const n=e.target;let r=n.value;const o=this.mask;if(o&&""!==r.trim()){const e=o({value:r,selectionStart:null!==(t=n.selectionStart)&&void 0!==t?t:r.length,selectionEnd:null!==(i=n.selectionEnd)&&void 0!==i?i:r.length},{locale:this.locale,format:this.format});r=e.value,this.pendingSelection=[e.selectionStart,e.selectionEnd]}if(""===r.trim())return this.inputText="",this.pendingSelection=void 0,void this.setValue(null,{reason:"input-clear"});if(!this.mask&&!this.isValidCharacterString(r))return;this.inputText=r;const s=this.parseValue?this.parseValue(r,this.locale,this.format):R(r,this.locale,this.format);null!==s&&this.setValue(s,{reason:"input-change"})}handlePaste(e){var t,i;if(e.defaultPrevented||this.readonly||this.disabled)return;if(this.mask)return;const n=null!==(i=null===(t=e.clipboardData)||void 0===t?void 0:t.getData("text/plain"))&&void 0!==i?i:"";e.preventDefault();const r=this.parseValue?this.parseValue(n,this.locale,this.format):R(n,this.locale,this.format);null!==r&&(this.allowInputSync=!0,this.setValue(r,{reason:"input-paste"}))}handleFocus(e){if(e.defaultPrevented||this.readonly||this.disabled)return;if(this.hasTouchedInput)return;this.hasTouchedInput=!0;const t=e.currentTarget,i=t.value.length;t.setSelectionRange(i,i)}handleBlur(e){if(e.defaultPrevented||this.readonly||this.disabled)return;if(this.allowInputSync=!0,""===this.inputText.trim())return this.setValue(null,{reason:"input-clear"}),void this.commit("input-clear");const t=this.parseValue?this.parseValue(this.inputText,this.locale,this.format):R(this.inputText,this.locale,this.format);if(null===t){const e=this.formatDisplay(this.value);return void(this.inputText!==e&&(this.inputText=e))}const i=B(this.format)?U(t,this.format):t;i!==this.value&&this.setValue(i,{reason:"input-blur"});const n=this.formatDisplay(this.value);this.inputText!==n&&(this.inputText=n),this.commit("input-blur")}commitDirtyInput(e,t){this.allowInputSync=!0;const i=this.parseValue?this.parseValue(this.inputText,this.locale,this.format):R(this.inputText,this.locale,this.format);null!==i&&this.setValue(i,{reason:e,direction:t})}handleStepPointerDown(e,t){var i;const n=!e.button||0===e.button;if(e.defaultPrevented||this.readonly||this.disabled||!n)return;const r=1===t?"increment-press":"decrement-press";this.skipNextClick=!0,this.activePointerId=e.pointerId,this.commitDirtyInput(r,t),"touch"!==e.pointerType&&"pen"!==e.pointerType&&(null===(i=this.inputRef.value)||void 0===i||i.focus()),this.allowInputSync=!0,this.incrementValue(this.getStepAmount(e),{direction:t,event:e,reason:r});const o=e.currentTarget;try{o.setPointerCapture(e.pointerId)}catch(e){}this.holdStartTimer=setTimeout((()=>{this.holdTickTimer=setInterval((()=>{this.allowInputSync=!0,this.incrementValue(this.getStepAmount(e),{direction:t,event:e,reason:r})}),60)}),400)}handleStepPointerStop(e,t){if(void 0!==this.activePointerId&&this.activePointerId!==e.pointerId)return;const i=null!=this.holdStartTimer||null!=this.holdTickTimer;this.stopHold(),this.activePointerId=void 0,(this.skipNextClick||i)&&this.commit(1===t?"increment-press":"decrement-press")}handleStepClick(e,t){const i=1===t?"increment-press":"decrement-press";if(e.defaultPrevented||this.readonly||this.disabled)return;if(this.skipNextClick)return void(this.skipNextClick=!1);this.commitDirtyInput(i,t),this.allowInputSync=!0;this.incrementValue(this.getStepAmount(e),{direction:t,event:e,reason:i})&&this.commit(i)}stopHold(){null!=this.holdStartTimer&&(clearTimeout(this.holdStartTimer),this.holdStartTimer=void 0),null!=this.holdTickTimer&&(clearInterval(this.holdTickTimer),this.holdTickTimer=void 0)}get incrementDisabled(){return this.disabled||this.readonly||null!=this.value&&this.value>=this.maxWithDefault}get decrementDisabled(){return this.disabled||this.readonly||null!=this.value&&this.value<=this.minWithDefault}attachWheel(){const e=this.inputRef.value;e&&(e.addEventListener("wheel",this.onWheel,{passive:!1}),this.wheelAttached=!0)}detachWheel(){const e=this.inputRef.value;null==e||e.removeEventListener("wheel",this.onWheel),this.wheelAttached=!1}handleWheel(e){if(e.ctrlKey||this.inputRef.value!==this.getActiveElement())return;e.preventDefault(),this.allowInputSync=!0;const t=this.getStepAmount(e);this.incrementValue(t,{direction:e.deltaY>0?-1:1,event:e,reason:"wheel"})}getActiveElement(){return this.getRootNode().activeElement}handleScrubPointerDown(e){var t;const i=!e.button||0===e.button;if(e.defaultPrevented||this.readonly||this.disabled||!i)return;"mouse"===e.pointerType&&(e.preventDefault(),null===(t=this.inputRef.value)||void 0===t||t.focus()),this.scrubbing=!0,this.scrubMoved=!1,this.scrubAccumulator=0,this.scrubPointerDownTarget=e.target;const n=this.ownerWindow();n.addEventListener("pointermove",this.onScrubMove,!0),n.addEventListener("pointerup",this.onScrubUp,!0)}handleScrubMove(e){if(this.scrubbing&&(e.preventDefault(),this.scrubAccumulator+=e.movementX,Math.abs(this.scrubAccumulator)>=2)){this.scrubAccumulator=0,this.scrubMoved=!0;const t=e.movementX,i=Math.abs(t*this.getStepAmount(e));0!==i&&(this.allowInputSync=!0,this.incrementValue(i,{direction:t>=0?1:-1,event:e,reason:"scrub"}))}}handleScrubUp(e){this.removeScrubListeners(),this.scrubbing=!1,this.commit("scrub"),!this.scrubMoved&&this.scrubPointerDownTarget&&this.scrubPointerDownTarget.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0})),this.scrubMoved=!1,this.scrubPointerDownTarget=null}removeScrubListeners(){const e=this.ownerWindow();e.removeEventListener("pointermove",this.onScrubMove,!0),e.removeEventListener("pointerup",this.onScrubUp,!0)}handleScrubTouchStart(e){this.disabled||this.readonly||1===e.touches.length&&e.preventDefault()}ownerWindow(){var e;return null!==(e=this.ownerDocument.defaultView)&&void 0!==e?e:window}get computedInputMode(){if(null!=this.min&&this.min<0)return"text";if(this.integer)return"numeric";const e="any"===this.step||Number.isInteger(this.numericStep),t=null==this.min||Number.isInteger(this.min)&&this.min>=0,i=null==this.max||Number.isInteger(this.max);return e&&t&&i?"numeric":"decimal"}get resolvedCurrencyPlacement(){return"start"===this.currencyPlacement||"end"===this.currencyPlacement?this.currencyPlacement:this.currency?z(this.locale,this.currency):"start"}render(){var e,t;const i=this.inputId,n=this.controlAriaLabel,r=this.controlAriaLabelledby,l=this.currency?D(this.locale,this.currency):void 0,a=this.currency?this.resolvedCurrencyPlacement:void 0,d=this.currency?`${i}-currency`:void 0,u=[this.controlAriaDescribedby,d].filter(Boolean).join(" ")||void 0,c=e=>null!=l&&a===e?o`<span id="${d}" class="n-currency-symbol" aria-hidden="true">${l}</span>`:s;return o`${this.renderLabel()}<div class="${h({"n-number-field":!0,"is-scrubbing":this.scrubbing})}">${this.scrub?o`<div class="n-scrub" role="presentation" aria-hidden="true" @pointerdown="${this.handleScrubPointerDown}" @touchstart="${this.handleScrubTouchStart}"><svg class="n-scrub-grip" width="24" height="14" viewBox="0 0 24 14" fill="currentColor" aria-hidden="true"><path d="M19.5 5.5L6.49737 5.51844V2L1 6.9999L6.5 12L6.49737 8.5L19.5 8.5V12L25 6.9999L19.5 2V5.5Z"></path></svg></div>`:s}<div class="${h({"n-control":!0,"is-invalid":this.effectiveInvalid})}">${this.hideControls?s:o`<button class="${h({"n-step":!0,"n-decrement":!0})}" type="button" tabindex="-1" aria-label="${this.localize.term("decreaseLabel")}" aria-controls="${i}" aria-readonly="${this.readonly?"true":s}" ?disabled="${this.decrementDisabled}" @pointerdown="${e=>this.handleStepPointerDown(e,-1)}" @pointerup="${e=>this.handleStepPointerStop(e,-1)}" @pointerleave="${e=>this.handleStepPointerStop(e,-1)}" @pointercancel="${e=>this.handleStepPointerStop(e,-1)}" @click="${e=>this.handleStepClick(e,-1)}"><nord-icon name="interface-remove-small"></nord-icon></button>`} <span class="n-number-field-slot n-number-field-slot-start">${c("start")}</span> <input ${m(this.inputRef)} class="n-input" type="text" id="${i}" inputmode="${this.computedInputMode}" .value="${this.inputText}" placeholder="${null!==(e=this.placeholder)&&void 0!==e?e:s}" ?disabled="${this.effectiveDisabled}" ?readonly="${this.readonly}" ?required="${this.required}" autocomplete="off" autocorrect="off" spellcheck="false" aria-roledescription="${this.localize.term("roleDescription")}" aria-label="${null!=n?n:s}" aria-labelledby="${null!=r?r:s}" aria-describedby="${null!=u?u:s}" aria-invalid="${null!==(t=this.controlAriaInvalid)&&void 0!==t?t:s}" aria-required="${this.required?"true":s}" aria-readonly="${this.readonly?"true":s}" @input="${this.handleInput}" @blur="${this.handleBlur}" @focus="${this.handleFocus}" @keydown="${this.handleKeydown}" @paste="${this.handlePaste}"> <span class="n-number-field-slot n-number-field-slot-end">${c("end")}</span> ${this.hideControls?s:o`<button class="${h({"n-step":!0,"n-increment":!0})}" type="button" tabindex="-1" aria-label="${this.localize.term("increaseLabel")}" aria-controls="${i}" aria-readonly="${this.readonly?"true":s}" ?disabled="${this.incrementDisabled}" @pointerdown="${e=>this.handleStepPointerDown(e,1)}" @pointerup="${e=>this.handleStepPointerStop(e,1)}" @pointerleave="${e=>this.handleStepPointerStop(e,1)}" @pointercancel="${e=>this.handleStepPointerStop(e,1)}" @click="${e=>this.handleStepClick(e,1)}"><nord-icon name="interface-add-small"></nord-icon></button>`}</div></div>${this.renderError()} ${this.isHintBelow?this.renderHint():s}`}};e([a(".n-number-field-slot-start")],G.prototype,"startZone",void 0),e([a(".n-number-field-slot-end")],G.prototype,"endZone",void 0),e([d({converter:function(e){if(null==e||""===e.trim())return null;const t=Number(e);return Number.isNaN(t)?null:t}})],G.prototype,"value",void 0),e([d({reflect:!0,type:Number})],G.prototype,"min",void 0),e([d({reflect:!0,type:Number})],G.prototype,"max",void 0),e([d({reflect:!0,converter:function(e){return null==e?1:"any"===e?"any":Z(Number(e),1)}})],G.prototype,"step",void 0),e([d({reflect:!0,type:Number,attribute:"large-step"})],G.prototype,"largeStep",void 0),e([d({reflect:!0,type:Number,attribute:"small-step"})],G.prototype,"smallStep",void 0),e([d({reflect:!0,type:Boolean,attribute:"snap-on-step"})],G.prototype,"snapOnStep",void 0),e([d({reflect:!0,type:Boolean,attribute:"allow-out-of-range"})],G.prototype,"allowOutOfRange",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"integer",void 0),e([d({reflect:!0,type:Boolean,attribute:"allow-wheel-scrub"})],G.prototype,"allowWheelScrub",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"scrub",void 0),e([d({attribute:!1})],G.prototype,"format",void 0),e([d({attribute:!1})],G.prototype,"formatValue",void 0),e([d({attribute:!1})],G.prototype,"parseValue",void 0),e([d({attribute:!1})],G.prototype,"mask",void 0),e([d({reflect:!0})],G.prototype,"locale",void 0),e([d({reflect:!0})],G.prototype,"placeholder",void 0),e([d({reflect:!0})],G.prototype,"name",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"disabled",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"readonly",void 0),e([d({reflect:!0,type:Boolean})],G.prototype,"invalid",void 0),e([d({reflect:!0,type:Boolean,attribute:"hide-controls"})],G.prototype,"hideControls",void 0),e([d({reflect:!0})],G.prototype,"currency",void 0),e([d({reflect:!0,attribute:"currency-placement"})],G.prototype,"currencyPlacement",void 0),e([u()],G.prototype,"inputText",void 0),e([u()],G.prototype,"scrubbing",void 0),e([d()],G.prototype,"form",null),G=e([c("nord-number-field")],G);var X=G;export{I as NumberFieldChangeEvent,X as default,D as getCurrencySymbol,z as resolveCurrencyPlacement};
|
|
2
2
|
//# sourceMappingURL=NumberField.js.map
|
package/lib/NumberField.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberField.js","sources":["../src/number-field/NumberField.ts"],"sourcesContent":["import type { PropertyValues, ReactiveControllerHost } from 'lit'\nimport type { InputMixinInterface } from '../common/mixins/InputMixin.js'\nimport type { NumberFieldChangeReason } from './NumberFieldEvents.js'\nimport * as addIcon from '@nordhealth/icons/lib/assets/interface-add-small.js'\nimport * as removeIcon from '@nordhealth/icons/lib/assets/interface-remove-small.js'\nimport { html, isServer, LitElement, nothing } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\n\nimport { FormDataController } from '../common/controllers/FormDataController.js'\nimport { StateAttributesController } from '../common/controllers/StateAttributesController.js'\nimport { NordEvent } from '../common/events.js'\nimport { injectLightDomStyles } from '../common/light-dom-styles.js'\nimport { DraftComponentMixin } from '../common/mixins/DraftComponentMixin.js'\nimport { FieldControlMixin } from '../common/mixins/FieldControlMixin.js'\nimport { SizeMixin } from '../common/mixins/SizeMixin.js'\nimport Icon from '../icon/Icon.js'\nimport { LocalizeController } from '../localization/LocalizeController.js'\nimport { getCurrencySymbol, resolveCurrencyPlacement } from './currency.js'\nimport { formatNumber } from './format.js'\nimport style from './NumberField.css'\nimport { NumberFieldChangeEvent } from './NumberFieldEvents.js'\nimport {\n ANY_MINUS_DETECT_RE,\n ANY_PLUS_DETECT_RE,\n ARABIC_DETECT_RE,\n BASE_NON_NUMERIC_SYMBOLS,\n FULLWIDTH_DETECT_RE,\n getNumberLocaleDetails,\n HAN_DETECT_RE,\n MINUS_SIGNS_WITH_ASCII,\n parseNumber,\n PERCENTAGES,\n PERMILLE,\n PERSIAN_DETECT_RE,\n PLUS_SIGNS_WITH_ASCII,\n SPACE_SEPARATOR_RE,\n} from './parse.js'\nimport {\n clamp,\n hasNumberFormatRoundingOptions,\n removeFloatingPointErrors,\n toValidatedNumber,\n} from './validate.js'\n\nexport { getCurrencySymbol, resolveCurrencyPlacement } from './currency.js'\nexport { NumberFieldChangeEvent } from './NumberFieldEvents.js'\nexport type { NumberFieldChangeReason } from './NumberFieldEvents.js'\n\n/**\n * The editable state of the inner input handed to (and returned from) a custom\n * `mask` function: the current text and the caret/selection offsets. The shape\n * mirrors a mask library's element state (e.g. Maskito's `ElementState`), so a\n * mask transformer can be adapted with a thin wrapper.\n */\nexport interface NumberFieldMaskState {\n value: string\n selectionStart: number\n selectionEnd: number\n}\n\n/** Contextual info passed to a custom `mask` function alongside the editable state. */\nexport interface NumberFieldMaskContext {\n locale?: string | string[]\n format?: Intl.NumberFormatOptions\n}\n\n/** Signature of a custom `mask` transformer function. */\nexport type NumberFieldMaskInput = (\n state: NumberFieldMaskState,\n context: NumberFieldMaskContext,\n) => NumberFieldMaskState\n\nIcon.registerIcon(addIcon)\nIcon.registerIcon(removeIcon)\n\n/** Press-and-hold timing (ms) and pointer thresholds, ported from Base UI constants. */\nconst START_AUTO_CHANGE_DELAY = 400\nconst CHANGE_VALUE_TICK_DELAY = 60\nconst SCRUB_PIXEL_SENSITIVITY = 2\n// Base UI's DEFAULT_STEP (1) is the fallback when getStepAmount() returns\n// undefined; here getStepAmount always resolves to a concrete number (step is\n// never undefined and 'any' is normalised to 1), so no separate fallback is needed.\n\n/** Per-instance counter so light-DOM ids (input + scrub label `for`) stay unique. */\nlet numberFieldCount = 1\n\n/** A keyboard-like event carrying the modifier flags `getStepAmount` reads. */\ninterface StepEventLike {\n altKey?: boolean\n shiftKey?: boolean\n}\n\n/** Converts the `value` attribute (a numeric string) to `number | null`. */\nfunction valueConverter(attr: string | null): number | null {\n if (attr == null || attr.trim() === '') {\n return null\n }\n const num = Number(attr)\n return Number.isNaN(num) ? null : num\n}\n\n/** A step must be a finite, strictly-positive number; otherwise fall back. */\nfunction sanitizeStep(value: number, fallback: number): number {\n return Number.isFinite(value) && value > 0 ? value : fallback\n}\n\n/**\n * Converts the `step` attribute, keeping the literal `'any'` but coercing\n * numbers. Rejects NaN / non-positive values, falling back to `1`, since a\n * zero or negative step makes stepping (and snap-on-step) meaningless.\n */\nfunction stepConverter(attr: string | null): number | 'any' {\n if (attr == null) {\n return 1\n }\n if (attr === 'any') {\n return 'any'\n }\n return sanitizeStep(Number(attr), 1)\n}\n\n/**\n * Number Field is a control for entering a numeric value, with decrement and\n * increment stepper buttons, optional drag-to-scrub, and locale-aware\n * formatting. It ports the behaviour of Base UI's Number Field: arrow/Page\n * stepping, Home/End to the bounds, press-and-hold auto-repeat, optional\n * wheel/scrub, clamping, and `Intl.NumberFormat` formatting.\n *\n * Number Field is a control only — it has no built-in label, hint or error.\n * Compose it inside a [Field](/components/field/) with a\n * [Field Label](/components/field-label/) to give it a label, hint and error,\n * the same way you would a native input.\n *\n * FORM PARTICIPATION: the value is submitted as the raw numeric string under the\n * host `name` via `FormDataController` (the inner input has no `name`, avoiding a\n * double submit). Unlike Base UI there is no hidden native number input, and —\n * like the other new Nord controls — Number Field does NOT use\n * `ElementInternals`, so it performs no native constraint validation: `required`,\n * `min`, `max` and `step` are NOT enforced as form-blocking validity. `min`/`max`\n * govern interactive stepping and (unless `allow-out-of-range` is set) clamp the\n * value; `required` only surfaces as `required`/`aria-required` on the inner input\n * for assistive technology. Authors must validate constraints themselves and\n * reflect failures via `invalid` and a `nord-field-error`.\n *\n * ACCESSIBILITY NOTE: the inner control is a text input (with `inputmode`) and\n * `aria-roledescription=\"Number field\"`, NOT `role=\"spinbutton\"` and NOT a\n * native `type=\"number\"`. A text input lets us show locale-formatted text\n * (currency, percent, grouping) while keeping full text editing — assistive tech\n * reads the formatted value from the input's text. We do NOT add\n * `aria-valuenow`/`valuemin`/`valuemax`/`valuetext`: those are only valid on a\n * range role (e.g. `spinbutton`/`slider`) and would fail `aria-allowed-attr`\n * validation on a text input. This mirrors what Base UI's `NumberFieldInput`\n * actually ships (only `aria-roledescription`).\n *\n * @status draft\n * @category form\n * @lightDom true\n *\n * @fires {NumberFieldChangeEvent} change - Dispatched when the value is committed (on blur, keyboard step, or pointer-up after a button hold or scrub). Carries the numeric value and a `reason`.\n * @fires {NordEvent} input - Dispatched as the value changes during interaction (typing a parseable value, clearing, each step, each button tick, each wheel notch, each scrub increment).\n *\n * @slot start - Leading content inside the control, before the input (e.g. a unit or currency symbol). Sits after the decrement stepper button.\n * @slot end - Trailing content inside the control, after the input (e.g. a unit label or `%`). Sits before the increment stepper button.\n *\n * @localization increaseLabel - Accessible label for the increment stepper button.\n * @localization decreaseLabel - Accessible label for the decrement stepper button.\n * @localization roleDescription - Role description announced for the input (aria-roledescription).\n *\n * @cssprop [--n-number-field-control-min-inline-size=8ch] - Minimum inline size (width) of the numeric input portion of the control.\n * @cssprop [--n-number-field-step-inline-size=var(--n-space-xl)] - Inline size (width) of each increment/decrement stepper button.\n * @cssprop [--n-number-field-background=var(--n-input-background, var(--n-color-surface))] - Background of the control.\n * @cssprop [--n-number-field-border-color=var(--n-input-border-color, var(--n-color-border-strong))] - Border color of the control.\n * @cssprop [--n-number-field-border-radius=var(--n-input-border-radius, var(--n-border-radius-s))] - Corner radius of the control.\n *\n * @attr {boolean} data-invalid - Present while the field is invalid (mirrors the `invalid` property). Style hook for the whole control.\n * @attr {boolean} data-focused - Present while the control has focus. Style hook for focus-within treatments.\n */\n@customElement('nord-number-field')\nexport default class NumberField extends DraftComponentMixin(SizeMixin(FieldControlMixin(LitElement))) {\n /** Stable, unique id base for this instance's light-DOM ARIA wiring. */\n private numberFieldId = `nord-number-field-${numberFieldCount++}`\n\n // The built-in `<label for>` (from FieldControlMixin) must point at the real\n // focusable input, so override the mixin's default id with the input's id.\n protected override inputId = `${this.numberFieldId}-input`\n\n private localize = new LocalizeController<'nord-number-field'>(this)\n\n private inputRef = createRef<HTMLInputElement>()\n\n // NumberField's `value` is `number | null`, which doesn't overlap with the\n // controller's `InputMixinInterface` host type (`value: string`). The\n // controller only reads `form`/`name`/`disabled` and the `value()` option (not\n // `host.value`), so casting the host through `unknown` is safe — same approach\n // as the combobox, which widens rather than narrows `value`.\n private formData = new FormDataController(\n this as unknown as ReactiveControllerHost & InputMixinInterface,\n { value: () => (this.value == null ? undefined : String(this.value)) },\n )\n\n /** Reflects state to Base-UI-style `data-*` attributes (data-invalid, data-focused). */\n private stateAttributes = new StateAttributesController(\n this,\n () => ({ invalid: this.effectiveInvalid }),\n { trackFocus: true },\n )\n\n // The control's own invalid state for the field context: its `invalid` prop or\n // a built-in `error` (when used standalone).\n protected override get ownInvalid() {\n return this.invalid || super.ownInvalid\n }\n\n /** Re-renders when the host's forwarded ARIA labelling attributes change. */\n private ariaObserver?: MutationObserver\n\n // Landing zones for relocated `slot=\"start\"`/`slot=\"end\"` host children. The\n // control renders into light DOM, so real `<slot>` projection isn't available.\n @query('.n-number-field-slot-start') private startZone?: HTMLElement\n @query('.n-number-field-slot-end') private endZone?: HTMLElement\n\n // Watches for `slot=\"start\"`/`slot=\"end\"` children being added or removed so\n // they get relocated into their landing zones on the next render.\n private slotObserver?: MutationObserver\n\n // ----- Form association (own implementation, mirroring InputMixin) ----------\n // NumberField does NOT use InputMixin because it diverges from `value: string`;\n // it provides its own `name`/`disabled`/`form` so the FormDataController host\n // shape (name/disabled/form) is satisfied.\n\n private formAncestor: HTMLFormElement | null = null\n private _formId?: string\n\n /**\n * The raw numeric value of the field, or `null` when empty.\n *\n * The `value` attribute may be set as a numeric string in HTML; it is coerced\n * with a custom converter (`Number(v)`, `NaN -> null`).\n */\n @property({ converter: valueConverter }) value: number | null = null\n\n /**\n * The minimum allowed value. Step interactions and blur-clamping clamp to it,\n * and the decrement button disables at/below it. Home jumps to it only when it\n * is finite.\n */\n @property({ reflect: true, type: Number }) min?: number\n\n /**\n * The maximum allowed value. Step interactions and blur-clamping clamp to it,\n * and the increment button disables at/above it. End jumps to it only when it\n * is finite.\n */\n @property({ reflect: true, type: Number }) max?: number\n\n /**\n * Amount each arrow press / button click / wheel notch changes the value.\n * `'any'` disables step semantics (treated as `1` internally for stepping).\n */\n @property({ reflect: true, converter: stepConverter }) step: number | 'any' = 1\n\n /** Step used for PageUp/PageDown and Shift+Arrow. */\n @property({ reflect: true, type: Number, attribute: 'large-step' }) largeStep = 10\n\n /** Step used when Alt/Option is held with Arrow keys. */\n @property({ reflect: true, type: Number, attribute: 'small-step' }) smallStep = 0.1\n\n /** When `true`, stepping snaps the value to the nearest multiple of the step. */\n @property({ reflect: true, type: Boolean, attribute: 'snap-on-step' }) snapOnStep = false\n\n /**\n * When `true`, direct text entry may go outside `min`/`max` without clamping\n * (so range validation can flag it); step interactions still clamp.\n */\n @property({ reflect: true, type: Boolean, attribute: 'allow-out-of-range' }) allowOutOfRange = false\n\n /**\n * Restricts the field to whole numbers. The decimal separator can no longer be\n * typed, pasted or programmatic fractional values are rounded to the nearest\n * integer, and the built-in `mask` caps the fraction to zero. The soft keyboard\n * shows the plain numeric keypad.\n */\n @property({ reflect: true, type: Boolean }) integer = false\n\n /** When `true`, scrolling the mouse wheel while focused changes the value. */\n @property({ reflect: true, type: Boolean, attribute: 'allow-wheel-scrub' }) allowWheelScrub = false\n\n /** When `true`, renders a drag handle so the user can click-drag to change the value. */\n @property({ reflect: true, type: Boolean }) scrub = false\n\n /**\n * `Intl.NumberFormat` options used to format the displayed text. Set as a\n * property only (e.g. `{ style: 'currency', currency: 'EUR' }`).\n */\n @property({ attribute: false }) format?: Intl.NumberFormatOptions\n\n /**\n * Overrides the default `Intl.NumberFormat`-based formatting of the displayed\n * text. Receives the current numeric value (`null` when the field is empty),\n * the active `locale`, and the `format` options, and must return the string to\n * display. Following the default behaviour, a `null` value should map to an\n * empty string. Set as a property only.\n */\n @property({ attribute: false })\n formatValue?: (value: number | null, locale?: string | string[], options?: Intl.NumberFormatOptions) => string\n\n /**\n * Overrides the default locale-aware parsing of the typed text. Receives the\n * raw input text, the active `locale`, and the `format` options, and must\n * return the parsed number, or `null` when the text is empty or cannot be\n * parsed. Set as a property only.\n */\n @property({ attribute: false })\n parseValue?: (text: string, locale?: string | string[], options?: Intl.NumberFormatOptions) => number | null\n\n /**\n * An optional custom edit-time mask: a function that reshapes the input text on\n * every insertion, deletion, and paste, and reports where the caret should\n * land. This is the live-typing counterpart to `formatValue` (which formats an\n * already-committed value). Use it to insert grouping separators, localise the\n * decimal, or cap fraction digits as the user types — typically by wrapping a\n * mask library (e.g. Maskito's `maskitoTransform(elementState, options)`).\n *\n * The field does NOT format live on its own: without a mask, the text is kept\n * as typed and the auto (`Intl`) formatting is applied only on blur and on\n * paste. When a mask is set it becomes the source of truth for the text — the\n * per-character guard is bypassed (the mask decides validity) and paste lands\n * natively so it flows through the mask. Keep the mask output parseable in, and\n * consistent with, the active `locale`/`format`. Set as a property.\n */\n @property({ attribute: false })\n mask?: NumberFieldMaskInput\n\n /** BCP-47 locale(s) for `Intl` formatting/parsing. Defaults to the runtime locale. */\n @property({ reflect: true }) locale?: string | string[]\n\n /** Placeholder text shown when the field is empty. */\n @property({ reflect: true }) placeholder?: string\n\n /** Form field name. The value is submitted under this name via FormDataController. */\n @property({ reflect: true }) name?: string\n\n /** Disables the control. */\n @property({ reflect: true, type: Boolean }) disabled = false\n\n /** Read-only: the user cannot change the value, but the value is still submitted. */\n @property({ reflect: true, type: Boolean }) readonly = false\n\n /** Applies error styling and sets `aria-invalid=\"true\"` on the inner input. */\n @property({ reflect: true, type: Boolean }) invalid = false\n\n /** Hides the increment/decrement stepper buttons (still steppable via keyboard). */\n @property({ reflect: true, type: Boolean, attribute: 'hide-controls' }) hideControls = false\n\n /**\n * ISO 4217 currency code (e.g. `USD`, `EUR`, `SEK`). Setting it enables the\n * currency convenience: a decorative, locale-aware symbol is rendered into the\n * `start`/`end` slot resolved from `currencyPlacement`, and the currency is\n * folded into the input's accessible description so assistive tech announces it.\n *\n * This is layered on top of the generic `start`/`end` slots — for a fully\n * custom symbol, omit `currency` and slot your own content instead. It is\n * independent of `format`: the symbol lives beside the number rather than in\n * the formatted text, so it composes cleanly with `mask`.\n */\n @property({ reflect: true }) currency?: string\n\n /**\n * Which side the `currency` symbol is placed on. `'auto'` (the default)\n * derives the side from the `locale`/`currency` pair via `Intl` (symbol-first\n * locales like en-US → `start`, symbol-last locales like de-DE → `end`);\n * `'start'`/`'end'` force the side. Has no effect unless `currency` is set.\n */\n @property({ reflect: true, attribute: 'currency-placement' }) currencyPlacement: 'start' | 'end' | 'auto' = 'auto'\n\n /** The text shown in the input — formatted, unless the user is mid-edit. */\n @state() private inputText = ''\n\n /** Whether the user is scrubbing (adds an `is-scrubbing` cursor). */\n @state() private scrubbing = false\n\n /**\n * While the user is actively typing this is `false`, so we don't reformat\n * mid-keystroke. Reset to `true` on blur / keydown-step / button / wheel / scrub.\n */\n private allowInputSync = true\n\n /** Set after the first focus so we only move the caret to the end once. */\n private hasTouchedInput = false\n\n /**\n * Caret/selection a mask asked us to place after it rewrote the text. Applied\n * in `updated()` once Lit has committed the new `.value` (which resets the\n * caret to the end).\n */\n private pendingSelection?: [number, number]\n\n // ----- press-and-hold timers ----------------------------------------------\n private holdStartTimer?: ReturnType<typeof setTimeout>\n private holdTickTimer?: ReturnType<typeof setInterval>\n private activePointerId?: number\n /** When a mouse pointerdown handled the change, suppress the synthetic click. */\n private skipNextClick = false\n\n // ----- scrub state ---------------------------------------------------------\n private scrubAccumulator = 0\n private scrubMoved = false\n private scrubPointerDownTarget: EventTarget | null = null\n private onScrubMove = (e: PointerEvent) => this.handleScrubMove(e)\n private onScrubUp = (e: PointerEvent) => this.handleScrubUp(e)\n\n /** Bound non-passive wheel listener (attached only while wheel scrub is enabled). */\n private onWheel = (e: WheelEvent) => this.handleWheel(e)\n private wheelAttached = false\n\n // ----- form-id getter/setter (copied from InputMixin) ----------------------\n set form(id: string) {\n const oldVal = this._formId\n this._formId = id\n this.requestUpdate('form', oldVal)\n }\n\n @property()\n get form(): HTMLFormElement | null {\n if (this._formId) {\n const root = this.getRootNode() as ShadowRoot | Document\n return root.querySelector<HTMLFormElement>(`form#${this._formId}`)\n }\n return this.formAncestor\n }\n\n /** The effective lower bound for clamping, mirroring Base UI `minWithDefault`. */\n private get minWithDefault(): number {\n return this.min ?? Number.MIN_SAFE_INTEGER\n }\n\n /** The effective upper bound for clamping, mirroring Base UI `maxWithDefault`. */\n private get maxWithDefault(): number {\n return this.max ?? Number.MAX_SAFE_INTEGER\n }\n\n /** The base used by `incrementValue` when stepping from empty. */\n private get minWithZeroDefault(): number {\n return this.min ?? 0\n }\n\n /**\n * Whether a negative value is allowed, gating the minus sign in the input. A\n * `min` of `0` or higher forbids negatives; an unset or negative `min` allows\n * them (mirroring `minWithDefault`, which backs unset `min` with a value < 0).\n */\n private get allowsNegative(): boolean {\n return this.minWithDefault < 0\n }\n\n /**\n * The numeric step used for stepping; `'any'` behaves as `1`. Guards against a\n * non-positive / NaN `step` set directly as a property (which bypasses the\n * attribute converter) by falling back to `1`.\n */\n private get numericStep(): number {\n return this.step === 'any' ? 1 : sanitizeStep(this.step, 1)\n }\n\n /** The PageUp/PageDown + Shift step, guarded against non-positive / NaN values. */\n private get effectiveLargeStep(): number {\n return sanitizeStep(this.largeStep, 10)\n }\n\n /** The Alt/Option step, guarded against non-positive / NaN values. */\n private get effectiveSmallStep(): number {\n return sanitizeStep(this.smallStep, 0.1)\n }\n\n /**\n * The canonical displayed text for a value: the `formatValue` override if set,\n * otherwise the locale `Intl` format. Applied on blur and on paste.\n */\n private formatDisplay(value: number | null): string {\n if (this.formatValue) {\n return this.formatValue(value, this.locale, this.format)\n }\n return formatNumber(value, this.locale, this.format)\n }\n\n connectedCallback() {\n super.connectedCallback()\n injectLightDomStyles(this, style)\n\n // Cache the form ancestor; it only changes on (re)connection.\n this.formAncestor = this.closest('form')\n\n // An external label (e.g. nord-field-label) names the control by setting\n // aria-label / aria-labelledby on the host; forward it to the inner input.\n this.ariaObserver ??= new MutationObserver(() => this.requestUpdate())\n this.ariaObserver.observe(this, {\n attributes: true,\n attributeFilter: ['aria-label', 'aria-labelledby', 'aria-describedby'],\n })\n\n // Relocate `slot=\"start\"`/`slot=\"end\"` children when they are added or\n // removed; the control is light DOM, so there are no real `<slot>` elements.\n this.slotObserver ??= new MutationObserver(() => this.requestUpdate())\n this.slotObserver.observe(this, { childList: true })\n\n // Format the initial value if one was set before connect.\n if (this.allowInputSync) {\n this.inputText = this.formatDisplay(this.value)\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.ariaObserver?.disconnect()\n this.ariaObserver = undefined\n this.slotObserver?.disconnect()\n this.slotObserver = undefined\n this.formAncestor = null\n this.stopHold()\n this.detachWheel()\n this.removeScrubListeners()\n }\n\n // Render in the light DOM so the control participates in the document tree\n // (form labelling, Tailwind), like the other new Nord components, but into a\n // layout-transparent (`display: contents`) wrapper rather than the host\n // itself, so slotted `slot=\"start\"`/`slot=\"end\"` children can live as\n // untouched siblings instead of being wiped by this element's own render\n // (mirrors `nord-filter-input` / `nord-combobox`). On the server there is no\n // DOM to append to, so render into the host directly.\n protected createRenderRoot() {\n if (isServer) {\n return this\n }\n const root = document.createElement('div')\n root.className = 'n-number-field-root'\n this.appendChild(root)\n return root\n }\n\n /**\n * Relocates the host's `slot=\"start\"`/`slot=\"end\"` children into the matching\n * landing zone inside the control. Only direct host children are considered,\n * and a child already sitting in its zone is left untouched, so repeated\n * updates are idempotent and don't disturb focus, caret or pointer state.\n */\n private relocateSlots() {\n if (isServer) {\n return\n }\n\n const move = (name: 'start' | 'end', target?: HTMLElement) => {\n if (!target) {\n return\n }\n for (const child of Array.from(this.children)) {\n if (child.getAttribute('slot') === name && child.parentElement !== target) {\n target.appendChild(child)\n }\n }\n }\n\n move('start', this.startZone)\n move('end', this.endZone)\n }\n\n protected willUpdate(changed: PropertyValues<this>) {\n // A programmatic `value` set (the property changing while the user is NOT\n // mid-edit) must respect the bounds when out-of-range entry isn't allowed.\n // Interactive clamping is handled in `setValue`, which always runs with\n // `allowInputSync === false` during typing; gating on `allowInputSync` here\n // means we only clamp non-interactive sources and never fight live input.\n if (\n changed.has('value')\n && this.allowInputSync\n && !this.allowOutOfRange\n && this.value != null\n ) {\n const clamped = clamp(this.value, this.minWithDefault, this.maxWithDefault)\n if (clamped !== this.value) {\n this.value = clamped\n }\n }\n\n // A whole-number field never holds a fraction; round any that slips in via a\n // programmatic set or a pasted decimal so the model stays integral.\n if (changed.has('value') && this.integer && this.value != null && !Number.isInteger(this.value)) {\n this.value = Math.round(this.value)\n }\n\n // Keep the displayed text in sync when the value or formatting changes\n // programmatically, but never overwrite text the user is actively editing.\n if (\n this.allowInputSync\n && (changed.has('value') || changed.has('format') || changed.has('locale'))\n ) {\n this.inputText = this.formatDisplay(this.value)\n }\n }\n\n protected updated(changed: PropertyValues) {\n super.updated(changed)\n\n // Move any `slot=\"start\"`/`slot=\"end\"` host children into their landing\n // zones inside the control. The control is light DOM, so real `<slot>`\n // projection isn't available; relocating keeps the public API like `nord-input`.\n this.relocateSlots()\n\n // Attach or detach the non-passive wheel listener as conditions change.\n const wheelWanted = this.allowWheelScrub && !this.disabled && !this.readonly\n if (wheelWanted && !this.wheelAttached) {\n this.attachWheel()\n }\n else if (!wheelWanted && this.wheelAttached) {\n this.detachWheel()\n }\n\n // A mask rewrote the text this cycle; Lit has now committed the new `.value`\n // (which parks the caret at the end), so restore the caret/selection the\n // mask requested. Runs in `updated` (before paint) so there is no flash.\n if (this.pendingSelection) {\n const [start, end] = this.pendingSelection\n this.pendingSelection = undefined\n this.inputRef.value?.setSelectionRange(start, end)\n }\n }\n\n /** Focus the inner input (mirrors combobox `focus`). */\n focus(options?: FocusOptions) {\n this.inputRef.value?.focus(options)\n }\n\n // ---------------------------------------------------------------------------\n // value model\n // ---------------------------------------------------------------------------\n\n /** The reasons that DON'T clamp when `allowOutOfRange` is set (direct text entry). */\n private isTextReason(reason: NumberFieldChangeReason): boolean {\n return (\n reason === 'input-change'\n || reason === 'input-blur'\n || reason === 'input-paste'\n || reason === 'input-clear'\n || reason === 'none'\n )\n }\n\n /**\n * The amount a single step changes the value, mirroring Base UI `getStepAmount`:\n * Alt -> smallStep, Shift -> largeStep, otherwise step.\n */\n private getStepAmount(event?: StepEventLike): number {\n if (event?.altKey) {\n return this.effectiveSmallStep\n }\n if (event?.shiftKey) {\n return this.effectiveLargeStep\n }\n return this.numericStep\n }\n\n /**\n * Validate, store, format and announce a new value.\n *\n * Returns whether a `change`/value update fired so callers can mirror Base UI's\n * commit semantics.\n */\n private setValue(\n unvalidated: number | null,\n {\n reason,\n direction,\n event,\n }: { reason: NumberFieldChangeReason, direction?: 1 | -1, event?: StepEventLike },\n ): boolean {\n // Only direct text entry may stay out of range when allowOutOfRange is set;\n // step interactions (keyboard/buttons/wheel/scrub) always clamp.\n const shouldClamp = !this.allowOutOfRange || !this.isTextReason(reason)\n\n const validated = toValidatedNumber(unvalidated, {\n step: direction ? this.getStepAmount(event) * direction : undefined,\n format: this.format,\n minWithDefault: this.minWithDefault,\n maxWithDefault: this.maxWithDefault,\n minWithZeroDefault: this.minWithZeroDefault,\n snapOnStep: this.snapOnStep,\n small: event?.altKey ?? false,\n clamp: shouldClamp,\n })\n\n const isTextReason = this.isTextReason(reason)\n const shouldFire\n = validated !== this.value\n || (isTextReason && (unvalidated !== this.value || this.allowInputSync === false))\n\n if (shouldFire) {\n this.value = validated\n // Fired as the value changes during interaction.\n this.dispatchEvent(new NordEvent('input'))\n }\n\n // Keep the visible input in sync immediately for programmatic/step changes;\n // during direct typing we gate on allowInputSync so we don't clobber the text.\n if (this.allowInputSync) {\n this.inputText = this.formatDisplay(validated)\n }\n\n this.requestUpdate()\n return shouldFire\n }\n\n /**\n * Step the value by `amount * direction`, mirroring Base UI `incrementValue`.\n * Stepping from empty starts at `max(0, min ?? 0)`.\n */\n private incrementValue(\n amount: number,\n {\n direction,\n currentValue,\n event,\n reason,\n }: {\n direction: 1 | -1\n currentValue?: number | null\n event?: StepEventLike\n reason: NumberFieldChangeReason\n },\n ): boolean {\n const prev = currentValue == null ? this.value : currentValue\n const next\n = typeof prev === 'number' ? prev + amount * direction : Math.max(0, this.min ?? 0)\n return this.setValue(next, { reason, direction, event })\n }\n\n /** Dispatch the committed `change` event with the current value + reason. */\n private commit(reason: NumberFieldChangeReason) {\n this.dispatchEvent(new NumberFieldChangeEvent(this.value, reason))\n }\n\n // ---------------------------------------------------------------------------\n // keyboard\n // ---------------------------------------------------------------------------\n\n /**\n * The set of non-numeric keys allowed in the input, mirroring Base UI's\n * `getAllowedNonNumericKeys` (locale decimal/group/currency/percent + signs).\n */\n private getAllowedNonNumericKeys(): Set<string> {\n // This guard only runs when no mask is active (`mask=\"off\"`); the built-in and\n // custom masks decide validity themselves and bypass it.\n const { decimal, group, currency, literal } = getNumberLocaleDetails(this.locale, this.format)\n const keys = new Set<string>()\n\n BASE_NON_NUMERIC_SYMBOLS.forEach(symbol => keys.add(symbol))\n if (decimal) {\n keys.add(decimal)\n }\n if (group) {\n keys.add(group)\n if (SPACE_SEPARATOR_RE.test(group)) {\n keys.add(' ')\n }\n }\n\n const formatStyle = this.format?.style\n const allowPercent\n = formatStyle === 'percent' || (formatStyle === 'unit' && this.format?.unit === 'percent')\n const allowPermille\n = formatStyle === 'percent' || (formatStyle === 'unit' && this.format?.unit === 'permille')\n\n if (allowPercent) {\n PERCENTAGES.forEach(key => keys.add(key))\n }\n if (allowPermille) {\n PERMILLE.forEach(key => keys.add(key))\n }\n if (formatStyle === 'currency' && currency) {\n keys.add(currency)\n }\n if (literal) {\n Array.from(literal).forEach(char => keys.add(char))\n if (SPACE_SEPARATOR_RE.test(literal)) {\n keys.add(' ')\n }\n }\n\n PLUS_SIGNS_WITH_ASCII.forEach(key => keys.add(key))\n if (this.minWithDefault < 0) {\n MINUS_SIGNS_WITH_ASCII.forEach(key => keys.add(key))\n }\n\n // A whole-number field drops every decimal separator (the locale's own plus\n // the universal ASCII/fullwidth/Arabic glyphs that `BASE_NON_NUMERIC_SYMBOLS`\n // seeds), but keeps the group separator so grouped input still parses — even\n // where the group char (e.g. de-DE `.`) coincides with a decimal glyph.\n if (this.integer) {\n for (const separator of [decimal, '.', '.', '٫']) {\n if (separator && separator !== group) {\n keys.delete(separator)\n }\n }\n }\n\n return keys\n }\n\n private handleKeydown(e: KeyboardEvent) {\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n this.allowInputSync = true\n\n const stepKeys = new Set(['ArrowUp', 'ArrowDown', 'PageUp', 'PageDown', 'Home', 'End'])\n\n if (!stepKeys.has(e.key)) {\n // A custom mask governs which characters are valid and reshapes the text on\n // the `input` event, so let every key through when one is set.\n if (this.mask) {\n return\n }\n\n // Let typing through, but reject characters that can never be part of a\n // number (porting Base UI's allowed-key guard for parity). Composition\n // (which===229), navigation keys, and modifier combos always pass.\n const allowed = this.getAllowedNonNumericKeys()\n const isAsciiDigit = e.key >= '0' && e.key <= '9'\n const isNamedKey = e.key.length > 1 // ArrowLeft, Backspace, Tab, Enter, …\n const passes\n = e.which === 229\n || e.ctrlKey\n || e.metaKey\n || (e.altKey)\n || isAsciiDigit\n || isNamedKey\n || allowed.has(e.key)\n || ARABIC_DETECT_RE.test(e.key)\n || PERSIAN_DETECT_RE.test(e.key)\n || HAN_DETECT_RE.test(e.key)\n || FULLWIDTH_DETECT_RE.test(e.key)\n || (this.allowsNegative && ANY_MINUS_DETECT_RE.test(e.key))\n || ANY_PLUS_DETECT_RE.test(e.key)\n\n if (!passes) {\n e.preventDefault()\n }\n return\n }\n\n // Parse the current (possibly unblurred) text so edits are honoured.\n const currentValue = this.parseValue\n ? this.parseValue(this.inputText, this.locale, this.format)\n : parseNumber(this.inputText, this.locale, this.format)\n\n switch (e.key) {\n case 'ArrowUp':\n e.preventDefault()\n this.incrementValue(this.getStepAmount(e), {\n direction: 1,\n currentValue,\n event: e,\n reason: 'keyboard',\n })\n this.commit('keyboard')\n break\n case 'ArrowDown':\n e.preventDefault()\n this.incrementValue(this.getStepAmount(e), {\n direction: -1,\n currentValue,\n event: e,\n reason: 'keyboard',\n })\n this.commit('keyboard')\n break\n case 'PageUp':\n e.preventDefault()\n // PageUp/Down always use largeStep regardless of modifiers.\n this.incrementValue(this.effectiveLargeStep, { direction: 1, currentValue, reason: 'keyboard' })\n this.commit('keyboard')\n break\n case 'PageDown':\n e.preventDefault()\n this.incrementValue(this.effectiveLargeStep, { direction: -1, currentValue, reason: 'keyboard' })\n this.commit('keyboard')\n break\n case 'Home':\n if (this.min != null && Number.isFinite(this.min)) {\n e.preventDefault()\n this.setValue(this.min, { reason: 'keyboard' })\n this.commit('keyboard')\n }\n break\n case 'End':\n if (this.max != null && Number.isFinite(this.max)) {\n e.preventDefault()\n this.setValue(this.max, { reason: 'keyboard' })\n this.commit('keyboard')\n }\n break\n default:\n break\n }\n }\n\n // ---------------------------------------------------------------------------\n // input / paste / focus / blur\n // ---------------------------------------------------------------------------\n\n /**\n * Whether every character in `text` could legitimately be part of a number in\n * the current locale/format, mirroring Base UI `NumberFieldInput.onChange`'s\n * `isValidCharacterString`. The keydown guard only sees physical keystrokes;\n * this catches disallowed characters arriving via IME composition,\n * drag-and-drop, or a programmatic `input.value` set, so they never land in\n * the bound value.\n */\n private isValidCharacterString(text: string): boolean {\n const allowed = this.getAllowedNonNumericKeys()\n return Array.from(text).every((ch) => {\n const isAsciiDigit = ch >= '0' && ch <= '9'\n return (\n isAsciiDigit\n || ARABIC_DETECT_RE.test(ch)\n || HAN_DETECT_RE.test(ch)\n || PERSIAN_DETECT_RE.test(ch)\n || FULLWIDTH_DETECT_RE.test(ch)\n || (this.allowsNegative && ANY_MINUS_DETECT_RE.test(ch))\n || allowed.has(ch)\n )\n })\n }\n\n private handleInput(e: Event) {\n e.stopPropagation()\n\n // While typing, don't reformat the text from underneath the user.\n this.allowInputSync = false\n\n const input = e.target as HTMLInputElement\n let text = input.value\n\n // A custom mask, when set, reshapes the raw text (grouping separators,\n // decimal-separator localisation, fraction cap, …) and tells us where the\n // caret should land afterwards.\n const mask = this.mask\n if (mask && text.trim() !== '') {\n const masked = mask(\n {\n value: text,\n selectionStart: input.selectionStart ?? text.length,\n selectionEnd: input.selectionEnd ?? text.length,\n },\n { locale: this.locale, format: this.format },\n )\n text = masked.value\n this.pendingSelection = [masked.selectionStart, masked.selectionEnd]\n }\n\n if (text.trim() === '') {\n this.inputText = ''\n this.pendingSelection = undefined\n this.setValue(null, { reason: 'input-clear' })\n return\n }\n\n // Reject input that contains characters that can never be part of a number\n // (composition / drag-drop / programmatic sets bypass the keydown guard);\n // bail before committing so disallowed characters never reach the value. A\n // mask governs character validity itself, so skip the generic guard for it.\n if (!this.mask && !this.isValidCharacterString(text)) {\n return\n }\n\n this.inputText = text\n const parsed = this.parseValue\n ? this.parseValue(text, this.locale, this.format)\n : parseNumber(text, this.locale, this.format)\n if (parsed !== null) {\n this.setValue(parsed, { reason: 'input-change' })\n }\n }\n\n private handlePaste(e: ClipboardEvent) {\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n // With a custom mask, let the paste land natively; the resulting `input`\n // event runs through the mask, which reshapes pasted text like any insertion.\n if (this.mask) {\n return\n }\n\n const pasted = e.clipboardData?.getData('text/plain') ?? ''\n // Prevent the native change so we control the parsed text.\n e.preventDefault()\n\n const parsed = this.parseValue\n ? this.parseValue(pasted, this.locale, this.format)\n : parseNumber(pasted, this.locale, this.format)\n if (parsed !== null) {\n // Apply the auto (Intl) formatting to pasted content immediately, the same\n // as on blur. `allowInputSync` lets `setValue` refresh the displayed text.\n this.allowInputSync = true\n this.setValue(parsed, { reason: 'input-paste' })\n }\n }\n\n private handleFocus(e: FocusEvent) {\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n if (this.hasTouchedInput) {\n return\n }\n this.hasTouchedInput = true\n\n // Browsers select the start by default; move the caret to the end on first focus.\n const target = e.currentTarget as HTMLInputElement\n const length = target.value.length\n target.setSelectionRange(length, length)\n }\n\n private handleBlur(e: FocusEvent) {\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n this.allowInputSync = true\n\n if (this.inputText.trim() === '') {\n this.setValue(null, { reason: 'input-clear' })\n this.commit('input-clear')\n return\n }\n\n const parsed = this.parseValue\n ? this.parseValue(this.inputText, this.locale, this.format)\n : parseNumber(this.inputText, this.locale, this.format)\n if (parsed === null) {\n // The typed text can't be parsed — e.g. stray signs or decimals the\n // lenient per-character guard let through (\"--.-1,234\"). Rather than leave\n // the garbage sitting in the field, revert the display to the last\n // committed value (its canonical format, or empty when there is none). The\n // value itself is unchanged, so no change event fires.\n const reverted = this.formatDisplay(this.value)\n if (this.inputText !== reverted) {\n this.inputText = reverted\n }\n return\n }\n\n const hasRounding = hasNumberFormatRoundingOptions(this.format)\n const committed = hasRounding ? removeFloatingPointErrors(parsed, this.format) : parsed\n\n const shouldUpdate = committed !== this.value\n if (shouldUpdate) {\n this.setValue(committed, { reason: 'input-blur' })\n }\n\n // Normalise the displayed text to the canonical formatted value.\n const canonical = this.formatDisplay(this.value)\n if (this.inputText !== canonical) {\n this.inputText = canonical\n }\n\n this.commit('input-blur')\n }\n\n // ---------------------------------------------------------------------------\n // stepper buttons (press-and-hold)\n // ---------------------------------------------------------------------------\n\n /** Sync the (possibly dirty) input text into `value` before a button press. */\n private commitDirtyInput(reason: NumberFieldChangeReason, direction: 1 | -1) {\n this.allowInputSync = true\n const parsed = this.parseValue\n ? this.parseValue(this.inputText, this.locale, this.format)\n : parseNumber(this.inputText, this.locale, this.format)\n if (parsed !== null) {\n this.setValue(parsed, { reason, direction })\n }\n }\n\n private handleStepPointerDown(e: PointerEvent, direction: 1 | -1) {\n const isMainButton = !e.button || e.button === 0\n if (e.defaultPrevented || this.readonly || this.disabled || !isMainButton) {\n return\n }\n\n const reason: NumberFieldChangeReason\n = direction === 1 ? 'increment-press' : 'decrement-press'\n\n // Pointerdown handles the change here, so suppress the follow-up click\n // (including the synthetic click browsers fire after touch).\n this.skipNextClick = true\n this.activePointerId = e.pointerId\n\n // Sync the dirty input value before stepping.\n this.commitDirtyInput(reason, direction)\n\n if (e.pointerType !== 'touch' && e.pointerType !== 'pen') {\n // Focus the input so keyboard interaction can continue.\n this.inputRef.value?.focus()\n }\n\n // One immediate step, then auto-repeat after the start delay.\n this.allowInputSync = true\n this.incrementValue(this.getStepAmount(e), { direction, event: e, reason })\n\n const target = e.currentTarget as HTMLButtonElement\n try {\n target.setPointerCapture(e.pointerId)\n }\n catch {\n // Ignore: capture isn't essential, pointerup is still observed.\n }\n\n this.holdStartTimer = setTimeout(() => {\n this.holdTickTimer = setInterval(() => {\n this.allowInputSync = true\n this.incrementValue(this.getStepAmount(e), { direction, event: e, reason })\n }, CHANGE_VALUE_TICK_DELAY)\n }, START_AUTO_CHANGE_DELAY)\n }\n\n private handleStepPointerStop(e: PointerEvent, direction: 1 | -1) {\n // Ignore stop events from a different pointer than the one that started.\n if (this.activePointerId !== undefined && this.activePointerId !== e.pointerId) {\n return\n }\n const wasHolding = this.holdStartTimer != null || this.holdTickTimer != null\n this.stopHold()\n this.activePointerId = undefined\n\n // Commit on pointer-up if the pointer press drove the change.\n if (this.skipNextClick || wasHolding) {\n this.commit(direction === 1 ? 'increment-press' : 'decrement-press')\n }\n }\n\n private handleStepClick(e: MouseEvent, direction: 1 | -1) {\n const reason: NumberFieldChangeReason\n = direction === 1 ? 'increment-press' : 'decrement-press'\n\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n // Pointerdown already handled the change; this suppresses the follow-up click.\n if (this.skipNextClick) {\n this.skipNextClick = false\n return\n }\n\n this.commitDirtyInput(reason, direction)\n this.allowInputSync = true\n const changed = this.incrementValue(this.getStepAmount(e as StepEventLike), {\n direction,\n event: e as StepEventLike,\n reason,\n })\n if (changed) {\n this.commit(reason)\n }\n }\n\n private stopHold() {\n if (this.holdStartTimer != null) {\n clearTimeout(this.holdStartTimer)\n this.holdStartTimer = undefined\n }\n if (this.holdTickTimer != null) {\n clearInterval(this.holdTickTimer)\n this.holdTickTimer = undefined\n }\n }\n\n /** Whether the increment button should be disabled. */\n private get incrementDisabled(): boolean {\n return (\n this.disabled\n || this.readonly\n || (this.value != null && this.value >= this.maxWithDefault)\n )\n }\n\n /** Whether the decrement button should be disabled. */\n private get decrementDisabled(): boolean {\n return (\n this.disabled\n || this.readonly\n || (this.value != null && this.value <= this.minWithDefault)\n )\n }\n\n // ---------------------------------------------------------------------------\n // wheel\n // ---------------------------------------------------------------------------\n\n private attachWheel() {\n const input = this.inputRef.value\n if (!input) {\n return\n }\n input.addEventListener('wheel', this.onWheel, { passive: false })\n this.wheelAttached = true\n }\n\n private detachWheel() {\n const input = this.inputRef.value\n input?.removeEventListener('wheel', this.onWheel)\n this.wheelAttached = false\n }\n\n private handleWheel(e: WheelEvent) {\n // Allow pinch-zoom (ctrlKey) and ignore when the input isn't focused.\n if (e.ctrlKey || this.inputRef.value !== this.getActiveElement()) {\n return\n }\n e.preventDefault()\n this.allowInputSync = true\n const amount = this.getStepAmount(e as StepEventLike)\n this.incrementValue(amount, {\n direction: e.deltaY > 0 ? -1 : 1,\n event: e as StepEventLike,\n reason: 'wheel',\n })\n }\n\n /** The active element within this node's root (document or shadow root). */\n private getActiveElement(): Element | null {\n const root = this.getRootNode() as Document | ShadowRoot\n return root.activeElement\n }\n\n // ---------------------------------------------------------------------------\n // scrub\n //\n // NOTE: this is a SIMPLIFIED port of Base UI's ScrubArea. Pointer Lock, the\n // virtual cursor, and teleport are intentionally OUT OF SCOPE — this is a\n // basic relative drag (movementX * step, gated by pixelSensitivity), like the\n // combobox's \"no virtualization\" note.\n // ---------------------------------------------------------------------------\n\n private handleScrubPointerDown(e: PointerEvent) {\n const isMainButton = !e.button || e.button === 0\n if (e.defaultPrevented || this.readonly || this.disabled || !isMainButton) {\n return\n }\n\n if (e.pointerType === 'mouse') {\n e.preventDefault()\n this.inputRef.value?.focus()\n }\n\n this.scrubbing = true\n this.scrubMoved = false\n this.scrubAccumulator = 0\n this.scrubPointerDownTarget = e.target\n\n const win = this.ownerWindow()\n win.addEventListener('pointermove', this.onScrubMove, true)\n win.addEventListener('pointerup', this.onScrubUp, true)\n }\n\n private handleScrubMove(e: PointerEvent) {\n if (!this.scrubbing) {\n return\n }\n // Prevent text selection while dragging.\n e.preventDefault()\n\n this.scrubAccumulator += e.movementX\n\n if (Math.abs(this.scrubAccumulator) >= SCRUB_PIXEL_SENSITIVITY) {\n this.scrubAccumulator = 0\n this.scrubMoved = true\n const dValue = e.movementX\n const amount = Math.abs(dValue * this.getStepAmount(e as StepEventLike))\n if (amount !== 0) {\n this.allowInputSync = true\n this.incrementValue(amount, {\n direction: dValue >= 0 ? 1 : -1,\n event: e as StepEventLike,\n reason: 'scrub',\n })\n }\n }\n }\n\n private handleScrubUp(e: PointerEvent) {\n this.removeScrubListeners()\n this.scrubbing = false\n this.commit('scrub')\n\n // If no movement happened, synthesise a click on the original target so a\n // click-without-drag still focuses the input.\n if (!this.scrubMoved && this.scrubPointerDownTarget) {\n this.scrubPointerDownTarget.dispatchEvent(\n new MouseEvent('click', { bubbles: true, cancelable: true }),\n )\n }\n this.scrubMoved = false\n this.scrubPointerDownTarget = null\n }\n\n private removeScrubListeners() {\n const win = this.ownerWindow()\n win.removeEventListener('pointermove', this.onScrubMove, true)\n win.removeEventListener('pointerup', this.onScrubUp, true)\n }\n\n private handleScrubTouchStart(e: TouchEvent) {\n if (this.disabled || this.readonly) {\n return\n }\n if (e.touches.length === 1) {\n // Stop the page scrolling while dragging.\n e.preventDefault()\n }\n }\n\n private ownerWindow(): Window {\n return this.ownerDocument.defaultView ?? window\n }\n\n // ---------------------------------------------------------------------------\n // render\n // ---------------------------------------------------------------------------\n\n /**\n * The `inputmode` for the inner input.\n *\n * Mirrors Base UI's `setDynamicInputModeForIOS` intent: the iOS numeric and\n * decimal soft keyboards have no minus key, so when negatives are explicitly\n * allowed (an explicit `min < 0`) we yield `text` so the user can type a\n * minus sign on iOS. Otherwise the base mode is `numeric` when step/min/max\n * are all integers and min >= 0, and `decimal` when fractional values are in\n * play (so the iOS keyboard exposes a decimal point).\n *\n * Base UI swaps to `text`/`decimal` at runtime only on iOS; we compute the\n * mode statically, and `text`/`decimal` are harmless supersets on platforms\n * whose numeric keyboard already has the relevant keys. We gate the `text`\n * mode on the explicit `min` prop (not the `Number.MIN_SAFE_INTEGER`-backed\n * `minWithDefault`) so an unconstrained field keeps the friendlier `numeric`\n * keypad, matching Base UI's default of `numeric`.\n */\n private get computedInputMode(): 'numeric' | 'decimal' | 'text' {\n // iOS numeric/decimal keyboards lack a minus key; fall back to text so a\n // negative value can be entered when negatives are explicitly allowed.\n if (this.min != null && this.min < 0) {\n return 'text'\n }\n // Whole-number fields never accept a decimal point, so the numeric keypad\n // (no decimal key) is always the right soft keyboard.\n if (this.integer) {\n return 'numeric'\n }\n const stepIsInt = this.step === 'any' || Number.isInteger(this.numericStep)\n const minOk = this.min == null || (Number.isInteger(this.min) && this.min >= 0)\n const maxOk = this.max == null || Number.isInteger(this.max)\n return stepIsInt && minOk && maxOk ? 'numeric' : 'decimal'\n }\n\n /**\n * The resolved side for the `currency` symbol: the explicit `currencyPlacement`\n * when it is `'start'`/`'end'`, otherwise derived from the locale/currency via\n * `Intl`. Only meaningful when `currency` is set.\n */\n private get resolvedCurrencyPlacement(): 'start' | 'end' {\n if (this.currencyPlacement === 'start' || this.currencyPlacement === 'end') {\n return this.currencyPlacement\n }\n return this.currency ? resolveCurrencyPlacement(this.locale, this.currency) : 'start'\n }\n\n render() {\n const inputId = this.inputId\n\n // Naming/description from the field context (in a field), the built-in\n // <label for>/hint/error (standalone), or consumer-set host aria-* — all\n // resolved by the FieldControlMixin getters.\n const ariaLabel = this.controlAriaLabel\n const ariaLabelledby = this.controlAriaLabelledby\n\n // Currency convenience: a decorative, locale-aware symbol placed in the\n // resolved slot. It is `aria-hidden` (the number lives in the input's text),\n // so we fold its id into `aria-describedby` — a directly-referenced element\n // still contributes to the accessible description even when hidden — so\n // assistive tech announces the currency alongside the value. This is the\n // non-destructive analogue of NFE's aria-label composition: it works in every\n // labelling mode (field label, `aria-label`, `label` prop) without clobbering\n // the accessible name.\n const currencySymbol = this.currency ? getCurrencySymbol(this.locale, this.currency) : undefined\n const currencyPlacement = this.currency ? this.resolvedCurrencyPlacement : undefined\n const currencySymbolId = this.currency ? `${inputId}-currency` : undefined\n const ariaDescribedby\n = [this.controlAriaDescribedby, currencySymbolId].filter(Boolean).join(' ') || undefined\n\n const renderCurrencySymbol = (side: 'start' | 'end') =>\n currencySymbol != null && currencyPlacement === side\n ? html`<span id=${currencySymbolId} class=\"n-currency-symbol\" aria-hidden=\"true\">${currencySymbol}</span>`\n : nothing\n\n return html`\n ${this.renderLabel()}\n\n <div\n class=${classMap({\n 'n-number-field': true,\n 'is-scrubbing': this.scrubbing,\n })}\n >\n ${this.scrub\n ? html`\n <!-- A drag-to-scrub handle. It is purely a pointer affordance\n (role=\"presentation\"); the field's accessible name still comes from\n the external nord-field-label pointed at the input id, so keyboard\n and AT users are unaffected. The grip mirrors Base UI's ew-resize\n cursor icon. -->\n <div\n class=\"n-scrub\"\n role=\"presentation\"\n aria-hidden=\"true\"\n @pointerdown=${this.handleScrubPointerDown}\n @touchstart=${this.handleScrubTouchStart}\n >\n <svg\n class=\"n-scrub-grip\"\n width=\"24\"\n height=\"14\"\n viewBox=\"0 0 24 14\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M19.5 5.5L6.49737 5.51844V2L1 6.9999L6.5 12L6.49737 8.5L19.5 8.5V12L25 6.9999L19.5 2V5.5Z\"\n ></path>\n </svg>\n </div>\n `\n : nothing}\n\n <div class=${classMap({ 'n-control': true, 'is-invalid': this.effectiveInvalid })}>\n ${this.hideControls\n ? nothing\n : html`\n <button\n class=${classMap({ 'n-step': true, 'n-decrement': true })}\n type=\"button\"\n tabindex=\"-1\"\n aria-label=${this.localize.term('decreaseLabel')}\n aria-controls=${inputId}\n aria-readonly=${this.readonly ? 'true' : nothing}\n ?disabled=${this.decrementDisabled}\n @pointerdown=${(e: PointerEvent) => this.handleStepPointerDown(e, -1)}\n @pointerup=${(e: PointerEvent) => this.handleStepPointerStop(e, -1)}\n @pointerleave=${(e: PointerEvent) => this.handleStepPointerStop(e, -1)}\n @pointercancel=${(e: PointerEvent) => this.handleStepPointerStop(e, -1)}\n @click=${(e: MouseEvent) => this.handleStepClick(e, -1)}\n >\n <nord-icon name=\"interface-remove-small\"></nord-icon>\n </button>\n `}\n\n <span class=\"n-number-field-slot n-number-field-slot-start\">${renderCurrencySymbol('start')}</span>\n\n <input\n ${ref(this.inputRef)}\n class=\"n-input\"\n type=\"text\"\n id=${inputId}\n inputmode=${this.computedInputMode}\n .value=${this.inputText}\n placeholder=${this.placeholder ?? nothing}\n ?disabled=${this.effectiveDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n autocomplete=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n aria-roledescription=${this.localize.term('roleDescription')}\n aria-label=${ariaLabel ?? nothing}\n aria-labelledby=${ariaLabelledby ?? nothing}\n aria-describedby=${ariaDescribedby ?? nothing}\n aria-invalid=${this.controlAriaInvalid ?? nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-readonly=${this.readonly ? 'true' : nothing}\n @input=${this.handleInput}\n @blur=${this.handleBlur}\n @focus=${this.handleFocus}\n @keydown=${this.handleKeydown}\n @paste=${this.handlePaste}\n />\n\n <span class=\"n-number-field-slot n-number-field-slot-end\">${renderCurrencySymbol('end')}</span>\n\n ${this.hideControls\n ? nothing\n : html`\n <button\n class=${classMap({ 'n-step': true, 'n-increment': true })}\n type=\"button\"\n tabindex=\"-1\"\n aria-label=${this.localize.term('increaseLabel')}\n aria-controls=${inputId}\n aria-readonly=${this.readonly ? 'true' : nothing}\n ?disabled=${this.incrementDisabled}\n @pointerdown=${(e: PointerEvent) => this.handleStepPointerDown(e, 1)}\n @pointerup=${(e: PointerEvent) => this.handleStepPointerStop(e, 1)}\n @pointerleave=${(e: PointerEvent) => this.handleStepPointerStop(e, 1)}\n @pointercancel=${(e: PointerEvent) => this.handleStepPointerStop(e, 1)}\n @click=${(e: MouseEvent) => this.handleStepClick(e, 1)}\n >\n <nord-icon name=\"interface-add-small\"></nord-icon>\n </button>\n `}\n </div>\n </div>\n\n ${this.renderError()}\n ${this.isHintBelow ? this.renderHint() : nothing}\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-number-field': NumberField\n }\n}\n"],"names":["Icon","registerIcon","addIcon","removeIcon","numberFieldCount","sanitizeStep","value","fallback","Number","isFinite","NumberField","DraftComponentMixin","SizeMixin","FieldControlMixin","LitElement","constructor","this","numberFieldId","inputId","localize","LocalizeController","inputRef","createRef","formData","FormDataController","undefined","String","stateAttributes","StateAttributesController","invalid","effectiveInvalid","trackFocus","formAncestor","step","largeStep","smallStep","snapOnStep","allowOutOfRange","integer","allowWheelScrub","scrub","disabled","readonly","hideControls","currencyPlacement","inputText","scrubbing","allowInputSync","hasTouchedInput","skipNextClick","scrubAccumulator","scrubMoved","scrubPointerDownTarget","onScrubMove","e","handleScrubMove","onScrubUp","handleScrubUp","onWheel","handleWheel","wheelAttached","ownInvalid","super","form","id","oldVal","_formId","requestUpdate","getRootNode","querySelector","minWithDefault","_a","min","MIN_SAFE_INTEGER","maxWithDefault","max","MAX_SAFE_INTEGER","minWithZeroDefault","allowsNegative","numericStep","effectiveLargeStep","effectiveSmallStep","formatDisplay","formatValue","locale","format","formatNumber","connectedCallback","injectLightDomStyles","style","closest","ariaObserver","MutationObserver","observe","attributes","attributeFilter","_b","slotObserver","childList","disconnectedCallback","disconnect","stopHold","detachWheel","removeScrubListeners","createRenderRoot","isServer","root","document","createElement","className","appendChild","relocateSlots","move","name","target","child","Array","from","children","getAttribute","parentElement","startZone","endZone","willUpdate","changed","has","clamped","clamp","isInteger","Math","round","updated","wheelWanted","attachWheel","pendingSelection","start","end","setSelectionRange","focus","options","isTextReason","reason","getStepAmount","event","altKey","shiftKey","setValue","unvalidated","direction","shouldClamp","validated","toValidatedNumber","small","shouldFire","dispatchEvent","NordEvent","incrementValue","amount","currentValue","prev","next","commit","NumberFieldChangeEvent","getAllowedNonNumericKeys","decimal","group","currency","literal","getNumberLocaleDetails","keys","Set","BASE_NON_NUMERIC_SYMBOLS","forEach","symbol","add","SPACE_SEPARATOR_RE","test","formatStyle","allowPercent","unit","allowPermille","_c","PERCENTAGES","key","PERMILLE","char","PLUS_SIGNS_WITH_ASCII","MINUS_SIGNS_WITH_ASCII","separator","delete","handleKeydown","defaultPrevented","mask","allowed","isAsciiDigit","isNamedKey","length","which","ctrlKey","metaKey","ARABIC_DETECT_RE","PERSIAN_DETECT_RE","HAN_DETECT_RE","FULLWIDTH_DETECT_RE","ANY_MINUS_DETECT_RE","ANY_PLUS_DETECT_RE","preventDefault","parseValue","parseNumber","isValidCharacterString","text","every","ch","handleInput","stopPropagation","input","trim","masked","selectionStart","selectionEnd","parsed","handlePaste","pasted","clipboardData","getData","handleFocus","currentTarget","handleBlur","reverted","committed","hasNumberFormatRoundingOptions","removeFloatingPointErrors","canonical","commitDirtyInput","handleStepPointerDown","isMainButton","button","activePointerId","pointerId","pointerType","setPointerCapture","holdStartTimer","setTimeout","holdTickTimer","setInterval","handleStepPointerStop","wasHolding","handleStepClick","clearTimeout","clearInterval","incrementDisabled","decrementDisabled","addEventListener","passive","removeEventListener","getActiveElement","deltaY","activeElement","handleScrubPointerDown","win","ownerWindow","movementX","abs","dValue","MouseEvent","bubbles","cancelable","handleScrubTouchStart","touches","ownerDocument","defaultView","window","computedInputMode","stepIsInt","minOk","maxOk","resolvedCurrencyPlacement","resolveCurrencyPlacement","render","ariaLabel","controlAriaLabel","ariaLabelledby","controlAriaLabelledby","currencySymbol","getCurrencySymbol","currencySymbolId","ariaDescribedby","controlAriaDescribedby","filter","Boolean","join","renderCurrencySymbol","side","html","nothing","renderLabel","classMap","term","ref","placeholder","effectiveDisabled","required","controlAriaInvalid","renderError","isHintBelow","renderHint","__decorate","query","prototype","property","converter","attr","num","isNaN","reflect","type","attribute","state","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NumberField.js","sources":["../src/number-field/NumberField.ts"],"sourcesContent":["import type { PropertyValues, ReactiveControllerHost } from 'lit'\nimport type { InputMixinInterface } from '../common/mixins/InputMixin.js'\nimport type { NumberFieldChangeReason } from './NumberFieldEvents.js'\nimport * as addIcon from '@nordhealth/icons/lib/assets/interface-add-small.js'\nimport * as removeIcon from '@nordhealth/icons/lib/assets/interface-remove-small.js'\nimport { html, isServer, LitElement, nothing } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\n\nimport { FormDataController } from '../common/controllers/FormDataController.js'\nimport { StateAttributesController } from '../common/controllers/StateAttributesController.js'\nimport { NordEvent } from '../common/events.js'\nimport { injectLightDomStyles } from '../common/light-dom-styles.js'\nimport { DraftComponentMixin } from '../common/mixins/DraftComponentMixin.js'\nimport { FieldControlMixin } from '../common/mixins/FieldControlMixin.js'\nimport { SizeMixin } from '../common/mixins/SizeMixin.js'\nimport Icon from '../icon/Icon.js'\nimport { LocalizeController } from '../localization/LocalizeController.js'\nimport { getCurrencySymbol, resolveCurrencyPlacement } from './currency.js'\nimport { formatNumber } from './format.js'\nimport style from './NumberField.css'\nimport { NumberFieldChangeEvent } from './NumberFieldEvents.js'\nimport {\n ANY_MINUS_DETECT_RE,\n ANY_PLUS_DETECT_RE,\n ARABIC_DETECT_RE,\n BASE_NON_NUMERIC_SYMBOLS,\n FULLWIDTH_DETECT_RE,\n getNumberLocaleDetails,\n HAN_DETECT_RE,\n MINUS_SIGNS_WITH_ASCII,\n parseNumber,\n PERCENTAGES,\n PERMILLE,\n PERSIAN_DETECT_RE,\n PLUS_SIGNS_WITH_ASCII,\n SPACE_SEPARATOR_RE,\n} from './parse.js'\nimport {\n clamp,\n hasNumberFormatRoundingOptions,\n removeFloatingPointErrors,\n toValidatedNumber,\n} from './validate.js'\n\nexport { getCurrencySymbol, resolveCurrencyPlacement } from './currency.js'\nexport { NumberFieldChangeEvent } from './NumberFieldEvents.js'\nexport type { NumberFieldChangeReason } from './NumberFieldEvents.js'\n\n/**\n * The editable state of the inner input handed to (and returned from) a custom\n * `mask` function: the current text and the caret/selection offsets. The shape\n * mirrors a mask library's element state (e.g. Maskito's `ElementState`), so a\n * mask transformer can be adapted with a thin wrapper.\n */\nexport interface NumberFieldMaskState {\n value: string\n selectionStart: number\n selectionEnd: number\n}\n\n/** Contextual info passed to a custom `mask` function alongside the editable state. */\nexport interface NumberFieldMaskContext {\n locale?: string | string[]\n format?: Intl.NumberFormatOptions\n}\n\n/** Signature of a custom `mask` transformer function. */\nexport type NumberFieldMaskInput = (\n state: NumberFieldMaskState,\n context: NumberFieldMaskContext,\n) => NumberFieldMaskState\n\nIcon.registerIcon(addIcon)\nIcon.registerIcon(removeIcon)\n\n/** Press-and-hold timing (ms) and pointer thresholds, ported from Base UI constants. */\nconst START_AUTO_CHANGE_DELAY = 400\nconst CHANGE_VALUE_TICK_DELAY = 60\nconst SCRUB_PIXEL_SENSITIVITY = 2\n// Base UI's DEFAULT_STEP (1) is the fallback when getStepAmount() returns\n// undefined; here getStepAmount always resolves to a concrete number (step is\n// never undefined and 'any' is normalised to 1), so no separate fallback is needed.\n\n/** Per-instance counter so light-DOM ids (input + scrub label `for`) stay unique. */\nlet numberFieldCount = 1\n\n/** A keyboard-like event carrying the modifier flags `getStepAmount` reads. */\ninterface StepEventLike {\n altKey?: boolean\n shiftKey?: boolean\n}\n\n/** Converts the `value` attribute (a numeric string) to `number | null`. */\nfunction valueConverter(attr: string | null): number | null {\n if (attr == null || attr.trim() === '') {\n return null\n }\n const num = Number(attr)\n return Number.isNaN(num) ? null : num\n}\n\n/** A step must be a finite, strictly-positive number; otherwise fall back. */\nfunction sanitizeStep(value: number, fallback: number): number {\n return Number.isFinite(value) && value > 0 ? value : fallback\n}\n\n/**\n * Converts the `step` attribute, keeping the literal `'any'` but coercing\n * numbers. Rejects NaN / non-positive values, falling back to `1`, since a\n * zero or negative step makes stepping (and snap-on-step) meaningless.\n */\nfunction stepConverter(attr: string | null): number | 'any' {\n if (attr == null) {\n return 1\n }\n if (attr === 'any') {\n return 'any'\n }\n return sanitizeStep(Number(attr), 1)\n}\n\n/**\n * Number Field is a control for entering a numeric value, with decrement and\n * increment stepper buttons, optional drag-to-scrub, and locale-aware\n * formatting. It ports the behaviour of Base UI's Number Field: arrow/Page\n * stepping, Home/End to the bounds, press-and-hold auto-repeat, optional\n * wheel/scrub, clamping, and `Intl.NumberFormat` formatting.\n *\n * Number Field is a control only — it has no built-in label, hint or error.\n * Compose it inside a [Field](/components/field/) with a\n * [Field Label](/components/field-label/) to give it a label, hint and error,\n * the same way you would a native input.\n *\n * FORM PARTICIPATION: the value is submitted as the raw numeric string under the\n * host `name` via `FormDataController` (the inner input has no `name`, avoiding a\n * double submit). Unlike Base UI there is no hidden native number input, and —\n * like the other new Nord controls — Number Field does NOT use\n * `ElementInternals`, so it performs no native constraint validation: `required`,\n * `min`, `max` and `step` are NOT enforced as form-blocking validity. `min`/`max`\n * govern interactive stepping and (unless `allow-out-of-range` is set) clamp the\n * value; `required` only surfaces as `required`/`aria-required` on the inner input\n * for assistive technology. Authors must validate constraints themselves and\n * reflect failures via `invalid` and a `nord-field-error`.\n *\n * ACCESSIBILITY NOTE: the inner control is a text input (with `inputmode`) and\n * `aria-roledescription=\"Number field\"`, NOT `role=\"spinbutton\"` and NOT a\n * native `type=\"number\"`. A text input lets us show locale-formatted text\n * (currency, percent, grouping) while keeping full text editing — assistive tech\n * reads the formatted value from the input's text. We do NOT add\n * `aria-valuenow`/`valuemin`/`valuemax`/`valuetext`: those are only valid on a\n * range role (e.g. `spinbutton`/`slider`) and would fail `aria-allowed-attr`\n * validation on a text input. This mirrors what Base UI's `NumberFieldInput`\n * actually ships (only `aria-roledescription`).\n *\n * @status draft\n * @category form\n * @lightDom true\n *\n * @fires {NumberFieldChangeEvent} change - Dispatched when the value is committed (on blur, keyboard step, or pointer-up after a button hold or scrub). Carries the numeric value and a `reason`.\n * @fires {NordEvent} input - Dispatched as the value changes during interaction (typing a parseable value, clearing, each step, each button tick, each wheel notch, each scrub increment).\n *\n * @slot start - Leading content inside the control, before the input (e.g. a unit or currency symbol). Sits after the decrement stepper button.\n * @slot end - Trailing content inside the control, after the input (e.g. a unit label or `%`). Sits before the increment stepper button.\n *\n * @localization increaseLabel - Accessible label for the increment stepper button.\n * @localization decreaseLabel - Accessible label for the decrement stepper button.\n * @localization roleDescription - Role description announced for the input (aria-roledescription).\n *\n * @cssprop [--n-number-field-control-min-inline-size=8ch] - Minimum inline size (width) of the numeric input portion of the control.\n * @cssprop [--n-number-field-step-inline-size=var(--n-space-xl)] - Inline size (width) of each increment/decrement stepper button.\n * @cssprop [--n-number-field-background=var(--n-input-background, var(--n-color-surface))] - Background of the control.\n * @cssprop [--n-number-field-border-color=var(--n-input-border-color, var(--n-color-border-strong))] - Border color of the control.\n * @cssprop [--n-number-field-border-radius=var(--n-input-border-radius, var(--n-border-radius-s))] - Corner radius of the control.\n *\n * @attr {boolean} data-invalid - Present while the field is invalid (mirrors the `invalid` property). Style hook for the whole control.\n * @attr {boolean} data-focused - Present while the control has focus. Style hook for focus-within treatments.\n */\n@customElement('nord-number-field')\nexport default class NumberField extends DraftComponentMixin(SizeMixin(FieldControlMixin(LitElement))) {\n /** Stable, unique id base for this instance's light-DOM ARIA wiring. */\n private numberFieldId = `nord-number-field-${numberFieldCount++}`\n\n // The built-in `<label for>` (from FieldControlMixin) must point at the real\n // focusable input, so override the mixin's default id with the input's id.\n protected override inputId = `${this.numberFieldId}-input`\n\n private localize = new LocalizeController<'nord-number-field'>(this)\n\n private inputRef = createRef<HTMLInputElement>()\n\n // NumberField's `value` is `number | null`, which doesn't overlap with the\n // controller's `InputMixinInterface` host type (`value: string`). The\n // controller only reads `form`/`name`/`disabled` and the `value()` option (not\n // `host.value`), so casting the host through `unknown` is safe — same approach\n // as the combobox, which widens rather than narrows `value`.\n private formData = new FormDataController(\n this as unknown as ReactiveControllerHost & InputMixinInterface,\n { value: () => (this.value == null ? undefined : String(this.value)) },\n )\n\n /** Reflects state to Base-UI-style `data-*` attributes (data-invalid, data-focused). */\n private stateAttributes = new StateAttributesController(\n this,\n () => ({ invalid: this.effectiveInvalid }),\n { trackFocus: true },\n )\n\n // The control's own invalid state for the field context: its `invalid` prop or\n // a built-in `error` (when used standalone).\n protected override get ownInvalid() {\n return this.invalid || super.ownInvalid\n }\n\n /** Re-renders when the host's forwarded ARIA labelling attributes change. */\n private ariaObserver?: MutationObserver\n\n // Landing zones for relocated `slot=\"start\"`/`slot=\"end\"` host children. The\n // control renders into light DOM, so real `<slot>` projection isn't available.\n @query('.n-number-field-slot-start') private startZone?: HTMLElement\n @query('.n-number-field-slot-end') private endZone?: HTMLElement\n\n // Watches for `slot=\"start\"`/`slot=\"end\"` children being added or removed so\n // they get relocated into their landing zones on the next render.\n private slotObserver?: MutationObserver\n\n // ----- Form association (own implementation, mirroring InputMixin) ----------\n // NumberField does NOT use InputMixin because it diverges from `value: string`;\n // it provides its own `name`/`disabled`/`form` so the FormDataController host\n // shape (name/disabled/form) is satisfied.\n\n private formAncestor: HTMLFormElement | null = null\n private _formId?: string\n\n /**\n * The raw numeric value of the field, or `null` when empty.\n *\n * The `value` attribute may be set as a numeric string in HTML; it is coerced\n * with a custom converter (`Number(v)`, `NaN -> null`).\n */\n @property({ converter: valueConverter }) value: number | null = null\n\n /**\n * The minimum allowed value. Step interactions and blur-clamping clamp to it,\n * and the decrement button disables at/below it. Home jumps to it only when it\n * is finite.\n */\n @property({ reflect: true, type: Number }) min?: number\n\n /**\n * The maximum allowed value. Step interactions and blur-clamping clamp to it,\n * and the increment button disables at/above it. End jumps to it only when it\n * is finite.\n */\n @property({ reflect: true, type: Number }) max?: number\n\n /**\n * Amount each arrow press / button click / wheel notch changes the value.\n * `'any'` disables step semantics (treated as `1` internally for stepping).\n */\n @property({ reflect: true, converter: stepConverter }) step: number | 'any' = 1\n\n /** Step used for PageUp/PageDown and Shift+Arrow. */\n @property({ reflect: true, type: Number, attribute: 'large-step' }) largeStep = 10\n\n /** Step used when Alt/Option is held with Arrow keys. */\n @property({ reflect: true, type: Number, attribute: 'small-step' }) smallStep = 0.1\n\n /** When `true`, stepping snaps the value to the nearest multiple of the step. */\n @property({ reflect: true, type: Boolean, attribute: 'snap-on-step' }) snapOnStep = false\n\n /**\n * When `true`, direct text entry may go outside `min`/`max` without clamping\n * (so range validation can flag it); step interactions still clamp.\n */\n @property({ reflect: true, type: Boolean, attribute: 'allow-out-of-range' }) allowOutOfRange = false\n\n /**\n * Restricts the field to whole numbers. The decimal separator can no longer be\n * typed, pasted or programmatic fractional values are rounded to the nearest\n * integer, and the built-in `mask` caps the fraction to zero. The soft keyboard\n * shows the plain numeric keypad.\n */\n @property({ reflect: true, type: Boolean }) integer = false\n\n /** When `true`, scrolling the mouse wheel while focused changes the value. */\n @property({ reflect: true, type: Boolean, attribute: 'allow-wheel-scrub' }) allowWheelScrub = false\n\n /** When `true`, renders a drag handle so the user can click-drag to change the value. */\n @property({ reflect: true, type: Boolean }) scrub = false\n\n /**\n * `Intl.NumberFormat` options used to format the displayed text. Set as a\n * property only (e.g. `{ style: 'currency', currency: 'EUR' }`).\n */\n @property({ attribute: false }) format?: Intl.NumberFormatOptions\n\n /**\n * Overrides the default `Intl.NumberFormat`-based formatting of the displayed\n * text. Receives the current numeric value (`null` when the field is empty),\n * the active `locale`, and the `format` options, and must return the string to\n * display. Following the default behaviour, a `null` value should map to an\n * empty string. Set as a property only.\n */\n @property({ attribute: false })\n formatValue?: (value: number | null, locale?: string | string[], options?: Intl.NumberFormatOptions) => string\n\n /**\n * Overrides the default locale-aware parsing of the typed text. Receives the\n * raw input text, the active `locale`, and the `format` options, and must\n * return the parsed number, or `null` when the text is empty or cannot be\n * parsed. Set as a property only.\n */\n @property({ attribute: false })\n parseValue?: (text: string, locale?: string | string[], options?: Intl.NumberFormatOptions) => number | null\n\n /**\n * An optional custom edit-time mask: a function that reshapes the input text on\n * every insertion, deletion, and paste, and reports where the caret should\n * land. This is the live-typing counterpart to `formatValue` (which formats an\n * already-committed value). Use it to insert grouping separators, localise the\n * decimal, or cap fraction digits as the user types — typically by wrapping a\n * mask library (e.g. Maskito's `maskitoTransform(elementState, options)`).\n *\n * The field does NOT format live on its own: without a mask, the text is kept\n * as typed and the auto (`Intl`) formatting is applied only on blur and on\n * paste. When a mask is set it becomes the source of truth for the text — the\n * per-character guard is bypassed (the mask decides validity) and paste lands\n * natively so it flows through the mask. Keep the mask output parseable in, and\n * consistent with, the active `locale`/`format`. Set as a property.\n */\n @property({ attribute: false })\n mask?: NumberFieldMaskInput\n\n /** BCP-47 locale(s) for `Intl` formatting/parsing. Defaults to the runtime locale. */\n @property({ reflect: true }) locale?: string | string[]\n\n /** Placeholder text shown when the field is empty. */\n @property({ reflect: true }) placeholder?: string\n\n /** Form field name. The value is submitted under this name via FormDataController. */\n @property({ reflect: true }) name?: string\n\n /** Disables the control. */\n @property({ reflect: true, type: Boolean }) disabled = false\n\n /** Read-only: the user cannot change the value, but the value is still submitted. */\n @property({ reflect: true, type: Boolean }) readonly = false\n\n /** Applies error styling and sets `aria-invalid=\"true\"` on the inner input. */\n @property({ reflect: true, type: Boolean }) invalid = false\n\n /** Hides the increment/decrement stepper buttons (still steppable via keyboard). */\n @property({ reflect: true, type: Boolean, attribute: 'hide-controls' }) hideControls = false\n\n /**\n * ISO 4217 currency code (e.g. `USD`, `EUR`, `SEK`). Setting it enables the\n * currency convenience: a decorative, locale-aware symbol is rendered into the\n * `start`/`end` slot resolved from `currencyPlacement`, and the currency is\n * folded into the input's accessible description so assistive tech announces it.\n *\n * This is layered on top of the generic `start`/`end` slots — for a fully\n * custom symbol, omit `currency` and slot your own content instead. It is\n * independent of `format`: the symbol lives beside the number rather than in\n * the formatted text, so it composes cleanly with `mask`.\n */\n @property({ reflect: true }) currency?: string\n\n /**\n * Which side the `currency` symbol is placed on. `'auto'` (the default)\n * derives the side from the `locale`/`currency` pair via `Intl` (symbol-first\n * locales like en-US → `start`, symbol-last locales like de-DE → `end`);\n * `'start'`/`'end'` force the side. Has no effect unless `currency` is set.\n */\n @property({ reflect: true, attribute: 'currency-placement' }) currencyPlacement: 'start' | 'end' | 'auto' = 'auto'\n\n /** The text shown in the input — formatted, unless the user is mid-edit. */\n @state() private inputText = ''\n\n /** Whether the user is scrubbing (adds an `is-scrubbing` cursor). */\n @state() private scrubbing = false\n\n /**\n * While the user is actively typing this is `false`, so we don't reformat\n * mid-keystroke. Reset to `true` on blur / keydown-step / button / wheel / scrub.\n */\n private allowInputSync = true\n\n /** Set after the first focus so we only move the caret to the end once. */\n private hasTouchedInput = false\n\n /**\n * Caret/selection a mask asked us to place after it rewrote the text. Applied\n * in `updated()` once Lit has committed the new `.value` (which resets the\n * caret to the end).\n */\n private pendingSelection?: [number, number]\n\n // ----- press-and-hold timers ----------------------------------------------\n private holdStartTimer?: ReturnType<typeof setTimeout>\n private holdTickTimer?: ReturnType<typeof setInterval>\n private activePointerId?: number\n /** When a mouse pointerdown handled the change, suppress the synthetic click. */\n private skipNextClick = false\n\n // ----- scrub state ---------------------------------------------------------\n private scrubAccumulator = 0\n private scrubMoved = false\n private scrubPointerDownTarget: EventTarget | null = null\n private onScrubMove = (e: PointerEvent) => this.handleScrubMove(e)\n private onScrubUp = (e: PointerEvent) => this.handleScrubUp(e)\n\n /** Bound non-passive wheel listener (attached only while wheel scrub is enabled). */\n private onWheel = (e: WheelEvent) => this.handleWheel(e)\n private wheelAttached = false\n\n // ----- form-id getter/setter (copied from InputMixin) ----------------------\n set form(id: string) {\n const oldVal = this._formId\n this._formId = id\n this.requestUpdate('form', oldVal)\n }\n\n @property()\n get form(): HTMLFormElement | null {\n if (this._formId) {\n const root = this.getRootNode() as ShadowRoot | Document\n return root.querySelector<HTMLFormElement>(`form#${this._formId}`)\n }\n return this.formAncestor\n }\n\n /** The effective lower bound for clamping, mirroring Base UI `minWithDefault`. */\n private get minWithDefault(): number {\n return this.min ?? Number.MIN_SAFE_INTEGER\n }\n\n /** The effective upper bound for clamping, mirroring Base UI `maxWithDefault`. */\n private get maxWithDefault(): number {\n return this.max ?? Number.MAX_SAFE_INTEGER\n }\n\n /** The base used by `incrementValue` when stepping from empty. */\n private get minWithZeroDefault(): number {\n return this.min ?? 0\n }\n\n /**\n * Whether a negative value is allowed, gating the minus sign in the input. A\n * `min` of `0` or higher forbids negatives; an unset or negative `min` allows\n * them (mirroring `minWithDefault`, which backs unset `min` with a value < 0).\n */\n private get allowsNegative(): boolean {\n return this.minWithDefault < 0\n }\n\n /**\n * The numeric step used for stepping; `'any'` behaves as `1`. Guards against a\n * non-positive / NaN `step` set directly as a property (which bypasses the\n * attribute converter) by falling back to `1`.\n */\n private get numericStep(): number {\n return this.step === 'any' ? 1 : sanitizeStep(this.step, 1)\n }\n\n /** The PageUp/PageDown + Shift step, guarded against non-positive / NaN values. */\n private get effectiveLargeStep(): number {\n return sanitizeStep(this.largeStep, 10)\n }\n\n /** The Alt/Option step, guarded against non-positive / NaN values. */\n private get effectiveSmallStep(): number {\n return sanitizeStep(this.smallStep, 0.1)\n }\n\n /**\n * The canonical displayed text for a value: the `formatValue` override if set,\n * otherwise the locale `Intl` format. Applied on blur and on paste.\n */\n private formatDisplay(value: number | null): string {\n if (this.formatValue) {\n return this.formatValue(value, this.locale, this.format)\n }\n return formatNumber(value, this.locale, this.format)\n }\n\n connectedCallback() {\n super.connectedCallback()\n injectLightDomStyles(this, style)\n\n // Cache the form ancestor; it only changes on (re)connection.\n this.formAncestor = this.closest('form')\n\n // An external label (e.g. nord-field-label) names the control by setting\n // aria-label / aria-labelledby on the host; forward it to the inner input.\n this.ariaObserver ??= new MutationObserver(() => this.requestUpdate())\n this.ariaObserver.observe(this, {\n attributes: true,\n attributeFilter: ['aria-label', 'aria-labelledby', 'aria-describedby'],\n })\n\n // Relocate `slot=\"start\"`/`slot=\"end\"` children when they are added or\n // removed; the control is light DOM, so there are no real `<slot>` elements.\n this.slotObserver ??= new MutationObserver(() => this.requestUpdate())\n this.slotObserver.observe(this, { childList: true })\n\n // Format the initial value if one was set before connect.\n if (this.allowInputSync) {\n this.inputText = this.formatDisplay(this.value)\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.ariaObserver?.disconnect()\n this.ariaObserver = undefined\n this.slotObserver?.disconnect()\n this.slotObserver = undefined\n this.formAncestor = null\n this.stopHold()\n this.detachWheel()\n this.removeScrubListeners()\n }\n\n // Render in the light DOM so the control participates in the document tree\n // (form labelling, Tailwind), like the other new Nord components, but into a\n // layout-transparent (`display: contents`) wrapper rather than the host\n // itself, so slotted `slot=\"start\"`/`slot=\"end\"` children can live as\n // untouched siblings instead of being wiped by this element's own render\n // (mirrors `nord-filter-input` / `nord-combobox`). On the server there is no\n // DOM to append to, so render into the host directly.\n protected createRenderRoot() {\n if (isServer) {\n return this\n }\n const root = document.createElement('div')\n root.className = 'n-number-field-root'\n this.appendChild(root)\n return root\n }\n\n /**\n * Relocates the host's `slot=\"start\"`/`slot=\"end\"` children into the matching\n * landing zone inside the control. Only direct host children are considered,\n * and a child already sitting in its zone is left untouched, so repeated\n * updates are idempotent and don't disturb focus, caret or pointer state.\n */\n private relocateSlots() {\n if (isServer) {\n return\n }\n\n const move = (name: 'start' | 'end', target?: HTMLElement) => {\n if (!target) {\n return\n }\n for (const child of Array.from(this.children)) {\n if (child.getAttribute('slot') === name && child.parentElement !== target) {\n target.appendChild(child)\n }\n }\n }\n\n move('start', this.startZone)\n move('end', this.endZone)\n }\n\n protected willUpdate(changed: PropertyValues<this>) {\n // A programmatic `value` set (the property changing while the user is NOT\n // mid-edit) must respect the bounds when out-of-range entry isn't allowed.\n // Interactive clamping is handled in `setValue`, which always runs with\n // `allowInputSync === false` during typing; gating on `allowInputSync` here\n // means we only clamp non-interactive sources and never fight live input.\n if (\n changed.has('value')\n && this.allowInputSync\n && !this.allowOutOfRange\n && this.value != null\n ) {\n const clamped = clamp(this.value, this.minWithDefault, this.maxWithDefault)\n if (clamped !== this.value) {\n this.value = clamped\n }\n }\n\n // A whole-number field never holds a fraction; round any that slips in via a\n // programmatic set or a pasted decimal so the model stays integral.\n if (changed.has('value') && this.integer && this.value != null && !Number.isInteger(this.value)) {\n this.value = Math.round(this.value)\n }\n\n // Keep the displayed text in sync when the value or formatting changes\n // programmatically, but never overwrite text the user is actively editing.\n if (\n this.allowInputSync\n && (changed.has('value') || changed.has('format') || changed.has('locale'))\n ) {\n this.inputText = this.formatDisplay(this.value)\n }\n }\n\n protected updated(changed: PropertyValues) {\n super.updated(changed)\n\n // Move any `slot=\"start\"`/`slot=\"end\"` host children into their landing\n // zones inside the control. The control is light DOM, so real `<slot>`\n // projection isn't available; relocating keeps the public API like `nord-input`.\n this.relocateSlots()\n\n // Attach or detach the non-passive wheel listener as conditions change.\n const wheelWanted = this.allowWheelScrub && !this.disabled && !this.readonly\n if (wheelWanted && !this.wheelAttached) {\n this.attachWheel()\n }\n else if (!wheelWanted && this.wheelAttached) {\n this.detachWheel()\n }\n\n // A mask rewrote the text this cycle; Lit has now committed the new `.value`\n // (which parks the caret at the end), so restore the caret/selection the\n // mask requested. Runs in `updated` (before paint) so there is no flash.\n if (this.pendingSelection) {\n const [start, end] = this.pendingSelection\n this.pendingSelection = undefined\n this.inputRef.value?.setSelectionRange(start, end)\n }\n }\n\n /** Focus the inner input (mirrors combobox `focus`). */\n focus(options?: FocusOptions) {\n this.inputRef.value?.focus(options)\n }\n\n // ---------------------------------------------------------------------------\n // value model\n // ---------------------------------------------------------------------------\n\n /** The reasons that DON'T clamp when `allowOutOfRange` is set (direct text entry). */\n private isTextReason(reason: NumberFieldChangeReason): boolean {\n return (\n reason === 'input-change'\n || reason === 'input-blur'\n || reason === 'input-paste'\n || reason === 'input-clear'\n || reason === 'none'\n )\n }\n\n /**\n * The amount a single step changes the value, mirroring Base UI `getStepAmount`:\n * Alt -> smallStep, Shift -> largeStep, otherwise step.\n */\n private getStepAmount(event?: StepEventLike): number {\n if (event?.altKey) {\n return this.effectiveSmallStep\n }\n if (event?.shiftKey) {\n return this.effectiveLargeStep\n }\n return this.numericStep\n }\n\n /**\n * Validate, store, format and announce a new value.\n *\n * Returns whether a `change`/value update fired so callers can mirror Base UI's\n * commit semantics.\n */\n private setValue(\n unvalidated: number | null,\n {\n reason,\n direction,\n event,\n }: { reason: NumberFieldChangeReason, direction?: 1 | -1, event?: StepEventLike },\n ): boolean {\n // Only direct text entry may stay out of range when allowOutOfRange is set;\n // step interactions (keyboard/buttons/wheel/scrub) always clamp.\n const shouldClamp = !this.allowOutOfRange || !this.isTextReason(reason)\n\n const validated = toValidatedNumber(unvalidated, {\n step: direction ? this.getStepAmount(event) * direction : undefined,\n format: this.format,\n minWithDefault: this.minWithDefault,\n maxWithDefault: this.maxWithDefault,\n minWithZeroDefault: this.minWithZeroDefault,\n snapOnStep: this.snapOnStep,\n small: event?.altKey ?? false,\n clamp: shouldClamp,\n })\n\n const isTextReason = this.isTextReason(reason)\n const shouldFire\n = validated !== this.value\n || (isTextReason && (unvalidated !== this.value || this.allowInputSync === false))\n\n if (shouldFire) {\n this.value = validated\n // Fired as the value changes during interaction.\n this.dispatchEvent(new NordEvent('input'))\n }\n\n // Keep the visible input in sync immediately for programmatic/step changes;\n // during direct typing we gate on allowInputSync so we don't clobber the text.\n if (this.allowInputSync) {\n this.inputText = this.formatDisplay(validated)\n }\n\n this.requestUpdate()\n return shouldFire\n }\n\n /**\n * Step the value by `amount * direction`, mirroring Base UI `incrementValue`.\n * Stepping from empty starts at `max(0, min ?? 0)`.\n */\n private incrementValue(\n amount: number,\n {\n direction,\n currentValue,\n event,\n reason,\n }: {\n direction: 1 | -1\n currentValue?: number | null\n event?: StepEventLike\n reason: NumberFieldChangeReason\n },\n ): boolean {\n const prev = currentValue == null ? this.value : currentValue\n const next\n = typeof prev === 'number' ? prev + amount * direction : Math.max(0, this.min ?? 0)\n return this.setValue(next, { reason, direction, event })\n }\n\n /** Dispatch the committed `change` event with the current value + reason. */\n private commit(reason: NumberFieldChangeReason) {\n this.dispatchEvent(new NumberFieldChangeEvent(this.value, reason))\n }\n\n // ---------------------------------------------------------------------------\n // keyboard\n // ---------------------------------------------------------------------------\n\n /**\n * The set of non-numeric keys allowed in the input, mirroring Base UI's\n * `getAllowedNonNumericKeys` (locale decimal/group/currency/percent + signs).\n */\n private getAllowedNonNumericKeys(): Set<string> {\n // This guard only runs when no mask is active (`mask=\"off\"`); the built-in and\n // custom masks decide validity themselves and bypass it.\n const { decimal, group, currency, literal } = getNumberLocaleDetails(this.locale, this.format)\n const keys = new Set<string>()\n\n BASE_NON_NUMERIC_SYMBOLS.forEach(symbol => keys.add(symbol))\n if (decimal) {\n keys.add(decimal)\n }\n if (group) {\n keys.add(group)\n if (SPACE_SEPARATOR_RE.test(group)) {\n keys.add(' ')\n }\n }\n\n const formatStyle = this.format?.style\n const allowPercent\n = formatStyle === 'percent' || (formatStyle === 'unit' && this.format?.unit === 'percent')\n const allowPermille\n = formatStyle === 'percent' || (formatStyle === 'unit' && this.format?.unit === 'permille')\n\n if (allowPercent) {\n PERCENTAGES.forEach(key => keys.add(key))\n }\n if (allowPermille) {\n PERMILLE.forEach(key => keys.add(key))\n }\n if (formatStyle === 'currency' && currency) {\n keys.add(currency)\n }\n if (literal) {\n Array.from(literal).forEach(char => keys.add(char))\n if (SPACE_SEPARATOR_RE.test(literal)) {\n keys.add(' ')\n }\n }\n\n PLUS_SIGNS_WITH_ASCII.forEach(key => keys.add(key))\n if (this.minWithDefault < 0) {\n MINUS_SIGNS_WITH_ASCII.forEach(key => keys.add(key))\n }\n\n // A whole-number field drops every decimal separator (the locale's own plus\n // the universal ASCII/fullwidth/Arabic glyphs that `BASE_NON_NUMERIC_SYMBOLS`\n // seeds), but keeps the group separator so grouped input still parses — even\n // where the group char (e.g. de-DE `.`) coincides with a decimal glyph.\n if (this.integer) {\n for (const separator of [decimal, '.', '.', '٫']) {\n if (separator && separator !== group) {\n keys.delete(separator)\n }\n }\n }\n\n return keys\n }\n\n private handleKeydown(e: KeyboardEvent) {\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n this.allowInputSync = true\n\n const stepKeys = new Set(['ArrowUp', 'ArrowDown', 'PageUp', 'PageDown', 'Home', 'End'])\n\n if (!stepKeys.has(e.key)) {\n // A custom mask governs which characters are valid and reshapes the text on\n // the `input` event, so let every key through when one is set.\n if (this.mask) {\n return\n }\n\n // Let typing through, but reject characters that can never be part of a\n // number (porting Base UI's allowed-key guard for parity). Composition\n // (which===229), navigation keys, and modifier combos always pass.\n const allowed = this.getAllowedNonNumericKeys()\n const isAsciiDigit = e.key >= '0' && e.key <= '9'\n const isNamedKey = e.key.length > 1 // ArrowLeft, Backspace, Tab, Enter, …\n const passes\n = e.which === 229\n || e.ctrlKey\n || e.metaKey\n || (e.altKey)\n || isAsciiDigit\n || isNamedKey\n || allowed.has(e.key)\n || ARABIC_DETECT_RE.test(e.key)\n || PERSIAN_DETECT_RE.test(e.key)\n || HAN_DETECT_RE.test(e.key)\n || FULLWIDTH_DETECT_RE.test(e.key)\n || (this.allowsNegative && ANY_MINUS_DETECT_RE.test(e.key))\n || ANY_PLUS_DETECT_RE.test(e.key)\n\n if (!passes) {\n e.preventDefault()\n }\n return\n }\n\n // Parse the current (possibly unblurred) text so edits are honoured.\n const currentValue = this.parseValue\n ? this.parseValue(this.inputText, this.locale, this.format)\n : parseNumber(this.inputText, this.locale, this.format)\n\n switch (e.key) {\n case 'ArrowUp':\n e.preventDefault()\n this.incrementValue(this.getStepAmount(e), {\n direction: 1,\n currentValue,\n event: e,\n reason: 'keyboard',\n })\n this.commit('keyboard')\n break\n case 'ArrowDown':\n e.preventDefault()\n this.incrementValue(this.getStepAmount(e), {\n direction: -1,\n currentValue,\n event: e,\n reason: 'keyboard',\n })\n this.commit('keyboard')\n break\n case 'PageUp':\n e.preventDefault()\n // PageUp/Down always use largeStep regardless of modifiers.\n this.incrementValue(this.effectiveLargeStep, { direction: 1, currentValue, reason: 'keyboard' })\n this.commit('keyboard')\n break\n case 'PageDown':\n e.preventDefault()\n this.incrementValue(this.effectiveLargeStep, { direction: -1, currentValue, reason: 'keyboard' })\n this.commit('keyboard')\n break\n case 'Home':\n if (this.min != null && Number.isFinite(this.min)) {\n e.preventDefault()\n this.setValue(this.min, { reason: 'keyboard' })\n this.commit('keyboard')\n }\n break\n case 'End':\n if (this.max != null && Number.isFinite(this.max)) {\n e.preventDefault()\n this.setValue(this.max, { reason: 'keyboard' })\n this.commit('keyboard')\n }\n break\n default:\n break\n }\n }\n\n // ---------------------------------------------------------------------------\n // input / paste / focus / blur\n // ---------------------------------------------------------------------------\n\n /**\n * Whether every character in `text` could legitimately be part of a number in\n * the current locale/format, mirroring Base UI `NumberFieldInput.onChange`'s\n * `isValidCharacterString`. The keydown guard only sees physical keystrokes;\n * this catches disallowed characters arriving via IME composition,\n * drag-and-drop, or a programmatic `input.value` set, so they never land in\n * the bound value.\n */\n private isValidCharacterString(text: string): boolean {\n const allowed = this.getAllowedNonNumericKeys()\n return Array.from(text).every((ch) => {\n const isAsciiDigit = ch >= '0' && ch <= '9'\n return (\n isAsciiDigit\n || ARABIC_DETECT_RE.test(ch)\n || HAN_DETECT_RE.test(ch)\n || PERSIAN_DETECT_RE.test(ch)\n || FULLWIDTH_DETECT_RE.test(ch)\n || (this.allowsNegative && ANY_MINUS_DETECT_RE.test(ch))\n || allowed.has(ch)\n )\n })\n }\n\n private handleInput(e: Event) {\n e.stopPropagation()\n\n // While typing, don't reformat the text from underneath the user.\n this.allowInputSync = false\n\n const input = e.target as HTMLInputElement\n let text = input.value\n\n // A custom mask, when set, reshapes the raw text (grouping separators,\n // decimal-separator localisation, fraction cap, …) and tells us where the\n // caret should land afterwards.\n const mask = this.mask\n if (mask && text.trim() !== '') {\n const masked = mask(\n {\n value: text,\n selectionStart: input.selectionStart ?? text.length,\n selectionEnd: input.selectionEnd ?? text.length,\n },\n { locale: this.locale, format: this.format },\n )\n text = masked.value\n this.pendingSelection = [masked.selectionStart, masked.selectionEnd]\n }\n\n if (text.trim() === '') {\n this.inputText = ''\n this.pendingSelection = undefined\n this.setValue(null, { reason: 'input-clear' })\n return\n }\n\n // Reject input that contains characters that can never be part of a number\n // (composition / drag-drop / programmatic sets bypass the keydown guard);\n // bail before committing so disallowed characters never reach the value. A\n // mask governs character validity itself, so skip the generic guard for it.\n if (!this.mask && !this.isValidCharacterString(text)) {\n return\n }\n\n this.inputText = text\n const parsed = this.parseValue\n ? this.parseValue(text, this.locale, this.format)\n : parseNumber(text, this.locale, this.format)\n if (parsed !== null) {\n this.setValue(parsed, { reason: 'input-change' })\n }\n }\n\n private handlePaste(e: ClipboardEvent) {\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n // With a custom mask, let the paste land natively; the resulting `input`\n // event runs through the mask, which reshapes pasted text like any insertion.\n if (this.mask) {\n return\n }\n\n const pasted = e.clipboardData?.getData('text/plain') ?? ''\n // Prevent the native change so we control the parsed text.\n e.preventDefault()\n\n const parsed = this.parseValue\n ? this.parseValue(pasted, this.locale, this.format)\n : parseNumber(pasted, this.locale, this.format)\n if (parsed !== null) {\n // Apply the auto (Intl) formatting to pasted content immediately, the same\n // as on blur. `allowInputSync` lets `setValue` refresh the displayed text.\n this.allowInputSync = true\n this.setValue(parsed, { reason: 'input-paste' })\n }\n }\n\n private handleFocus(e: FocusEvent) {\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n if (this.hasTouchedInput) {\n return\n }\n this.hasTouchedInput = true\n\n // Browsers select the start by default; move the caret to the end on first focus.\n const target = e.currentTarget as HTMLInputElement\n const length = target.value.length\n target.setSelectionRange(length, length)\n }\n\n private handleBlur(e: FocusEvent) {\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n this.allowInputSync = true\n\n if (this.inputText.trim() === '') {\n this.setValue(null, { reason: 'input-clear' })\n this.commit('input-clear')\n return\n }\n\n const parsed = this.parseValue\n ? this.parseValue(this.inputText, this.locale, this.format)\n : parseNumber(this.inputText, this.locale, this.format)\n if (parsed === null) {\n // The typed text can't be parsed — e.g. stray signs or decimals the\n // lenient per-character guard let through (\"--.-1,234\"). Rather than leave\n // the garbage sitting in the field, revert the display to the last\n // committed value (its canonical format, or empty when there is none). The\n // value itself is unchanged, so no change event fires.\n const reverted = this.formatDisplay(this.value)\n if (this.inputText !== reverted) {\n this.inputText = reverted\n }\n return\n }\n\n const hasRounding = hasNumberFormatRoundingOptions(this.format)\n const committed = hasRounding ? removeFloatingPointErrors(parsed, this.format) : parsed\n\n const shouldUpdate = committed !== this.value\n if (shouldUpdate) {\n this.setValue(committed, { reason: 'input-blur' })\n }\n\n // Normalise the displayed text to the canonical formatted value.\n const canonical = this.formatDisplay(this.value)\n if (this.inputText !== canonical) {\n this.inputText = canonical\n }\n\n this.commit('input-blur')\n }\n\n // ---------------------------------------------------------------------------\n // stepper buttons (press-and-hold)\n // ---------------------------------------------------------------------------\n\n /** Sync the (possibly dirty) input text into `value` before a button press. */\n private commitDirtyInput(reason: NumberFieldChangeReason, direction: 1 | -1) {\n this.allowInputSync = true\n const parsed = this.parseValue\n ? this.parseValue(this.inputText, this.locale, this.format)\n : parseNumber(this.inputText, this.locale, this.format)\n if (parsed !== null) {\n this.setValue(parsed, { reason, direction })\n }\n }\n\n private handleStepPointerDown(e: PointerEvent, direction: 1 | -1) {\n const isMainButton = !e.button || e.button === 0\n if (e.defaultPrevented || this.readonly || this.disabled || !isMainButton) {\n return\n }\n\n const reason: NumberFieldChangeReason\n = direction === 1 ? 'increment-press' : 'decrement-press'\n\n // Pointerdown handles the change here, so suppress the follow-up click\n // (including the synthetic click browsers fire after touch).\n this.skipNextClick = true\n this.activePointerId = e.pointerId\n\n // Sync the dirty input value before stepping.\n this.commitDirtyInput(reason, direction)\n\n if (e.pointerType !== 'touch' && e.pointerType !== 'pen') {\n // Focus the input so keyboard interaction can continue.\n this.inputRef.value?.focus()\n }\n\n // One immediate step, then auto-repeat after the start delay.\n this.allowInputSync = true\n this.incrementValue(this.getStepAmount(e), { direction, event: e, reason })\n\n const target = e.currentTarget as HTMLButtonElement\n try {\n target.setPointerCapture(e.pointerId)\n }\n catch {\n // Ignore: capture isn't essential, pointerup is still observed.\n }\n\n this.holdStartTimer = setTimeout(() => {\n this.holdTickTimer = setInterval(() => {\n this.allowInputSync = true\n this.incrementValue(this.getStepAmount(e), { direction, event: e, reason })\n }, CHANGE_VALUE_TICK_DELAY)\n }, START_AUTO_CHANGE_DELAY)\n }\n\n private handleStepPointerStop(e: PointerEvent, direction: 1 | -1) {\n // Ignore stop events from a different pointer than the one that started.\n if (this.activePointerId !== undefined && this.activePointerId !== e.pointerId) {\n return\n }\n const wasHolding = this.holdStartTimer != null || this.holdTickTimer != null\n this.stopHold()\n this.activePointerId = undefined\n\n // Commit on pointer-up if the pointer press drove the change.\n if (this.skipNextClick || wasHolding) {\n this.commit(direction === 1 ? 'increment-press' : 'decrement-press')\n }\n }\n\n private handleStepClick(e: MouseEvent, direction: 1 | -1) {\n const reason: NumberFieldChangeReason\n = direction === 1 ? 'increment-press' : 'decrement-press'\n\n if (e.defaultPrevented || this.readonly || this.disabled) {\n return\n }\n\n // Pointerdown already handled the change; this suppresses the follow-up click.\n if (this.skipNextClick) {\n this.skipNextClick = false\n return\n }\n\n this.commitDirtyInput(reason, direction)\n this.allowInputSync = true\n const changed = this.incrementValue(this.getStepAmount(e as StepEventLike), {\n direction,\n event: e as StepEventLike,\n reason,\n })\n if (changed) {\n this.commit(reason)\n }\n }\n\n private stopHold() {\n if (this.holdStartTimer != null) {\n clearTimeout(this.holdStartTimer)\n this.holdStartTimer = undefined\n }\n if (this.holdTickTimer != null) {\n clearInterval(this.holdTickTimer)\n this.holdTickTimer = undefined\n }\n }\n\n /** Whether the increment button should be disabled. */\n private get incrementDisabled(): boolean {\n return (\n this.disabled\n || this.readonly\n || (this.value != null && this.value >= this.maxWithDefault)\n )\n }\n\n /** Whether the decrement button should be disabled. */\n private get decrementDisabled(): boolean {\n return (\n this.disabled\n || this.readonly\n || (this.value != null && this.value <= this.minWithDefault)\n )\n }\n\n // ---------------------------------------------------------------------------\n // wheel\n // ---------------------------------------------------------------------------\n\n private attachWheel() {\n const input = this.inputRef.value\n if (!input) {\n return\n }\n input.addEventListener('wheel', this.onWheel, { passive: false })\n this.wheelAttached = true\n }\n\n private detachWheel() {\n const input = this.inputRef.value\n input?.removeEventListener('wheel', this.onWheel)\n this.wheelAttached = false\n }\n\n private handleWheel(e: WheelEvent) {\n // Allow pinch-zoom (ctrlKey) and ignore when the input isn't focused.\n if (e.ctrlKey || this.inputRef.value !== this.getActiveElement()) {\n return\n }\n e.preventDefault()\n this.allowInputSync = true\n const amount = this.getStepAmount(e as StepEventLike)\n this.incrementValue(amount, {\n direction: e.deltaY > 0 ? -1 : 1,\n event: e as StepEventLike,\n reason: 'wheel',\n })\n }\n\n /** The active element within this node's root (document or shadow root). */\n private getActiveElement(): Element | null {\n const root = this.getRootNode() as Document | ShadowRoot\n return root.activeElement\n }\n\n // ---------------------------------------------------------------------------\n // scrub\n //\n // NOTE: this is a SIMPLIFIED port of Base UI's ScrubArea. Pointer Lock, the\n // virtual cursor, and teleport are intentionally OUT OF SCOPE — this is a\n // basic relative drag (movementX * step, gated by pixelSensitivity), like the\n // combobox's \"no virtualization\" note.\n // ---------------------------------------------------------------------------\n\n private handleScrubPointerDown(e: PointerEvent) {\n const isMainButton = !e.button || e.button === 0\n if (e.defaultPrevented || this.readonly || this.disabled || !isMainButton) {\n return\n }\n\n if (e.pointerType === 'mouse') {\n e.preventDefault()\n this.inputRef.value?.focus()\n }\n\n this.scrubbing = true\n this.scrubMoved = false\n this.scrubAccumulator = 0\n this.scrubPointerDownTarget = e.target\n\n const win = this.ownerWindow()\n win.addEventListener('pointermove', this.onScrubMove, true)\n win.addEventListener('pointerup', this.onScrubUp, true)\n }\n\n private handleScrubMove(e: PointerEvent) {\n if (!this.scrubbing) {\n return\n }\n // Prevent text selection while dragging.\n e.preventDefault()\n\n this.scrubAccumulator += e.movementX\n\n if (Math.abs(this.scrubAccumulator) >= SCRUB_PIXEL_SENSITIVITY) {\n this.scrubAccumulator = 0\n this.scrubMoved = true\n const dValue = e.movementX\n const amount = Math.abs(dValue * this.getStepAmount(e as StepEventLike))\n if (amount !== 0) {\n this.allowInputSync = true\n this.incrementValue(amount, {\n direction: dValue >= 0 ? 1 : -1,\n event: e as StepEventLike,\n reason: 'scrub',\n })\n }\n }\n }\n\n private handleScrubUp(e: PointerEvent) {\n this.removeScrubListeners()\n this.scrubbing = false\n this.commit('scrub')\n\n // If no movement happened, synthesise a click on the original target so a\n // click-without-drag still focuses the input.\n if (!this.scrubMoved && this.scrubPointerDownTarget) {\n this.scrubPointerDownTarget.dispatchEvent(\n new MouseEvent('click', { bubbles: true, cancelable: true }),\n )\n }\n this.scrubMoved = false\n this.scrubPointerDownTarget = null\n }\n\n private removeScrubListeners() {\n const win = this.ownerWindow()\n win.removeEventListener('pointermove', this.onScrubMove, true)\n win.removeEventListener('pointerup', this.onScrubUp, true)\n }\n\n private handleScrubTouchStart(e: TouchEvent) {\n if (this.disabled || this.readonly) {\n return\n }\n if (e.touches.length === 1) {\n // Stop the page scrolling while dragging.\n e.preventDefault()\n }\n }\n\n private ownerWindow(): Window {\n return this.ownerDocument.defaultView ?? window\n }\n\n // ---------------------------------------------------------------------------\n // render\n // ---------------------------------------------------------------------------\n\n /**\n * The `inputmode` for the inner input.\n *\n * Mirrors Base UI's `setDynamicInputModeForIOS` intent: the iOS numeric and\n * decimal soft keyboards have no minus key, so when negatives are explicitly\n * allowed (an explicit `min < 0`) we yield `text` so the user can type a\n * minus sign on iOS. Otherwise the base mode is `numeric` when step/min/max\n * are all integers and min >= 0, and `decimal` when fractional values are in\n * play (so the iOS keyboard exposes a decimal point).\n *\n * Base UI swaps to `text`/`decimal` at runtime only on iOS; we compute the\n * mode statically, and `text`/`decimal` are harmless supersets on platforms\n * whose numeric keyboard already has the relevant keys. We gate the `text`\n * mode on the explicit `min` prop (not the `Number.MIN_SAFE_INTEGER`-backed\n * `minWithDefault`) so an unconstrained field keeps the friendlier `numeric`\n * keypad, matching Base UI's default of `numeric`.\n */\n private get computedInputMode(): 'numeric' | 'decimal' | 'text' {\n // iOS numeric/decimal keyboards lack a minus key; fall back to text so a\n // negative value can be entered when negatives are explicitly allowed.\n if (this.min != null && this.min < 0) {\n return 'text'\n }\n // Whole-number fields never accept a decimal point, so the numeric keypad\n // (no decimal key) is always the right soft keyboard.\n if (this.integer) {\n return 'numeric'\n }\n const stepIsInt = this.step === 'any' || Number.isInteger(this.numericStep)\n const minOk = this.min == null || (Number.isInteger(this.min) && this.min >= 0)\n const maxOk = this.max == null || Number.isInteger(this.max)\n return stepIsInt && minOk && maxOk ? 'numeric' : 'decimal'\n }\n\n /**\n * The resolved side for the `currency` symbol: the explicit `currencyPlacement`\n * when it is `'start'`/`'end'`, otherwise derived from the locale/currency via\n * `Intl`. Only meaningful when `currency` is set.\n */\n private get resolvedCurrencyPlacement(): 'start' | 'end' {\n if (this.currencyPlacement === 'start' || this.currencyPlacement === 'end') {\n return this.currencyPlacement\n }\n return this.currency ? resolveCurrencyPlacement(this.locale, this.currency) : 'start'\n }\n\n render() {\n const inputId = this.inputId\n\n // Naming/description from the field context (in a field), the built-in\n // <label for>/hint/error (standalone), or consumer-set host aria-* — all\n // resolved by the FieldControlMixin getters.\n const ariaLabel = this.controlAriaLabel\n const ariaLabelledby = this.controlAriaLabelledby\n\n // Currency convenience: a decorative, locale-aware symbol placed in the\n // resolved slot. It is `aria-hidden` (the number lives in the input's text),\n // so we fold its id into `aria-describedby` — a directly-referenced element\n // still contributes to the accessible description even when hidden — so\n // assistive tech announces the currency alongside the value. This is the\n // non-destructive analogue of NFE's aria-label composition: it works in every\n // labelling mode (field label, `aria-label`, `label` prop) without clobbering\n // the accessible name.\n const currencySymbol = this.currency ? getCurrencySymbol(this.locale, this.currency) : undefined\n const currencyPlacement = this.currency ? this.resolvedCurrencyPlacement : undefined\n const currencySymbolId = this.currency ? `${inputId}-currency` : undefined\n const ariaDescribedby\n = [this.controlAriaDescribedby, currencySymbolId].filter(Boolean).join(' ') || undefined\n\n const renderCurrencySymbol = (side: 'start' | 'end') =>\n currencySymbol != null && currencyPlacement === side\n ? html`<span id=${currencySymbolId} class=\"n-currency-symbol\" aria-hidden=\"true\">${currencySymbol}</span>`\n : nothing\n\n return html`\n ${this.renderLabel()}\n\n <div\n class=${classMap({\n 'n-number-field': true,\n 'is-scrubbing': this.scrubbing,\n })}\n >\n ${this.scrub\n ? html`\n <!-- A drag-to-scrub handle. It is purely a pointer affordance\n (role=\"presentation\"); the field's accessible name still comes from\n the external nord-field-label pointed at the input id, so keyboard\n and AT users are unaffected. The grip mirrors Base UI's ew-resize\n cursor icon. -->\n <div\n class=\"n-scrub\"\n role=\"presentation\"\n aria-hidden=\"true\"\n @pointerdown=${this.handleScrubPointerDown}\n @touchstart=${this.handleScrubTouchStart}\n >\n <svg\n class=\"n-scrub-grip\"\n width=\"24\"\n height=\"14\"\n viewBox=\"0 0 24 14\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M19.5 5.5L6.49737 5.51844V2L1 6.9999L6.5 12L6.49737 8.5L19.5 8.5V12L25 6.9999L19.5 2V5.5Z\"\n ></path>\n </svg>\n </div>\n `\n : nothing}\n\n <div class=${classMap({ 'n-control': true, 'is-invalid': this.effectiveInvalid })}>\n ${this.hideControls\n ? nothing\n : html`\n <button\n class=${classMap({ 'n-step': true, 'n-decrement': true })}\n type=\"button\"\n tabindex=\"-1\"\n aria-label=${this.localize.term('decreaseLabel')}\n aria-controls=${inputId}\n aria-readonly=${this.readonly ? 'true' : nothing}\n ?disabled=${this.decrementDisabled}\n @pointerdown=${(e: PointerEvent) => this.handleStepPointerDown(e, -1)}\n @pointerup=${(e: PointerEvent) => this.handleStepPointerStop(e, -1)}\n @pointerleave=${(e: PointerEvent) => this.handleStepPointerStop(e, -1)}\n @pointercancel=${(e: PointerEvent) => this.handleStepPointerStop(e, -1)}\n @click=${(e: MouseEvent) => this.handleStepClick(e, -1)}\n >\n <nord-icon name=\"interface-remove-small\"></nord-icon>\n </button>\n `}\n\n <span class=\"n-number-field-slot n-number-field-slot-start\">${renderCurrencySymbol('start')}</span>\n\n <input\n ${ref(this.inputRef)}\n class=\"n-input\"\n type=\"text\"\n id=${inputId}\n inputmode=${this.computedInputMode}\n .value=${this.inputText}\n placeholder=${this.placeholder ?? nothing}\n ?disabled=${this.effectiveDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n autocomplete=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\"\n aria-roledescription=${this.localize.term('roleDescription')}\n aria-label=${ariaLabel ?? nothing}\n aria-labelledby=${ariaLabelledby ?? nothing}\n aria-describedby=${ariaDescribedby ?? nothing}\n aria-invalid=${this.controlAriaInvalid ?? nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-readonly=${this.readonly ? 'true' : nothing}\n @input=${this.handleInput}\n @blur=${this.handleBlur}\n @focus=${this.handleFocus}\n @keydown=${this.handleKeydown}\n @paste=${this.handlePaste}\n />\n\n <span class=\"n-number-field-slot n-number-field-slot-end\">${renderCurrencySymbol('end')}</span>\n\n ${this.hideControls\n ? nothing\n : html`\n <button\n class=${classMap({ 'n-step': true, 'n-increment': true })}\n type=\"button\"\n tabindex=\"-1\"\n aria-label=${this.localize.term('increaseLabel')}\n aria-controls=${inputId}\n aria-readonly=${this.readonly ? 'true' : nothing}\n ?disabled=${this.incrementDisabled}\n @pointerdown=${(e: PointerEvent) => this.handleStepPointerDown(e, 1)}\n @pointerup=${(e: PointerEvent) => this.handleStepPointerStop(e, 1)}\n @pointerleave=${(e: PointerEvent) => this.handleStepPointerStop(e, 1)}\n @pointercancel=${(e: PointerEvent) => this.handleStepPointerStop(e, 1)}\n @click=${(e: MouseEvent) => this.handleStepClick(e, 1)}\n >\n <nord-icon name=\"interface-add-small\"></nord-icon>\n </button>\n `}\n </div>\n </div>\n\n ${this.renderError()}\n ${this.isHintBelow ? this.renderHint() : nothing}\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nord-number-field': NumberField\n }\n}\n"],"names":["Icon","registerIcon","addIcon","removeIcon","numberFieldCount","sanitizeStep","value","fallback","Number","isFinite","NumberField","DraftComponentMixin","SizeMixin","FieldControlMixin","LitElement","constructor","this","numberFieldId","inputId","localize","LocalizeController","inputRef","createRef","formData","FormDataController","undefined","String","stateAttributes","StateAttributesController","invalid","effectiveInvalid","trackFocus","formAncestor","step","largeStep","smallStep","snapOnStep","allowOutOfRange","integer","allowWheelScrub","scrub","disabled","readonly","hideControls","currencyPlacement","inputText","scrubbing","allowInputSync","hasTouchedInput","skipNextClick","scrubAccumulator","scrubMoved","scrubPointerDownTarget","onScrubMove","e","handleScrubMove","onScrubUp","handleScrubUp","onWheel","handleWheel","wheelAttached","ownInvalid","super","form","id","oldVal","_formId","requestUpdate","getRootNode","querySelector","minWithDefault","_a","min","MIN_SAFE_INTEGER","maxWithDefault","max","MAX_SAFE_INTEGER","minWithZeroDefault","allowsNegative","numericStep","effectiveLargeStep","effectiveSmallStep","formatDisplay","formatValue","locale","format","formatNumber","connectedCallback","injectLightDomStyles","style","closest","ariaObserver","MutationObserver","observe","attributes","attributeFilter","_b","slotObserver","childList","disconnectedCallback","disconnect","stopHold","detachWheel","removeScrubListeners","createRenderRoot","isServer","root","document","createElement","className","appendChild","relocateSlots","move","name","target","child","Array","from","children","getAttribute","parentElement","startZone","endZone","willUpdate","changed","has","clamped","clamp","isInteger","Math","round","updated","wheelWanted","attachWheel","pendingSelection","start","end","setSelectionRange","focus","options","isTextReason","reason","getStepAmount","event","altKey","shiftKey","setValue","unvalidated","direction","shouldClamp","validated","toValidatedNumber","small","shouldFire","dispatchEvent","NordEvent","incrementValue","amount","currentValue","prev","next","commit","NumberFieldChangeEvent","getAllowedNonNumericKeys","decimal","group","currency","literal","getNumberLocaleDetails","keys","Set","BASE_NON_NUMERIC_SYMBOLS","forEach","symbol","add","SPACE_SEPARATOR_RE","test","formatStyle","allowPercent","unit","allowPermille","_c","PERCENTAGES","key","PERMILLE","char","PLUS_SIGNS_WITH_ASCII","MINUS_SIGNS_WITH_ASCII","separator","delete","handleKeydown","defaultPrevented","mask","allowed","isAsciiDigit","isNamedKey","length","which","ctrlKey","metaKey","ARABIC_DETECT_RE","PERSIAN_DETECT_RE","HAN_DETECT_RE","FULLWIDTH_DETECT_RE","ANY_MINUS_DETECT_RE","ANY_PLUS_DETECT_RE","preventDefault","parseValue","parseNumber","isValidCharacterString","text","every","ch","handleInput","stopPropagation","input","trim","masked","selectionStart","selectionEnd","parsed","handlePaste","pasted","clipboardData","getData","handleFocus","currentTarget","handleBlur","reverted","committed","hasNumberFormatRoundingOptions","removeFloatingPointErrors","canonical","commitDirtyInput","handleStepPointerDown","isMainButton","button","activePointerId","pointerId","pointerType","setPointerCapture","holdStartTimer","setTimeout","holdTickTimer","setInterval","handleStepPointerStop","wasHolding","handleStepClick","clearTimeout","clearInterval","incrementDisabled","decrementDisabled","addEventListener","passive","removeEventListener","getActiveElement","deltaY","activeElement","handleScrubPointerDown","win","ownerWindow","movementX","abs","dValue","MouseEvent","bubbles","cancelable","handleScrubTouchStart","touches","ownerDocument","defaultView","window","computedInputMode","stepIsInt","minOk","maxOk","resolvedCurrencyPlacement","resolveCurrencyPlacement","render","ariaLabel","controlAriaLabel","ariaLabelledby","controlAriaLabelledby","currencySymbol","getCurrencySymbol","currencySymbolId","ariaDescribedby","controlAriaDescribedby","filter","Boolean","join","renderCurrencySymbol","side","html","nothing","renderLabel","classMap","term","ref","placeholder","effectiveDisabled","required","controlAriaInvalid","renderError","isHintBelow","renderHint","__decorate","query","prototype","property","converter","attr","num","isNaN","reflect","type","attribute","state","customElement"],"mappings":"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"}
|