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.
- package/dist/components/Button/Button.svelte +36 -78
- package/dist/components/Button/button.css +3 -80
- package/dist/components/Tooltip/Tooltip.svelte +99 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +23 -0
- package/dist/components/Tooltip/tooltip.css +88 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -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
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
/*
|
|
97
|
-
.btn-
|
|
98
|
-
|
|
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';
|