@mutt/widget-email
Version:
Mutt Forms Widget - Email Input
2 lines (1 loc) • 12.3 kB
JavaScript
import MuttVue from"@mutt/widgets-vue";var script={name:"mutt-email",mixins:[MuttVue.mixin],data:()=>({errors:[],inputStyle:null,inputWidthCalcAreaStyle:null,maxSeekLimit:10,autoScale:!0,minFontSize:14,value:""}),watch:{"field.value":function(){this.fitText()}},methods:{init(){this.value=this.field.value,this.field.options.hasOwnProperty("autoScale")&&(this.autoScale=this.field.options.autoScale),this.field.options.hasOwnProperty("minFontSize")&&(this.autoScale||console.warn("Mutt Email: autoScale option set to false so minFontSize option will be ignored"),this.minFontSize=this.field.options.minFontSize)},callback(){this.field.validate(),0===this.errors.length&&this.$emit("callback",{action:"toggleOverlay",validated:!0}),this.field.options.callback&&this.field.options.callback(this.field)},focus(){this.$nextTick(()=>{this.$refs.input.focus(),this.fitText()})},fitText(){if(this.autoScale){const $input=this.$refs.input,inputWidth=$input.getBoundingClientRect().width,$inputWidthCalcArea=this.$refs.inputWidthCalcArea;this.inputStyle=null,this.inputWidthCalcAreaStyle=null,this.$nextTick(()=>{const maxFontSize=window.getComputedStyle($input).fontSize.split("px")[0];this.field.value&&this.shrinkTextTargetSeek(inputWidth,$inputWidthCalcArea,maxFontSize,0)})}},shrinkTextTargetSeek(inputWidth,$inputWidthCalcArea,maxFontSize,count){const textWidth=$inputWidthCalcArea.getBoundingClientRect().width,textToInputRatio=inputWidth/textWidth,currentFontSize=window.getComputedStyle($inputWidthCalcArea).fontSize.split("px")[0];if(textToInputRatio<=1.1&&textToInputRatio>=1||count===this.maxSeekLimit){const clampedFontSize=Math.max(Math.min(currentFontSize,maxFontSize),this.minFontSize);this.inputStyle={fontSize:`${clampedFontSize}px`}}else this.inputWidthCalcAreaStyle={fontSize:`${currentFontSize*textToInputRatio}px`},this.$nextTick(()=>{this.shrinkTextTargetSeek(inputWidth,$inputWidthCalcArea,maxFontSize,++count)})}}};function normalizeComponent(compiledTemplate,injectStyle,defaultExport,scopeId,isFunctionalTemplate,moduleIdentifier,isShadowMode,createInjector,createInjectorSSR,createInjectorShadow){"function"==typeof isShadowMode&&(createInjectorSSR=createInjector,createInjector=isShadowMode,isShadowMode=!1);const options="function"==typeof defaultExport?defaultExport.options:defaultExport;let hook;if(compiledTemplate&&compiledTemplate.render&&(options.render=compiledTemplate.render,options.staticRenderFns=compiledTemplate.staticRenderFns,options._compiled=!0,isFunctionalTemplate&&(options.functional=!0)),scopeId&&(options._scopeId=scopeId),moduleIdentifier?(hook=function(context){(context=context||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(context=__VUE_SSR_CONTEXT__),injectStyle&&injectStyle.call(this,createInjectorSSR(context)),context&&context._registeredComponents&&context._registeredComponents.add(moduleIdentifier)},options._ssrRegister=hook):injectStyle&&(hook=isShadowMode?function(){injectStyle.call(this,createInjectorShadow(this.$root.$options.shadowRoot))}:function(context){injectStyle.call(this,createInjector(context))}),hook)if(options.functional){const originalRender=options.render;options.render=function renderWithStyleInjection(h,context){return hook.call(context),originalRender(h,context)}}else{const existing=options.beforeCreate;options.beforeCreate=existing?[].concat(existing,hook):[hook]}return defaultExport}const isOldIE="undefined"!=typeof navigator&&/msie [6-9]\\b/.test(navigator.userAgent.toLowerCase());function createInjector(context){return(id,style)=>addStyle(id,style)}const HEAD=document.head||document.getElementsByTagName("head")[0],styles={};function addStyle(id,css){const group=isOldIE?css.media||"default":id,style=styles[group]||(styles[group]={ids:new Set,styles:[]});if(!style.ids.has(id)){style.ids.add(id);let code=css.source;if(css.map&&(code+="\n/*# sourceURL="+css.map.sources[0]+" */",code+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(css.map))))+" */"),style.element||(style.element=document.createElement("style"),style.element.type="text/css",css.media&&style.element.setAttribute("media",css.media),HEAD.appendChild(style.element)),"styleSheet"in style.element)style.styles.push(code),style.element.styleSheet.cssText=style.styles.filter(Boolean).join("\n");else{const index=style.ids.size-1,textNode=document.createTextNode(code),nodes=style.element.childNodes;nodes[index]&&style.element.removeChild(nodes[index]),nodes.length?style.element.insertBefore(textNode,nodes[index]):style.element.appendChild(textNode)}}}const __vue_script__=script;var __vue_render__=function(){var _vm=this,_h=_vm.$createElement,_c=_vm._self._c||_h;return _c("div",[_vm.field?_c("div",{class:_vm.getFieldWrapperClass()},[_c("label-widget",{attrs:{field:_vm.field,"field-id":_vm.getFieldId(),"data-qa-locator":_vm.qaLocator?_vm.qaLocator+"-label":null}}),_vm._v(" "),_c("input",{ref:"input",staticClass:"mutt-field mutt-field-text",style:_vm.inputStyle,attrs:{type:"email",name:_vm.field.name},domProps:{value:_vm.getFieldValue},on:{input:function($event){_vm.value=$event.target.value},keydown:function($event){return!$event.type.indexOf("key")&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")?null:($event.preventDefault(),_vm.callback($event))}}}),_vm._v(" "),_c("help-widget",{attrs:{field:_vm.field}}),_vm._v(" "),_vm.isReadOnly?_vm._e():_c("error-widget",{attrs:{field:_vm.field,errors:_vm.errors,"error-class":_vm.getErrorClass()}}),_vm._v(" "),_c("span",{ref:"inputWidthCalcArea",staticClass:"input-width-calc-area",style:_vm.inputWidthCalcAreaStyle,attrs:{"aria-hidden":"true"}},[_vm._v(_vm._s(_vm.field.value))])],1):_vm._e()])},__vue_staticRenderFns__=[];__vue_render__._withStripped=!0;const __vue_inject_styles__=function(inject){inject&&inject("data-v-2cb9e06f_0",{source:".input-width-calc-area[data-v-2cb9e06f] {\n position: absolute;\n top: 0;\n left: 0;\n visibility: hidden;\n white-space: nowrap;\n pointer-events: none;\n padding: 0 10px;\n box-sizing: content-box;\n}\n\n/*# sourceMappingURL=email.vue.map */",map:{version:3,sources:["/Users/sambateman/Documents/projects/widget-email/src/email.vue","email.vue"],names:[],mappings:"AAwKA;EACA,kBAAA;EACA,MAAA;EACA,OAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,uBAAA;AAAA;;ACtKA,oCAAoC",file:"email.vue",sourcesContent:['<template>\n <div>\n <div v-if="field" :class="getFieldWrapperClass()">\n <label-widget\n :field="field"\n :field-id="getFieldId()"\n :data-qa-locator="qaLocator ? `${qaLocator}-label` : null"\n />\n\n \x3c!-- eslint-disable-next-line vue-a11y/form-has-label - label is programmatically linked to field with label widget --\x3e\n <input\n ref="input"\n :value="getFieldValue"\n @input="value = $event.target.value"\n type="email"\n class="mutt-field mutt-field-text"\n :style="inputStyle"\n :name="field.name"\n @keydown.enter.prevent="callback"\n />\n\n <help-widget :field="field" />\n\n <error-widget\n v-if="!isReadOnly"\n :field="field"\n :errors="errors"\n :error-class="getErrorClass()"\n />\n\n <span\n ref="inputWidthCalcArea"\n :style="inputWidthCalcAreaStyle"\n aria-hidden="true"\n class="input-width-calc-area"\n >{{ field.value }}</span\n >\n </div>\n </div>\n</template>\n\n<script>\nimport MuttVue from \'@mutt/widgets-vue\'\n\nexport default {\n name: \'mutt-email\',\n mixins: [MuttVue.mixin],\n data() {\n return {\n errors: [],\n inputStyle: null,\n inputWidthCalcAreaStyle: null,\n maxSeekLimit: 10, // allows an escape valve in case seeking gets stuck in a loop\n autoScale: true,\n minFontSize: 14,\n value: \'\',\n }\n },\n watch: {\n \'field.value\': function() {\n this.fitText()\n },\n },\n methods: {\n init() {\n // set the component value. This overrides the default init() statement in the mixin.\n this.value = this.field.value\n if (this.field.options.hasOwnProperty(\'autoScale\')) {\n this.autoScale = this.field.options.autoScale\n }\n if (this.field.options.hasOwnProperty(\'minFontSize\')) {\n if (!this.autoScale) {\n console.warn(\n \'Mutt Email: autoScale option set to false so minFontSize option will be ignored\'\n )\n }\n this.minFontSize = this.field.options.minFontSize\n }\n },\n callback() {\n this.field.validate()\n if (this.errors.length === 0) {\n this.$emit(\'callback\', {\n action: \'toggleOverlay\',\n validated: true,\n })\n }\n\n // fields can have an optional callback, this happens\n // everytime, regardless of validation preferences\n if (this.field.options.callback) {\n this.field.options.callback(this.field)\n }\n },\n focus() {\n this.$nextTick(() => {\n this.$refs.input.focus()\n this.fitText()\n })\n },\n fitText() {\n if (this.autoScale) {\n const $input = this.$refs.input\n const inputWidth = $input.getBoundingClientRect().width\n const $inputWidthCalcArea = this.$refs.inputWidthCalcArea\n\n // reset styles back to css\n this.inputStyle = null\n this.inputWidthCalcAreaStyle = null\n\n this.$nextTick(() => {\n const maxFontSize = window\n .getComputedStyle($input)\n .fontSize.split(\'px\')[0]\n\n if (this.field.value) {\n this.shrinkTextTargetSeek(\n inputWidth,\n $inputWidthCalcArea,\n maxFontSize,\n 0\n )\n }\n })\n }\n },\n shrinkTextTargetSeek(inputWidth, $inputWidthCalcArea, maxFontSize, count) {\n const textWidth = $inputWidthCalcArea.getBoundingClientRect().width\n const textToInputRatio = inputWidth / textWidth // smaller value means hidden span is bigger\n // get the font size of the hidden span\n const currentFontSize = window\n .getComputedStyle($inputWidthCalcArea)\n .fontSize.split(\'px\')[0]\n\n if (\n // target seek to within 10% smaller - close enough but allows for slightly quicker resolution\n (textToInputRatio <= 1.1 && textToInputRatio >= 1) ||\n count === this.maxSeekLimit\n ) {\n // update the font size of the input to match the textWidth\n const clampedFontSize = Math.max(\n Math.min(currentFontSize, maxFontSize),\n this.minFontSize\n )\n this.inputStyle = {\n fontSize: `${clampedFontSize}px`,\n }\n return\n }\n\n // adjust the font size by the ratio of the two inputs\n this.inputWidthCalcAreaStyle = {\n fontSize: `${currentFontSize * textToInputRatio}px`,\n }\n this.$nextTick(() => {\n this.shrinkTextTargetSeek(\n inputWidth,\n $inputWidthCalcArea,\n maxFontSize,\n ++count\n )\n })\n },\n },\n}\n<\/script>\n\n<style scoped lang="scss">\n.input-width-calc-area {\n position: absolute;\n top: 0;\n left: 0;\n visibility: hidden;\n white-space: nowrap; // ensure it doesn\'t wrap - important for calculating width correctly\n pointer-events: none; // ensure it doesn\'t get in the way of other clicking elements\n padding: 0 10px;\n box-sizing: content-box; // ensure the padding is included in the width calculation\n}\n</style>\n',".input-width-calc-area {\n position: absolute;\n top: 0;\n left: 0;\n visibility: hidden;\n white-space: nowrap;\n pointer-events: none;\n padding: 0 10px;\n box-sizing: content-box; }\n\n/*# sourceMappingURL=email.vue.map */"]},media:void 0})},__vue_scope_id__="data-v-2cb9e06f",__vue_module_identifier__=void 0,__vue_is_functional_template__=!1;var email=normalizeComponent({render:__vue_render__,staticRenderFns:__vue_staticRenderFns__},__vue_inject_styles__,__vue_script__,__vue_scope_id__,!1,void 0,createInjector,void 0);export default email;