Fixed #892 - show password feature
parent
c29299537e
commit
c360807e23
|
@ -4,7 +4,6 @@
|
||||||
@import '../../components/checkbox/Checkbox.css';
|
@import '../../components/checkbox/Checkbox.css';
|
||||||
@import '../../components/colorpicker/ColorPicker.css';
|
@import '../../components/colorpicker/ColorPicker.css';
|
||||||
@import '../../components/inputtext/InputText.css';
|
@import '../../components/inputtext/InputText.css';
|
||||||
@import '../../components/password/Password.css';
|
|
||||||
@import '../../components/radiobutton/RadioButton.css';
|
@import '../../components/radiobutton/RadioButton.css';
|
||||||
@import '../../components/ripple/Ripple.css';
|
@import '../../components/ripple/Ripple.css';
|
||||||
@import '../../components/tooltip/Tooltip.css';
|
@import '../../components/tooltip/Tooltip.css';
|
|
@ -9,6 +9,8 @@ declare class Password extends Vue {
|
||||||
mediumLabel?: string;
|
mediumLabel?: string;
|
||||||
strongLabel?: string;
|
strongLabel?: string;
|
||||||
feedback?: boolean;
|
feedback?: boolean;
|
||||||
|
appendTo?: string;
|
||||||
|
toggleMask?: boolean;
|
||||||
$emit(eventName: string, event: Event): this;
|
$emit(eventName: string, event: Event): this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<input ref="input" type="password" :class="['p-inputtext p-component', {'p-filled': filled}]" :value="modelValue"
|
<div :class="containerClass" :style="style">
|
||||||
@input="onInput" @focus="onFocus" @blur="onBlur" @keyup="onKeyUp" v-bind="$attrs" />
|
<PInputText ref="input" :type="inputType" :value="modelValue" @input="onInput" @focus="onFocus" @blur="onBlur" @keyup="onKeyUp" v-bind="$attrs" autocomplete="fff"/>
|
||||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave">
|
<i v-if="toggleMask" :class="toggleIconClass" @click="onMaskToggle" />
|
||||||
<div :ref="overlayRef" class="p-password-panel p-component" v-if="overlayVisible">
|
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave">
|
||||||
<div class="p-password-meter" :style="{'background-position': meterPosition}"></div>
|
<div :ref="overlayRef" class="p-password-panel p-component" v-if="overlayVisible">
|
||||||
<div class="p-password-info">
|
<slot name="header"></slot>
|
||||||
{{infoText}}
|
<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>
|
||||||
</div>
|
</transition>
|
||||||
</transition>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {ConnectedOverlayScrollHandler} from 'primevue/utils';
|
import {ConnectedOverlayScrollHandler} from 'primevue/utils';
|
||||||
import {DomHandler} from 'primevue/utils';
|
import {DomHandler} from 'primevue/utils';
|
||||||
|
import InputText from 'primevue/inputtext';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
|
@ -47,13 +52,25 @@ export default {
|
||||||
feedback: {
|
feedback: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
},
|
||||||
|
appendTo: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
toggleMask: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
style: null,
|
||||||
|
class: null
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
overlayVisible: false,
|
overlayVisible: false,
|
||||||
meterPosition: '',
|
meterPosition: '',
|
||||||
infoText: this.promptLabel
|
infoText: null,
|
||||||
|
focused: false,
|
||||||
|
unmasked: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mediumCheckRegExp: null,
|
mediumCheckRegExp: null,
|
||||||
|
@ -62,10 +79,12 @@ export default {
|
||||||
scrollHandler: null,
|
scrollHandler: null,
|
||||||
overlay: null,
|
overlay: null,
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.infoText = this.promptText;
|
||||||
this.mediumCheckRegExp = new RegExp(this.mediumRegex);
|
this.mediumCheckRegExp = new RegExp(this.mediumRegex);
|
||||||
this.strongCheckRegExp = new RegExp(this.strongRegex);
|
this.strongCheckRegExp = new RegExp(this.strongRegex);
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
|
this.restoreAppend();
|
||||||
this.unbindResizeListener();
|
this.unbindResizeListener();
|
||||||
if (this.scrollHandler) {
|
if (this.scrollHandler) {
|
||||||
this.scrollHandler.destroy();
|
this.scrollHandler.destroy();
|
||||||
|
@ -75,8 +94,8 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
onOverlayEnter() {
|
onOverlayEnter() {
|
||||||
this.overlay.style.zIndex = String(DomHandler.generateZIndex());
|
this.overlay.style.zIndex = String(DomHandler.generateZIndex());
|
||||||
this.overlay.style.minWidth = DomHandler.getOuterWidth(this.$refs.input) + 'px';
|
this.appendContainer();
|
||||||
DomHandler.absolutePosition(this.overlay, this.$refs.input);
|
this.alignOverlay();
|
||||||
this.bindScrollListener();
|
this.bindScrollListener();
|
||||||
this.bindResizeListener();
|
this.bindResizeListener();
|
||||||
},
|
},
|
||||||
|
@ -85,6 +104,41 @@ export default {
|
||||||
this.unbindResizeListener();
|
this.unbindResizeListener();
|
||||||
this.overlay = null;
|
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) {
|
testStrength(str) {
|
||||||
let level = 0;
|
let level = 0;
|
||||||
|
|
||||||
|
@ -101,11 +155,13 @@ export default {
|
||||||
this.$emit('update:modelValue', event.target.value)
|
this.$emit('update:modelValue', event.target.value)
|
||||||
},
|
},
|
||||||
onFocus() {
|
onFocus() {
|
||||||
|
this.focused = true;
|
||||||
if (this.feedback) {
|
if (this.feedback) {
|
||||||
this.overlayVisible = true;
|
this.overlayVisible = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onBlur() {
|
onBlur() {
|
||||||
|
this.focused = false;
|
||||||
if (this.feedback) {
|
if (this.feedback) {
|
||||||
this.overlayVisible = false;
|
this.overlayVisible = false;
|
||||||
}
|
}
|
||||||
|
@ -148,7 +204,7 @@ export default {
|
||||||
},
|
},
|
||||||
bindScrollListener() {
|
bindScrollListener() {
|
||||||
if (!this.scrollHandler) {
|
if (!this.scrollHandler) {
|
||||||
this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.input, () => {
|
this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.input.$el, () => {
|
||||||
if (this.overlayVisible) {
|
if (this.overlayVisible) {
|
||||||
this.overlayVisible = false;
|
this.overlayVisible = false;
|
||||||
}
|
}
|
||||||
|
@ -180,9 +236,30 @@ export default {
|
||||||
},
|
},
|
||||||
overlayRef(el) {
|
overlayRef(el) {
|
||||||
this.overlay = el;
|
this.overlay = el;
|
||||||
|
},
|
||||||
|
onMaskToggle() {
|
||||||
|
this.unmasked = !this.unmasked;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
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() {
|
filled() {
|
||||||
return (this.modelValue != null && this.modelValue.toString().length > 0)
|
return (this.modelValue != null && this.modelValue.toString().length > 0)
|
||||||
},
|
},
|
||||||
|
@ -198,6 +275,28 @@ export default {
|
||||||
promptText() {
|
promptText() {
|
||||||
return this.promptLabel || this.$primevue.config.locale.passwordPrompt;
|
return this.promptLabel || this.$primevue.config.locale.passwordPrompt;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
'PInputText': InputText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</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="content-section implementation">
|
||||||
<div class="card">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -24,7 +49,10 @@ import PasswordDoc from './PasswordDoc';
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
value: ''
|
value1: null,
|
||||||
|
value2: null,
|
||||||
|
value3: null,
|
||||||
|
value4: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -32,3 +60,9 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</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>
|
<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>
|
<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>
|
<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">
|
<div class="doc-tablewrapper">
|
||||||
|
@ -99,6 +121,18 @@ import Password from 'primevue/password';
|
||||||
<td>boolean</td>
|
<td>boolean</td>
|
||||||
<td>true</td>
|
<td>true</td>
|
||||||
<td>Whether to show the strength indicator or not.</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>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
Loading…
Reference in New Issue