@postnord/web-components
Version:
PostNord Web Components
241 lines (234 loc) • 13.7 kB
JavaScript
/*!
* Built with Stencil
* By PostNord.
*/
import { r as registerInstance, c as createEvent, g as getElement, h, a as Host } from './index-C247oTEA.js';
import { uuidv4, awaitTopbar, en } from './index.js';
const icon$1 = '<svg class="pn-icon-svg" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M2 12a1 1 0 0 1 1-1h18a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1" clip-rule="evenodd"/></svg>';
const minus = icon$1;
const icon = '<svg class="pn-icon-svg" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M12 2a1 1 0 0 1 1 1v8h8a1 1 0 1 1 0 2h-8v8a1 1 0 1 1-2 0v-8H3a1 1 0 1 1 0-2h8V3a1 1 0 0 1 1-1" clip-rule="evenodd"/></svg>';
const plus = icon;
const translations = {
DECREASE: {
en: 'Decrease',
sv: 'Minska',
da: 'Reducere',
fi: 'Vähentää',
no: 'Redusere',
},
INCREASE: {
en: 'Increase',
sv: 'Öka',
da: 'Øge',
fi: 'Lisääntyä',
no: 'Øke',
},
MAX_VALUE_MESSAGE: {
en: 'Maximum value is ',
sv: 'Högsta värde är ',
da: 'Højeste værdi er',
fi: 'Korkein arvo on',
no: 'Høyeste verdi er',
},
MIN_VALUE_MESSAGE: {
en: 'Minimum value is ',
sv: 'Lägsta värde är ',
da: 'Laveste værdi er ',
fi: 'Pienin arvo on ',
no: 'Laveste verdi er ',
},
};
const pnCounterCss = "pn-counter{display:inline-block}pn-counter .pn-counter{display:inline-flex;flex-direction:column}pn-counter .pn-counter-label{cursor:pointer;display:flex;justify-content:space-between;align-items:flex-end;font-weight:400;color:#2d2013;margin:0 0 0.25em 0;gap:0.5em;-webkit-tap-highlight-color:transparent;transition-property:color;transition-duration:0.2s;transition-timing-function:cubic-bezier(0.7, 0, 0.3, 1)}@media (prefers-reduced-motion: reduce){pn-counter .pn-counter-label{transition-duration:0s;transition-delay:0s}}pn-counter .pn-counter-label>span{font-size:0.875em}pn-counter .pn-counter-input{--pn-counter-ch:0ch;color:#2d2013;background-color:#ffffff;border:0.0625em solid #969087;border-radius:0.5em;padding:0.75em;font-family:inherit;font-size:1em;font-weight:500;line-height:1.5em;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent}pn-counter .pn-counter-input:-webkit-autofill,pn-counter .pn-counter-input:-webkit-autofill:hover,pn-counter .pn-counter-input:-webkit-autofill:focus,pn-counter .pn-counter-input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 10em #e0f8ff inset;-webkit-text-fill-color:#2d2013}pn-counter .pn-counter-input{outline:0.2rem solid transparent;outline-offset:0.2rem}pn-counter .pn-counter-input:focus-visible{outline-color:#005d92;background-color:#ffffff;border-color:#005d92}pn-counter .pn-counter-input{transition-property:outline-color, background-color, border-color, color;transition-duration:0.2s;transition-timing-function:cubic-bezier(0.7, 0, 0.3, 1)}@media (prefers-reduced-motion: reduce){pn-counter .pn-counter-input{transition-duration:0s;transition-delay:0s}}pn-counter .pn-counter-input::placeholder{color:#5e554a;font-weight:normal}pn-counter .pn-counter-input:hover{border-color:#005d92}pn-counter .pn-counter-input:disabled{color:#5e554a;background-color:#f3f2f2;border-color:#f3f2f2}pn-counter .pn-counter-input{text-align:center;width:100%;max-width:calc(3.5em + var(--pn-counter-ch));outline:0.2rem solid transparent;outline-offset:0.2rem}pn-counter .pn-counter-input:focus-visible{outline-color:#005d92}pn-counter .pn-counter-input:read-only{border-color:#ffffff}pn-counter .pn-counter-input:disabled{color:#5e554a;background-color:#f3f2f2;border-color:#f3f2f2}pn-counter .pn-counter-input::-webkit-inner-spin-button,pn-counter .pn-counter-input::-webkit-outer-spin-button{-webkit-appearance:none;-moz-appearance:none;appearance:none}pn-counter .pn-counter-input[type=number]{appearance:textfield}pn-counter .pn-counter-items{display:inline-flex;flex-direction:row;gap:0.5em;transition-property:gap;transition-duration:0.2s;transition-timing-function:cubic-bezier(0.7, 0, 0.3, 1)}@media (prefers-reduced-motion: reduce){pn-counter .pn-counter-items{transition-duration:0s;transition-delay:0s}}pn-counter .pn-counter-items[data-hidebuttons]{gap:0}pn-counter .pn-counter-items[data-hidebuttons]>pn-button{transform:scale(0) translateZ(0);width:0}pn-counter .pn-counter-items>pn-button{transform:scale(1) translateZ(0);transform-origin:center center;width:3em;transition-property:transform, width;transition-duration:0.2s;transition-timing-function:cubic-bezier(0.7, 0, 0.3, 1)}@media (prefers-reduced-motion: reduce){pn-counter .pn-counter-items>pn-button{transition-duration:0s;transition-delay:0s}}pn-counter .pn-counter-helpertext{color:#5e554a;font-size:0.875em;font-weight:400;margin:0.25em 0 0 0;display:flex;flex-direction:column;gap:0.25em}pn-counter .pn-counter-sr-only{position:absolute;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);margin:-1px;white-space:nowrap}";
const PnCounter = class {
constructor(hostRef) {
registerInstance(this, hostRef);
this.counterInput = createEvent(this, "counterInput");
}
id = `pn-counter-${uuidv4()}`;
idLabel = `${this.id}-label`;
idText = `${this.id}-text`;
idAmount = `${this.id}-count`;
counterElement;
get hostElement() { return getElement(this); }
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) {
event.stopImmediatePropagation();
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: 'cf4905affaebff5728ae6a943e93edf88f41106c' }, h("div", { key: '692eeac9d69a65fe36f3b28d56d849e7708a2c32', class: "pn-counter", role: "group", "aria-labelledby": this.idLabel, "aria-describedby": this.describedbyIds() }, h("label", { key: '963f8482b869d06b9a01c97181d01adf2eb403ca', htmlFor: this.counterid, class: "pn-counter-label", id: this.idLabel }, h("span", { key: 'de1dc9c7160062fb477d3b899bb9ceb20a917353' }, this.label)), h("div", { key: '41d37ae0de6931b2414170c12bafd114cd305e8a', class: "pn-counter-items", "data-hidebuttons": this.noButtons() }, h("pn-button", { key: '1937886eb9f98277275e520c2a8fc96485a63d3d', "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: 'a7b89bbc5bfd0aa1c42ddfebd0ee4f3c2010e569', 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: 'f2bcfc3ac8ec4641574635e1a01d71419cf6e944', "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: '092fe9eeb8ea838ca08abc124b332f162d3c71e9', id: this.idText, class: "pn-counter-helpertext" }, h("span", { key: '1fac2a88f958af1d8804a07dc5f13ed3ed9175c4' }, this.getTextMessage()))), h("p", { key: '9593aaa39d8d8fe66f159b6fb31ad979b065c4c6', id: this.idAmount, class: "pn-counter-sr-only", "aria-live": "assertive" }, this.displaySrValue ? this.getTextMessage(true) : ''))));
}
static get watchers() { return {
"value": ["watchValue"],
"counterid": ["handleId"]
}; }
};
PnCounter.style = pnCounterCss;
export { PnCounter as pn_counter };
//# sourceMappingURL=pn-counter.entry.js.map