@postnord/web-components
Version:
PostNord Web Components
308 lines (300 loc) • 18.9 kB
JavaScript
/*!
* Built with Stencil
* By PostNord.
*/
'use strict';
var index = require('./index-DVv2io0H.js');
var index$1 = require('./index.cjs.js');
var alert_exclamation_circle = require('./alert_exclamation_circle-BiBEJK9P.js');
var check = require('./check-CQU2BD5S.js');
var close = require('./close-Bn5ieKko.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="M1.81 11.727a1 1 0 0 1 1.363.378c.29.513.835 1.343 1.7 2.174a9.5 9.5 0 0 0 3.457 2.128c1.03.361 2.244.593 3.67.593s2.64-.232 3.67-.593a9.5 9.5 0 0 0 3.458-2.128 9.7 9.7 0 0 0 1.698-2.174 1 1 0 0 1 1.742.984 11.5 11.5 0 0 1-1.445 2.001l1.686 2.322a1 1 0 1 1-1.618 1.175l-1.537-2.116a11.4 11.4 0 0 1-2.453 1.48l.763 2.785a1 1 0 1 1-1.928.528l-.73-2.661c-.993.251-2.093.397-3.306.397s-2.313-.146-3.306-.397l-.73 2.661a1 1 0 1 1-1.928-.528l.763-2.786a11.4 11.4 0 0 1-2.453-1.479L2.81 18.587a1 1 0 0 1-1.618-1.175l1.686-2.322a11.5 11.5 0 0 1-1.445-2 1 1 0 0 1 .379-1.363" clip-rule="evenodd"/></svg>';
const preview_off = 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 7c-5.382 0-7.908 3.33-8.758 4.775a.43.43 0 0 0 0 .45C4.092 13.67 6.618 17 12 17s7.908-3.33 8.758-4.775a.43.43 0 0 0 0-.45C19.908 10.33 17.382 7 12 7M1.518 10.761C2.55 9.005 5.618 5 12 5s9.45 4.005 10.482 5.761a2.43 2.43 0 0 1 0 2.478C21.45 14.995 18.382 19 12 19s-9.45-4.005-10.482-5.761a2.43 2.43 0 0 1 0-2.478M8 12a4 4 0 1 1 8 0 4 4 0 0 1-8 0m4-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4" clip-rule="evenodd"/></svg>';
const preview_on = icon;
const translations = {
SHOW: {
sv: 'Visa',
en: 'Show',
da: 'At vise',
fi: 'Näytä',
no: 'Vise',
},
HIDE: {
sv: 'Dölj',
en: 'Hide',
da: 'Skjule',
fi: 'Piilottaa',
no: 'Gjemme seg',
},
CLEAR: {
sv: 'Rensa',
en: 'Clear',
da: 'Klar',
fi: 'Asia selvä',
no: 'Klar',
},
};
const pnInputCss = "pn-input{display:inline-flex;flex-direction:column}pn-input .pn-input-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-input .pn-input-label{transition-duration:0s;transition-delay:0s}}pn-input .pn-input-label>span{font-size:0.875em}pn-input .pn-input-group{position:relative}pn-input .pn-input-element{--pn-input-offset-left:0em;--pn-input-offset-right:0em;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-input .pn-input-element:-webkit-autofill,pn-input .pn-input-element:-webkit-autofill:hover,pn-input .pn-input-element:-webkit-autofill:focus,pn-input .pn-input-element:-webkit-autofill:active{-webkit-box-shadow:0 0 0 10em #e0f8ff inset;-webkit-text-fill-color:#2d2013}pn-input .pn-input-element{outline:0.2rem solid transparent;outline-offset:0.2rem}pn-input .pn-input-element:focus-visible{outline-color:#005d92;background-color:#ffffff;border-color:#005d92}pn-input .pn-input-element{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-input .pn-input-element{transition-duration:0s;transition-delay:0s}}pn-input .pn-input-element::placeholder{color:#5e554a;font-weight:normal}pn-input .pn-input-element:hover{border-color:#005d92}pn-input .pn-input-element:disabled{color:#5e554a;background-color:#f3f2f2;border-color:#f3f2f2}pn-input .pn-input-element{width:100%;padding-left:calc(var(--pn-input-offset-left) + 0.75em);padding-right:calc(var(--pn-input-offset-right) + 0.75em)}pn-input .pn-input-element:read-only{border-color:#ffffff}pn-input .pn-input-element:disabled~.pn-input-eyecandy>pn-icon>.pn-icon-svg>path{fill:#969087}pn-input .pn-input-element::-webkit-calendar-picker-indicator{visibility:hidden;display:none}pn-input .pn-input-element::-webkit-textfield-decoration-container,pn-input .pn-input-element::-webkit-search-cancel-button,pn-input .pn-input-element::-webkit-inner-spin-button,pn-input .pn-input-element::-webkit-outer-spin-button,pn-input .pn-input-element::-webkit-search-decoration{-webkit-appearance:none;-moz-appearance:none;appearance:none}pn-input .pn-input-element[type=number]{appearance:textfield}pn-input .pn-input-eyecandy{position:absolute;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;gap:0.5em;height:1.5em}pn-input .pn-input-eyecandy[data-prefix]{left:0.75em}pn-input .pn-input-eyecandy[data-suffix]{right:0.75em}pn-input .pn-input-text{color:#5e554a;font-weight:400;line-height:1.5em}pn-input .pn-input-message{color:#5e554a;font-size:0.875em;font-weight:400;margin:0.25em 0 0 0;display:flex;flex-direction:column;gap:0.25em}pn-input .pn-input-message[hidden],pn-input .pn-input-message>span[hidden]{display:none}pn-input .pn-input[data-error]>.pn-input-label{color:#a70707}pn-input .pn-input[data-error]>.pn-input-group>.pn-input-element{border-color:#a70707}pn-input .pn-input[data-error]>.pn-input-group>.pn-input-element:hover{border-color:#500715}pn-input .pn-input[data-error]>.pn-input-group>.pn-input-element:focus-visible{background-color:#ffffff;border-color:#a70707;outline-color:#a70707}pn-input .pn-input[data-error]>.pn-input-message[role=alert]{color:#a70707}pn-input .pn-input[data-valid]>.pn-input-label{color:#005e41}pn-input .pn-input[data-valid]>.pn-input-group>.pn-input-element{border-color:#005e41}pn-input .pn-input[data-valid]>.pn-input-group>.pn-input-element:hover{border-color:#002f24}pn-input .pn-input[data-valid]>.pn-input-group>.pn-input-element:focus-visible{background-color:#ffffff;border-color:#005e41;outline-color:#005e41}";
const PnInput = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
}
id = `pn-input-${index$1.uuidv4()}`;
idMessage = `${this.id}-message`;
prefix;
suffix;
mo;
get hostElement() { return index.getElement(this); }
offsetLeft = 0;
offsetRight = 0;
showHelperSlot = false;
showErrorSlot = false;
showPassword = false;
togglePassword() {
this.showPassword = !this.showPassword;
}
/** Text label placed above the input field. */
label;
/** Text message placed underneath the input field. */
helpertext;
/**
* Provide a unique HTML id to connect the input with the label.
* A unique uuid ID will be generated if this field is left empty.
**/
inputid = this.id;
/** Set the value of the input. */
value = '';
/**
* Set the language manually for the translations of show/hide/clear button text.
* Not needed if you have the pntopbar on the page.
**/
language = null;
/**
* Select an icon to display before the input field value.
* `icon` takes precedence over the `text-prefix` prop.
*
* @see {@link textPrefix}
* @category Visual
**/
icon;
/**
* Set a small text before the input field value.
* Cannot be used together with an `icon` at the same time.
*
* @see {@link icon}
* @see {@link textSuffix}
* @category Visual
**/
textPrefix;
/**
* Set a small text after the input field value.
* Cannot be used with the `text-prefix` prop at the same time.
*
* @see {@link textPrefix}
* @category Visual
**/
textSuffix;
/** pn-input supports: `text`, `password`, `url`, `tel`, `search`, `number` & `email`. @category HTML input */
type = 'text';
/**
* HTML input name. Setting a name will help the browser understand what type of data the input have
* and can better assist with autofill data based on previous entires much better.
*
* @category HTML input
**/
name;
/**
* Provide a placeholder text. Remember that this is no replacement for a label.
* The placeholder should be a nice addition to the label/helpertext, not important information.
*
* @category HTML input
**/
placeholder;
/**
* Let the browser know what type of autocorrects the input should use.
* Works much better if a `name` and `inputid` is supplied.
* @see {@link name}
* @see {@link inputid}
*
* @category HTML input */
autocomplete;
/** The maximum number of characters the user should be able to add, also adds a visible counter. @category HTML input */
maxlength;
/**
* Hint the browser about what type of virtual keyboard should be used.
* The browser will be able to decide this on its own most of the time,
* especially if you use the `type`, `name` and `inputid` props.
*
* Leave empty or with a `''` value if you want the browsers default behaviour (`text`).
*
* @category HTML input
**/
inputmode;
/** Point to a datalist element for this input. @category HTML input */
list;
/** Pattern prop. @category HTML input*/
pattern;
/** Set the `min` value of the `number` input. @category HTML input */
min;
/** Set the `max` value of the `number` input. @category HTML input */
max;
/** Set a `step` for the number input. @category HTML input */
step;
/** While you can use the `aria-label`, using a `label` is far more accessible. @category HTML input */
arialabel;
/** Set the ID of what this input controls. @category HTML input */
ariacontrols;
/** Set the input as `required`. @category State */
required = false;
/** Set the input as `disabled`. @category State */
disabled = false;
/** Set the input as `readonly`. @category State */
readonly = false;
/** Set the input as `valid`. Provides a green color and a check icon. @category Validation */
valid = false;
/**
* Set the input as `invalid`. Provides a red color and red warning icon.
* @see{@link error Provide an error message.}
* @category Validation
**/
invalid = false;
/**
* Set the input as `invalid` and display an error message (applies the same style as `invalid`).
*
* Error message; will take precedence over helpertext if both are provided.
* @see{@link invalid Set invalid without an error message.}
* @category Validation
**/
error;
handleOffset() {
requestAnimationFrame(() => requestAnimationFrame(() => {
const left = this.prefix?.clientWidth ? this.prefix.clientWidth + 8 : 0;
const right = this.suffix?.clientWidth ? this.suffix.clientWidth + 8 : 0;
this.offsetLeft = left;
this.offsetRight = right;
}));
}
handleMessage() {
this.checkSlottedHelper();
this.checkSlottedError();
}
async componentWillLoad() {
this.handleMessage();
if (this.inputid !== this.id)
this.idMessage = `${this.inputid}-message`;
if (this.language === null)
await index$1.awaitTopbar(this.hostElement);
}
componentDidLoad() {
this.handleOffset();
if (this.mo)
this.mo.disconnect();
this.mo = new MutationObserver(() => {
this.handleOffset();
this.handleMessage();
});
this.mo.observe(this.hostElement, {
subtree: true,
childList: true,
});
}
setVal(event) {
const target = event.composedPath?.()[0];
this.value = target.value;
}
clearVal() {
const inputClear = new InputEvent('input', { bubbles: true, data: '' });
const input = this.hostElement.querySelector('input');
this.value = '';
input.focus();
input.value = this.value;
input.dispatchEvent(inputClear);
}
translate(prop) {
return translations?.[prop]?.[this.language || index$1.en];
}
hasHelperText() {
return this.helpertext?.length > 0 || this.showHelperSlot;
}
hasErrorMessage() {
return this.error?.length > 0 || this.showErrorSlot;
}
/** If any error is active, either via the prop `invalid` or `error` prop/slot. */
hasError() {
return this.hasErrorMessage() || this.invalid || this.showErrorSlot;
}
/** If any helpertext or error message is active. Checks both props and slots. */
hasMessage() {
return this.hasHelperText() || this.hasErrorMessage();
}
checkSlottedHelper() {
const slottedHelper = this.hostElement.querySelector('[slot=helpertext]')?.textContent;
this.showHelperSlot = !!slottedHelper?.length;
}
checkSlottedError() {
const slottedError = this.hostElement.querySelector('[slot=error]')?.textContent;
this.showErrorSlot = !!slottedError?.length;
}
getInputType() {
const types = ['text', 'password', 'url', 'tel', 'search', 'number', 'email'];
return types.includes(this.type) && !this.showPassword ? this.type : 'text';
}
isPassword() {
if (this.disabled)
return false;
return this.type === 'password' || (this.type === 'text' && this.showPassword);
}
isSearch() {
if (this.disabled)
return false;
return this.type === 'search' && !!this.value?.length;
}
passwordText() {
return this.translate(this.showPassword ? 'HIDE' : 'SHOW');
}
/** Check if there is a valid or error state active. Returns false if neither is true. */
stateDisplay() {
return this.valid || this.hasError();
}
/**
* Returns the correct icon for the current state (valid or error).
* Defaults to error icon.
* @see {@link stateDisplay}
*/
stateIcon() {
if (this.valid)
return check.check;
return alert_exclamation_circle.alert_exclamation_circle;
}
/**
* Returns the correct color for the validation icon. Defaults to red.
* @see {@link stateDisplay}
*/
stateColor() {
if (this.valid)
return 'green700';
return 'warning';
}
showPrefix() {
return this.textPrefix && !this.icon;
}
showSuffix() {
return !this.showPrefix() && !!this.textSuffix?.length && !this.isPassword() && this.type !== 'search';
}
render() {
return (index.h(index.Host, { key: '0d4a9879999a8e204fca4a3c81cded7c21740da2' }, index.h("div", { key: '6e2709439ee8d31d9619ebbaa4dd28d96c064b59', class: "pn-input", "data-valid": this.valid, "data-error": this.hasError() }, (this.label || this.maxlength >= 1) && (index.h("label", { key: '12832ec67f343466bbaba8e9c68f1b3af90d58ac', htmlFor: this.inputid, class: "pn-input-label" }, this.label && index.h("span", { key: 'ad82f70b5dd71e1929d3995fcbaa31aa254db4b5' }, this.label), this.maxlength >= 0 && index.h("span", { key: '4759e48c59a106ac55439be78785a516901e745b' }, `${this.value.length}/${this.maxlength}`))), index.h("div", { key: '577c037d0616b86e97055fb0ce4edb49c536f8bb', class: "pn-input-group" }, index.h("input", { key: 'aab45abdacfecf591f22bc693371565f63fabae4', type: this.getInputType(), id: this.inputid, class: "pn-input-element", name: this.name, placeholder: this.placeholder, autocomplete: this.autocomplete, maxlength: this.maxlength, list: this.list, pattern: this.pattern, min: this.min, max: this.max, step: this.step, value: this.value, inputmode: this.inputmode, disabled: this.disabled, required: this.required, readonly: this.readonly, "aria-label": this.arialabel, "aria-describedby": this.hasMessage() ? this.idMessage : null, "aria-controls": this.ariacontrols, "aria-invalid": this.hasError()?.toString(), style: {
'--pn-input-offset-left': `${this.offsetLeft}px`,
'--pn-input-offset-right': `${this.offsetRight}px`,
}, onInput: e => this.setVal(e) }), index.h("div", { key: 'cf095938814c403e0242c4e213d2b0c9e4ef8064', class: "pn-input-eyecandy", "data-prefix": true, ref: el => (this.prefix = el) }, !!this.icon && index.h("pn-icon", { key: '9bea524d1d3ed19568c9818c4c60c939255f0643', icon: this.icon, "aria-hidden": "true" }), this.showPrefix() && index.h("span", { key: 'f90f8da4971a12fe912c251b02373fb733cef4e5', class: "pn-input-text" }, this.textPrefix)), index.h("div", { key: 'b2f6a49fe1f724be4af0e66da7512ec0d49454cb', class: "pn-input-eyecandy", "data-suffix": true, ref: el => (this.suffix = el) }, this.showSuffix() && index.h("span", { key: 'cdc14085e3547eef9fa7c46d128c5fec787f124d', class: "pn-input-text" }, this.textSuffix), this.stateDisplay() && index.h("pn-icon", { key: 'f8162a1b3245dcf0ddb30375b01b4e3215394c8e', "aria-hidden": "true", icon: this.stateIcon(), color: this.stateColor() }), this.isPassword() && (index.h("pn-button", { key: '68bcb77cd844ee4588c600d8e35c427a78ba0d3d', icon: this.showPassword ? preview_on : preview_off, iconOnly: true, arialabel: this.passwordText(), small: true, appearance: "light", variant: "borderless", onClick: () => this.togglePassword() })), this.isSearch() && (index.h("pn-button", { key: '077acc14f0c79f1110c513a84fcaa16e2aa13327', icon: close.close, iconOnly: true, arialabel: this.translate('CLEAR'), small: true, appearance: "light", variant: "borderless", onClick: () => this.clearVal() })))), index.h("p", { key: 'bcae72610a31aa08a05a52ed32123b54954401e7', class: "pn-input-message", id: this.idMessage, role: this.hasErrorMessage() ? 'alert' : null, hidden: !this.hasMessage() }, this.hasHelperText() && !this.hasError() && index.h("span", { key: 'd2c8e42cdf2c9eff4b849c0d33fb2e78d20ef0d5', class: "pn-input-helper" }, this.helpertext), index.h("span", { key: '2bb15a762271f6e1c04f7fc1ee517b1063aab04d', class: "pn-input-helper-slot", hidden: !this.showHelperSlot || this.hasError() }, index.h("slot", { key: 'df13d9a1e79af9631603d5c1f3f881c8ddd86e4b', name: "helpertext" })), this.hasErrorMessage() && index.h("span", { key: '1cd5456bd72138710c3e36658b04e35bb8457d4f', class: "pn-input-error" }, this.error), index.h("span", { key: 'd9e9a6420aed187a3fdc925ba5ae1002f09566c0', class: "pn-input-error-slot", hidden: !this.showErrorSlot }, index.h("slot", { key: 'd81505fbdf01e5c9237342c0f7d381e25b8d30f8', name: "error" }))))));
}
static get watchers() { return {
"textPrefix": ["handleOffset"],
"textSuffix": ["handleOffset"],
"helpertext": ["handleMessage"],
"error": ["handleMessage"]
}; }
};
PnInput.style = pnInputCss;
exports.pn_input = PnInput;
//# sourceMappingURL=pn-input.entry.cjs.js.map