mertani-web-toolkit 0.1.70 → 0.1.71

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,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import './button.css';
3
- import { Icon } from '../../index.js';
3
+ import { Icon, Tooltip } from '../../index.js';
4
4
  import type { TIconName } from '../../icons/index.js';
5
5
  import type { HTMLButtonAttributes } from 'svelte/elements';
6
6
 
@@ -209,38 +209,6 @@
209
209
  return classes.join(' ');
210
210
  });
211
211
 
212
- let showTooltip = $state(false);
213
- let actualPosition = $state<'top' | 'bottom' | 'left' | 'right'>('right');
214
- let buttonEl: HTMLButtonElement | null = $state(null);
215
-
216
- function updateTooltipPosition() {
217
- if (tooltipPosition !== 'auto') {
218
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
219
- actualPosition = tooltipPosition as any;
220
- return;
221
- }
222
-
223
- if (!buttonEl) return;
224
-
225
- const rect = buttonEl.getBoundingClientRect();
226
- const viewportWidth = window.innerWidth;
227
- // const viewportHeight = window.innerHeight;
228
- const tooltipMargin = 12;
229
- const estimatedWidth = 120; // heuristic
230
- const estimatedHeight = 40; // heuristic
231
-
232
- // Priority: Right -> Left -> Top -> Bottom
233
- if (rect.right + estimatedWidth + tooltipMargin < viewportWidth) {
234
- actualPosition = 'right';
235
- } else if (rect.left - estimatedWidth - tooltipMargin > 0) {
236
- actualPosition = 'left';
237
- } else if (rect.top - estimatedHeight - tooltipMargin > 0) {
238
- actualPosition = 'top';
239
- } else {
240
- actualPosition = 'bottom';
241
- }
242
- }
243
-
244
212
  function handleClick() {
245
213
  if (!disabled && !isLoading && onclick) {
246
214
  onclick();
@@ -248,17 +216,12 @@
248
216
  }
249
217
 
250
218
  function handleMouseEnter() {
251
- if (tooltip) {
252
- updateTooltipPosition();
253
- showTooltip = true;
254
- }
255
219
  if (!disabled && !isLoading && onmouseenter) {
256
220
  onmouseenter();
257
221
  }
258
222
  }
259
223
 
260
224
  function handleMouseLeave() {
261
- showTooltip = false;
262
225
  if (!disabled && !isLoading && onmouseleave) {
263
226
  onmouseleave();
264
227
  }
@@ -266,46 +229,41 @@
266
229
  </script>
267
230
 
268
231
  {#if isShow}
269
- <button
270
- bind:this={buttonEl}
271
- type="button"
272
- class={buttonClasses()}
273
- style={buttonStyles()}
232
+ <Tooltip
233
+ text={tooltip}
234
+ position={tooltipPosition}
235
+ bgColor={tooltipBgColor}
236
+ textColor={tooltipTextColor}
274
237
  disabled={disabled || isLoading}
275
- onclick={handleClick}
276
- onmouseenter={handleMouseEnter}
277
- onmouseleave={handleMouseLeave}
278
- {...props}
279
238
  >
280
- {#if isLoading}
281
- <span
282
- class="btn-loader"
283
- style={loaderColor
284
- ? `border-top-color: ${loaderColor}; border-right-color: ${loaderColor};`
285
- : ''}
286
- ></span>
287
- {:else if icon && iconPosition === 'left'}
288
- <Icon name={icon} color={iconColor || 'currentColor'} width={16} height={16} />
289
- {/if}
290
-
291
- {#if label}
292
- <span class="btn-label">{label}</span>
293
- {/if}
294
-
295
- {#if !isLoading && icon && iconPosition === 'right'}
296
- <Icon name={icon} color={iconColor || 'currentColor'} width={16} height={16} />
297
- {/if}
298
-
299
- {#if tooltip && showTooltip}
300
- <div class="btn-tooltip-container tooltip-{actualPosition}">
301
- <div
302
- class="btn-tooltip"
303
- style={`--tooltip-bg-color: ${tooltipBgColor}; --tooltip-text-color: ${tooltipTextColor};`}
304
- >
305
- {tooltip}
306
- <div class="tooltip-arrow"></div>
307
- </div>
308
- </div>
309
- {/if}
310
- </button>
239
+ <button
240
+ type="button"
241
+ class={buttonClasses()}
242
+ style={buttonStyles()}
243
+ disabled={disabled || isLoading}
244
+ onclick={handleClick}
245
+ onmouseenter={handleMouseEnter}
246
+ onmouseleave={handleMouseLeave}
247
+ {...props}
248
+ >
249
+ {#if isLoading}
250
+ <span
251
+ class="btn-loader"
252
+ style={loaderColor
253
+ ? `border-top-color: ${loaderColor}; border-right-color: ${loaderColor};`
254
+ : ''}
255
+ ></span>
256
+ {:else if icon && iconPosition === 'left'}
257
+ <Icon name={icon} color={iconColor || 'currentColor'} width={16} height={16} />
258
+ {/if}
259
+
260
+ {#if label}
261
+ <span class="btn-label">{label}</span>
262
+ {/if}
263
+
264
+ {#if !isLoading && icon && iconPosition === 'right'}
265
+ <Icon name={icon} color={iconColor || 'currentColor'} width={16} height={16} />
266
+ {/if}
267
+ </button>
268
+ </Tooltip>
311
269
  {/if}
@@ -93,85 +93,8 @@
93
93
  align-items: center;
94
94
  }
95
95
 
96
- /* Tooltip */
97
- .btn-tooltip-container {
98
- position: absolute;
99
- z-index: 50;
100
- pointer-events: none;
101
- display: flex;
96
+ /* Button Label */
97
+ .btn-label {
98
+ display: inline-flex;
102
99
  align-items: center;
103
- justify-content: center;
104
- opacity: 0;
105
- animation: tooltip-fade-in 0.2s forwards;
106
- }
107
-
108
- @keyframes tooltip-fade-in {
109
- from {
110
- opacity: 0;
111
- }
112
- to {
113
- opacity: 1;
114
- }
115
- }
116
-
117
- .btn-tooltip {
118
- background-color: var(--tooltip-bg-color);
119
- color: var(--tooltip-text-color);
120
- padding: 8px 14px;
121
- border-radius: 10px;
122
- font-size: 13px;
123
- font-weight: 500;
124
- white-space: nowrap;
125
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2);
126
- position: relative;
127
- line-height: 1.2;
128
- }
129
-
130
- .tooltip-arrow {
131
- position: absolute;
132
- width: 8px;
133
- height: 8px;
134
- background-color: var(--tooltip-bg-color);
135
- transform: rotate(45deg);
136
- }
137
-
138
- /* Positions */
139
- .tooltip-top {
140
- bottom: 100%;
141
- left: 50%;
142
- transform: translateX(-50%) translateY(-10px);
143
- }
144
- .tooltip-top .tooltip-arrow {
145
- bottom: -4px;
146
- left: calc(50% - 4px);
147
- }
148
-
149
- .tooltip-bottom {
150
- top: 100%;
151
- left: 50%;
152
- transform: translateX(-50%) translateY(10px);
153
- }
154
- .tooltip-bottom .tooltip-arrow {
155
- top: -4px;
156
- left: calc(50% - 4px);
157
- }
158
-
159
- .tooltip-left {
160
- right: 100%;
161
- top: 50%;
162
- transform: translateY(-50%) translateX(-10px);
163
- }
164
- .tooltip-left .tooltip-arrow {
165
- right: -4px;
166
- top: calc(50% - 4px);
167
- }
168
-
169
- .tooltip-right {
170
- left: 100%;
171
- top: 50%;
172
- transform: translateY(-50%) translateX(10px);
173
- }
174
- .tooltip-right .tooltip-arrow {
175
- left: -4px;
176
- top: calc(50% - 4px);
177
100
  }
@@ -0,0 +1,99 @@
1
+ <script lang="ts">
2
+ import './tooltip.css';
3
+ import type { Snippet } from 'svelte';
4
+
5
+ interface Props {
6
+ /** Teks yang akan ditampilkan dalam tooltip */
7
+ text?: string;
8
+ /** Posisi tooltip: top, bottom, left, right, atau auto (default: auto) */
9
+ position?: 'top' | 'bottom' | 'left' | 'right' | 'auto';
10
+ /** Warna background kustom untuk tooltip */
11
+ bgColor?: string;
12
+ /** Warna teks kustom untuk tooltip */
13
+ textColor?: string;
14
+ /** Konten yang akan diberikan tooltip saat di-hover */
15
+ children?: Snippet;
16
+ /** Jika true, tooltip tidak akan muncul */
17
+ disabled?: boolean;
18
+ /** Custom CSS class untuk wrapper */
19
+ class?: string;
20
+ /** Custom inline style untuk wrapper */
21
+ style?: string;
22
+ }
23
+
24
+ let {
25
+ text = '',
26
+ position = 'auto',
27
+ bgColor = 'var(--color-bg-inverse)',
28
+ textColor = 'var(--color-text-inverse)',
29
+ children,
30
+ disabled = false,
31
+ class: className = '',
32
+ style: customStyle = ''
33
+ }: Props = $props();
34
+
35
+ let showTooltip = $state(false);
36
+ let actualPosition = $state<'top' | 'bottom' | 'left' | 'right'>('right');
37
+ let wrapperEl: HTMLDivElement | null = $state(null);
38
+
39
+ function updateTooltipPosition() {
40
+ if (position !== 'auto') {
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ actualPosition = position as any;
43
+ return;
44
+ }
45
+
46
+ if (!wrapperEl) return;
47
+
48
+ const rect = wrapperEl.getBoundingClientRect();
49
+ const viewportWidth = window.innerWidth;
50
+ const tooltipMargin = 12;
51
+ const estimatedWidth = 120; // heuristic
52
+ const estimatedHeight = 40; // heuristic
53
+
54
+ // Priority: Right -> Left -> Top -> Bottom
55
+ if (rect.right + estimatedWidth + tooltipMargin < viewportWidth) {
56
+ actualPosition = 'right';
57
+ } else if (rect.left - estimatedWidth - tooltipMargin > 0) {
58
+ actualPosition = 'left';
59
+ } else if (rect.top - estimatedHeight - tooltipMargin > 0) {
60
+ actualPosition = 'top';
61
+ } else {
62
+ actualPosition = 'bottom';
63
+ }
64
+ }
65
+
66
+ function handleMouseEnter() {
67
+ if (!disabled && text) {
68
+ updateTooltipPosition();
69
+ showTooltip = true;
70
+ }
71
+ }
72
+
73
+ function handleMouseLeave() {
74
+ showTooltip = false;
75
+ }
76
+ </script>
77
+
78
+ <div
79
+ bind:this={wrapperEl}
80
+ class="tooltip-wrapper {className}"
81
+ style={customStyle}
82
+ onmouseenter={handleMouseEnter}
83
+ onmouseleave={handleMouseLeave}
84
+ role="presentation"
85
+ >
86
+ {@render children?.()}
87
+
88
+ {#if text && showTooltip}
89
+ <div class="tooltip-container tooltip-{actualPosition}">
90
+ <div
91
+ class="tooltip-content"
92
+ style={`--tooltip-bg-color: ${bgColor}; --tooltip-text-color: ${textColor};`}
93
+ >
94
+ {text}
95
+ <div class="tooltip-arrow"></div>
96
+ </div>
97
+ </div>
98
+ {/if}
99
+ </div>
@@ -0,0 +1,23 @@
1
+ import './tooltip.css';
2
+ import type { Snippet } from 'svelte';
3
+ interface Props {
4
+ /** Teks yang akan ditampilkan dalam tooltip */
5
+ text?: string;
6
+ /** Posisi tooltip: top, bottom, left, right, atau auto (default: auto) */
7
+ position?: 'top' | 'bottom' | 'left' | 'right' | 'auto';
8
+ /** Warna background kustom untuk tooltip */
9
+ bgColor?: string;
10
+ /** Warna teks kustom untuk tooltip */
11
+ textColor?: string;
12
+ /** Konten yang akan diberikan tooltip saat di-hover */
13
+ children?: Snippet;
14
+ /** Jika true, tooltip tidak akan muncul */
15
+ disabled?: boolean;
16
+ /** Custom CSS class untuk wrapper */
17
+ class?: string;
18
+ /** Custom inline style untuk wrapper */
19
+ style?: string;
20
+ }
21
+ declare const Tooltip: import("svelte").Component<Props, {}, "">;
22
+ type Tooltip = ReturnType<typeof Tooltip>;
23
+ export default Tooltip;
@@ -0,0 +1,88 @@
1
+ /* Tooltip Container */
2
+ .tooltip-container {
3
+ position: absolute;
4
+ z-index: 50;
5
+ pointer-events: none;
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+ opacity: 0;
10
+ animation: tooltip-fade-in 0.2s forwards;
11
+ }
12
+
13
+ @keyframes tooltip-fade-in {
14
+ from {
15
+ opacity: 0;
16
+ }
17
+ to {
18
+ opacity: 1;
19
+ }
20
+ }
21
+
22
+ .tooltip-content {
23
+ background-color: var(--tooltip-bg-color, var(--color-bg-inverse, #111111));
24
+ color: var(--tooltip-text-color, var(--color-text-inverse, #ffffff));
25
+ padding: 8px 14px;
26
+ border-radius: 10px;
27
+ font-size: 13px;
28
+ font-weight: 500;
29
+ white-space: nowrap;
30
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2);
31
+ position: relative;
32
+ line-height: 1.2;
33
+ }
34
+
35
+ .tooltip-arrow {
36
+ position: absolute;
37
+ width: 8px;
38
+ height: 8px;
39
+ background-color: var(--tooltip-bg-color, var(--color-bg-inverse, #111111));
40
+ transform: rotate(45deg);
41
+ }
42
+
43
+ /* Positions */
44
+ .tooltip-top {
45
+ bottom: 100%;
46
+ left: 50%;
47
+ transform: translateX(-50%) translateY(-10px);
48
+ }
49
+ .tooltip-top .tooltip-arrow {
50
+ bottom: -4px;
51
+ left: calc(50% - 4px);
52
+ }
53
+
54
+ .tooltip-bottom {
55
+ top: 100%;
56
+ left: 50%;
57
+ transform: translateX(-50%) translateY(10px);
58
+ }
59
+ .tooltip-bottom .tooltip-arrow {
60
+ top: -4px;
61
+ left: calc(50% - 4px);
62
+ }
63
+
64
+ .tooltip-left {
65
+ right: 100%;
66
+ top: 50%;
67
+ transform: translateY(-50%) translateX(-10px);
68
+ }
69
+ .tooltip-left .tooltip-arrow {
70
+ right: -4px;
71
+ top: calc(50% - 4px);
72
+ }
73
+
74
+ .tooltip-right {
75
+ left: 100%;
76
+ top: 50%;
77
+ transform: translateY(-50%) translateX(10px);
78
+ }
79
+ .tooltip-right .tooltip-arrow {
80
+ left: -4px;
81
+ top: calc(50% - 4px);
82
+ }
83
+
84
+ /* Wrapper */
85
+ .tooltip-wrapper {
86
+ position: relative;
87
+ display: inline-flex;
88
+ }
package/dist/index.d.ts CHANGED
@@ -26,4 +26,5 @@ export { default as MapMarkerGroup } from './components/map/MapMarkerGroup.svelt
26
26
  export { default as MapGeoJSON } from './components/map/MapGeoJSON.svelte';
27
27
  export { default as MapTopoJSON } from './components/map/MapTopoJSON.svelte';
28
28
  export { default as MapVelocity } from './components/map/MapVelocity.svelte';
29
+ export { default as Tooltip } from './components/Tooltip/Tooltip.svelte';
29
30
  export type { Column, DeviceStatus, TableAction } from './components/Table/types.js';
package/dist/index.js CHANGED
@@ -26,3 +26,4 @@ export { default as MapMarkerGroup } from './components/map/MapMarkerGroup.svelt
26
26
  export { default as MapGeoJSON } from './components/map/MapGeoJSON.svelte';
27
27
  export { default as MapTopoJSON } from './components/map/MapTopoJSON.svelte';
28
28
  export { default as MapVelocity } from './components/map/MapVelocity.svelte';
29
+ export { default as Tooltip } from './components/Tooltip/Tooltip.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mertani-web-toolkit",
3
- "version": "0.1.70",
3
+ "version": "0.1.71",
4
4
  "homepage": "https://storybook.mertani.com/",
5
5
  "scripts": {
6
6
  "dev": "vite dev",