From e0a9c12690b775be8e99cf9ef2fd513e3af4ccbd Mon Sep 17 00:00:00 2001 From: Adam DeHaven Date: Wed, 9 Jun 2021 14:36:24 -0400 Subject: [PATCH] Refactoring for Vue 3 and TypeScript :tada: --- .browserslistrc | 3 + .editorconfig | 15 +- .eslintrc.js | 20 + .gitignore | 5 +- .prettierignore | 38 +- .prettierrc | 2 +- README.md | 225 +- babel.config.js | 3 + dist/VueCustomTooltip.vue.d.ts | 42 + dist/index.d.ts | 7 + dist/types.d.ts | 8 + dist/vue-custom-tooltip.cjs.js | 154 + dist/vue-custom-tooltip.esm.js | 512 +- dist/vue-custom-tooltip.min.js | 400 +- dist/vue-custom-tooltip.ssr.js | 391 - package-lock.json | 36093 +++++++++++++++++++++++++++++++ package.json | 81 +- public/index.html | 22 + rollup.config.js | 73 +- src/App.vue | 15 + src/VueCustomTooltip.vue | 353 +- src/index.ts | 52 + src/main.ts | 18 + src/shims-vue.d.ts | 6 + src/tooltip.scss | 166 + src/types.ts | 15 + src/wrapper.js | 81 - tsconfig.json | 49 + vue.config.js | 13 - yarn.lock | 8585 -------- 30 files changed, 37162 insertions(+), 10285 deletions(-) create mode 100644 .browserslistrc create mode 100644 .eslintrc.js create mode 100644 babel.config.js create mode 100644 dist/VueCustomTooltip.vue.d.ts create mode 100644 dist/index.d.ts create mode 100644 dist/types.d.ts create mode 100644 dist/vue-custom-tooltip.cjs.js delete mode 100644 dist/vue-custom-tooltip.ssr.js create mode 100644 package-lock.json create mode 100644 public/index.html create mode 100644 src/App.vue create mode 100644 src/index.ts create mode 100644 src/main.ts create mode 100644 src/shims-vue.d.ts create mode 100644 src/tooltip.scss create mode 100644 src/types.ts delete mode 100644 src/wrapper.js create mode 100644 tsconfig.json delete mode 100644 vue.config.js delete mode 100644 yarn.lock diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..214388f --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,3 @@ +> 1% +last 2 versions +not dead diff --git a/.editorconfig b/.editorconfig index 9cbf5e5..5d12634 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,12 +1,13 @@ -# EditorConfig is awesome: https://EditorConfig.org - -# top-most EditorConfig file +# editorconfig.org root = true -# Unix-style newlines with a newline ending every file [*] +indent_style = space +indent_size = 2 end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true insert_final_newline = true -trim_trailing_whitespace=true -indent_style=space -indent_size=2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..a6f3b7a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,20 @@ +module.exports = { + root: true, + env: { + node: true, + }, + extends: [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/typescript/recommended', + '@vue/prettier', + '@vue/prettier/@typescript-eslint', + ], + parserOptions: { + ecmaVersion: 2020, + }, + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + }, +} diff --git a/.gitignore b/.gitignore index 8c8cf23..7df6d18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ .DS_Store node_modules -# env files -.env +# local env files +.env.local +.env.*.local # Log files npm-debug.log* diff --git a/.prettierignore b/.prettierignore index e35e2ef..11f5d71 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,28 +1,22 @@ -# Dist -dist/ - -# node modules +.DS_Store node_modules -package.json -package-lock.json +/dist -# MacOS desktop services store -.DS_Store +# local env files +.env.local +.env.*.local # Log files -*.log - -# Map files -*.map +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* -# Editors +# Editor directories and files +.idea .vscode - -# VuePress temp directory -.temp - -# Source Control -.github - -# Secure -.env +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.prettierrc b/.prettierrc index 72619d9..8fdb761 100644 --- a/.prettierrc +++ b/.prettierrc @@ -12,5 +12,5 @@ "tabWidth": 2, "trailingComma": "all", "useTabs": false, - "vueIndentScriptAndStyle": true + "vueIndentScriptAndStyle": false } diff --git a/README.md b/README.md index eaa903a..1e009f0 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ # Vue-Custom-Tooltip -A customizable, reusable, and reactive tooltip component for Vue (and VuePress) projects. +A customizable, reusable, and reactive tooltip component for Vue 2 & 3 (and VuePress) projects. ![Tooltip Vue component examples](img/vue-custom-tooltip-examples.png) ## Installation +Installation instructions depend on the version of Vue.js you are using (2 or 3). + +### Vue 2 + ```sh # With npm npm install @adamdehaven/vue-custom-tooltip @@ -14,12 +18,26 @@ npm install @adamdehaven/vue-custom-tooltip yarn add @adamdehaven/vue-custom-tooltip ``` -### Vue.js (Global Install) +### Vue 3 (including TypeScript compatibility) + +When installing, make sure to pass the `@next` tag + +```sh +# With npm +npm install @adamdehaven/vue-custom-tooltip@next + +# or Yarn +yarn add @adamdehaven/vue-custom-tooltip@next +``` + +## Initialize Plugin + +### Vue.js (globally available) -It is recommended to install the plugin in your Vue project's entry file. For projects created with [`@vue/cli`](https://cli.vuejs.org/), this is likely your `main.js` file where you are already importing `Vue`. +It is recommended to initialize the plugin in your Vue project's entry file. For projects created with [`@vue/cli`](https://cli.vuejs.org/), this is likely your `main.{js|ts}` file where you are already importing `Vue`. ```js -// main.js (or your Vue entry file) +// main.{js|ts} (or your Vue entry file) // Import Vue... you're probably already doing this import Vue from 'vue' @@ -40,12 +58,130 @@ Vue.use(VueCustomTooltip, { name: 'VueCustomTooltip', color: '#fff', background: '#000', - borderRadius: 12, + borderRadius: 100, fontWeight: 400, }) ``` -### VuePress (Global Install) +### In-Component (locally available) + +Alternatively, you may initialize the component directly within a single file in your project. + +**Notes on in-component initialization**: + +- Initializing within a component does not allow for customizing the [Plugin Options](#options); however, you may still utilize all [`props`](#props) on the `` element. + +```html + + + +``` + +### Via CDN + +Load the tooltip component after first importing Vue. + +**Notes on CDN Import with Vue 2**: + +- Initializing via CDN with Vue 2 requires using the kebab-case component name. +- Initializing via CDN with Vue 2 does not allow for customizing the [Plugin Options](#options); however, you may still utilize all [`props`](#props) on the `` element. + +```html + +
+ +

This is a tooltip.

+
+ + + + + + + + + + + + + + + + +``` + +### Manual / Local Import + +Download the correct version of `dist/vue-custom-tooltip.min.js` based on your version of Vue, and include it in your file after importing Vue. + +**Notes on Manual / Local Import with Vue 2**: + +- Initializing manually with Vue 2 requires using the kebab-case component name. +- Initializing manually with Vue 2 does not allow for customizing the [Plugin Options](#options); however, you may still utilize all [`props`](#props) on the `` element. + +```html + +
+ +

This is a tooltip.

+
+ + + + + + + + + + + + + + + + + +``` + +### VuePress (Global) > **VuePress Standalone Plugin** > @@ -84,67 +220,12 @@ export default ({ name: 'VueCustomTooltip', color: '#fff', background: '#000', - borderRadius: 12, + borderRadius: 100, fontWeight: 400, }) } ``` -### In-Component Install - -Alternatively, you may install the component directly within a single file in your project; however, you will not be able to customize the `Vue.use()` options. - -```html - - - -``` - -**Note**: Installing inside a single component (instead of globally) does not allow you to customize the [Plugin Options](#options); however, you may still utilize all [`props`](#props) on the `` element. - -### CDN - -Import the tooltip component after importing Vue in your file after importing Vue. Installing via CDN does not allow for customizing [Plugin Options](#options). - -Installing via the CDN requires using the kebob-case component name. - -```html - - - - - - -

This is a tooltip.

-``` - -### Manual - -Download `dist/vue-custom-tooltip.min.js` and include it in your file after importing Vue. Installing manually does not allow for customizing [Plugin Options](#options). - -Installing manually requires using the kebob-case component name. - -```html - - - - - - -

This is a tooltip.

-``` - ## Usage ```html @@ -177,29 +258,29 @@ Pass any of the options listed below to `Vue.use(VueCustomTooltip, {...})` to cu - Type: `String` - Default: `VueCustomTooltip` -Customize the name of the component you will use in your project. **Camel-case names are preferred**, as this allows for camel-case or kebob-case usage within your project. +Customize the name of the component you will use in your project. **PascalCase names are preferred**, as this allows for PascalCase or kebab-case usage within your project. ```js Vue.use(VueCustomTooltip, { - name: 'SuperCoolTooltip', // camel-case preferred + name: 'SuperCoolTooltip', // PascalCase preferred }) ``` -If you registered the name using camel-case, you can reference the tooltip component via camel-case _or_ kebob-case: +If you registered the name using PascalCase, you can reference the tooltip component via PascalCase _or_ kebab-case: ```html - + Nice tooltip! - + Nice tooltip! - + Nice tooltip! - + Nice tooltip! ``` @@ -232,13 +313,13 @@ Vue.use(VueCustomTooltip, { ### `borderRadius` - Type: `Number` -- Default: `12` +- Default: `100` Customize the border-radius of the tooltip. Must be an integer. ```js Vue.use(VueCustomTooltip, { - borderRadius: 4, + borderRadius: 24, }) ``` @@ -327,7 +408,7 @@ Add a dotted border under the contained text (the same color as the [background] - Type: `Boolean` - Default: `false` -Allows the tooltip text to wrap to multiple lines as needed. Can be used in conjunction with the [`size`](#size) prop to adjust the width of the tooltip. +Allows the tooltip text to wrap to multiple lines as needed. If set to true, automatically sets the [`size`](#size) prop to `is-large`. ### `size` diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..c1b783e --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['@vue/cli-plugin-babel/preset'], +} diff --git a/dist/VueCustomTooltip.vue.d.ts b/dist/VueCustomTooltip.vue.d.ts new file mode 100644 index 0000000..515dc17 --- /dev/null +++ b/dist/VueCustomTooltip.vue.d.ts @@ -0,0 +1,42 @@ +declare const _default: import("vue").DefineComponent<{ + label: StringConstructor; + active: { + type: BooleanConstructor; + default: boolean; + }; + sticky: BooleanConstructor; + multiline: BooleanConstructor; + underlined: BooleanConstructor; + abbreviation: BooleanConstructor; + position: { + type: StringConstructor; + default: string; + validator(this: void, value: string): boolean; + }; + size: { + type: StringConstructor; + default: string; + validator(this: void, value: string): boolean; + }; +}, () => import("vue").VNode[], unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<{ + active: boolean; + sticky: boolean; + multiline: boolean; + underlined: boolean; + abbreviation: boolean; + position: string; + size: string; +} & { + label?: string | undefined; +}>, { + active: boolean; + sticky: boolean; + multiline: boolean; + underlined: boolean; + abbreviation: boolean; + position: string; + size: string; +}>; +export default _default; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..6fdaaf6 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,7 @@ +import { App } from 'vue'; +import { TooltipOptions } from './types'; +import './tooltip.scss'; +declare const _default: { + install: (app: App, options?: TooltipOptions | undefined) => void; +}; +export default _default; diff --git a/dist/types.d.ts b/dist/types.d.ts new file mode 100644 index 0000000..a8cda52 --- /dev/null +++ b/dist/types.d.ts @@ -0,0 +1,8 @@ +export interface TooltipOptions { + name?: string; + color?: string; + background?: string; + borderRadius?: number; + fontWeight?: number; +} +export declare const defaultTooltipOptions: TooltipOptions; diff --git a/dist/vue-custom-tooltip.cjs.js b/dist/vue-custom-tooltip.cjs.js new file mode 100644 index 0000000..7dd57e9 --- /dev/null +++ b/dist/vue-custom-tooltip.cjs.js @@ -0,0 +1,154 @@ +'use strict'; + +var vue = require('vue'); + +var defaultTooltipOptions = { + name: 'VueCustomTooltip', + color: '#fff', + background: '#000', + borderRadius: 100, + fontWeight: 400, +}; + +var script = vue.defineComponent({ + name: 'VueCustomTooltip', + props: { + label: String, + // If user hovers (or sticky) should the tooltip show? + active: { + type: Boolean, + default: true, + }, + sticky: Boolean, + multiline: Boolean, + underlined: Boolean, + abbreviation: Boolean, + // Where to position the tooltip + position: { + type: String, + default: 'is-top', + validator: function validator(value) { + return ['is-top', 'is-bottom', 'is-left', 'is-right'].indexOf(value) > -1; + }, + }, + // Size of the tooltip - forces 'is-large' if multiline is true + size: { + type: String, + default: 'is-medium', + validator: function validator(value) { + return ['is-small', 'is-medium', 'is-large'].indexOf(value) > -1; + }, + }, + }, + setup: function setup(props, ref) { + var slots = ref.slots; + var attrs = ref.attrs; + + var tooltipOptions = vue.inject('vue-custom-tooltip', defaultTooltipOptions); + var setCssVars = function () { + var htmlRoot = document && document.documentElement ? document.documentElement : null; + if (htmlRoot) { + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + htmlRoot.style.setProperty('--vue-custom-tooltip-color', tooltipOptions.color); + htmlRoot.style.setProperty('--vue-custom-tooltip-background', tooltipOptions.background); + htmlRoot.style.setProperty('--vue-custom-tooltip-border-radius', ((tooltipOptions.borderRadius) + "px")); + htmlRoot.style.setProperty('--vue-custom-tooltip-font-weight', tooltipOptions.fontWeight.toString()); + /* eslint-enable @typescript-eslint/no-non-null-assertion */ + } + }; + vue.onMounted(setCssVars); + return function () { return [ + vue.h(props.abbreviation ? 'abbr' : 'span', Object.assign({}, attrs, { + 'class': [ + props.position, + props.multiline ? 'is-large' : props.size, + { + 'vue-custom-tooltip': props.active && props.label, + 'is-sticky': props.sticky, + 'has-multiline': props.multiline, + 'is-underlined': props.underlined || props.abbreviation, + } ], + 'data-label': props.label, + 'aria-label': props.label, + 'role': 'tooltip', + 'style': [{ cursor: props.abbreviation ? 'help' : 'pointer' }], + }), slots) ]; }; + }, +}); + +script.__file = "src/VueCustomTooltip.vue"; + +function styleInject(css, ref) { + if ( ref === void 0 ) { ref = {}; } + var insertAt = ref.insertAt; + + if (!css || typeof document === 'undefined') { return; } + + var head = document.head || document.getElementsByTagName('head')[0]; + var style = document.createElement('style'); + style.type = 'text/css'; + + if (insertAt === 'top') { + if (head.firstChild) { + head.insertBefore(style, head.firstChild); + } else { + head.appendChild(style); + } + } else { + head.appendChild(style); + } + + if (style.styleSheet) { + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } +} + +var css_248z = ".vue-custom-tooltip{position:relative;display:inline-block;text-decoration-line:none!important}.vue-custom-tooltip.is-top:after,.vue-custom-tooltip.is-top:before{top:auto;right:auto;bottom:calc(100% + 7px);left:50%;transform:translateX(-50%)}.vue-custom-tooltip.is-top:before{border-top:5px solid #000;border-top:5px solid var(--vue-custom-tooltip-background,#000);border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.vue-custom-tooltip.is-top.has-multiline.is-small:after{width:140px}.vue-custom-tooltip.is-top.has-multiline.is-medium:after{width:210px}.vue-custom-tooltip.is-top.has-multiline.is-large:after{width:340px;padding:.5rem 1rem .65rem}.vue-custom-tooltip.is-right:after,.vue-custom-tooltip.is-right:before{top:50%;right:auto;bottom:auto;left:calc(100% + 7px);transform:translateY(-50%)}.vue-custom-tooltip.is-right:before{border-top:5px solid transparent;border-right:5px solid #000;border-right:5px solid var(--vue-custom-tooltip-background,#000);border-bottom:5px solid transparent;left:calc(100% + 3px)}.vue-custom-tooltip.is-right.has-multiline.is-small:after{width:140px}.vue-custom-tooltip.is-right.has-multiline.is-medium:after{width:210px}.vue-custom-tooltip.is-right.has-multiline.is-large:after{width:340px;padding:.5rem 1rem .65rem}.vue-custom-tooltip.is-bottom:after,.vue-custom-tooltip.is-bottom:before{top:calc(100% + 7px);right:auto;bottom:auto;left:50%;transform:translateX(-50%)}.vue-custom-tooltip.is-bottom:before{border-right:5px solid transparent;border-bottom:5px solid #000;border-bottom:5px solid var(--vue-custom-tooltip-background,#000);border-left:5px solid transparent;top:calc(100% + 2px)}.vue-custom-tooltip.is-bottom.has-multiline.is-small:after{width:140px}.vue-custom-tooltip.is-bottom.has-multiline.is-medium:after{width:210px}.vue-custom-tooltip.is-bottom.has-multiline.is-large:after{width:340px;padding:.5rem 1rem .65rem}.vue-custom-tooltip.is-left:after,.vue-custom-tooltip.is-left:before{top:50%;right:calc(100% + 7px);bottom:auto;left:auto;transform:translateY(-50%)}.vue-custom-tooltip.is-left:before{border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000;border-left:5px solid var(--vue-custom-tooltip-background,#000);right:calc(100% + 3px)}.vue-custom-tooltip.is-left.has-multiline.is-small:after{width:140px}.vue-custom-tooltip.is-left.has-multiline.is-medium:after{width:210px}.vue-custom-tooltip.is-left.has-multiline.is-large:after{width:340px;padding:.5rem 1rem .65rem}.vue-custom-tooltip.is-underlined{border-bottom:1px dotted #000;border-bottom:1px dotted var(--vue-custom-tooltip-background,#000);line-height:1.2}.vue-custom-tooltip:after,.vue-custom-tooltip:before{position:absolute;content:\"\";opacity:0;visibility:hidden;pointer-events:none;transition:opacity 86ms ease-out,visibility 86ms ease-out}.vue-custom-tooltip:before{z-index:889}.vue-custom-tooltip:after{content:attr(data-label);color:#fff;color:var(--vue-custom-tooltip-color,#fff);background:#000;background:var(--vue-custom-tooltip-background,#000);width:auto;padding:.45rem .75rem;border-radius:12px;border-radius:var(--vue-custom-tooltip-border-radius,100px);font-size:.85rem!important;font-weight:400;font-weight:var(--vue-custom-tooltip-font-weight,400);line-height:1.3;letter-spacing:normal!important;text-transform:none;box-shadow:0 1px 2px 1px rgba(0,1,0,.2);z-index:888;white-space:nowrap}.vue-custom-tooltip:not([data-label=\"\"]):hover:after,.vue-custom-tooltip:not([data-label=\"\"]):hover:before{opacity:1;visibility:visible}:disabled .vue-custom-tooltip{pointer-events:none}.vue-custom-tooltip:not([data-label=\"\"]).is-sticky:after,.vue-custom-tooltip:not([data-label=\"\"]).is-sticky:before{opacity:1;visibility:visible}.vue-custom-tooltip.has-multiline:after{display:flex-block;padding:.5rem .75rem .65rem;text-align:center;line-height:1.4;white-space:pre-wrap}"; +styleInject(css_248z); + +var index = { + install: function (app, options) { + var userOptions = Object.assign({}, options); + /** + * Validate HEX values + * Hash, plus 3 or 6 valid characters + */ + var hexRegex = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/; + // Test color for valid HEX + if (userOptions && userOptions.color && !hexRegex.test(userOptions.color)) { + delete userOptions.color; + } + // Test background for valid HEX + if (userOptions && userOptions.background && !hexRegex.test(userOptions.background)) { + delete userOptions.background; + } + /** + * Validate borderRadius + * Number between 1-9, then any other numbers + */ + var borderRadiusRegex = /^[0-9]+$/; + // Test borderRadius for integer + if (userOptions && userOptions.borderRadius && !borderRadiusRegex.test(userOptions.borderRadius.toString())) { + delete userOptions.borderRadius; + } + /** + * Validate fontWeight + * Number between 1-9 followed by two zeros + */ + var fontWeightRegex = /^[1-9]{1}00$/; + // Test fontWeight for integer + if (userOptions && userOptions.fontWeight && !fontWeightRegex.test(userOptions.fontWeight.toString())) { + delete userOptions.fontWeight; + } + // Merge defaults with user options + var pluginOptions = Object.assign({}, defaultTooltipOptions, userOptions); + app.provide('vue-custom-tooltip', pluginOptions); + // Register component, using options.name + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ + app.component(pluginOptions.name, script); + }, +}; + +module.exports = index; diff --git a/dist/vue-custom-tooltip.esm.js b/dist/vue-custom-tooltip.esm.js index 9829002..366f061 100644 --- a/dist/vue-custom-tooltip.esm.js +++ b/dist/vue-custom-tooltip.esm.js @@ -1,390 +1,152 @@ -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// - -var script = { - name: 'VueCustomTooltip', - props: { - label: String, - active: { - type: Boolean, - default: true, - }, - sticky: Boolean, // Always showing - multiline: Boolean, // Multiple lines - underlined: Boolean, - abbreviation: Boolean, - position: { - type: String, - default: 'is-top', - validator: function validator(value) { - return ['is-top', 'is-bottom', 'is-left', 'is-right'].indexOf(value) > -1 - }, - }, - size: { - type: String, - default: 'is-medium', - validator: function validator(value) { - return ['is-small', 'is-medium', 'is-large'].indexOf(value) > -1 - }, - }, - }, - data: function data() { - return { - labelText: this.label || null, - isActive: this.active || true, - isSticky: this.sticky || false, - isMultiline: this.multiline || false, - isUnderlined: this.underlined || false, - isAbbreviation: this.abbreviation || false, - hasPosition: this.position || 'is-top', - hasSize: this.size || 'is-medium', - } - }, - computed: { - dynamicStyles: function dynamicStyles() { - return { - '--vue-custom-tooltip-color': - this.$vueCustomTooltip && this.$vueCustomTooltip.hasOwnProperty('color') - ? this.$vueCustomTooltip.color - : null, - '--vue-custom-tooltip-background': - this.$vueCustomTooltip && this.$vueCustomTooltip.hasOwnProperty('background') - ? this.$vueCustomTooltip.background - : null, - '--vue-custom-tooltip-border-radius': - this.$vueCustomTooltip && this.$vueCustomTooltip.hasOwnProperty('borderRadius') - ? this.$vueCustomTooltip.borderRadius - : null, - '--vue-custom-tooltip-font-weight': - this.$vueCustomTooltip && this.$vueCustomTooltip.hasOwnProperty('fontWeight') - ? this.$vueCustomTooltip.fontWeight - : null, - } - }, - }, - watch: { - label: { - handler: function handler(value) { - this.labelText = value; - }, - immediate: true, - }, - active: { - handler: function handler(value) { - this.isActive = value; - }, - immediate: true, - }, - sticky: { - handler: function handler(value) { - this.isSticky = value; - }, - immediate: true, - }, - multiline: { - handler: function handler(value) { - this.isMultiline = value; - }, - immediate: true, - }, - underlined: { - handler: function handler(value) { - this.isUnderlined = value; - }, - immediate: true, - }, - abbreviation: { - handler: function handler(value) { - this.isAbbreviation = value; - }, - immediate: true, - }, - position: { - handler: function handler(value) { - this.hasPosition = value; - }, - immediate: true, - }, - size: { - handler: function handler(value) { - this.hasSize = value; - }, - immediate: true, - }, - }, +import { defineComponent, inject, onMounted, h } from 'vue'; + +var defaultTooltipOptions = { + name: 'VueCustomTooltip', + color: '#fff', + background: '#000', + borderRadius: 100, + fontWeight: 400, }; -function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { - if (typeof shadowMode !== 'boolean') { - createInjectorSSR = createInjector; - createInjector = shadowMode; - shadowMode = false; - } - // Vue.extend constructor export interop. - var options = typeof script === 'function' ? script.options : script; - // render functions - if (template && template.render) { - options.render = template.render; - options.staticRenderFns = template.staticRenderFns; - options._compiled = true; - // functional template - if (isFunctionalTemplate) { - options.functional = true; - } - } - // scopedId - if (scopeId) { - options._scopeId = scopeId; - } - var hook; - if (moduleIdentifier) { - // server build - hook = function (context) { - // 2.3 injection - context = - context || // cached call - (this.$vnode && this.$vnode.ssrContext) || // stateful - (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional - // 2.2 with runInNewContext: true - if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { - context = __VUE_SSR_CONTEXT__; - } - // inject component styles - if (style) { - style.call(this, createInjectorSSR(context)); - } - // register component module identifier for async chunk inference - if (context && context._registeredComponents) { - context._registeredComponents.add(moduleIdentifier); +var script = defineComponent({ + name: 'VueCustomTooltip', + props: { + label: String, + // If user hovers (or sticky) should the tooltip show? + active: { + type: Boolean, + default: true, + }, + sticky: Boolean, + multiline: Boolean, + underlined: Boolean, + abbreviation: Boolean, + // Where to position the tooltip + position: { + type: String, + default: 'is-top', + validator: function validator(value) { + return ['is-top', 'is-bottom', 'is-left', 'is-right'].indexOf(value) > -1; + }, + }, + // Size of the tooltip - forces 'is-large' if multiline is true + size: { + type: String, + default: 'is-medium', + validator: function validator(value) { + return ['is-small', 'is-medium', 'is-large'].indexOf(value) > -1; + }, + }, + }, + setup: function setup(props, ref) { + var slots = ref.slots; + var attrs = ref.attrs; + + var tooltipOptions = inject('vue-custom-tooltip', defaultTooltipOptions); + var setCssVars = function () { + var htmlRoot = document && document.documentElement ? document.documentElement : null; + if (htmlRoot) { + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + htmlRoot.style.setProperty('--vue-custom-tooltip-color', tooltipOptions.color); + htmlRoot.style.setProperty('--vue-custom-tooltip-background', tooltipOptions.background); + htmlRoot.style.setProperty('--vue-custom-tooltip-border-radius', ((tooltipOptions.borderRadius) + "px")); + htmlRoot.style.setProperty('--vue-custom-tooltip-font-weight', tooltipOptions.fontWeight.toString()); + /* eslint-enable @typescript-eslint/no-non-null-assertion */ } }; - // used by ssr in case component is cached and beforeCreate - // never gets called - options._ssrRegister = hook; - } - else if (style) { - hook = shadowMode - ? function (context) { - style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); - } - : function (context) { - style.call(this, createInjector(context)); - }; - } - if (hook) { - if (options.functional) { - // register for functional component in vue file - var originalRender = options.render; - options.render = function renderWithStyleInjection(h, context) { - hook.call(context); - return originalRender(h, context); - }; - } - else { - // inject component registration as beforeCreate hook - var existing = options.beforeCreate; - options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; - } - } - return script; -} - -var isOldIE = typeof navigator !== 'undefined' && - /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase()); -function createInjector(context) { - return function (id, style) { return addStyle(id, style); }; -} -var HEAD; -var styles = {}; -function addStyle(id, css) { - var group = isOldIE ? css.media || 'default' : id; - var style = styles[group] || (styles[group] = { ids: new Set(), styles: [] }); - if (!style.ids.has(id)) { - style.ids.add(id); - var code = css.source; - if (css.map) { - // https://developer.chrome.com/devtools/docs/javascript-debugging - // this makes source maps inside style tags work properly in Chrome - code += '\n/*# sourceURL=' + css.map.sources[0] + ' */'; - // http://stackoverflow.com/a/26603875 - code += - '\n/*# sourceMappingURL=data:application/json;base64,' + - btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + - ' */'; - } - if (!style.element) { - style.element = document.createElement('style'); - style.element.type = 'text/css'; - if (css.media) - { style.element.setAttribute('media', css.media); } - if (HEAD === undefined) { - HEAD = document.head || document.getElementsByTagName('head')[0]; - } - HEAD.appendChild(style.element); - } - if ('styleSheet' in style.element) { - style.styles.push(code); - style.element.styleSheet.cssText = style.styles - .filter(Boolean) - .join('\n'); - } - else { - var index = style.ids.size - 1; - var textNode = document.createTextNode(code); - var nodes = style.element.childNodes; - if (nodes[index]) - { style.element.removeChild(nodes[index]); } - if (nodes.length) - { style.element.insertBefore(textNode, nodes[index]); } - else - { style.element.appendChild(textNode); } - } - } -} - -/* script */ -var __vue_script__ = script; - -/* template */ -var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(_vm.isAbbreviation ? 'abbr' : 'span',{tag:"component",class:[ - _vm.hasPosition, - _vm.hasSize, - { - 'vue-custom-tooltip': _vm.isActive && _vm.labelText, - 'is-sticky': _vm.isSticky, - 'has-multiline': _vm.isMultiline, - 'is-underlined': _vm.isUnderlined || _vm.isAbbreviation, - } ],style:([_vm.dynamicStyles, { cursor: _vm.isAbbreviation ? 'help' : 'pointer' }]),attrs:{"data-label":_vm.labelText,"aria-label":_vm.labelText,"role":"tooltip"}},[_vm._t("default")],2)}; -var __vue_staticRenderFns__ = []; - - /* style */ - var __vue_inject_styles__ = function (inject) { - if (!inject) { return } - inject("data-v-60bf38c6_0", { source: ".vue-custom-tooltip{--vue-custom-tooltip-color:#fff;--vue-custom-tooltip-background:#000;--vue-custom-tooltip-border-radius:12px;--vue-custom-tooltip-font-weight:400}", map: undefined, media: undefined }) -,inject("data-v-60bf38c6_1", { source: ".vue-custom-tooltip{position:relative;display:inline-block;text-decoration-line:none!important}.vue-custom-tooltip.is-top:after,.vue-custom-tooltip.is-top:before{top:auto;right:auto;bottom:calc(100% + 5px + 2px);left:50%;transform:translateX(-50%)}.vue-custom-tooltip.is-top:before{border-top:5px solid #000;border-top:5px solid var(--vue-custom-tooltip-background);border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.vue-custom-tooltip.is-top.has-multiline.is-small:after{width:140px}.vue-custom-tooltip.is-top.has-multiline.is-medium:after{width:210px}.vue-custom-tooltip.is-top.has-multiline.is-large:after{width:280px}.vue-custom-tooltip.is-right:after,.vue-custom-tooltip.is-right:before{top:50%;right:auto;bottom:auto;left:calc(100% + 5px + 2px);transform:translateY(-50%)}.vue-custom-tooltip.is-right:before{border-top:5px solid transparent;border-right:5px solid #000;border-right:5px solid var(--vue-custom-tooltip-background);border-bottom:5px solid transparent;left:calc(100% + 2px)}.vue-custom-tooltip.is-right.has-multiline.is-small:after{width:140px}.vue-custom-tooltip.is-right.has-multiline.is-medium:after{width:210px}.vue-custom-tooltip.is-right.has-multiline.is-large:after{width:280px}.vue-custom-tooltip.is-bottom:after,.vue-custom-tooltip.is-bottom:before{top:calc(100% + 5px + 2px);right:auto;bottom:auto;left:50%;transform:translateX(-50%)}.vue-custom-tooltip.is-bottom:before{border-right:5px solid transparent;border-bottom:5px solid #000;border-bottom:5px solid var(--vue-custom-tooltip-background);border-left:5px solid transparent;top:calc(100% + 2px)}.vue-custom-tooltip.is-bottom.has-multiline.is-small:after{width:140px}.vue-custom-tooltip.is-bottom.has-multiline.is-medium:after{width:210px}.vue-custom-tooltip.is-bottom.has-multiline.is-large:after{width:280px}.vue-custom-tooltip.is-left:after,.vue-custom-tooltip.is-left:before{top:50%;right:calc(100% + 5px + 2px);bottom:auto;left:auto;transform:translateY(-50%)}.vue-custom-tooltip.is-left:before{border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000;border-left:5px solid var(--vue-custom-tooltip-background);right:calc(100% + 2px)}.vue-custom-tooltip.is-left.has-multiline.is-small:after{width:140px}.vue-custom-tooltip.is-left.has-multiline.is-medium:after{width:210px}.vue-custom-tooltip.is-left.has-multiline.is-large:after{width:280px}.vue-custom-tooltip.is-underlined{border-bottom:1px dotted #000;border-bottom:1px dotted var(--vue-custom-tooltip-background);line-height:1.2}.vue-custom-tooltip:after,.vue-custom-tooltip:before{position:absolute;content:'';opacity:0;visibility:hidden;pointer-events:none;transition:opacity 86ms ease-out,visibility 86ms ease-out}.vue-custom-tooltip:before{z-index:889}.vue-custom-tooltip:after{content:attr(data-label);color:#fff;color:var(--vue-custom-tooltip-color);background:#000;background:var(--vue-custom-tooltip-background);width:auto;padding:.35rem .75rem .45rem;border-radius:12px;border-radius:var(--vue-custom-tooltip-border-radius);font-size:.85rem!important;font-weight:400;font-weight:var(--vue-custom-tooltip-font-weight);line-height:1.3;letter-spacing:normal!important;text-transform:none;box-shadow:0 1px 2px 1px rgba(0,1,0,.2);z-index:888;white-space:nowrap}.vue-custom-tooltip:not([data-label='']):hover:after,.vue-custom-tooltip:not([data-label='']):hover:before{opacity:1;visibility:visible}:disabled .vue-custom-tooltip{pointer-events:none}.vue-custom-tooltip:not([data-label='']).is-sticky:after,.vue-custom-tooltip:not([data-label='']).is-sticky:before{opacity:1;visibility:visible}.vue-custom-tooltip.has-multiline:after{display:flex-block;padding:.5rem .75rem .65rem;text-align:center;line-height:1.4;white-space:pre-wrap}", map: undefined, media: undefined }); - - }; - /* scoped */ - var __vue_scope_id__ = undefined; - /* module identifier */ - var __vue_module_identifier__ = undefined; - /* functional template */ - var __vue_is_functional_template__ = false; - /* style inject SSR */ - - /* style inject shadow dom */ - - - - var __vue_component__ = /*#__PURE__*/normalizeComponent( - { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, - __vue_inject_styles__, - __vue_script__, - __vue_scope_id__, - __vue_is_functional_template__, - __vue_module_identifier__, - false, - createInjector, - undefined, - undefined - ); - -// Import vue component + onMounted(setCssVars); + return function () { return [ + h(props.abbreviation ? 'abbr' : 'span', Object.assign({}, attrs, { + 'class': [ + props.position, + props.multiline ? 'is-large' : props.size, + { + 'vue-custom-tooltip': props.active && props.label, + 'is-sticky': props.sticky, + 'has-multiline': props.multiline, + 'is-underlined': props.underlined || props.abbreviation, + } ], + 'data-label': props.label, + 'aria-label': props.label, + 'role': 'tooltip', + 'style': [{ cursor: props.abbreviation ? 'help' : 'pointer' }], + }), slots) ]; }; + }, +}); -var defaultOptions = { - name: 'VueCustomTooltip', - color: '#fff', - background: '#000', - borderRadius: 12, - fontWeight: 400, -}; +script.__file = "src/VueCustomTooltip.vue"; -// Declare install function executed by Vue.use() -var install = function installMyComponent(Vue, opt) { - // Don't install if already installed, or SSR - if (install.installed || Vue.prototype.$isServer) { return } - install.installed = true; +function styleInject(css, ref) { + if ( ref === void 0 ) { ref = {}; } + var insertAt = ref.insertAt; - // Grab user options - var userOptions = Object.assign({}, opt); + if (!css || typeof document === 'undefined') { return; } - // HEX regex: Hash, plus 3 or 6 valid characters - var hexRegex = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/; - // Test color for valid HEX - if (userOptions.hasOwnProperty('color') && !hexRegex.test(userOptions.color)) { - delete userOptions.color; - } - // Test background for valid HEX - if (userOptions.hasOwnProperty('background') && !hexRegex.test(userOptions.background)) { - delete userOptions.background; - } + var head = document.head || document.getElementsByTagName('head')[0]; + var style = document.createElement('style'); + style.type = 'text/css'; - // borderRadius regex: number between 1-9, then any other numbers - var borderRadiusRegex = /^[0-9]+$/; - // Test borderRadius for integer - if (userOptions.hasOwnProperty('borderRadius') && !borderRadiusRegex.test(userOptions.borderRadius)) { - delete userOptions.borderRadius; + if (insertAt === 'top') { + if (head.firstChild) { + head.insertBefore(style, head.firstChild); + } else { + head.appendChild(style); + } + } else { + head.appendChild(style); } - // fontWeight regex: number between 1-9 followed by two zeros - var fontWeightRegex = /^[1-9]{1}00$/; - // Test fontWeight for integer - if (userOptions.hasOwnProperty('fontWeight') && !fontWeightRegex.test(userOptions.fontWeight)) { - delete userOptions.fontWeight; + if (style.styleSheet) { + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); } - - // Merge options - var options = Object.assign({}, defaultOptions, userOptions); - - // Mutate borderRadius - options.borderRadius = options.borderRadius + 'px'; - - // Add global property (mainly for passing styles) - Vue.prototype.$vueCustomTooltip = options; - - // Register component, using options.name. - // e.g. - Vue.component(options.name, __vue_component__); -}; - -// Create module definition for Vue.use() -var plugin = { - install: install, -}; - -// Auto-install when vue is found (eg. in browser via diff --git a/src/VueCustomTooltip.vue b/src/VueCustomTooltip.vue index a191347..35d6b25 100644 --- a/src/VueCustomTooltip.vue +++ b/src/VueCustomTooltip.vue @@ -1,289 +1,76 @@ - - - - - - - diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..182dc91 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,52 @@ +import { App } from 'vue' +import VueCustomTooltip from './VueCustomTooltip.vue' +import { TooltipOptions, defaultTooltipOptions } from './types' +import './tooltip.scss' + +export default { + install: (app: App, options?: TooltipOptions): void => { + const userOptions = Object.assign({}, options) + + /** + * Validate HEX values + * Hash, plus 3 or 6 valid characters + */ + const hexRegex = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/ + // Test color for valid HEX + if (userOptions && userOptions.color && !hexRegex.test(userOptions.color)) { + delete userOptions.color + } + // Test background for valid HEX + if (userOptions && userOptions.background && !hexRegex.test(userOptions.background)) { + delete userOptions.background + } + + /** + * Validate borderRadius + * Number between 1-9, then any other numbers + */ + const borderRadiusRegex = /^[0-9]+$/ + // Test borderRadius for integer + if (userOptions && userOptions.borderRadius && !borderRadiusRegex.test(userOptions.borderRadius.toString())) { + delete userOptions.borderRadius + } + + /** + * Validate fontWeight + * Number between 1-9 followed by two zeros + */ + const fontWeightRegex = /^[1-9]{1}00$/ + // Test fontWeight for integer + if (userOptions && userOptions.fontWeight && !fontWeightRegex.test(userOptions.fontWeight.toString())) { + delete userOptions.fontWeight + } + + // Merge defaults with user options + const pluginOptions: TooltipOptions = Object.assign({}, defaultTooltipOptions, userOptions) + app.provide('vue-custom-tooltip', pluginOptions) + + // Register component, using options.name + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ + app.component(pluginOptions.name!, VueCustomTooltip) + }, +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..093b8ad --- /dev/null +++ b/src/main.ts @@ -0,0 +1,18 @@ +import { createApp } from 'vue' +import App from './App.vue' +import VueCustomTooltipPlugin from './index' +import { TooltipOptions } from './types' + +const app = createApp(App) + +const tooltipOptions: TooltipOptions = { + // name: 'VueCustomTooltip', + // color: '#fff', + background: '#007ac1', + // borderRadius: 12, + // fontWeight: 400, +} + +app.use(VueCustomTooltipPlugin, tooltipOptions) + +app.mount('#app') diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts new file mode 100644 index 0000000..3804a43 --- /dev/null +++ b/src/shims-vue.d.ts @@ -0,0 +1,6 @@ +/* eslint-disable */ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/src/tooltip.scss b/src/tooltip.scss new file mode 100644 index 0000000..ed7bb37 --- /dev/null +++ b/src/tooltip.scss @@ -0,0 +1,166 @@ +$tooltip-color: var(--vue-custom-tooltip-color, #fff); // default color +$tooltip-background: var(--vue-custom-tooltip-background, #000); // default background color +$tooltip-radius: var(--vue-custom-tooltip-border-radius, 100px); // default border radius +$weight-normal: var(--vue-custom-tooltip-font-weight, 400); // default font weight +$speed: 86ms; +$easing: ease-out; + +@mixin tooltip-arrow($direction, $color) { + @if $direction == "is-top" { + border-top: 5px solid #000; // default for IE + border-top: 5px solid $color; + border-right: 5px solid transparent; + border-left: 5px solid transparent; + bottom: calc(100% + 2px); + } @else if $direction == "is-bottom" { + border-right: 5px solid transparent; + border-bottom: 5px solid #000; // default for IE + border-bottom: 5px solid $color; + border-left: 5px solid transparent; + top: calc(100% + 2px); + } @else if $direction == "is-right" { + border-top: 5px solid transparent; + border-right: 5px solid #000; // default for IE + border-right: 5px solid $color; + border-bottom: 5px solid transparent; + left: calc(100% + 3px); + } @else if $direction == "is-left" { + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000; // default for IE + border-left: 5px solid $color; + right: calc(100% + 3px); + } +} + +@mixin tooltip($direction) { + &.#{$direction} { + &:before, + &:after { + @if ($direction == "is-top") { + top: auto; + right: auto; + bottom: calc(100% + 5px + 2px); + left: 50%; + transform: translateX(-50%); + } @else if ($direction == "is-bottom") { + top: calc(100% + 5px + 2px); + right: auto; + bottom: auto; + left: 50%; + transform: translateX(-50%); + } @else if ($direction == "is-right") { + top: 50%; + right: auto; + bottom: auto; + left: calc(100% + 5px + 2px); + transform: translateY(-50%); + } @else if ($direction == "is-left") { + top: 50%; + right: calc(100% + 5px + 2px); + bottom: auto; + left: auto; + transform: translateY(-50%); + } + } + + &:before { + @include tooltip-arrow($direction, $tooltip-background); + } + + &.has-multiline { + &.is-small:after { + width: 140px; + } + + &.is-medium:after { + width: 210px; + } + + &.is-large:after { + width: 340px; + padding: 0.5rem 1rem 0.65rem; + } + } + } +} +// Base +.vue-custom-tooltip { + @include tooltip("is-top"); + @include tooltip("is-right"); + @include tooltip("is-bottom"); + @include tooltip("is-left"); + position: relative; + display: inline-block; + text-decoration-line: none !important; + + &.is-underlined { + border-bottom: 1px dotted #000; // default for IE + border-bottom: 1px dotted $tooltip-background; + line-height: 1.2; + } + + &:before, + &:after { + position: absolute; + content: ""; + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity $speed $easing, visibility $speed $easing; + } + + &:before { + z-index: 889; + } + + &:after { + content: attr(data-label); + color: #fff; // default for IE + color: $tooltip-color; + background: #000; // default for IE + background: $tooltip-background; + width: auto; + padding: 0.45rem 0.75rem 0.45rem; + border-radius: 12px; // default for IE + border-radius: $tooltip-radius; + font-size: 0.85rem !important; + font-weight: 400; // default for IE + font-weight: $weight-normal; + line-height: 1.3; + letter-spacing: normal !important; + text-transform: none; + box-shadow: 0px 1px 2px 1px rgba(0, 1, 0, 0.2); + z-index: 888; + white-space: nowrap; + } + + &:not([data-label=""]):hover:before, + &:not([data-label=""]):hover:after { + opacity: 1; + visibility: visible; + } + + // If parent is disabled + :disabled & { + pointer-events: none; + } + + &:not([data-label=""]).is-sticky { + &:before, + &:after { + opacity: 1; + visibility: visible; + } + } + + &.has-multiline { + &:after { + display: flex-block; + padding: 0.5rem 0.75rem 0.65rem; + text-align: center; + line-height: 1.4; + white-space: pre-wrap; + } + } +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..b7072f6 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,15 @@ +export interface TooltipOptions { + name?: string + color?: string + background?: string + borderRadius?: number + fontWeight?: number +} + +export const defaultTooltipOptions: TooltipOptions = { + name: 'VueCustomTooltip', + color: '#fff', + background: '#000', + borderRadius: 100, + fontWeight: 400, +} diff --git a/src/wrapper.js b/src/wrapper.js deleted file mode 100644 index a8a6230..0000000 --- a/src/wrapper.js +++ /dev/null @@ -1,81 +0,0 @@ -// Import vue component -import VueCustomTooltip from './VueCustomTooltip.vue' - -const defaultOptions = { - name: 'VueCustomTooltip', - color: '#fff', - background: '#000', - borderRadius: 12, - fontWeight: 400, -} - -// Declare install function executed by Vue.use() -const install = function installMyComponent(Vue, opt) { - // Don't install if already installed, or SSR - if (install.installed || Vue.prototype.$isServer) return - install.installed = true - - // Grab user options - let userOptions = Object.assign({}, opt) - - // HEX regex: Hash, plus 3 or 6 valid characters - const hexRegex = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/ - // Test color for valid HEX - if (userOptions.hasOwnProperty('color') && !hexRegex.test(userOptions.color)) { - delete userOptions.color - } - // Test background for valid HEX - if (userOptions.hasOwnProperty('background') && !hexRegex.test(userOptions.background)) { - delete userOptions.background - } - - // borderRadius regex: number between 1-9, then any other numbers - const borderRadiusRegex = /^[0-9]+$/ - // Test borderRadius for integer - if (userOptions.hasOwnProperty('borderRadius') && !borderRadiusRegex.test(userOptions.borderRadius)) { - delete userOptions.borderRadius - } - - // fontWeight regex: number between 1-9 followed by two zeros - const fontWeightRegex = /^[1-9]{1}00$/ - // Test fontWeight for integer - if (userOptions.hasOwnProperty('fontWeight') && !fontWeightRegex.test(userOptions.fontWeight)) { - delete userOptions.fontWeight - } - - // Merge options - let options = Object.assign({}, defaultOptions, userOptions) - - // Mutate borderRadius - options.borderRadius = options.borderRadius + 'px' - - // Add global property (mainly for passing styles) - Vue.prototype.$vueCustomTooltip = options - - // Register component, using options.name. - // e.g. - Vue.component(options.name, VueCustomTooltip) -} - -// Create module definition for Vue.use() -const plugin = { - install, -} - -// Auto-install when vue is found (eg. in browser via