@vue-interface/tooltip 1.0.0-beta.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,127 +1,111 @@
1
- import type { App } from 'vue';
2
- import { h, render } from 'vue';
3
- import Tooltip from './Tooltip.vue';
4
-
5
- type TooltipPluginOptions = {
6
- delay?: number,
7
- prefix: string,
8
- triggers: {
9
- open: string[],
10
- close: string[],
11
- }
1
+ import { h, render, type App, type Directive } from 'vue';
2
+ import Tooltip, { type TooltipProps } from './Tooltip.vue';
3
+
4
+ const prefix = 'data-tooltip';
5
+ const prefixRegExp = new RegExp(`^${prefix}\-`);
6
+
7
+ function getAttributes(el: Element): Record<string,string> {
8
+ return Array.from(el.attributes)
9
+ .map(a => [a.name, a.value])
10
+ .filter(([key]) => key === 'title' || key.match(prefixRegExp))
11
+ .map(([key, value]) => [key.replace(new RegExp(prefixRegExp), ''), value])
12
+ .reduce((carry, attr) => Object.assign(carry, { [attr[0]]: attr[1] }), {});
12
13
  }
13
14
 
14
- export default function (app: App, opts: Partial<TooltipPluginOptions> = {}) {
15
- const tooltips: Map<string,Function> = new Map;
15
+ function createTooltip(target: Element, props?: TooltipProps) {
16
+ const container = document.createElement('template');
17
+
18
+ const vnode = h(Tooltip, Object.assign({
19
+ target
20
+ }, getAttributes(target), props));
16
21
 
17
- const options: TooltipPluginOptions = Object.assign({
18
- delay: undefined,
19
- prefix: 'data-tooltip',
20
- triggers: {
21
- open: ['mouseover:350'],
22
- close: ['mouseout:100'],
23
- }
24
- }, opts);
22
+ render(vnode, container);
25
23
 
26
- const prefix = options.prefix.replace(/[-]+$/, '');
27
- const prefixRegExp = new RegExp(`^${prefix}\-`);
24
+ const title = target.getAttribute('title');
28
25
 
29
- function getAttributes(el: Element): Record<string,any> {
30
- return Array.from(el.attributes)
31
- .map(a => [a.name, a.value])
32
- .filter(([key]) => key === 'title' || key.match(prefixRegExp))
33
- .map(([key, value]) => [key.replace(new RegExp(prefixRegExp), ''), value])
34
- .reduce((carry, attr) => Object.assign(carry, { [attr[0]]: attr[1] }), {});
26
+ if(title) {
27
+ target.setAttribute(`${prefix}-og-title`, title);
28
+ target.removeAttribute('title');
35
29
  }
36
30
 
37
- function createTooltip(target: Element, props: Record<string,any> = {}, hash: string): Function {
38
- const container = document.createElement('template');
39
-
40
- const vnode = h(Tooltip, Object.assign({
41
- target,
42
- show: true
43
- }, props));
44
-
45
- render(vnode, container);
46
-
47
- const [el] = [...container.children];
48
-
49
- document.body.append(el);
50
-
51
- return () => {
52
- tooltips.delete(hash);
31
+ return () => {
32
+ if(vnode.component) {
33
+ vnode.component.exposed?.tooltipEl.value?.remove();
34
+ }
35
+ };
36
+ }
53
37
 
54
- // @ts-ignore
55
- vnode.component?.ctx.close();
56
-
57
- // @todo: Make the animation rate (150) dynamic. Should get value
58
- // from the CSS transition duration.
59
- setTimeout(() => el.remove(), 150);
60
- };
61
- }
38
+ function destroyTooltip(target: Element) {
39
+ const tooltips = document.querySelectorAll(
40
+ `[${prefix}-id="${target.getAttribute(`${prefix}-id`)}"]`
41
+ );
62
42
 
63
- function init(target: Element, props = {}) {
64
- const properties: Record<string,any> = Object.assign({
65
- title: target.getAttribute(prefix)
66
- }, props, getAttributes(target));
43
+ for(const tooltip of tooltips) {
44
+ tooltip.remove();
45
+ }
67
46
 
68
- // If the properties don't have a title, ignore this target.
69
- if(!properties.title || target.hasAttribute(`${prefix}-id`)) {
70
- return;
71
- }
47
+ target.removeAttribute(`${prefix}-id`);
72
48
 
73
- // Create a unique "hash" to show the node has been initialized.
74
- // This prevents double initializing on the same element.
75
- const hash = Math.random().toString(36).slice(2, 7);
76
-
77
- // Create the instance vars.
78
- let tooltip: Function|null, timer: number;
49
+ const title = target.getAttribute(`${prefix}-og-title`);
79
50
 
80
- //target.setAttribute(prefix, properties.title);
81
- target.setAttribute(`${prefix}-id`, hash);
82
- target.removeAttribute('title');
51
+ if(title) {
52
+ target.setAttribute('title', title);
53
+ }
54
+ }
83
55
 
84
- function open(delay = 0) {
85
- clearTimeout(timer);
86
-
87
- if(!tooltip) {
88
- timer = setTimeout(() => {
89
- // Do a check before creating the tooltip to ensure the dom
90
- // element still exists. Its possible for the element to
91
- // be removed after the timeout delay runs.
92
- if(document.contains(target)) {
93
- tooltip = createTooltip(target, properties, hash);
94
- tooltips.set(hash, tooltip);
95
- }
96
- }, delay);
97
- }
98
- }
56
+ function shouldCreateTooltip(target: Node): target is Element {
57
+ if(!(target instanceof Element)) {
58
+ return false;
59
+ }
99
60
 
100
- function close(delay = 0) {
101
- clearTimeout(timer);
61
+ const attrs = getAttributes(target);
102
62
 
103
- if(tooltip) {
104
- timer = setTimeout(() => {
105
- tooltip && tooltip();
106
- tooltip = null;
107
- }, delay);
108
- }
109
- }
63
+ return !attrs.id && !!attrs.title;
64
+ }
110
65
 
111
- function addEventListener(trigger: string, fn: Function) {
112
- const [ event, delayString ] = trigger.split(':');
66
+ function shouldRemoveTooltip(target: Node): target is Element {
67
+ if(!(target instanceof Element)) {
68
+ return false;
69
+ }
113
70
 
114
- target.addEventListener(event, () => fn(Number(delayString || 0)));
115
- }
71
+ return target.hasAttribute(`${prefix}-id`);
72
+ }
116
73
 
117
- (target.getAttribute(`${prefix}-trigger-open`)?.split(',') || options.triggers.open)
118
- .map(trigger => addEventListener(trigger, open));
119
-
120
- (target.getAttribute(`${prefix}-trigger-close`)?.split(',') || options.triggers.close)
121
- .map(trigger => addEventListener(trigger, close));
74
+ export const TooltipDirective: Directive<Element, string|TooltipProps> = {
75
+ beforeMount(target, binding) {
76
+ createTooltip(target, typeof binding.value === 'string' ? {
77
+ title: binding.value
78
+ }: binding.value);
79
+ },
80
+ beforeUnmount(target) {
81
+ destroyTooltip(target);
122
82
  }
123
-
83
+ };
84
+
85
+ export function TooltipPlugin(app: App<Element>) {
124
86
  app.mixin({
87
+ beforeMount() {
88
+ const observer = new MutationObserver((mutations) => {
89
+ for(const { addedNodes, removedNodes } of mutations) {
90
+ addedNodes.forEach((node) => {
91
+ if(shouldCreateTooltip(node)) {
92
+ createTooltip(node);
93
+ }
94
+ });
95
+
96
+ removedNodes.forEach((node) => {
97
+ if(shouldRemoveTooltip(node)) {
98
+ destroyTooltip(node);
99
+ }
100
+ });
101
+ }
102
+ });
103
+
104
+ observer.observe(document.body, {
105
+ childList: true,
106
+ subtree: true
107
+ });
108
+ },
125
109
  mounted() {
126
110
  let el = this.$el;
127
111
 
@@ -129,11 +113,6 @@ export default function (app: App, opts: Partial<TooltipPluginOptions> = {}) {
129
113
  el = this.$el.parentNode;
130
114
  }
131
115
 
132
- if(el instanceof HTMLElement) {
133
- init(el);
134
- }
135
-
136
- // Create the tree walker.
137
116
  const walker = document.createTreeWalker(
138
117
  el,
139
118
  NodeFilter.SHOW_ALL,
@@ -146,42 +125,13 @@ export default function (app: App, opts: Partial<TooltipPluginOptions> = {}) {
146
125
  }
147
126
  );
148
127
 
149
- // Step through and alert all child nodes
150
128
  while(walker.nextNode()) {
151
- if(walker.currentNode instanceof Element) {
152
- init(<Element> walker.currentNode);
129
+ if(shouldCreateTooltip(walker.currentNode)) {
130
+ createTooltip(walker.currentNode);
153
131
  }
154
132
  }
155
-
156
- const observer = new MutationObserver((changes) => {
157
- for(const { removedNodes } of changes) {
158
- for(const node of removedNodes) {
159
- for(const el of (node as Element).querySelectorAll(`[${prefix}-id]`)) {
160
- const tooltip = tooltips.get(
161
- el.getAttribute(`${prefix}-id`) as string
162
- );
163
-
164
- tooltip && tooltip();
165
- }
166
- }
167
- }
168
- });
169
-
170
- observer.observe(el, { childList: true });
171
133
  }
172
134
  });
173
135
 
174
- app.directive('tooltip', {
175
- created(target, binding) {
176
- init(target, Object.assign({}, binding.modifiers, binding.value));
177
- },
178
- beforeUnmount(target) {
179
- const id = target.getAttribute(`${prefix}-id`);
180
- const tooltip = tooltips.get(id);
181
-
182
- console.log('beforeUnmount');
183
-
184
- tooltip && tooltip();
185
- }
186
- });
136
+ app.directive<Element, string|TooltipProps>('tooltip', TooltipDirective);
187
137
  }
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "customConditions": ["source"],
10
+ "allowImportingTsExtensions": true,
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "noEmit": true,
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "jsx": "preserve",
19
+ "jsxImportSource": "vue"
20
+ },
21
+ "include": [
22
+ "**/*.ts",
23
+ "**/*.tsx",
24
+ "**/*.vue",
25
+ ],
26
+ "exclude": ["node_modules"]
27
+ }
package/vite.config.js ADDED
@@ -0,0 +1,47 @@
1
+ import tailwindcss from '@tailwindcss/vite';
2
+ import vue from '@vitejs/plugin-vue';
3
+ import { pascalCase } from 'change-case';
4
+ import path from 'path';
5
+ import { defineConfig } from 'vite';
6
+ import dts from 'vite-plugin-dts';
7
+ import pkg from './package.json';
8
+
9
+ const fileName = pkg.name.split('/')[1];
10
+
11
+ const external = [
12
+ ...(pkg.dependencies ? Object.keys(pkg.dependencies) : []),
13
+ ...(pkg.peerDependencies ? Object.keys(pkg.peerDependencies) : [])
14
+ ];
15
+
16
+ export default ({ command }) => defineConfig({
17
+ build: {
18
+ sourcemap: command === 'build',
19
+ lib: {
20
+ entry: path.resolve(__dirname, 'index.ts'),
21
+ name: pascalCase(fileName),
22
+ fileName,
23
+ },
24
+ rollupOptions: {
25
+ external,
26
+ output: {
27
+ globals: external.reduce((carry, dep) => {
28
+ return Object.assign(carry, {
29
+ [dep]: pascalCase(dep)
30
+ });
31
+ }, {}),
32
+ }
33
+ },
34
+ watch: !process.env.NODE_ENV && {
35
+ include: [
36
+ './tailwindcss/**/*.js'
37
+ ]
38
+ }
39
+ },
40
+ plugins: [
41
+ vue(),
42
+ dts({
43
+ entryRoot: path.resolve(__dirname, './'),
44
+ }),
45
+ tailwindcss()
46
+ ],
47
+ });
package/dist/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import Tooltip from './src/Tooltip.vue';
2
- import TooltipPlugin from './src/TooltipPlugin';
3
- export { Tooltip, TooltipPlugin };
@@ -1,73 +0,0 @@
1
- declare const _default: import("vue").DefineComponent<{
2
- offset: {
3
- type: ArrayConstructor;
4
- default: undefined;
5
- };
6
- popper: {
7
- type: ObjectConstructor;
8
- default: undefined;
9
- };
10
- target: {
11
- type: {
12
- new (): Element;
13
- prototype: Element;
14
- };
15
- required: true;
16
- };
17
- title: {
18
- type: StringConstructor;
19
- default: undefined;
20
- };
21
- show: BooleanConstructor;
22
- top: BooleanConstructor;
23
- bottom: BooleanConstructor;
24
- left: BooleanConstructor;
25
- right: BooleanConstructor;
26
- }, unknown, {
27
- currentShow: boolean;
28
- popperInstance: null;
29
- }, {
30
- computedPlacement(): any;
31
- tooltipClasses(): {
32
- [x: string]: any;
33
- show: any;
34
- };
35
- }, {
36
- open(): void;
37
- close(): void;
38
- }, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
39
- offset: {
40
- type: ArrayConstructor;
41
- default: undefined;
42
- };
43
- popper: {
44
- type: ObjectConstructor;
45
- default: undefined;
46
- };
47
- target: {
48
- type: {
49
- new (): Element;
50
- prototype: Element;
51
- };
52
- required: true;
53
- };
54
- title: {
55
- type: StringConstructor;
56
- default: undefined;
57
- };
58
- show: BooleanConstructor;
59
- top: BooleanConstructor;
60
- bottom: BooleanConstructor;
61
- left: BooleanConstructor;
62
- right: BooleanConstructor;
63
- }>>, {
64
- offset: unknown[];
65
- popper: Record<string, any>;
66
- title: string;
67
- show: boolean;
68
- top: boolean;
69
- bottom: boolean;
70
- left: boolean;
71
- right: boolean;
72
- }>;
73
- export default _default;
@@ -1,73 +0,0 @@
1
- declare const _sfc_main: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").DefineComponent<{
2
- offset: {
3
- type: ArrayConstructor;
4
- default: undefined;
5
- };
6
- popper: {
7
- type: ObjectConstructor;
8
- default: undefined;
9
- };
10
- target: {
11
- type: {
12
- new (): Element;
13
- prototype: Element;
14
- };
15
- required: true;
16
- };
17
- title: {
18
- type: StringConstructor;
19
- default: undefined;
20
- };
21
- show: BooleanConstructor;
22
- top: BooleanConstructor;
23
- bottom: BooleanConstructor;
24
- left: BooleanConstructor;
25
- right: BooleanConstructor;
26
- }, unknown, {
27
- currentShow: boolean;
28
- popperInstance: null;
29
- }, {
30
- computedPlacement(): any;
31
- tooltipClasses(): {
32
- [x: string]: any;
33
- show: any;
34
- };
35
- }, {
36
- open(): void;
37
- close(): void;
38
- }, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
39
- offset: {
40
- type: ArrayConstructor;
41
- default: undefined;
42
- };
43
- popper: {
44
- type: ObjectConstructor;
45
- default: undefined;
46
- };
47
- target: {
48
- type: {
49
- new (): Element;
50
- prototype: Element;
51
- };
52
- required: true;
53
- };
54
- title: {
55
- type: StringConstructor;
56
- default: undefined;
57
- };
58
- show: BooleanConstructor;
59
- top: BooleanConstructor;
60
- bottom: BooleanConstructor;
61
- left: BooleanConstructor;
62
- right: BooleanConstructor;
63
- }>>, {
64
- offset: unknown[];
65
- popper: Record<string, any>;
66
- title: string;
67
- show: boolean;
68
- top: boolean;
69
- bottom: boolean;
70
- left: boolean;
71
- right: boolean;
72
- }>, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
73
- export default _sfc_main;
@@ -1,11 +0,0 @@
1
- import type { App } from 'vue';
2
- type TooltipPluginOptions = {
3
- delay?: number;
4
- prefix: string;
5
- triggers: {
6
- open: string[];
7
- close: string[];
8
- };
9
- };
10
- export default function (app: App, opts?: Partial<TooltipPluginOptions>): void;
11
- export {};
package/dist/style.css DELETED
@@ -1 +0,0 @@
1
- .tooltip:not(.show){z-index:-1}