@nextcloud/vue
Version:
Nextcloud vue components
1 lines • 10.5 kB
Source Map (JSON)
{"version":3,"file":"NcPasswordField-djttkA5Q.mjs","sources":["../../src/components/NcPasswordField/NcPasswordField.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<docs>\n### Description\nSee [NcInputField](#/Components/NcFields?id=ncinputfield) for a list of all available props.\n\nGeneral purpose password field component.\n\n```\n<template>\n\t<div class=\"wrapper\">\n\t\t<NcPasswordField v-model=\"text1\"\n\t\t\tlabel=\"Old password\" />\n\t\t<div class=\"external-label\">\n\t\t\t<label for=\"textField\">New password</label>\n\t\t\t<NcPasswordField v-model=\"text2\"\n\t\t\t\tid=\"textField\"\n\t\t\t\t:label-outside=\"true\"\n\t\t\t\tplaceholder=\"Min. 12 characters\" />\n\t\t</div>\n\t\t<div class=\"external-label\">\n\t\t\t<label for=\"textField2\">New password</label>\n\t\t\t<NcPasswordField v-model=\"text3\"\n\t\t\t\tid=\"textField2\"\n\t\t\t\t:error=\"true\"\n\t\t\t\t:label-outside=\"true\"\n\t\t\t\tplaceholder=\"Min. 12 characters\"\n\t\t\t\thelper-text=\"Password is insecure\" />\n\t\t</div>\n\n\t\t<NcPasswordField v-model=\"text4\"\n\t\t\tlabel=\"Good new password\"\n\t\t\t:success=\"true\"\n\t\t\tplaceholder=\"Min. 12 characters\"\n\t\t\thelper-text=\"Password is secure\">\n\t\t\t<template #icon>\n\t\t\t\t<IconLockOutline :size=\"20\" />\n\t\t\t</template>\n\t\t</NcPasswordField>\n\n\t\t<NcPasswordField v-model=\"text5\"\n\t\t\t:disabled=\"true\"\n\t\t\tlabel=\"Disabled\" />\n\n\t\t<NcPasswordField :value.sync=\"text6\"\n\t\t\tlabel=\"Secret token\"\n\t\t\tas-text />\n\t</div>\n</template>\n<script>\nimport IconLockOutline from 'vue-material-design-icons/LockOutline'\n\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\ttext1: '',\n\t\t\ttext2: '',\n\t\t\ttext3: 'hunter',\n\t\t\ttext4: '',\n\t\t\ttext5: '',\n\t\t\ttext6: 'secret-token',\n\t\t}\n\t},\n\n\tcomponents: {\n\t\tIconLockOutline,\n\t},\n}\n</script>\n<style lang=\"scss\" scoped>\n.wrapper {\n\tdisplay: flex;\n\tgap: 4px;\n\talign-items: flex-end;\n\tflex-wrap: wrap;\n}\n\n.external-label {\n\tdisplay: flex;\n\tgap: 14px;\n\twidth: 100%;\n\tmargin-top: 1rem;\n}\n\n.external-label label {\n\tpadding-top: 7px;\n\twhite-space: nowrap;\n}\n</style>\n```\n</docs>\n\n<script setup lang=\"ts\">\nimport type { Writable } from '../../utils/VueTypes.ts'\nimport type { NcInputFieldProps } from '../NcInputField/NcInputField.vue'\n\nimport { mdiEye, mdiEyeOff } from '@mdi/js'\nimport axios from '@nextcloud/axios'\nimport { getCapabilities } from '@nextcloud/capabilities'\nimport { generateOcsUrl } from '@nextcloud/router'\nimport debounce from 'debounce'\nimport { computed, ref, useTemplateRef, watch } from 'vue'\nimport NcIconSvgWrapper from '../NcIconSvgWrapper/NcIconSvgWrapper.vue'\nimport NcInputField from '../NcInputField/NcInputField.vue'\nimport { t } from '../../l10n.ts'\nimport { logger } from '../../utils/logger.ts'\n\nconst modelValue = defineModel<string>({ default: '' })\n\n/**\n * The visibility of the password.\n * If this is set to true then the password will not be obfuscated by the browser.\n */\nconst visible = defineModel<boolean>('visible', { default: false })\n\nconst props = withDefaults(defineProps<Omit<NcInputFieldProps, 'trailingButtonLabel' | 'type'> & {\n\t/**\n\t * Check if the user entered a valid password using the password_policy\n\t * app if available.\n\t *\n\t * Warning: this doesn't replace server side checking and will do nothing\n\t * if the password_policy app is disabled.\n\t */\n\tcheckPasswordStrength?: boolean\n\n\t/**\n\t * The minlength property defines the minimum number of characters\n\t * (as UTF-16 code units) the user can enter.\n\t */\n\tminlength?: number\n\n\t/**\n\t * Render as input[type=text] that looks like password field.\n\t * Allows to avoid unwanted password-specific browser behavior,\n\t * such as save or generate password prompt.\n\t * Useful for secret token fields.\n\t * Note: autocomplete=\"off\" is ignored by most browsers.\n\t */\n\tasText?: boolean\n}>(), {\n\tinputClass: '',\n\tminlength: undefined,\n\t// overwrite default\n\tshowTrailingButton: true,\n})\n\nconst emit = defineEmits<{\n\tvalid: []\n\tinvalid: []\n}>()\n\nwatch(modelValue, debounce(checkPassword, 500))\n\n// public API\ndefineExpose({\n\tfocus,\n\tselect,\n})\n\n// password policy\ninterface PasswordPolicy {\n\t/**\n\t * The URLs to the password_policy app methods\n\t */\n\tapi: {\n\t\t/**\n\t\t * The URL to the password generator\n\t\t */\n\t\tgenerate: string\n\n\t\t/**\n\t\t * The URL to the password validator\n\t\t */\n\t\tvalidate: string\n\t}\n\n\t/**\n\t * Whether to enforce non common passwords\n\t */\n\tenforceNonCommonPassword: boolean\n\n\t/**\n\t * Whether to enforce numeric characters\n\t */\n\tenforceNumericCharacters: boolean\n\n\t/**\n\t * Whether to enforce special characters\n\t */\n\tenforceSpecialCharacters: boolean\n\n\t/**\n\t * Whether to enforce upper and lower case\n\t */\n\tenforceUpperLowerCase: boolean\n\n\t/**\n\t * The minimum length of the password\n\t */\n\tminLength: number\n}\n\nconst { password_policy: passwordPolicy } = getCapabilities() as { password_policy?: PasswordPolicy }\n\n// internal state\nconst inputFieldInstance = useTemplateRef('inputField')\n\nconst internalHelpMessage = ref('')\nconst isValid = ref<boolean>()\n\nconst propsToForward = computed<Partial<NcInputFieldProps>>(() => {\n\tconst all = { ...props } as Partial<Writable<typeof props>>\n\t// our props\n\tdelete all.checkPasswordStrength\n\tdelete all.minlength\n\tdelete all.asText\n\t// other props already set in template\n\tdelete all.error\n\tdelete all.helperText\n\tdelete all.inputClass\n\tdelete all.success\n\n\treturn all satisfies Partial<NcInputFieldProps>\n})\n\nconst minLengthWithPolicy = computed(() => {\n\treturn props.minlength\n\t\t?? (props.checkPasswordStrength ? passwordPolicy?.minLength : undefined)\n\t\t?? undefined\n})\n\n/**\n * Validate the entered password.\n * If available this method will use the password-policy app API to validate the password.\n */\nasync function checkPassword() {\n\tif (!props.checkPasswordStrength) {\n\t\treturn\n\t}\n\n\ttry {\n\t\tconst { data } = await axios.post(generateOcsUrl('apps/password_policy/api/v1/validate'), { password: modelValue.value })\n\t\tisValid.value = data.ocs.data.passed\n\t\tif (data.ocs.data.passed) {\n\t\t\tinternalHelpMessage.value = t('Password is secure')\n\t\t\t/**\n\t\t\t * Triggers when the internal password_policy detect that the\n\t\t\t * password entered is valid.\n\t\t\t */\n\t\t\temit('valid')\n\t\t\treturn\n\t\t}\n\n\t\tinternalHelpMessage.value = data.ocs.data.reason\n\t\t/**\n\t\t * Triggers when the internal password_policy detect that the\n\t\t * password entered is invalid.\n\t\t */\n\t\temit('invalid')\n\t} catch (error) {\n\t\tlogger.error('Password policy returned an error', { error })\n\t}\n}\n\n/**\n * Toggle the visibility of the password\n */\nfunction toggleVisibility() {\n\tvisible.value = !visible.value\n}\n\n/**\n * Focus the input element\n *\n * @param options - Focus options\n * @public\n */\nfunction focus(options?: FocusOptions) {\n\tinputFieldInstance.value!.focus(options)\n}\n\n/**\n * Select all the text in the input\n *\n * @public\n */\nfunction select() {\n\tinputFieldInstance.value!.select()\n}\n</script>\n\n<template>\n\t<NcInputField\n\t\tv-bind=\"propsToForward\"\n\t\tref=\"inputField\"\n\t\tv-model=\"modelValue\"\n\t\t:error=\"error || isValid === false\"\n\t\t:helper-text=\"helperText || internalHelpMessage\"\n\t\t:input-class=\"[inputClass, { 'password-field__input--secure-text': !visible && asText }]\"\n\t\t:minlength=\"minLengthWithPolicy\"\n\t\t:success=\"success || isValid === true\"\n\t\t:trailing-button-label=\"visible ? t('Hide password') : t('Show password')\"\n\t\t:type=\"visible || asText ? 'text' : 'password'\"\n\t\t@trailing-button-click=\"toggleVisibility\">\n\t\t<template v-if=\"!!$slots.icon\" #icon>\n\t\t\t<!-- @slot Leading icon -->\n\t\t\t<slot name=\"icon\" />\n\t\t</template>\n\t\t<template #trailing-button-icon>\n\t\t\t<NcIconSvgWrapper :path=\"visible ? mdiEyeOff : mdiEye\" />\n\t\t</template>\n\t</NcInputField>\n</template>\n\n<style lang=\"scss\" scoped>\n:deep(.password-field__input--secure-text) {\n\t// Emulate password field look\n\t// This is not a part of the standard but well supported\n\t-webkit-text-security: disc;\n}\n</style>\n"],"names":["_useModel","_createBlock","_mergeProps","error","helperText","inputClass","asText","success","_unref","_createVNode","$slots","_renderSlot"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8GA,UAAM,aAAaA,SAAmB,SAAA,YAAgB;AAMtD,UAAM,UAAUA,SAAoB,SAAC,SAA6B;AAElE,UAAM,QAAQ;AA+Bd,UAAM,OAAO;AAKb,UAAM,YAAY,SAAS,eAAe,GAAG,CAAC;AAG9C,aAAa;AAAA,MACZ;AAAA,MACA;AAAA,IAAA,CACA;AA6CD,UAAM,EAAE,iBAAiB,eAAA,IAAmB,gBAAA;AAG5C,UAAM,qBAAqB,eAAe,YAAY;AAEtD,UAAM,sBAAsB,IAAI,EAAE;AAClC,UAAM,UAAU,IAAA;AAEhB,UAAM,iBAAiB,SAAqC,MAAM;AACjE,YAAM,MAAM,EAAE,GAAG,MAAA;AAEjB,aAAO,IAAI;AACX,aAAO,IAAI;AACX,aAAO,IAAI;AAEX,aAAO,IAAI;AACX,aAAO,IAAI;AACX,aAAO,IAAI;AACX,aAAO,IAAI;AAEX,aAAO;AAAA,IACR,CAAC;AAED,UAAM,sBAAsB,SAAS,MAAM;AAC1C,aAAO,MAAM,cACR,MAAM,wBAAwB,gBAAgB,YAAY,WAC3D;AAAA,IACL,CAAC;AAMD,mBAAe,gBAAgB;AAC9B,UAAI,CAAC,MAAM,uBAAuB;AACjC;AAAA,MACD;AAEA,UAAI;AACH,cAAM,EAAE,KAAA,IAAS,MAAM,MAAM,KAAK,eAAe,sCAAsC,GAAG,EAAE,UAAU,WAAW,OAAO;AACxH,gBAAQ,QAAQ,KAAK,IAAI,KAAK;AAC9B,YAAI,KAAK,IAAI,KAAK,QAAQ;AACzB,8BAAoB,QAAQ,EAAE,oBAAoB;AAKlD,eAAK,OAAO;AACZ;AAAA,QACD;AAEA,4BAAoB,QAAQ,KAAK,IAAI,KAAK;AAK1C,aAAK,SAAS;AAAA,MACf,SAAS,OAAO;AACf,eAAO,MAAM,qCAAqC,EAAE,MAAA,CAAO;AAAA,MAC5D;AAAA,IACD;AAKA,aAAS,mBAAmB;AAC3B,cAAQ,QAAQ,CAAC,QAAQ;AAAA,IAC1B;AAQA,aAAS,MAAM,SAAwB;AACtC,yBAAmB,MAAO,MAAM,OAAO;AAAA,IACxC;AAOA,aAAS,SAAS;AACjB,yBAAmB,MAAO,OAAA;AAAA,IAC3B;;0BAICC,YAmBe,cAnBfC,WAmBe,eAAA,OAlBQ;AAAA,QACtB,KAAI;AAAA,oBACK,WAAA;AAAA,qEAAA,WAAU,QAAA;AAAA,QAClB,OAAOC,KAAAA,SAAS,QAAA,UAAO;AAAA,QACvB,eAAaC,KAAAA,cAAc,oBAAA;AAAA,QAC3B,eAAW,CAAGC,KAAAA,YAAU,EAAA,sCAAA,CAA2C,QAAA,SAAWC,KAAAA,QAAM;AAAA,QACpF,WAAW,oBAAA;AAAA,QACX,SAASC,KAAAA,WAAW,QAAA,UAAO;AAAA,QAC3B,yBAAuB,QAAA,QAAUC,MAAA,CAAA,qBAAqBA,MAAA,CAAA,EAAC,eAAA;AAAA,QACvD,MAAM,QAAA,SAAWF,KAAAA,SAAM,SAAA;AAAA,QACvB,uBAAuB;AAAA,MAAA;QAKb,gCACV,MAAyD;AAAA,UAAzDG,YAAyD,kBAAA;AAAA,YAAtC,MAAM,QAAA,QAAUD,MAAA,SAAA,IAAYA,MAAA,MAAA;AAAA,UAAA;;;;QAL9BE,CAAAA,CAAAA,KAAAA,OAAO;gBAAO;AAAA,sBAE/B,MAAoB;AAAA,YAApBC,WAAoB,KAAA,QAAA,QAAA,CAAA,GAAA,QAAA,IAAA;AAAA,UAAA;;;;;;;;"}