@spectrum-web-components/number-field
Advanced tools
Comparing version 0.1.3-alpha.1 to 0.1.3
@@ -10,3 +10,3 @@ { | ||
"name": "format-options", | ||
"description": "An `<sp-number-field>` element will process its numeric value with\n`new Intl.NumberFormat(navigator.language, this.formatOptions).format(this.valueAsNumber)`\nin order to prepare it for visual delivery in the input. In order to customize this\nprocessing supply your own `Intl.NumberFormatOptions` object here.\n\nSee: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat", | ||
"description": "An `<sp-number-field>` element will process its numeric value with\n`new Intl.NumberFormat(this.resolvedLanguage, this.formatOptions).format(this.valueAsNumber)`\nin order to prepare it for visual delivery in the input. In order to customize this\nprocessing supply your own `Intl.NumberFormatOptions` object here.\n\nSee: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat", | ||
"type": "NumberFormatOptions", | ||
@@ -40,2 +40,7 @@ "default": "{}" | ||
{ | ||
"name": "step-modifier", | ||
"type": "number", | ||
"default": "10" | ||
}, | ||
{ | ||
"name": "allowed-keys", | ||
@@ -128,3 +133,3 @@ "type": "string", | ||
"attribute": "format-options", | ||
"description": "An `<sp-number-field>` element will process its numeric value with\n`new Intl.NumberFormat(navigator.language, this.formatOptions).format(this.valueAsNumber)`\nin order to prepare it for visual delivery in the input. In order to customize this\nprocessing supply your own `Intl.NumberFormatOptions` object here.\n\nSee: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat", | ||
"description": "An `<sp-number-field>` element will process its numeric value with\n`new Intl.NumberFormat(this.resolvedLanguage, this.formatOptions).format(this.valueAsNumber)`\nin order to prepare it for visual delivery in the input. In order to customize this\nprocessing supply your own `Intl.NumberFormatOptions` object here.\n\nSee: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat", | ||
"type": "NumberFormatOptions", | ||
@@ -163,2 +168,8 @@ "default": "{}" | ||
{ | ||
"name": "stepModifier", | ||
"attribute": "step-modifier", | ||
"type": "number", | ||
"default": "10" | ||
}, | ||
{ | ||
"name": "valueAsString", | ||
@@ -283,2 +294,5 @@ "description": "Retreive the value of the element parsed to a Number.", | ||
"name": "input" | ||
}, | ||
{ | ||
"name": "sp-language-context" | ||
} | ||
@@ -285,0 +299,0 @@ ] |
{ | ||
"name": "@spectrum-web-components/number-field", | ||
"version": "0.1.3-alpha.1+23ae605e", | ||
"version": "0.1.3", | ||
"publishConfig": { | ||
@@ -48,11 +48,11 @@ "access": "public" | ||
"@internationalized/number": "^3.0.0", | ||
"@spectrum-web-components/action-button": "^0.5.3-alpha.1+23ae605e", | ||
"@spectrum-web-components/base": "^0.4.5-alpha.1+23ae605e", | ||
"@spectrum-web-components/icon": "^0.9.8-alpha.1+23ae605e", | ||
"@spectrum-web-components/icons-ui": "^0.6.8-alpha.1+23ae605e", | ||
"@spectrum-web-components/textfield": "^0.8.10-alpha.1+23ae605e", | ||
"@spectrum-web-components/action-button": "^0.5.3", | ||
"@spectrum-web-components/base": "^0.4.4", | ||
"@spectrum-web-components/icon": "^0.9.7", | ||
"@spectrum-web-components/icons-ui": "^0.6.7", | ||
"@spectrum-web-components/textfield": "^0.8.10", | ||
"tslib": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"@formatjs/intl-numberformat": "^7.1.0", | ||
"@formatjs/intl-numberformat": "^7.1.4", | ||
"@spectrum-css/stepper": "^3.0.3" | ||
@@ -65,3 +65,3 @@ }, | ||
], | ||
"gitHead": "23ae605e3718dd3f86bc3aa5b10229a4eb7f83c9" | ||
"gitHead": "ed13341debd82f86abc062f647d42458c77633cc" | ||
} |
## Description | ||
`<sp-number-field>`s are used for numeric inputs. Upon interaction, the input value incrementally increases or decreases. | ||
`<sp-number-field>` elements are used for numeric inputs. Upon interaction via the <kbd>ArrowUp</kbd> or <kbd>ArrowDown</kbd> keys, the scroll wheel, or the stepper UI, when not hidden by the `hide-stepper` attribute, the input value incrementally increases or decreases by the value of the `step` attribute. The <kbd>shift</kbd> key can be used to apply steps at 10 time (or the value of the `step-modifier` attribute times) their normal rate. | ||
@@ -35,4 +35,6 @@ ### Usage | ||
An `<sp-number-field>` element will process its numeric value with `new Intl.NumberFormat(navigator.language, this.formatOptions).format(this.value)` in order to prepare it for visual delivery in the input. In order to customize this processing supply your own `Intl.NumberFormatOptions` via the `formatOptions` property, or `format-options` attribute as follows. | ||
An `<sp-number-field>` element will process its numeric value with `new Intl.NumberFormat(this.resolvedLanguage, this.formatOptions).format(this.value)` in order to prepare it for visual delivery in the input. In order to customize this processing supply your own `Intl.NumberFormatOptions` via the `formatOptions` property, or `format-options` attribute as seen below. | ||
`this.resolvedLanguage` represents the language in which the `<sp-number-field>` element is currently being delivered. By default, this value will represent the language established by the `lang` attribute on the root `<html>` element while falling back to `navigator.language` when that is not present. This value can be customized via a language context provided by a parent element that listens for the `sp-language-context` event and supplies update language settings to the `callback` function contained therein. Applications leveraging the [`<sp-theme>`](./components/theme) element to manage the visual delivery or text direction of their content will be also be provided a reactive context for supplying language information to its descendants. | ||
### Decimals | ||
@@ -39,0 +41,0 @@ |
@@ -1,2 +0,2 @@ | ||
declare const styles: import("@spectrum-web-components/base").CSSResultGroup; | ||
declare const styles: import("@spectrum-web-components/base").CSSResult; | ||
export default styles; |
import { CSSResultArray, TemplateResult, PropertyValues } from '@spectrum-web-components/base'; | ||
import { NumberFormatter, NumberParser } from '@internationalized/number'; | ||
import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron75.js'; | ||
@@ -15,3 +16,3 @@ import '@spectrum-web-components/action-button/sp-action-button.js'; | ||
* An `<sp-number-field>` element will process its numeric value with | ||
* `new Intl.NumberFormat(navigator.language, this.formatOptions).format(this.valueAsNumber)` | ||
* `new Intl.NumberFormat(this.resolvedLanguage, this.formatOptions).format(this.valueAsNumber)` | ||
* in order to prepare it for visual delivery in the input. In order to customize this | ||
@@ -30,2 +31,3 @@ * processing supply your own `Intl.NumberFormatOptions` object here. | ||
min?: number; | ||
private resolvedLanguage; | ||
/** | ||
@@ -38,2 +40,3 @@ * The distance by which to alter the value of the element when taking a "step". | ||
step?: number; | ||
stepModifier: number; | ||
set value(value: number); | ||
@@ -74,5 +77,14 @@ get value(): number; | ||
protected get displayValue(): string; | ||
protected clearNumberFormatterCache(): void; | ||
protected get numberFormatter(): NumberFormatter; | ||
private _numberFormatter?; | ||
protected get numberParser(): NumberParser; | ||
private _numberParser?; | ||
protected render(): TemplateResult; | ||
protected update(changes: PropertyValues): void; | ||
protected firstUpdated(changes: PropertyValues): void; | ||
protected updated(changes: PropertyValues<this>): void; | ||
connectedCallback(): void; | ||
disconnectedCallback(): void; | ||
private resolveLanguage; | ||
} |
@@ -49,3 +49,3 @@ /* | ||
* An `<sp-number-field>` element will process its numeric value with | ||
* `new Intl.NumberFormat(navigator.language, this.formatOptions).format(this.valueAsNumber)` | ||
* `new Intl.NumberFormat(this.resolvedLanguage, this.formatOptions).format(this.valueAsNumber)` | ||
* in order to prepare it for visual delivery in the input. In order to customize this | ||
@@ -62,2 +62,4 @@ * processing supply your own `Intl.NumberFormatOptions` object here. | ||
this.keyboardFocused = false; | ||
this.resolvedLanguage = document.documentElement.lang || navigator.language; | ||
this.stepModifier = 10; | ||
this._value = NaN; | ||
@@ -87,3 +89,3 @@ this.changeCount = 0; | ||
set valueAsString(value) { | ||
this.value = new NumberParser(navigator.language, this.formatOptions).parse(value); | ||
this.value = this.numberParser.parse(value); | ||
} | ||
@@ -93,6 +95,6 @@ get formattedValue() { | ||
return ''; | ||
return new NumberFormatter(navigator.language, this.formatOptions).format(this.value); | ||
return this.numberFormatter.format(this.value); | ||
} | ||
convertValueToNumber(value) { | ||
return new NumberParser(navigator.language, this.formatOptions).parse(value); | ||
return this.numberParser.parse(value); | ||
} | ||
@@ -122,3 +124,3 @@ get _step() { | ||
event.clientY <= stepUpRect.y + stepUpRect.height) { | ||
this.change = () => this.increment(); | ||
this.change = (event) => this.increment(event.shiftKey ? this.stepModifier : 1); | ||
} | ||
@@ -129,17 +131,17 @@ else if (event.clientX >= stepDownRect.x && | ||
event.clientY <= stepDownRect.y + stepDownRect.height) { | ||
this.change = () => this.decrement(); | ||
this.change = (event) => this.decrement(event.shiftKey ? this.stepModifier : 1); | ||
} | ||
}; | ||
this.findChange(event); | ||
this.startChange(); | ||
this.startChange(event); | ||
} | ||
startChange() { | ||
startChange(event) { | ||
this.changeCount = 0; | ||
this.doChange(); | ||
this.doChange(event); | ||
this.safty = setTimeout(() => { | ||
this.doNextChange(); | ||
this.doNextChange(event); | ||
}, 400); | ||
} | ||
doChange() { | ||
this.change(); | ||
doChange(event) { | ||
this.change(event); | ||
} | ||
@@ -155,9 +157,9 @@ handlePointermove(event) { | ||
} | ||
doNextChange() { | ||
doNextChange(event) { | ||
this.changeCount += 1; | ||
if (this.changeCount % FRAMES_PER_CHANGE === 0) { | ||
this.doChange(); | ||
this.doChange(event); | ||
} | ||
return requestAnimationFrame(() => { | ||
this.nextChange = this.doNextChange(); | ||
this.nextChange = this.doNextChange(event); | ||
}); | ||
@@ -178,17 +180,13 @@ } | ||
} | ||
increment() { | ||
this.stepBy(1); | ||
increment(factor = 1) { | ||
this.stepBy(1 * factor); | ||
} | ||
decrement() { | ||
this.stepBy(-1); | ||
decrement(factor = 1) { | ||
this.stepBy(-1 * factor); | ||
} | ||
handleKeydown(event) { | ||
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) { | ||
// Don't do work when modifiers are present. | ||
return; | ||
} | ||
switch (event.code) { | ||
case 'ArrowUp': | ||
event.preventDefault(); | ||
this.increment(); | ||
this.increment(event.shiftKey ? this.stepModifier : 1); | ||
this.dispatchEvent(new Event('change', { bubbles: true, composed: true })); | ||
@@ -198,3 +196,3 @@ break; | ||
event.preventDefault(); | ||
this.decrement(); | ||
this.decrement(event.shiftKey ? this.stepModifier : 1); | ||
this.dispatchEvent(new Event('change', { bubbles: true, composed: true })); | ||
@@ -206,3 +204,8 @@ break; | ||
event.preventDefault(); | ||
this.stepBy(event.deltaY); | ||
const direction = event.shiftKey | ||
? event.deltaX / Math.abs(event.deltaX) | ||
: event.deltaY / Math.abs(event.deltaY); | ||
if (direction !== 0 && !isNaN(direction)) { | ||
this.stepBy(direction * (event.shiftKey ? this.stepModifier : 1)); | ||
} | ||
} | ||
@@ -260,10 +263,3 @@ onFocus() { | ||
if (typeof this.max !== 'undefined') { | ||
if (typeof this.step !== 'undefined') { | ||
while (value > this.max) { | ||
value -= this.step; | ||
} | ||
} | ||
else { | ||
value = Math.min(this.max, value); | ||
} | ||
value = Math.min(this.max, value); | ||
} | ||
@@ -275,2 +271,18 @@ return value; | ||
} | ||
clearNumberFormatterCache() { | ||
this._numberFormatter = undefined; | ||
this._numberParser = undefined; | ||
} | ||
get numberFormatter() { | ||
if (!this._numberFormatter) { | ||
this._numberFormatter = new NumberFormatter(this.resolvedLanguage, this.formatOptions); | ||
} | ||
return this._numberFormatter; | ||
} | ||
get numberParser() { | ||
if (!this._numberParser) { | ||
this._numberParser = new NumberParser(this.resolvedLanguage, this.formatOptions); | ||
} | ||
return this._numberParser; | ||
} | ||
render() { | ||
@@ -287,18 +299,17 @@ this.autocomplete = 'off'; | ||
@focusout=${this.handleFocusout} | ||
${streamingListener({ | ||
start: ['pointerdown', this.handlePointerdown], | ||
streamInside: [ | ||
[ | ||
'pointermove', | ||
'pointerenter', | ||
'pointerleave', | ||
'pointerover', | ||
'pointerout', | ||
], | ||
this.handlePointermove, | ||
@manage=${streamingListener({ | ||
type: 'pointerdown', | ||
fn: this.handlePointerdown, | ||
}, { | ||
type: [ | ||
'pointermove', | ||
'pointerenter', | ||
'pointerleave', | ||
'pointerover', | ||
'pointerout', | ||
], | ||
end: [ | ||
['pointerup', 'pointercancel'], | ||
this.handlePointerup, | ||
], | ||
fn: this.handlePointermove, | ||
}, { | ||
type: ['pointerup', 'pointercancel'], | ||
fn: this.handlePointerup, | ||
})} | ||
@@ -340,2 +351,8 @@ > | ||
} | ||
update(changes) { | ||
if (changes.has('formatOptions') || changes.has('resolvedLanguage')) { | ||
this.clearNumberFormatterCache(); | ||
} | ||
super.update(changes); | ||
} | ||
firstUpdated(changes) { | ||
@@ -351,3 +368,3 @@ super.firstUpdated(changes); | ||
changes.has('min')) { | ||
const value = new NumberParser(navigator.language, this.formatOptions).parse(this.inputElement.value); | ||
const value = this.numberParser.parse(this.inputElement.value); | ||
this.value = this.validateInput(value); | ||
@@ -360,2 +377,3 @@ } | ||
const hasDecimals = maximumFractionDigits && maximumFractionDigits > 0; | ||
/* c8 ignore next 18 */ | ||
if (isIPhone()) { | ||
@@ -385,2 +403,23 @@ // iPhone doesn't have a minus sign in either numeric or decimal. | ||
} | ||
connectedCallback() { | ||
super.connectedCallback(); | ||
this.resolveLanguage(); | ||
} | ||
disconnectedCallback() { | ||
this.resolveLanguage(); | ||
super.disconnectedCallback(); | ||
} | ||
resolveLanguage() { | ||
const queryThemeEvent = new CustomEvent('sp-language-context', { | ||
bubbles: true, | ||
composed: true, | ||
detail: { | ||
callback: (lang) => { | ||
this.resolvedLanguage = lang; | ||
}, | ||
}, | ||
cancelable: true, | ||
}); | ||
this.dispatchEvent(queryThemeEvent); | ||
} | ||
} | ||
@@ -409,7 +448,13 @@ __decorate([ | ||
__decorate([ | ||
property({ attribute: false }) | ||
], NumberField.prototype, "resolvedLanguage", void 0); | ||
__decorate([ | ||
property({ type: Number }) | ||
], NumberField.prototype, "step", void 0); | ||
__decorate([ | ||
property({ type: Number, reflect: true, attribute: 'step-modifier' }) | ||
], NumberField.prototype, "stepModifier", void 0); | ||
__decorate([ | ||
property({ type: Number }) | ||
], NumberField.prototype, "value", null); | ||
//# sourceMappingURL=NumberField.js.map |
@@ -1,2 +0,2 @@ | ||
declare const styles: import("@spectrum-web-components/base").CSSResultGroup; | ||
declare const styles: import("@spectrum-web-components/base").CSSResult; | ||
export default styles; |
@@ -102,2 +102,14 @@ /* | ||
}, | ||
stepModifier: { | ||
name: 'step modifier', | ||
type: { name: 'number', required: false }, | ||
description: 'Amount to scale the step increment/decrement when holding the shift key', | ||
table: { | ||
type: { summary: 'number' }, | ||
defaultValue: { summary: 10 }, | ||
}, | ||
control: { | ||
type: 'number', | ||
}, | ||
}, | ||
placeholder: { | ||
@@ -104,0 +116,0 @@ name: 'placeholder', |
@@ -68,2 +68,3 @@ /* | ||
await import('@formatjs/intl-numberformat/locale-data/en.js'); | ||
await import('@formatjs/intl-numberformat/locale-data/fr.js'); | ||
} | ||
@@ -76,15 +77,44 @@ }); | ||
}); | ||
it('receives input', async () => { | ||
const el = await getElFrom(Default({ value: 1337 })); | ||
expect(el.formattedValue).to.equal('1,337'); | ||
expect(el.valueAsString).to.equal('1337'); | ||
expect(el.value).to.equal(1337); | ||
el.focus(); | ||
await sendKeys({ type: '7331' }); | ||
await elementUpdated(el); | ||
await sendKeys({ press: 'Enter' }); | ||
await elementUpdated(el); | ||
expect(el.formattedValue).to.equal('13,377,331'); | ||
expect(el.valueAsString).to.equal('13377331'); | ||
expect(el.value).to.equal(13377331); | ||
describe('receives input', () => { | ||
it('without language context', async () => { | ||
const el = await getElFrom(Default({ value: 1337 })); | ||
expect(el.formattedValue).to.equal('1,337'); | ||
expect(el.valueAsString).to.equal('1337'); | ||
expect(el.value).to.equal(1337); | ||
el.focus(); | ||
await sendKeys({ type: '7331' }); | ||
await elementUpdated(el); | ||
await sendKeys({ press: 'Enter' }); | ||
await elementUpdated(el); | ||
expect(el.formattedValue).to.equal('13,377,331'); | ||
expect(el.valueAsString).to.equal('13377331'); | ||
expect(el.value).to.equal(13377331); | ||
}); | ||
it('with language context', async () => { | ||
const lang = 'fr'; | ||
const langResolvers = []; | ||
const createLangResolver = (event) => { | ||
langResolvers.push(event.detail.callback); | ||
resolveLanguage(); | ||
}; | ||
const resolveLanguage = () => { | ||
langResolvers.forEach((resolver) => resolver(lang)); | ||
}; | ||
const el = await getElFrom(html ` | ||
<div @sp-language-context=${createLangResolver}> | ||
${Default({ value: 1337 })} | ||
</div> | ||
`); | ||
expect(el.formattedValue).to.equal('1 337'); | ||
expect(el.valueAsString).to.equal('1337'); | ||
expect(el.value).to.equal(1337); | ||
el.focus(); | ||
await sendKeys({ type: '7331' }); | ||
await elementUpdated(el); | ||
await sendKeys({ press: 'Enter' }); | ||
await elementUpdated(el); | ||
expect(el.formattedValue).to.equal('13 377 331'); | ||
expect(el.valueAsString).to.equal('13377331'); | ||
expect(el.value).to.equal(13377331); | ||
}); | ||
}); | ||
@@ -138,3 +168,3 @@ describe('Increments', () => { | ||
expect(el.value).to.equal(0); | ||
el.dispatchEvent(new WheelEvent('wheel', { deltaY: 1 })); | ||
el.dispatchEvent(new WheelEvent('wheel', { deltaY: 100 })); | ||
await elementUpdated(el); | ||
@@ -193,3 +223,3 @@ expect(el.formattedValue).to.equal('1'); | ||
expect(el.value).to.equal(0); | ||
el.dispatchEvent(new WheelEvent('wheel', { deltaY: -1 })); | ||
el.dispatchEvent(new WheelEvent('wheel', { deltaY: -100 })); | ||
await elementUpdated(el); | ||
@@ -210,10 +240,8 @@ expect(el.formattedValue).to.equal('-1'); | ||
el.addEventListener('input', (event) => { | ||
var _a, _b; | ||
console.log('input event', (_a = event.target) === null || _a === void 0 ? void 0 : _a.value); | ||
inputSpy((_b = event.target) === null || _b === void 0 ? void 0 : _b.value); | ||
var _a; | ||
inputSpy((_a = event.target) === null || _a === void 0 ? void 0 : _a.value); | ||
}); | ||
el.addEventListener('change', (event) => { | ||
var _a, _b; | ||
console.log('change event', (_a = event.target) === null || _a === void 0 ? void 0 : _a.value); | ||
changeSpy((_b = event.target) === null || _b === void 0 ? void 0 : _b.value); | ||
var _a; | ||
changeSpy((_a = event.target) === null || _a === void 0 ? void 0 : _a.value); | ||
}); | ||
@@ -256,2 +284,3 @@ }); | ||
}; | ||
await elementUpdated(el); | ||
el.value = 45; | ||
@@ -803,2 +832,7 @@ expect(el.value).to.equal(45); | ||
expect(el.value).to.equal(7); | ||
el.value = 27; | ||
await elementUpdated(el); | ||
expect(el.formattedValue).to.equal('7'); | ||
expect(el.valueAsString).to.equal('7'); | ||
expect(el.value).to.equal(7); | ||
}); | ||
@@ -805,0 +839,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
274814
27
2199
161
0
5
60