@signal24/vue-foundation 3.8.0 → 4.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.
Files changed (122) hide show
  1. package/.eslintrc.cjs +35 -0
  2. package/.prettierrc.json +4 -2
  3. package/dist/src/components/ajax-select.vue.d.ts +21 -0
  4. package/dist/src/components/alert-helpers.d.ts +8 -0
  5. package/dist/src/components/alert-modal.vue.d.ts +27 -0
  6. package/dist/src/components/ez-smart-select.vue.d.ts +27 -0
  7. package/dist/src/components/index.d.ts +8 -0
  8. package/dist/src/components/modal-container.d.ts +33 -0
  9. package/dist/src/components/modal.vue.d.ts +34 -0
  10. package/dist/src/components/smart-select.vue.d.ts +121 -0
  11. package/dist/src/config.d.ts +8 -0
  12. package/dist/src/directives/autofocus.d.ts +2 -0
  13. package/dist/src/directives/confirm-button.d.ts +2 -0
  14. package/dist/src/directives/date-input.d.ts +2 -0
  15. package/dist/src/directives/datetime.d.ts +2 -0
  16. package/dist/src/directives/disabled.d.ts +2 -0
  17. package/dist/src/directives/duration.d.ts +2 -0
  18. package/dist/src/directives/index.d.ts +24 -0
  19. package/dist/src/directives/infinite-scroll.d.ts +3 -0
  20. package/dist/src/directives/readonly.d.ts +2 -0
  21. package/dist/src/directives/tooltip.d.ts +41 -0
  22. package/dist/src/filters/index.d.ts +39 -0
  23. package/dist/src/helpers/array.d.ts +3 -0
  24. package/dist/src/helpers/context-menu.d.ts +13 -0
  25. package/dist/src/helpers/delay.d.ts +2 -0
  26. package/dist/src/helpers/error.d.ts +7 -0
  27. package/dist/src/helpers/index.d.ts +9 -0
  28. package/dist/src/helpers/mask.d.ts +15 -0
  29. package/dist/src/helpers/number.d.ts +1 -0
  30. package/dist/src/helpers/object.d.ts +2 -0
  31. package/dist/src/helpers/openapi.d.ts +34 -0
  32. package/dist/src/helpers/string.d.ts +5 -0
  33. package/dist/src/hooks/index.d.ts +2 -0
  34. package/dist/src/hooks/infinite-scroll.d.ts +30 -0
  35. package/dist/src/hooks/resize-watcher.d.ts +1 -0
  36. package/dist/src/index.d.ts +8 -0
  37. package/dist/src/types.d.ts +14 -0
  38. package/dist/src/vite-plugins/index.d.ts +1 -0
  39. package/dist/src/vite-plugins/index.js +2 -0
  40. package/dist/src/vite-plugins/vite-openapi-plugin.d.ts +4 -0
  41. package/dist/src/vite-plugins/vite-openapi-plugin.js +58 -0
  42. package/dist/vue-foundation.css +1 -0
  43. package/dist/vue-foundation.es.js +1129 -0
  44. package/package.json +44 -16
  45. package/src/components/ajax-select.vue +44 -23
  46. package/src/components/alert-helpers.ts +45 -0
  47. package/src/components/alert-modal.vue +68 -0
  48. package/src/components/ez-smart-select.vue +51 -0
  49. package/src/components/index.ts +10 -0
  50. package/src/components/modal-container.ts +131 -0
  51. package/src/components/modal.vue +44 -129
  52. package/src/components/smart-select.vue +196 -243
  53. package/src/config.ts +15 -0
  54. package/src/directives/autofocus.ts +20 -0
  55. package/src/directives/confirm-button.ts +50 -0
  56. package/src/directives/date-input.ts +19 -0
  57. package/src/directives/datetime.ts +48 -0
  58. package/src/directives/disabled.ts +30 -0
  59. package/src/directives/duration.ts +79 -0
  60. package/src/directives/index.ts +37 -0
  61. package/src/directives/infinite-scroll.ts +9 -0
  62. package/src/directives/readonly.ts +15 -0
  63. package/src/directives/tooltip.ts +190 -0
  64. package/src/filters/index.ts +79 -0
  65. package/src/helpers/array.ts +7 -0
  66. package/src/helpers/context-menu.ts +108 -0
  67. package/src/helpers/delay.ts +2 -0
  68. package/src/helpers/error.ts +41 -0
  69. package/src/helpers/index.ts +9 -0
  70. package/src/helpers/mask.ts +105 -0
  71. package/src/helpers/number.ts +3 -0
  72. package/src/helpers/object.ts +19 -0
  73. package/src/helpers/openapi.ts +82 -0
  74. package/src/helpers/string.ts +27 -0
  75. package/src/hooks/index.ts +2 -0
  76. package/src/hooks/infinite-scroll.ts +107 -0
  77. package/src/hooks/resize-watcher.ts +8 -0
  78. package/src/index.ts +14 -0
  79. package/src/types.ts +14 -0
  80. package/src/vite-plugins/index.ts +2 -0
  81. package/src/vite-plugins/vite-openapi-plugin.ts +71 -0
  82. package/tsconfig.app.json +22 -0
  83. package/tsconfig.json +14 -0
  84. package/tsconfig.node.json +9 -0
  85. package/tsconfig.vite-plugins.json +10 -0
  86. package/tsconfig.vitest.json +9 -0
  87. package/vite.config.js +37 -35
  88. package/vitest.config.js +17 -0
  89. package/.eslintrc.js +0 -16
  90. package/CHANGES.md +0 -13
  91. package/postcss.config.cjs +0 -5
  92. package/src/app.js +0 -25
  93. package/src/components/alert.vue +0 -130
  94. package/src/components/index.js +0 -12
  95. package/src/config.js +0 -11
  96. package/src/directives/autofocus.js +0 -17
  97. package/src/directives/confirm-button.js +0 -40
  98. package/src/directives/date-input.js +0 -18
  99. package/src/directives/datetime.js +0 -46
  100. package/src/directives/disabled.js +0 -28
  101. package/src/directives/duration.js +0 -72
  102. package/src/directives/index.js +0 -10
  103. package/src/directives/infinite-scroll.js +0 -17
  104. package/src/directives/readonly.js +0 -17
  105. package/src/directives/tooltip.js +0 -178
  106. package/src/directives/user-text.js +0 -11
  107. package/src/filters/index.js +0 -82
  108. package/src/helpers/array.js +0 -99
  109. package/src/helpers/context-menu.js +0 -66
  110. package/src/helpers/delay.js +0 -3
  111. package/src/helpers/error.js +0 -36
  112. package/src/helpers/http.js +0 -44
  113. package/src/helpers/index.js +0 -9
  114. package/src/helpers/mask.js +0 -90
  115. package/src/helpers/number.js +0 -6
  116. package/src/helpers/string.js +0 -36
  117. package/src/helpers/vue.js +0 -5
  118. package/src/index.js +0 -33
  119. package/src/plugins/index.js +0 -10
  120. package/src/plugins/infinite-scroll/hook.js +0 -30
  121. package/src/plugins/infinite-scroll.js +0 -100
  122. package/src/plugins/resize-watcher.js +0 -28
package/src/config.ts ADDED
@@ -0,0 +1,15 @@
1
+ interface IOptions {
2
+ unhandledErrorSupportText: string;
3
+ errorHandler: (err: Error) => void;
4
+ defaultDateTimeFormat: string;
5
+ }
6
+
7
+ export const VfOptions: IOptions = {
8
+ unhandledErrorSupportText: 'please contact support',
9
+ errorHandler: err => console.error('Unhandled error:', err),
10
+ defaultDateTimeFormat: 'MM/dd/yy HH:mm'
11
+ };
12
+
13
+ export function configureVf(options: Partial<IOptions>) {
14
+ Object.assign(VfOptions, options);
15
+ }
@@ -0,0 +1,20 @@
1
+ import type { DirectiveBinding, ObjectDirective } from 'vue';
2
+
3
+ export const vAutofocus: ObjectDirective<HTMLElement, void> = {
4
+ mounted: fn,
5
+ updated: fn
6
+ };
7
+
8
+ const HasAutoFocused = Symbol('HasAutoFocused');
9
+ interface AutoFocusElement {
10
+ [HasAutoFocused]?: boolean;
11
+ }
12
+
13
+ function fn(el: HTMLElement & AutoFocusElement, binding: DirectiveBinding<void>) {
14
+ if (binding.value === undefined && el[HasAutoFocused]) return;
15
+ if (binding.value !== undefined && !binding.value) return;
16
+ if (binding.oldValue !== undefined && binding.value == binding.oldValue) return;
17
+ el[HasAutoFocused] = true;
18
+ const realEl = ['BUTTON', 'INPUT', 'TEXTAREA', 'SELECT'].indexOf(el.tagName) > -1 ? el : (el.querySelectorAll('input')[0] as HTMLElement);
19
+ setTimeout(() => realEl.focus(), 10);
20
+ }
@@ -0,0 +1,50 @@
1
+ import type { ObjectDirective } from 'vue';
2
+
3
+ export const vConfirmButton: ObjectDirective<HTMLElement, void> = {
4
+ mounted: fn
5
+ };
6
+
7
+ const ConfirmState = Symbol('ConfirmState');
8
+ interface IConfirmState {
9
+ initTime: number;
10
+ preconfirmHtml: string;
11
+ resetHandler: () => void;
12
+ }
13
+ interface IConfirmElement {
14
+ [ConfirmState]?: IConfirmState;
15
+ }
16
+
17
+ function fn(el: HTMLElement & IConfirmElement) {
18
+ el.addEventListener('click', e => {
19
+ const now = Date.now();
20
+
21
+ if (el[ConfirmState]) {
22
+ if (now - el[ConfirmState].initTime < 300) {
23
+ return;
24
+ }
25
+
26
+ el[ConfirmState].resetHandler();
27
+ el.dispatchEvent(new Event('confirm'));
28
+ return;
29
+ }
30
+
31
+ e.preventDefault();
32
+ e.stopImmediatePropagation();
33
+
34
+ const confirmState: IConfirmState = {
35
+ initTime: now,
36
+ preconfirmHtml: el.innerHTML,
37
+ resetHandler: () => {
38
+ el.innerHTML = confirmState.preconfirmHtml;
39
+ el.blur();
40
+
41
+ el.removeEventListener('mouseout', confirmState.resetHandler);
42
+ delete el[ConfirmState];
43
+ }
44
+ };
45
+ el[ConfirmState] = confirmState;
46
+ el.innerHTML = 'Confirm';
47
+
48
+ el.addEventListener('mouseout', confirmState.resetHandler);
49
+ });
50
+ }
@@ -0,0 +1,19 @@
1
+ import { format } from 'date-fns';
2
+ import type { ObjectDirective } from 'vue';
3
+
4
+ export const vDateInput: ObjectDirective<HTMLInputElement, void> = {
5
+ beforeMount: fn
6
+ };
7
+
8
+ function fn(el: HTMLInputElement) {
9
+ el.addEventListener('blur', () => {
10
+ let val = el.value;
11
+ if (/^\d{1,2}\/\d{1,2}$/.test(val)) val += '/' + format(new Date(), 'yy');
12
+
13
+ const ts = Date.parse(val);
14
+ if (isNaN(ts)) el.value = '';
15
+ else el.value = format(ts, 'MM/dd/yyyy');
16
+
17
+ el.dispatchEvent(new Event('input'));
18
+ });
19
+ }
@@ -0,0 +1,48 @@
1
+ import { format } from 'date-fns';
2
+ import type { DirectiveBinding, ObjectDirective } from 'vue';
3
+
4
+ import { VfOptions } from '@/config';
5
+
6
+ export const vDatetime: ObjectDirective<HTMLElement, string> = {
7
+ beforeMount: applyDateTime,
8
+ updated: applyDateTime
9
+ };
10
+
11
+ function applyDateTime(el: HTMLElement, binding: DirectiveBinding<string>) {
12
+ if (binding.value == binding.oldValue && el.innerHTML.length) return;
13
+ el.innerText = getDateTimeValue(el, binding);
14
+ }
15
+
16
+ function getDateTimeValue(el: HTMLElement, binding: DirectiveBinding<string>) {
17
+ if (!binding.value) {
18
+ return el.attributes.getNamedItem('placeholder')?.value ?? '';
19
+ }
20
+
21
+ let prefix = '';
22
+ const isoDateStr = binding.value.replace(/ /g, 'T').replace(/\.\d+Z$/, 'Z');
23
+ const tzDateStr = el.attributes.getNamedItem('local') !== null ? isoDateStr.replace(/Z$/, '') : isoDateStr.replace(/(Z|\+00:00)?$/, 'Z');
24
+ const theDate = new Date(tzDateStr);
25
+
26
+ if (!el.attributes.getNamedItem('display-utc') !== null) {
27
+ theDate.setMinutes(theDate.getMinutes() - theDate.getTimezoneOffset());
28
+ }
29
+
30
+ let formatSpec = el.attributes.getNamedItem('format')?.value;
31
+
32
+ if (!formatSpec && el.attributes.getNamedItem('relative-date') !== null) {
33
+ const now = new Date();
34
+ if (now.getFullYear() == theDate.getFullYear() && now.getMonth() == theDate.getMonth() && now.getDate() == theDate.getDate()) {
35
+ prefix = 'at';
36
+ formatSpec = 'HH:mm';
37
+ }
38
+ }
39
+
40
+ if (!formatSpec) {
41
+ formatSpec = VfOptions.defaultDateTimeFormat;
42
+ }
43
+
44
+ let result = format(theDate, formatSpec);
45
+ if (prefix) result = prefix + ' ' + result;
46
+
47
+ return result;
48
+ }
@@ -0,0 +1,30 @@
1
+ import type { DirectiveBinding, ObjectDirective } from 'vue';
2
+
3
+ export const vDisabled: ObjectDirective<HTMLElement, boolean> = {
4
+ beforeMount: fn,
5
+ updated: fn,
6
+ unmounted
7
+ };
8
+
9
+ function fn(el: HTMLElement, binding: DirectiveBinding<boolean>) {
10
+ if (el.tagName === 'LABEL') {
11
+ if (binding.value) {
12
+ el.classList.remove('disabled');
13
+ } else {
14
+ el.classList.add('disabled');
15
+ }
16
+ el = el.querySelector('input')!;
17
+ }
18
+
19
+ if (binding.value) el.setAttribute('disabled', 'disabled');
20
+ else el.removeAttribute('disabled');
21
+ }
22
+
23
+ function unmounted(el: HTMLElement) {
24
+ if (el.tagName === 'LABEL') {
25
+ el.classList.remove('disabled');
26
+ el = el.querySelector('input')!;
27
+ }
28
+
29
+ el.removeAttribute('disabled');
30
+ }
@@ -0,0 +1,79 @@
1
+ import { remove } from 'lodash';
2
+ import type { DirectiveBinding, ObjectDirective } from 'vue';
3
+
4
+ export const vDuration: ObjectDirective<HTMLElement, number> = {
5
+ beforeMount: applyDuration,
6
+ updated: applyDuration,
7
+ unmounted: removeDuration
8
+ };
9
+
10
+ const DurationState = Symbol('HasAutoFocused');
11
+ interface IDurationState {
12
+ startTs: number;
13
+ includeSeconds: boolean;
14
+ }
15
+ type DurationElement = HTMLElement & { [DurationState]?: IDurationState };
16
+
17
+ const durationEls: DurationElement[] = [];
18
+
19
+ function updateDurations() {
20
+ durationEls.forEach(updateDuration);
21
+ }
22
+ setInterval(updateDurations, 1000);
23
+
24
+ function applyDuration(el: DurationElement, binding: DirectiveBinding<number>) {
25
+ if (binding.value == binding.oldValue) return;
26
+ if (!binding.value) return removeDuration(el);
27
+
28
+ const bindingDate = new Date(binding.value);
29
+
30
+ const baseTime = el.attributes.getNamedItem('base-time')?.value;
31
+ const startTs = bindingDate.getTime() - (baseTime ? new Date(baseTime).getTime() - binding.value * 1000 : 0);
32
+
33
+ const includeSeconds = el.getAttribute('no-seconds') === null;
34
+
35
+ if (!el[DurationState]) {
36
+ durationEls.push(el);
37
+ }
38
+
39
+ el[DurationState] = {
40
+ startTs,
41
+ includeSeconds
42
+ };
43
+
44
+ updateDuration(el);
45
+ }
46
+
47
+ function updateDuration(el: DurationElement) {
48
+ const state = el[DurationState]!;
49
+ const diff = Math.round((Date.now() - state.startTs) / 1000);
50
+ el.innerText = secondsToString(diff, state.includeSeconds);
51
+ }
52
+
53
+ function removeDuration(el: DurationElement) {
54
+ if (el[DurationState]) {
55
+ remove(durationEls, el);
56
+ delete el[DurationState];
57
+ }
58
+
59
+ el.innerText = '-';
60
+ }
61
+
62
+ function secondsToString(seconds: number, shouldSkipSeconds?: boolean) {
63
+ const result = [];
64
+ const days = Math.floor(seconds / 86400);
65
+ days && result.push(days + 'd');
66
+ seconds -= days * 86400;
67
+ const hours = Math.floor(seconds / 3600);
68
+ (days || hours) && result.push(hours + 'h');
69
+ seconds -= hours * 3600;
70
+ const minutes = Math.floor(seconds / 60);
71
+ (days || hours || minutes) && result.push(minutes + 'm');
72
+ if (!shouldSkipSeconds) {
73
+ seconds -= minutes * 60;
74
+ result.push(seconds + 's');
75
+ } else if (!result.length) {
76
+ result.push('0m');
77
+ }
78
+ return result.join(' ');
79
+ }
@@ -0,0 +1,37 @@
1
+ import type { App } from 'vue';
2
+
3
+ import { vAutofocus } from './autofocus';
4
+ import { vConfirmButton } from './confirm-button';
5
+ import { vDateInput } from './date-input';
6
+ import { vDatetime } from './datetime';
7
+ import { vDisabled } from './disabled';
8
+ import { vDuration } from './duration';
9
+ import { vInfiniteScroll } from './infinite-scroll';
10
+ import { vReadonly } from './readonly';
11
+ import { vTooltip } from './tooltip';
12
+
13
+ declare module 'vue' {
14
+ export interface ComponentCustomProperties {
15
+ vAutofocus: typeof vAutofocus;
16
+ vConfirmButton: typeof vConfirmButton;
17
+ vDateInput: typeof vDateInput;
18
+ vDatetime: typeof vDatetime;
19
+ vDisabled: typeof vDisabled;
20
+ vDuration: typeof vDuration;
21
+ vInfiniteScroll: typeof vInfiniteScroll;
22
+ vReadonly: typeof vReadonly;
23
+ vTooltip: typeof vTooltip;
24
+ }
25
+ }
26
+
27
+ export function registerDirectives(app: App<Element>): void {
28
+ app.directive('autofocus', vAutofocus);
29
+ app.directive('confirm-button', vConfirmButton);
30
+ app.directive('date-input', vDateInput);
31
+ app.directive('datetime', vDatetime);
32
+ app.directive('disabled', vDisabled);
33
+ app.directive('duration', vDuration);
34
+ app.directive('infinite-scroll', vInfiniteScroll);
35
+ app.directive('readonly', vReadonly);
36
+ app.directive('tooltip', vTooltip);
37
+ }
@@ -0,0 +1,9 @@
1
+ import type { DirectiveBinding, ObjectDirective } from 'vue';
2
+
3
+ import { type IInfiniteScrollOptions, useInfiniteScroll } from '../hooks/infinite-scroll';
4
+
5
+ export const vInfiniteScroll: ObjectDirective<Element, IInfiniteScrollOptions> = {
6
+ created(_el: Element, binding: DirectiveBinding<IInfiniteScrollOptions>) {
7
+ useInfiniteScroll(binding.value, binding.instance!.$);
8
+ }
9
+ };
@@ -0,0 +1,15 @@
1
+ import type { DirectiveBinding, ObjectDirective } from 'vue';
2
+
3
+ export const vReadonly: ObjectDirective<HTMLElement, boolean> = {
4
+ beforeMount: fn,
5
+ updated: fn
6
+ };
7
+
8
+ function fn(el: HTMLElement, binding: DirectiveBinding<boolean>) {
9
+ if (el.tagName == 'LABEL') {
10
+ el = el.querySelector('input')!;
11
+ }
12
+
13
+ if (binding.value) el.setAttribute('readonly', 'readonly');
14
+ else el.removeAttribute('readonly');
15
+ }
@@ -0,0 +1,190 @@
1
+ import type { DirectiveBinding, ObjectDirective } from 'vue';
2
+
3
+ type TooltipValue = string | null | false | undefined;
4
+
5
+ export const vTooltip: ObjectDirective<TooltipElement, TooltipValue> = {
6
+ mounted: createTip,
7
+ updated: createTip,
8
+ unmounted: destroyTip
9
+ };
10
+
11
+ const TooltipState = Symbol('TooltipState');
12
+ interface ITooltipState {
13
+ [TooltipState]?: VfTooltip;
14
+ }
15
+ type TooltipElement = HTMLElement & ITooltipState;
16
+
17
+ function createTip(el: TooltipElement, binding: DirectiveBinding<TooltipValue>) {
18
+ let tipText = el.attributes.getNamedItem('tip')?.value ?? binding.value;
19
+ if (!binding.value) tipText = null;
20
+
21
+ if (tipText) {
22
+ const config: ITooltipOptions = {
23
+ content: tipText,
24
+ html: el.getAttribute('html') !== null
25
+ };
26
+
27
+ if (!el[TooltipState]) {
28
+ el[TooltipState] = new VfTooltip(el, config);
29
+ } else {
30
+ el[TooltipState].configure(config);
31
+ }
32
+ } else {
33
+ destroyTip(el);
34
+ }
35
+ }
36
+
37
+ function destroyTip(el: TooltipElement) {
38
+ el[TooltipState]?.destroy();
39
+ delete el[TooltipState];
40
+ }
41
+
42
+ interface ITooltipOptions {
43
+ content: string;
44
+ title?: string;
45
+ delay?: number;
46
+ html?: boolean;
47
+ class?: string | string[];
48
+ }
49
+
50
+ class VfTooltip {
51
+ private lastMoveEvt?: MouseEvent;
52
+ private checkInterval?: ReturnType<typeof setInterval>;
53
+ private shouldShow = false;
54
+ private tipEl?: HTMLElement;
55
+ private titleEl?: HTMLElement;
56
+ private contentEl?: HTMLElement;
57
+
58
+ private mouseMoveBound = false;
59
+ private handleMouseMoveWithContext = this.handleMouseMove.bind(this);
60
+ private handleTargetMouseEnterWithContext = this.handleTargetMouseEnter.bind(this);
61
+ private handleTargetMouseLeaveWithContext = this.handleTargetMouseLeave.bind(this);
62
+
63
+ constructor(private el: HTMLElement, private config: ITooltipOptions) {
64
+ el.addEventListener('mouseenter', this.handleTargetMouseEnterWithContext);
65
+ el.addEventListener('mouseleave', this.handleTargetMouseLeaveWithContext);
66
+ }
67
+
68
+ configure(config: ITooltipOptions) {
69
+ this.config = config;
70
+ }
71
+
72
+ handleTargetMouseEnter(e: MouseEvent) {
73
+ this.shouldShow = true;
74
+ setTimeout(() => this.show(e), this.config.delay ?? 0);
75
+ }
76
+
77
+ handleTargetMouseLeave() {
78
+ this.shouldShow = false;
79
+ this.removeTooltip();
80
+ }
81
+
82
+ show(e?: MouseEvent) {
83
+ if (!this.shouldShow) return;
84
+
85
+ this.renderTooltip();
86
+
87
+ if (e) this.handleMouseMove(e);
88
+ }
89
+
90
+ renderTooltip() {
91
+ if (!this.tipEl) {
92
+ this.tipEl = document.createElement('div');
93
+ this.tipEl.style.position = 'absolute';
94
+ this.tipEl.style.zIndex = '1000000';
95
+ document.body.appendChild(this.tipEl);
96
+ }
97
+
98
+ this.tipEl.className = this.getClasses().join(' ');
99
+
100
+ if (this.config.title) {
101
+ if (!this.titleEl) {
102
+ this.titleEl = document.createElement('div');
103
+ this.titleEl.className = 'title';
104
+ this.tipEl.appendChild(this.titleEl);
105
+ }
106
+
107
+ this.titleEl.innerText = this.config.title;
108
+ } else if (this.titleEl) {
109
+ this.titleEl.remove();
110
+ }
111
+
112
+ if (!this.contentEl) {
113
+ this.contentEl = document.createElement('div');
114
+ this.contentEl.className = 'content';
115
+ this.tipEl.appendChild(this.contentEl);
116
+ }
117
+
118
+ this.contentEl[this.config.html ? 'innerHTML' : 'innerText'] = this.config.content;
119
+
120
+ if (this.checkInterval) {
121
+ this.checkInterval = setInterval(() => this.checkMoveEvent(), 250);
122
+ }
123
+
124
+ if (!this.mouseMoveBound) {
125
+ this.mouseMoveBound = true;
126
+ window.addEventListener('mousemove', this.handleMouseMoveWithContext);
127
+ }
128
+ }
129
+
130
+ getClasses() {
131
+ return ['vf-tooltip', ...(Array.isArray(this.config.class) ? this.config.class : this.config.class ? [this.config.class] : [])];
132
+ }
133
+
134
+ removeTooltip() {
135
+ if (this.shouldShow) return;
136
+ if (!this.tipEl) return;
137
+
138
+ this.tipEl.remove();
139
+
140
+ this.tipEl = undefined;
141
+ this.titleEl = undefined;
142
+ this.contentEl = undefined;
143
+
144
+ if (this.checkInterval) {
145
+ clearInterval(this.checkInterval);
146
+ this.checkInterval = undefined;
147
+ }
148
+
149
+ window.removeEventListener('mousemove', this.handleMouseMoveWithContext);
150
+ this.mouseMoveBound = false;
151
+ }
152
+
153
+ handleMouseMove(e: MouseEvent) {
154
+ const tipWidth = this.tipEl!.offsetWidth;
155
+ const tipHeight = this.tipEl!.offsetHeight;
156
+
157
+ const viewWidth = window.innerWidth;
158
+ const viewHeight = window.innerHeight;
159
+
160
+ let tipX = e.pageX + 10,
161
+ tipY = e.pageY + 20;
162
+
163
+ if (tipX + tipWidth > viewWidth) tipX = e.pageX - 5 - tipWidth;
164
+ if (tipY + tipHeight > viewHeight) tipY = e.pageY - 5 - tipHeight;
165
+
166
+ this.tipEl!.style.left = tipX + 'px';
167
+ this.tipEl!.style.top = tipY + 'px';
168
+
169
+ this.lastMoveEvt = e;
170
+ }
171
+
172
+ checkMoveEvent() {
173
+ if (!this.lastMoveEvt) return;
174
+
175
+ if (this.tipEl !== this.lastMoveEvt.target) {
176
+ if (!this.tipEl?.contains(this.lastMoveEvt.target as Node)) {
177
+ this.handleTargetMouseLeave();
178
+ }
179
+ }
180
+ }
181
+
182
+ destroy() {
183
+ this.shouldShow = false;
184
+
185
+ this.removeTooltip();
186
+
187
+ this.el.removeEventListener('mouseenter', this.handleTargetMouseEnterWithContext);
188
+ this.el.removeEventListener('mouseleave', this.handleTargetMouseLeaveWithContext);
189
+ }
190
+ }
@@ -0,0 +1,79 @@
1
+ import { startCase as _startCase, upperFirst as _upperFirst } from 'lodash';
2
+
3
+ import { desnakeCase, formatNumber, formatPhone, formatUSCurrency } from '..';
4
+
5
+ function bytes(value: number) {
6
+ const p = Math.floor(Math.log(value) / Math.log(1024));
7
+ const numericResult = value / Math.pow(1024, p);
8
+ const stringResult = numericResult.toFixed(2);
9
+ const unit = ['B', 'KB', 'MB', 'GB', 'TB'][p];
10
+ return `${stringResult} ${unit}`;
11
+ }
12
+
13
+ function dash(value: any) {
14
+ return value !== null && value !== undefined && String(value).length ? value : '-';
15
+ }
16
+
17
+ function dashZeros(value: number | null) {
18
+ return value ? value : '-';
19
+ }
20
+
21
+ function number(value: string | number | null) {
22
+ if (value === null) return value;
23
+ if (typeof value === 'string' && !/^\d+$/.test(value)) return value;
24
+ return formatNumber(Number(value));
25
+ }
26
+
27
+ function phone(value: string | null) {
28
+ if (!value) return value;
29
+ return formatPhone(value);
30
+ }
31
+
32
+ function upperFirst(value: null): null;
33
+ function upperFirst(value: string): string;
34
+ function upperFirst(value: string | null) {
35
+ return value ? _upperFirst(value) : null;
36
+ }
37
+
38
+ function startCase(value: null): null;
39
+ function startCase(value: string): string;
40
+ function startCase(value: string | null) {
41
+ return value ? _startCase(value) : null;
42
+ }
43
+
44
+ function upperCase(value: null): null;
45
+ function upperCase(value: string): string;
46
+ function upperCase(value: string | null) {
47
+ return value ? value.toUpperCase() : null;
48
+ }
49
+
50
+ function desnake(value: null): null;
51
+ function desnake(value: string): string;
52
+ function desnake(value: string | null) {
53
+ return value ? desnakeCase(value) : null;
54
+ }
55
+
56
+ function usCurrency(value: string | number) {
57
+ return formatUSCurrency(value);
58
+ }
59
+
60
+ const FilterFns = {
61
+ bytes,
62
+ dash,
63
+ dashZeros,
64
+ number,
65
+ phone,
66
+ upperFirst,
67
+ startCase,
68
+ upperCase,
69
+ desnake,
70
+ usCurrency
71
+ };
72
+
73
+ // type FilterFn = (value: any, ...unknown: any[]) => any;
74
+ export const createFilters = <T>(factory: (baseFilters: typeof FilterFns) => T): typeof FilterFns & T => {
75
+ return {
76
+ ...FilterFns,
77
+ ...factory(FilterFns)
78
+ };
79
+ };
@@ -0,0 +1,7 @@
1
+ type ArraySearchPredicate<T> = Parameters<T[]['findIndex']>[0];
2
+ export function replaceElement<T>(array: T[], oldElement: T | ArraySearchPredicate<T>, newElement: T) {
3
+ const index = typeof oldElement === 'function' ? array.findIndex(oldElement as any) : array.indexOf(oldElement);
4
+ if (index === -1) return false;
5
+ array.splice(index, 1, newElement);
6
+ return true;
7
+ }