Fixed #892 - show password feature
parent
c29299537e
commit
c360807e23
|
@ -4,7 +4,6 @@
|
|||
@import '../../components/checkbox/Checkbox.css';
|
||||
@import '../../components/colorpicker/ColorPicker.css';
|
||||
@import '../../components/inputtext/InputText.css';
|
||||
@import '../../components/password/Password.css';
|
||||
@import '../../components/radiobutton/RadioButton.css';
|
||||
@import '../../components/ripple/Ripple.css';
|
||||
@import '../../components/tooltip/Tooltip.css';
|
|
@ -9,6 +9,8 @@ declare class Password extends Vue {
|
|||
mediumLabel?: string;
|
||||
strongLabel?: string;
|
||||
feedback?: boolean;
|
||||
appendTo?: string;
|
||||
toggleMask?: boolean;
|
||||
$emit(eventName: string, event: Event): this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
<template>
|
||||
<input ref="input" type="password" :class="['p-inputtext p-component', {'p-filled': filled}]" :value="modelValue"
|
||||
@input="onInput" @focus="onFocus" @blur="onBlur" @keyup="onKeyUp" v-bind="$attrs" />
|
||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave">
|
||||
<div :ref="overlayRef" class="p-password-panel p-component" v-if="overlayVisible">
|
||||
<div class="p-password-meter" :style="{'background-position': meterPosition}"></div>
|
||||
<div class="p-password-info">
|
||||
{{infoText}}
|
||||
<div :class="containerClass" :style="style">
|
||||
<PInputText ref="input" :type="inputType" :value="modelValue" @input="onInput" @focus="onFocus" @blur="onBlur" @keyup="onKeyUp" v-bind="$attrs" autocomplete="fff"/>
|
||||
<i v-if="toggleMask" :class="toggleIconClass" @click="onMaskToggle" />
|
||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave">
|
||||
<div :ref="overlayRef" class="p-password-panel p-component" v-if="overlayVisible">
|
||||
<slot name="header"></slot>
|
||||
<slot name="content">
|
||||
<div class="p-password-meter" :style="{'background-position': meterPosition}"></div>
|
||||
<div class="p-password-info">{{infoText}}</div>
|
||||
</slot>
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ConnectedOverlayScrollHandler} from 'primevue/utils';
|
||||
import {DomHandler} from 'primevue/utils';
|
||||
import InputText from 'primevue/inputtext';
|
||||
|
||||
export default {
|
||||
emits: ['update:modelValue'],
|
||||
|
@ -47,13 +52,25 @@ export default {
|
|||
feedback: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
appendTo: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
toggleMask: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
style: null,
|
||||
class: null
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
overlayVisible: false,
|
||||
meterPosition: '',
|
||||
infoText: this.promptLabel
|
||||
infoText: null,
|
||||
focused: false,
|
||||
unmasked: false
|
||||
};
|
||||
},
|
||||
mediumCheckRegExp: null,
|
||||
|
@ -62,10 +79,12 @@ export default {
|
|||
scrollHandler: null,
|
||||
overlay: null,
|
||||
mounted() {
|
||||
this.infoText = this.promptText;
|
||||
this.mediumCheckRegExp = new RegExp(this.mediumRegex);
|
||||
this.strongCheckRegExp = new RegExp(this.strongRegex);
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.restoreAppend();
|
||||
this.unbindResizeListener();
|
||||
if (this.scrollHandler) {
|
||||
this.scrollHandler.destroy();
|
||||
|
@ -75,8 +94,8 @@ export default {
|
|||
methods: {
|
||||
onOverlayEnter() {
|
||||
this.overlay.style.zIndex = String(DomHandler.generateZIndex());
|
||||
this.overlay.style.minWidth = DomHandler.getOuterWidth(this.$refs.input) + 'px';
|
||||
DomHandler.absolutePosition(this.overlay, this.$refs.input);
|
||||
this.appendContainer();
|
||||
this.alignOverlay();
|
||||
this.bindScrollListener();
|
||||
this.bindResizeListener();
|
||||
},
|
||||
|
@ -85,6 +104,41 @@ export default {
|
|||
this.unbindResizeListener();
|
||||
this.overlay = null;
|
||||
},
|
||||
alignOverlay() {
|
||||
if (this.appendTo) {
|
||||
this.overlay.style.minWidth = DomHandler.getOuterWidth(this.$refs.input.$el) + 'px';
|
||||
DomHandler.absolutePosition(this.overlay, this.$refs.input.$el);
|
||||
}
|
||||
else {
|
||||
DomHandler.relativePosition(this.overlay, this.$refs.input.$el);
|
||||
}
|
||||
},
|
||||
appendContainer() {
|
||||
if (this.appendTo) {
|
||||
if (this.appendTo === 'body')
|
||||
document.body.appendChild(this.overlay);
|
||||
else
|
||||
document.getElementById(this.appendTo).appendChild(this.overlay);
|
||||
}
|
||||
},
|
||||
restoreAppend() {
|
||||
if (this.overlay && this.appendTo) {
|
||||
if (this.appendTo === 'body')
|
||||
document.body.removeChild(this.overlay);
|
||||
else
|
||||
document.getElementById(this.appendTo).removeChild(this.overlay);
|
||||
}
|
||||
},
|
||||
bindOutsideClickListener() {
|
||||
if (!this.outsideClickListener) {
|
||||
this.outsideClickListener = (event) => {
|
||||
if (this.overlayVisible && this.overlay && this.isOutsideClicked(event)) {
|
||||
this.hideOverlay();
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', this.outsideClickListener);
|
||||
}
|
||||
},
|
||||
testStrength(str) {
|
||||
let level = 0;
|
||||
|
||||
|
@ -101,11 +155,13 @@ export default {
|
|||
this.$emit('update:modelValue', event.target.value)
|
||||
},
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
if (this.feedback) {
|
||||
this.overlayVisible = true;
|
||||
}
|
||||
},
|
||||
onBlur() {
|
||||
this.focused = false;
|
||||
if (this.feedback) {
|
||||
this.overlayVisible = false;
|
||||
}
|
||||
|
@ -148,7 +204,7 @@ export default {
|
|||
},
|
||||
bindScrollListener() {
|
||||
if (!this.scrollHandler) {
|
||||
this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.input, () => {
|
||||
this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.input.$el, () => {
|
||||
if (this.overlayVisible) {
|
||||
this.overlayVisible = false;
|
||||
}
|
||||
|
@ -180,9 +236,30 @@ export default {
|
|||
},
|
||||
overlayRef(el) {
|
||||
this.overlay = el;
|
||||
},
|
||||
onMaskToggle() {
|
||||
this.unmasked = !this.unmasked;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
containerClass() {
|
||||
return ['p-password p-component p-inputwrapper', this.class, {
|
||||
'p-inputwrapper-filled': this.filled,
|
||||
'p-inputwrapper-focus': this.focused,
|
||||
'p-input-icon-right': this.toggleMask
|
||||
}];
|
||||
},
|
||||
inputClass() {
|
||||
return ['p-password-input', {
|
||||
'p-disabled': this.$attrs.disabled
|
||||
}];
|
||||
},
|
||||
toggleIconClass() {
|
||||
return this.unmasked ? 'pi pi-eye-slash' : 'pi pi-eye';
|
||||
},
|
||||
inputType() {
|
||||
return this.unmasked ? 'text' : 'password';
|
||||
},
|
||||
filled() {
|
||||
return (this.modelValue != null && this.modelValue.toString().length > 0)
|
||||
},
|
||||
|
@ -198,6 +275,28 @@ export default {
|
|||
promptText() {
|
||||
return this.promptLabel || this.$primevue.config.locale.passwordPrompt;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'PInputText': InputText
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.p-password {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.p-password-panel {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.p-password .p-password-panel {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.p-password-meter {
|
||||
height: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 118 B |
|
@ -10,7 +10,32 @@
|
|||
|
||||
<div class="content-section implementation">
|
||||
<div class="card">
|
||||
<Password v-model="value" />
|
||||
<h5>Basic</h5>
|
||||
<Password v-model="value1" :feedback="false" />
|
||||
|
||||
<h5>Password Meter</h5>
|
||||
<Password v-model="value2" />
|
||||
|
||||
<h5>Show Password</h5>
|
||||
<Password v-model="value3" toggleMask></Password>
|
||||
|
||||
<h5>Templating</h5>
|
||||
<Password v-model="value4">
|
||||
<template #header>
|
||||
<h6>Pick a password</h6>
|
||||
</template>
|
||||
<template #footer="sp">
|
||||
{{sp.level}}
|
||||
<Divider />
|
||||
<p class="p-mt-2">Suggestions</p>
|
||||
<ul class="p-pl-2 p-ml-2 p-mt-0" style="line-height: 1.5">
|
||||
<li>At least one lowercase</li>
|
||||
<li>At least one uppercase</li>
|
||||
<li>At least one numeric</li>
|
||||
<li>Minimum 8 characters</li>
|
||||
</ul>
|
||||
</template>
|
||||
</Password>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -24,7 +49,10 @@ import PasswordDoc from './PasswordDoc';
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
value: ''
|
||||
value1: null,
|
||||
value2: null,
|
||||
value3: null,
|
||||
value4: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
@ -32,3 +60,9 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep(.p-password input) {
|
||||
width: 15rem
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -39,6 +39,28 @@ import Password from 'primevue/password';
|
|||
|
||||
<p>It is possible to define your own checks with the <i>mediumRegex</i> and <i>strongRegex</i> properties.</p>
|
||||
|
||||
<h5>Templating</h5>
|
||||
<p>3 slots are included to customize the overlay. These are <i>header</i>, <i>content</i> and <i>footer</i>. Note that content overrides the default meter.</p>
|
||||
<pre v-code>
|
||||
<code>
|
||||
<Password v-model="value4">
|
||||
<template #header>
|
||||
<h6>Pick a password</h6>
|
||||
</template>
|
||||
<template #footer>
|
||||
<Divider />
|
||||
<p class="p-mt-2">Suggestions</p>
|
||||
<ul class="p-pl-2 p-ml-2 p-mt-0" style="line-height: 1.5">
|
||||
<li>At least one lowercase</li>
|
||||
<li>At least one uppercase</li>
|
||||
<li>At least one numeric</li>
|
||||
<li>Minimum 8 characters</li>
|
||||
</ul>
|
||||
</template>
|
||||
</Password>
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h5>Properties</h5>
|
||||
<p>Any property such as name and placeholder are passed to the underlying input element. Following are the additional properties to configure the component.</p>
|
||||
<div class="doc-tablewrapper">
|
||||
|
@ -99,6 +121,18 @@ import Password from 'primevue/password';
|
|||
<td>boolean</td>
|
||||
<td>true</td>
|
||||
<td>Whether to show the strength indicator or not.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>toggleMask</td>
|
||||
<td>boolean</td>
|
||||
<td>false</td>
|
||||
<td>Whether to show an icon to display the password as plain text.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>appendTo</td>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
<td>Id of the element or "body" for document where the overlay should be appended to.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
Loading…
Reference in New Issue