@postnord/web-components
Version:
PostNord Web Components
621 lines (620 loc) • 23.8 kB
JavaScript
/*!
* Built with Stencil
* By PostNord.
*/
import { Host, h } from "@stencil/core";
import { uuidv4, en, awaitTopbar } from "../../../globals/helpers";
import { minus, plus } from "pn-design-assets/pn-assets/icons.js";
import { translations } from "./translations";
/**
* The `pn-counter` is an input with a button on each side that can be used to increase and decrease the input value.
*
* @nativeInput Use the `input` event to listen to content being modified by the user. It is emitted everytime a user writes or removes content in the input.
* @nativeChange The `change` event is emitted when the input loses focus, the user clicks `Enter` or makes a selection (such as auto complete or suggestions).
*/
export class PnCounter {
id = `pn-counter-${uuidv4()}`;
idLabel = `${this.id}-label`;
idText = `${this.id}-text`;
idAmount = `${this.id}-count`;
counterElement;
hostElement;
clearAriaTimer;
displaySrValue = false;
showMinMaxMessage = false;
/** Prevent double events when reaching min/max values. */
lastDispatchedValue;
interactType;
/** Label for the counter */
label;
/** Set a helpertext for the counter. */
helpertext;
/** Set a predefined value */
value = 0;
/** Set a unique HTML ID for the counter */
counterid = this.id;
/** Set the language manually for the built in translations. */
language = null;
/** Text for the decrease button. Default is "Decrease". @category Translation */
labelDecrease;
/** Text for the increase button. Default is "Increase". @category Translation */
labelIncrease;
/** Set a message that shows below input when user wants to set a value that is below `min`. Default is "Minimum value is x" @category Translation */
minMessage;
/** Set a message that shows below input when user wants to set a value that is above `max`. Default is "Maximum value is x" @category Translation */
maxMessage;
/** HTML input name. @category HTML input */
name;
/** Minimum value. @category HTML input */
min;
/** Maximum value. @category HTML input */
max;
/** Increase/decrease the value in steps, default is 1. @category HTML input */
step = 1;
/** Suggest values for the counter input. @category HTML input */
list;
/** Set the counter as required. @category State */
required = false;
/** Set the counter as readonly. @category State */
readonly = false;
/** Set the counter as disabled. @category State */
disabled = false;
/**
* Instead of listening to multiple input, change and/or click events, we bundled them into one.
* - `value` is the current counter value.
* - `input` is true if the user changed the value with the HTML input.
* - `decrease` is true if the user clicked on the decrease button.
* - `increase` is true if the user clicked on the increase button.
*/
counterInput;
watchValue() {
this.showSrContent();
this.showMinMaxMessage = false;
if (this.hasMin() && this.value < this.min) {
this.setMin();
this.showMinMaxMessage = true;
}
if (this.hasMax() && this.value > this.max) {
this.setMax();
this.showMinMaxMessage = true;
}
if (this.lastDispatchedValue !== this.value)
this.counterInput.emit({
value: this.value,
input: this.interactType === 'input',
decrease: this.interactType === 'decrease',
increase: this.interactType === 'increase',
});
this.lastDispatchedValue = this.value;
this.handleInputLength();
}
handleId() {
this.idLabel = `${this.counterid}-label`;
this.idText = `${this.counterid}-text`;
this.idAmount = `${this.counterid}-count`;
}
async componentWillLoad() {
this.handleId();
if (this.language)
return;
await awaitTopbar(this.hostElement);
}
componentDidLoad() {
this.counterElement = this.hostElement.querySelector('.pn-counter-input');
this.handleInputLength();
}
setVal(event) {
const { valueAsNumber } = event.target;
if (isNaN(valueAsNumber))
return;
this.interactType = 'input';
this.value = valueAsNumber;
}
setMin() {
if (this.hasMin())
this.value = this.min;
}
setMax() {
if (this.hasMax())
this.value = this.max;
}
hasMin() {
return typeof this.min === 'number';
}
hasMax() {
return typeof this.max === 'number';
}
decreaseAmount() {
if (this.hasMin() && this.value <= this.min)
this.showMinMaxMessage = true;
this.interactType = 'decrease';
this.value = this.value - this.step;
}
increaseAmount() {
if (this.hasMax() && this.value >= this.max)
this.showMinMaxMessage = true;
this.interactType = 'increase';
this.value = this.value + this.step;
}
keyBoardInput(event) {
if (!/^(Home|End)$/.test(event.key))
return;
event.preventDefault();
this.interactType = 'input';
if (event.key === 'Home')
this.setMin();
if (event.key === 'End')
this.setMax();
}
showSrContent() {
if (this.clearAriaTimer) {
clearTimeout(this.clearAriaTimer);
}
this.displaySrValue = true;
this.clearAriaTimer = setTimeout(() => {
this.displaySrValue = false;
}, 4000);
}
toggleMinMaxMessage() {
const isMax = this.value === this.max;
const i18ntxt = `${isMax ? 'MAX' : 'MIN'}_VALUE_MESSAGE`;
const message = this.translate(i18ntxt) + `${isMax ? this.max : this.min}`.toString();
return isMax ? this.maxMessage || message : this.minMessage || message;
}
getTextMessage(showValue = false) {
if (this.showMinMaxMessage)
return this.toggleMinMaxMessage();
return showValue ? this.value.toString() : this.helpertext;
}
describedbyIds() {
const list = [];
if (this.helpertext)
list.push(this.idText);
if (this.displaySrValue)
list.push(this.idAmount);
if (list.length === 0)
return null;
return list.join(' ');
}
translate(prop) {
return translations?.[prop]?.[this.language || en];
}
noButtons() {
return this.disabled || this.readonly;
}
handleInputLength() {
const ch = this.max ? this.max.toString() : this.value.toString();
if (ch.length > 3)
this.counterElement.style.setProperty('--pn-counter-ch', `${ch.length - 3}ch`);
else
this.counterElement.style.setProperty('--pn-counter-ch', '0ch');
}
render() {
return (h(Host, { key: '42278961c0d0656f60971018796b0f1d4e1f8450' }, h("div", { key: 'c1d68806774beaac97cb57c2a238ef8091291768', class: "pn-counter", role: "group", "aria-labelledby": this.idLabel, "aria-describedby": this.describedbyIds() }, h("label", { key: '2ca7b47e4ff075dc9c51acf9e35c14a43d77aef7', htmlFor: this.counterid, class: "pn-counter-label", id: this.idLabel }, h("span", { key: 'a37f9253f39cce2791b9b4e0fea677f80c5be710' }, this.label)), h("div", { key: '96e021d653b0f3b370697369dc3486eccb72c6e5', class: "pn-counter-items", "data-hidebuttons": this.noButtons() }, h("pn-button", { key: '1ee4498010d2858ca9af4c8ef15f25f8e8aeeeca', "data-decrease": true, appearance: "light", variant: "outlined", "no-tab": this.noButtons(), icon: minus, iconOnly: true, arialabel: this.labelDecrease || this.translate('DECREASE'), onClick: () => this.decreaseAmount() }), h("input", { key: 'b66bbcd5fdbac6cf4aee2d47f77b5926cfd7ff09', id: this.counterid, class: "pn-counter-input", type: "number", min: this.min, max: this.max, step: this.step, value: this.value, name: this.name, required: this.required, readonly: this.readonly, disabled: this.disabled, "aria-describedby": this.describedbyIds(), onInput: e => this.setVal(e), onKeyDown: (e) => this.keyBoardInput(e) }), h("pn-button", { key: 'a54ad20d1ea02a118be222be61a0892e59af4db7', "data-increase": true, appearance: "light", variant: "outlined", "no-tab": this.noButtons(), icon: plus, iconOnly: true, arialabel: this.labelIncrease || this.translate('INCREASE'), onClick: () => this.increaseAmount() })), !!this.getTextMessage() && (h("p", { key: '457c9380f1302a36f361c6b81a4b4abd530a5e6e', id: this.idText, class: "pn-counter-helpertext" }, h("span", { key: 'aaff9bc613f72b1f69c24cbcd020fd07246845c6' }, this.getTextMessage()))), h("p", { key: '5d029126c4b3efef92aa5e542ad103de953d34e3', id: this.idAmount, class: "pn-counter-sr-only", "aria-live": "assertive" }, this.displaySrValue ? this.getTextMessage(true) : ''))));
}
static get is() { return "pn-counter"; }
static get originalStyleUrls() {
return {
"$": ["pn-counter.scss"]
};
}
static get styleUrls() {
return {
"$": ["pn-counter.css"]
};
}
static get properties() {
return {
"label": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": true,
"optional": false,
"docs": {
"tags": [],
"text": "Label for the counter"
},
"getter": false,
"setter": false,
"attribute": "label",
"reflect": false
},
"helpertext": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Set a helpertext for the counter."
},
"getter": false,
"setter": false,
"attribute": "helpertext",
"reflect": false
},
"value": {
"type": "number",
"mutable": true,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Set a predefined value"
},
"getter": false,
"setter": false,
"attribute": "value",
"reflect": true,
"defaultValue": "0"
},
"counterid": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Set a unique HTML ID for the counter"
},
"getter": false,
"setter": false,
"attribute": "counterid",
"reflect": false,
"defaultValue": "this.id"
},
"language": {
"type": "string",
"mutable": false,
"complexType": {
"original": "PnLanguages",
"resolved": "\"\" | \"da\" | \"en\" | \"fi\" | \"no\" | \"sv\"",
"references": {
"PnLanguages": {
"location": "import",
"path": "@/globals/types",
"id": "src/globals/types.ts::PnLanguages"
}
}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Set the language manually for the built in translations."
},
"getter": false,
"setter": false,
"attribute": "language",
"reflect": false,
"defaultValue": "null"
},
"labelDecrease": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [{
"name": "category",
"text": "Translation"
}],
"text": "Text for the decrease button. Default is \"Decrease\"."
},
"getter": false,
"setter": false,
"attribute": "label-decrease",
"reflect": false
},
"labelIncrease": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [{
"name": "category",
"text": "Translation"
}],
"text": "Text for the increase button. Default is \"Increase\"."
},
"getter": false,
"setter": false,
"attribute": "label-increase",
"reflect": false
},
"minMessage": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [{
"name": "category",
"text": "Translation"
}],
"text": "Set a message that shows below input when user wants to set a value that is below `min`. Default is \"Minimum value is x\""
},
"getter": false,
"setter": false,
"attribute": "min-message",
"reflect": false
},
"maxMessage": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [{
"name": "category",
"text": "Translation"
}],
"text": "Set a message that shows below input when user wants to set a value that is above `max`. Default is \"Maximum value is x\""
},
"getter": false,
"setter": false,
"attribute": "max-message",
"reflect": false
},
"name": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [{
"name": "category",
"text": "HTML input"
}],
"text": "HTML input name."
},
"getter": false,
"setter": false,
"attribute": "name",
"reflect": false
},
"min": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "category",
"text": "HTML input"
}],
"text": "Minimum value."
},
"getter": false,
"setter": false,
"attribute": "min",
"reflect": false
},
"max": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "category",
"text": "HTML input"
}],
"text": "Maximum value."
},
"getter": false,
"setter": false,
"attribute": "max",
"reflect": false
},
"step": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "category",
"text": "HTML input"
}],
"text": "Increase/decrease the value in steps, default is 1."
},
"getter": false,
"setter": false,
"attribute": "step",
"reflect": false,
"defaultValue": "1"
},
"list": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [{
"name": "category",
"text": "HTML input"
}],
"text": "Suggest values for the counter input."
},
"getter": false,
"setter": false,
"attribute": "list",
"reflect": false
},
"required": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "category",
"text": "State"
}],
"text": "Set the counter as required."
},
"getter": false,
"setter": false,
"attribute": "required",
"reflect": false,
"defaultValue": "false"
},
"readonly": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "category",
"text": "State"
}],
"text": "Set the counter as readonly."
},
"getter": false,
"setter": false,
"attribute": "readonly",
"reflect": false,
"defaultValue": "false"
},
"disabled": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "category",
"text": "State"
}],
"text": "Set the counter as disabled."
},
"getter": false,
"setter": false,
"attribute": "disabled",
"reflect": false,
"defaultValue": "false"
}
};
}
static get states() {
return {
"clearAriaTimer": {},
"displaySrValue": {},
"showMinMaxMessage": {},
"lastDispatchedValue": {},
"interactType": {}
};
}
static get events() {
return [{
"method": "counterInput",
"name": "counterInput",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "Instead of listening to multiple input, change and/or click events, we bundled them into one.\n- `value` is the current counter value.\n- `input` is true if the user changed the value with the HTML input.\n- `decrease` is true if the user clicked on the decrease button.\n- `increase` is true if the user clicked on the increase button."
},
"complexType": {
"original": "{ value: number; input?: boolean; decrease?: boolean; increase?: boolean }",
"resolved": "{ value: number; input?: boolean; decrease?: boolean; increase?: boolean; }",
"references": {}
}
}];
}
static get elementRef() { return "hostElement"; }
static get watchers() {
return [{
"propName": "value",
"methodName": "watchValue"
}, {
"propName": "counterid",
"methodName": "handleId"
}];
}
}
//# sourceMappingURL=pn-counter.js.map