@vasakgroup/vue-libvasak 0.1.0 → 0.2.1
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/package.json +8 -8
- package/src/cards/DeviceCard.vue +82 -0
- package/src/cards/ListCard.vue +40 -0
- package/src/controls/ActionButton.vue +85 -0
- package/src/controls/ToggleControl.vue +55 -0
- package/src/forms/FormGroup.vue +27 -0
- package/src/forms/SliderControl.vue +100 -0
- package/src/forms/SwitchToggle.vue +49 -0
- package/src/index.ts +37 -2
- package/src/layout/ConfigSection.vue +28 -0
- package/src/sidebar/SideButton.vue +1 -3
- package/src/tray/TrayIconButton.vue +80 -0
- package/src/types/vue-libvasak.d.ts +153 -8
- package/src/window/TopBar.vue +3 -3
- package/dist/vue-libvasak.es.js +0 -2084
- package/dist/vue-libvasak.umd.js +0 -1
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vasakgroup/vue-libvasak",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Vue componenets for VSK Applications",
|
|
5
5
|
"main": "./dist/vue-libvasak.umd.js",
|
|
6
6
|
"module": "./dist/vue-libvasak.es.js",
|
|
7
|
-
"types": "
|
|
7
|
+
"types": "src/types/vue-libvasak.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
10
|
"src"
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
},
|
|
43
43
|
"license": "GPLv3",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@tauri-apps/api": "^2.
|
|
45
|
+
"@tauri-apps/api": "^2.10.1",
|
|
46
46
|
"@vasakgroup/plugin-vicons": "^2.0.0",
|
|
47
|
-
"vue": "^3.5.
|
|
48
|
-
"vue-router": "^
|
|
47
|
+
"vue": "^3.5.28",
|
|
48
|
+
"vue-router": "^5.0.3"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"vue": "^3.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"typescript": "^5.9.
|
|
55
|
-
"vite": "^7.1
|
|
56
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
54
|
+
"typescript": "^5.9.3",
|
|
55
|
+
"vite": "^7.3.1",
|
|
56
|
+
"@vitejs/plugin-vue": "^6.0.4"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "vite build",
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="flex items-center justify-between background rounded-vsk px-6 py-3 mb-4"
|
|
4
|
+
:class="[{ 'border-l-4 border-green-500': isConnected }, customClass]"
|
|
5
|
+
@click="handleClick"
|
|
6
|
+
>
|
|
7
|
+
<div class="flex items-center gap-3 flex-1 min-w-0">
|
|
8
|
+
<img :src="icon" :alt="title" class="h-7 w-7 shrink-0" />
|
|
9
|
+
<div class="min-w-0">
|
|
10
|
+
<div class="font-semibold truncate">
|
|
11
|
+
{{ title }}
|
|
12
|
+
</div>
|
|
13
|
+
<div v-if="subtitle" class="text-xs text-gray-400 truncate">
|
|
14
|
+
{{ subtitle }}
|
|
15
|
+
</div>
|
|
16
|
+
<div v-if="metadata" class="text-xs text-gray-400 truncate">
|
|
17
|
+
{{ metadata }}
|
|
18
|
+
</div>
|
|
19
|
+
<div v-if="extraInfo && extraInfo.length > 0" class="text-xs text-gray-400 flex gap-2 mt-1">
|
|
20
|
+
<span v-for="(info, index) in extraInfo" :key="index">
|
|
21
|
+
{{ info }}
|
|
22
|
+
</span>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<button
|
|
28
|
+
v-if="showActionButton"
|
|
29
|
+
class="bg-vsk-primary rounded-vsk px-4 py-2 text-sm font-semibold cursor-pointer hover:opacity-70"
|
|
30
|
+
@click.stop="handleAction"
|
|
31
|
+
>
|
|
32
|
+
{{ actionLabel }}
|
|
33
|
+
</button>
|
|
34
|
+
|
|
35
|
+
<!-- Status indicator for connected state -->
|
|
36
|
+
<div
|
|
37
|
+
v-if="showStatusIndicator && isConnected"
|
|
38
|
+
class="w-2 h-2 rounded-full bg-green-500"
|
|
39
|
+
/>
|
|
40
|
+
</div>
|
|
41
|
+
</template>
|
|
42
|
+
|
|
43
|
+
<script setup lang="ts">
|
|
44
|
+
interface Props {
|
|
45
|
+
icon: string;
|
|
46
|
+
title: string;
|
|
47
|
+
subtitle?: string;
|
|
48
|
+
metadata?: string;
|
|
49
|
+
extraInfo?: string[];
|
|
50
|
+
isConnected?: boolean;
|
|
51
|
+
showActionButton?: boolean;
|
|
52
|
+
actionLabel?: string;
|
|
53
|
+
showStatusIndicator?: boolean;
|
|
54
|
+
customClass?: string;
|
|
55
|
+
clickable?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
withDefaults(defineProps<Props>(), {
|
|
59
|
+
subtitle: '',
|
|
60
|
+
metadata: '',
|
|
61
|
+
extraInfo: () => [],
|
|
62
|
+
isConnected: false,
|
|
63
|
+
showActionButton: true,
|
|
64
|
+
actionLabel: 'Conectar',
|
|
65
|
+
showStatusIndicator: false,
|
|
66
|
+
customClass: '',
|
|
67
|
+
clickable: false,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const emit = defineEmits<{
|
|
71
|
+
action: [];
|
|
72
|
+
click: [];
|
|
73
|
+
}>();
|
|
74
|
+
|
|
75
|
+
const handleAction = () => {
|
|
76
|
+
emit('action');
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleClick = () => {
|
|
80
|
+
emit('click');
|
|
81
|
+
};
|
|
82
|
+
</script>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
clickable?: boolean;
|
|
4
|
+
customClass?: string | Record<string, boolean>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
8
|
+
clickable: false,
|
|
9
|
+
customClass: () => ({}),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const emit = defineEmits<{
|
|
13
|
+
click: [];
|
|
14
|
+
}>();
|
|
15
|
+
|
|
16
|
+
const handleClick = () => {
|
|
17
|
+
if (props.clickable) {
|
|
18
|
+
emit('click');
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div
|
|
25
|
+
:class="[
|
|
26
|
+
'flex items-center justify-between background p-3 rounded-vsk border border-vsk-primary/70 transition-colors duration-200',
|
|
27
|
+
{
|
|
28
|
+
'hover:bg-vsk-primary/5 cursor-pointer': props.clickable,
|
|
29
|
+
},
|
|
30
|
+
customClass,
|
|
31
|
+
]"
|
|
32
|
+
@click="handleClick"
|
|
33
|
+
>
|
|
34
|
+
<slot />
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style scoped>
|
|
39
|
+
/* Ningún estilo adicional requerido */
|
|
40
|
+
</style>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
label: string;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
variant?: 'primary' | 'secondary' | 'danger';
|
|
6
|
+
loading?: boolean;
|
|
7
|
+
customClass?: string | Record<string, boolean>;
|
|
8
|
+
size?: 'sm' | 'md' | 'lg';
|
|
9
|
+
fullWidth?: boolean;
|
|
10
|
+
iconSrc?: string;
|
|
11
|
+
iconAlt?: string;
|
|
12
|
+
iconRight?: boolean;
|
|
13
|
+
type?: 'button' | 'submit' | 'reset';
|
|
14
|
+
stopPropagation?: boolean;
|
|
15
|
+
preventDefault?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
19
|
+
disabled: false,
|
|
20
|
+
variant: 'primary',
|
|
21
|
+
loading: false,
|
|
22
|
+
customClass: () => ({}),
|
|
23
|
+
size: 'md',
|
|
24
|
+
fullWidth: false,
|
|
25
|
+
iconSrc: '',
|
|
26
|
+
iconAlt: '',
|
|
27
|
+
iconRight: false,
|
|
28
|
+
type: 'button',
|
|
29
|
+
stopPropagation: false,
|
|
30
|
+
preventDefault: false,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const emit = defineEmits<{
|
|
34
|
+
click: [];
|
|
35
|
+
}>();
|
|
36
|
+
|
|
37
|
+
const variantClasses: Record<string, string> = {
|
|
38
|
+
primary: 'bg-primary dark:bg-primary-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-primary/90 dark:hover:bg-primary-dark/90',
|
|
39
|
+
secondary: 'bg-secondary dark:bg-secondary-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-secondary/80 dark:hover:bg-secondary-dark/80',
|
|
40
|
+
danger: 'bg-status-error dark:bg-status-error-dark text-tx-on-primary dark:text-tx-on-primary-dark hover:bg-status-error/90 dark:hover:bg-status-error-dark/90',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const sizeClasses: Record<'sm' | 'md' | 'lg', string> = {
|
|
44
|
+
sm: 'px-2 py-1 text-xs',
|
|
45
|
+
md: 'px-3 py-1 text-sm',
|
|
46
|
+
lg: 'px-4 py-2 text-base',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const handleClick = (event: Event) => {
|
|
50
|
+
if (props.stopPropagation) event.stopPropagation();
|
|
51
|
+
if (props.preventDefault) event.preventDefault();
|
|
52
|
+
if (!props.disabled && !props.loading) {
|
|
53
|
+
emit('click');
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<button
|
|
60
|
+
:type="props.type"
|
|
61
|
+
@click="handleClick"
|
|
62
|
+
class="rounded-corner transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
|
|
63
|
+
:class="[
|
|
64
|
+
variantClasses[props.variant],
|
|
65
|
+
sizeClasses[props.size],
|
|
66
|
+
props.fullWidth ? 'w-full' : '',
|
|
67
|
+
props.iconSrc && !props.label ? 'px-2 py-2' : '',
|
|
68
|
+
customClass,
|
|
69
|
+
]"
|
|
70
|
+
:disabled="props.disabled || props.loading"
|
|
71
|
+
>
|
|
72
|
+
<span v-if="loading" class="w-4 h-4 animate-spin rounded-full border-2 border-current border-t-transparent"></span>
|
|
73
|
+
<template v-if="props.iconSrc && !props.iconRight">
|
|
74
|
+
<img :src="props.iconSrc" :alt="props.iconAlt || props.label" class="w-4 h-4" />
|
|
75
|
+
</template>
|
|
76
|
+
<span v-if="props.label">{{ props.label }}</span>
|
|
77
|
+
<template v-if="props.iconSrc && props.iconRight">
|
|
78
|
+
<img :src="props.iconSrc" :alt="props.iconAlt || props.label" class="w-4 h-4" />
|
|
79
|
+
</template>
|
|
80
|
+
</button>
|
|
81
|
+
</template>
|
|
82
|
+
|
|
83
|
+
<style scoped>
|
|
84
|
+
/* Ningún estilo adicional requerido */
|
|
85
|
+
</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
@click="handleClick"
|
|
4
|
+
class="p-2 rounded-corner background hover:opacity-50 transition-all duration-300 h-17.5 w-17.5 group relative overflow-hidden hover:scale-105 hover:shadow-lg active:scale-95"
|
|
5
|
+
:class="{
|
|
6
|
+
'animate-pulse': isLoading,
|
|
7
|
+
'ring-2 ring-primary dark:ring-primary-dark': isActive,
|
|
8
|
+
'opacity-60': !isActive,
|
|
9
|
+
...customClass
|
|
10
|
+
}"
|
|
11
|
+
:disabled="isLoading"
|
|
12
|
+
>
|
|
13
|
+
<img
|
|
14
|
+
:src="icon"
|
|
15
|
+
:alt="alt"
|
|
16
|
+
:title="tooltip"
|
|
17
|
+
class="m-auto w-12.5 h-12.5 transition-all duration-300 group-hover:scale-110 relative z-10"
|
|
18
|
+
:class="{
|
|
19
|
+
'animate-spin': isLoading,
|
|
20
|
+
'filter brightness-75': !isActive,
|
|
21
|
+
'drop-shadow-lg': isActive,
|
|
22
|
+
...iconClass
|
|
23
|
+
}"
|
|
24
|
+
/>
|
|
25
|
+
</button>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
interface Props {
|
|
30
|
+
icon: string;
|
|
31
|
+
alt?: string;
|
|
32
|
+
tooltip?: string;
|
|
33
|
+
isActive?: boolean;
|
|
34
|
+
isLoading?: boolean;
|
|
35
|
+
iconClass?: Record<string, boolean>;
|
|
36
|
+
customClass?: Record<string, boolean>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
withDefaults(defineProps<Props>(), {
|
|
40
|
+
alt: '',
|
|
41
|
+
tooltip: '',
|
|
42
|
+
isActive: false,
|
|
43
|
+
isLoading: false,
|
|
44
|
+
iconClass: () => ({}),
|
|
45
|
+
customClass: () => ({}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const emit = defineEmits<{
|
|
49
|
+
click: [];
|
|
50
|
+
}>();
|
|
51
|
+
|
|
52
|
+
const handleClick = () => {
|
|
53
|
+
emit('click');
|
|
54
|
+
};
|
|
55
|
+
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
label: string;
|
|
4
|
+
htmlFor?: string;
|
|
5
|
+
customClass?: string | Record<string, boolean>;
|
|
6
|
+
labelClass?: string | Record<string, boolean>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
withDefaults(defineProps<Props>(), {
|
|
10
|
+
htmlFor: '',
|
|
11
|
+
customClass: () => ({}),
|
|
12
|
+
labelClass: () => ({}),
|
|
13
|
+
});
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<div class="flex flex-col gap-2" :class="customClass">
|
|
18
|
+
<label v-if="label" :for="htmlFor" class="text-sm font-medium text-primary dark:text-primary-dark" :class="labelClass">
|
|
19
|
+
{{ label }}
|
|
20
|
+
</label>
|
|
21
|
+
<slot />
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<style scoped>
|
|
26
|
+
/* Ningún estilo adicional requerido */
|
|
27
|
+
</style>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="background rounded-corner flex flex-row items-center gap-2 justify-between w-full h-auto p-4 transition-all duration-200 hover:bg-ui-surface/80 dark:hover:bg-ui-surface-dark/80"
|
|
4
|
+
>
|
|
5
|
+
<button
|
|
6
|
+
v-if="showButton"
|
|
7
|
+
@click="handleButtonClick"
|
|
8
|
+
class="w-8 h-8 flex items-center justify-center rounded-corner transition-all duration-200 hover:bg-ui-surface/80 dark:hover:bg-ui-surface-dark/80 hover:scale-110 active:scale-95"
|
|
9
|
+
>
|
|
10
|
+
<img
|
|
11
|
+
:src="icon"
|
|
12
|
+
:alt="alt"
|
|
13
|
+
:title="tooltip"
|
|
14
|
+
class="w-6 h-6 transition-all duration-200"
|
|
15
|
+
:class="iconClass"
|
|
16
|
+
/>
|
|
17
|
+
</button>
|
|
18
|
+
|
|
19
|
+
<div
|
|
20
|
+
v-else
|
|
21
|
+
class="w-8 h-8 flex items-center justify-center"
|
|
22
|
+
>
|
|
23
|
+
<img
|
|
24
|
+
:src="icon"
|
|
25
|
+
:alt="alt"
|
|
26
|
+
class="w-6 h-6 transition-all duration-200"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<input
|
|
31
|
+
type="range"
|
|
32
|
+
:min="min"
|
|
33
|
+
:max="max"
|
|
34
|
+
:value="modelValue"
|
|
35
|
+
@input="handleInput"
|
|
36
|
+
class="flex-1 transition-all duration-200 hover:scale-105"
|
|
37
|
+
/>
|
|
38
|
+
|
|
39
|
+
<span
|
|
40
|
+
class="w-12 text-right transition-all duration-200 font-medium"
|
|
41
|
+
:class="percentageClass"
|
|
42
|
+
>
|
|
43
|
+
{{ percentage }}%
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
import { computed } from 'vue';
|
|
50
|
+
|
|
51
|
+
interface Props {
|
|
52
|
+
icon: string;
|
|
53
|
+
alt?: string;
|
|
54
|
+
tooltip?: string;
|
|
55
|
+
modelValue: number;
|
|
56
|
+
min?: number;
|
|
57
|
+
max?: number;
|
|
58
|
+
showButton?: boolean;
|
|
59
|
+
iconClass?: string | Record<string, boolean>;
|
|
60
|
+
getPercentageClass?: (percentage: number) => string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
64
|
+
alt: '',
|
|
65
|
+
tooltip: '',
|
|
66
|
+
min: 0,
|
|
67
|
+
max: 100,
|
|
68
|
+
showButton: false,
|
|
69
|
+
iconClass: () => ({}),
|
|
70
|
+
getPercentageClass: () => '',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const emit = defineEmits<{
|
|
74
|
+
'update:modelValue': [value: number];
|
|
75
|
+
'buttonClick': [];
|
|
76
|
+
}>();
|
|
77
|
+
|
|
78
|
+
const percentage = computed(() => {
|
|
79
|
+
if (props.max <= props.min) return 0;
|
|
80
|
+
const range = props.max - props.min;
|
|
81
|
+
const value = props.modelValue - props.min;
|
|
82
|
+
return Math.round((value / range) * 100);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const percentageClass = computed(() => {
|
|
86
|
+
if (props.getPercentageClass) {
|
|
87
|
+
return props.getPercentageClass(percentage.value);
|
|
88
|
+
}
|
|
89
|
+
return '';
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const handleInput = (event: Event) => {
|
|
93
|
+
const target = event.target as HTMLInputElement;
|
|
94
|
+
emit('update:modelValue', Number(target.value));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const handleButtonClick = () => {
|
|
98
|
+
emit('buttonClick');
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
@click="handleClick"
|
|
5
|
+
:disabled="disabled"
|
|
6
|
+
:class="[
|
|
7
|
+
'relative inline-flex items-center rounded-full transition-colors',
|
|
8
|
+
size === 'small' ? 'h-6 w-11' : 'h-7 w-12',
|
|
9
|
+
isOn ? activeClass : inactiveClass,
|
|
10
|
+
disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',
|
|
11
|
+
customClass
|
|
12
|
+
]"
|
|
13
|
+
>
|
|
14
|
+
<span
|
|
15
|
+
:class="[
|
|
16
|
+
'inline-block transform rounded-full bg-white shadow transition-transform',
|
|
17
|
+
size === 'small' ? 'h-4 w-4' : 'h-6 w-6',
|
|
18
|
+
isOn ? (size === 'small' ? 'translate-x-6' : 'translate-x-5') : 'translate-x-1'
|
|
19
|
+
]"
|
|
20
|
+
></span>
|
|
21
|
+
</button>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
interface Props {
|
|
26
|
+
isOn: boolean;
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
size?: 'small' | 'medium';
|
|
29
|
+
activeClass?: string;
|
|
30
|
+
inactiveClass?: string;
|
|
31
|
+
customClass?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
35
|
+
disabled: false,
|
|
36
|
+
size: 'small',
|
|
37
|
+
activeClass: 'bg-primary dark:bg-primary-dark',
|
|
38
|
+
inactiveClass: 'background',
|
|
39
|
+
customClass: '',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits<{
|
|
43
|
+
toggle: [value: boolean];
|
|
44
|
+
}>();
|
|
45
|
+
|
|
46
|
+
const handleClick = () => {
|
|
47
|
+
emit('toggle', !props.isOn);
|
|
48
|
+
};
|
|
49
|
+
</script>
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
import SideBar from "./sidebar/SideBar.vue";
|
|
2
2
|
import SideButton from "./sidebar/SideButton.vue";
|
|
3
3
|
import WindowFrame from "./window/WindowFrame.vue";
|
|
4
|
+
import ActionButton from "./controls/ActionButton.vue";
|
|
5
|
+
import ConfigSection from "./layout/ConfigSection.vue";
|
|
6
|
+
import DeviceCard from "./cards/DeviceCard.vue";
|
|
7
|
+
import FormGroup from "./forms/FormGroup.vue";
|
|
8
|
+
import ListCard from "./cards/ListCard.vue";
|
|
9
|
+
import SliderControl from "./forms/SliderControl.vue";
|
|
10
|
+
import SwitchToggle from "./forms/SwitchToggle.vue";
|
|
11
|
+
import ToggleControl from "./controls/ToggleControl.vue";
|
|
12
|
+
import TrayIconButton from "./tray/TrayIconButton.vue";
|
|
4
13
|
import type { App } from "vue";
|
|
5
14
|
|
|
6
|
-
const components = [
|
|
15
|
+
const components = [
|
|
16
|
+
SideBar,
|
|
17
|
+
SideButton,
|
|
18
|
+
WindowFrame,
|
|
19
|
+
ActionButton,
|
|
20
|
+
ConfigSection,
|
|
21
|
+
DeviceCard,
|
|
22
|
+
FormGroup,
|
|
23
|
+
ListCard,
|
|
24
|
+
SliderControl,
|
|
25
|
+
SwitchToggle,
|
|
26
|
+
ToggleControl,
|
|
27
|
+
TrayIconButton,
|
|
28
|
+
];
|
|
7
29
|
|
|
8
30
|
export default {
|
|
9
31
|
install(app: App) {
|
|
@@ -13,4 +35,17 @@ export default {
|
|
|
13
35
|
},
|
|
14
36
|
};
|
|
15
37
|
|
|
16
|
-
export {
|
|
38
|
+
export {
|
|
39
|
+
SideBar,
|
|
40
|
+
SideButton,
|
|
41
|
+
WindowFrame,
|
|
42
|
+
ActionButton,
|
|
43
|
+
ConfigSection,
|
|
44
|
+
DeviceCard,
|
|
45
|
+
FormGroup,
|
|
46
|
+
ListCard,
|
|
47
|
+
SliderControl,
|
|
48
|
+
SwitchToggle,
|
|
49
|
+
ToggleControl,
|
|
50
|
+
TrayIconButton,
|
|
51
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
title: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
customClass?: string | Record<string, boolean>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
withDefaults(defineProps<Props>(), {
|
|
9
|
+
icon: '',
|
|
10
|
+
customClass: () => ({}),
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div
|
|
16
|
+
class="flex flex-col gap-4 p-4 background rounded-corner"
|
|
17
|
+
:class="customClass"
|
|
18
|
+
>
|
|
19
|
+
<h3 class="text-base font-semibold m-0 text-primary dark:text-primary-dark">
|
|
20
|
+
{{ icon ? `${icon} ${title}` : title }}
|
|
21
|
+
</h3>
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<style scoped>
|
|
27
|
+
/* Ningún estilo adicional requerido */
|
|
28
|
+
</style>
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { defineComponent } from "vue";
|
|
3
|
-
|
|
4
2
|
const props = defineProps({
|
|
5
3
|
title: {
|
|
6
4
|
type: String,
|
|
@@ -15,6 +13,6 @@ const props = defineProps({
|
|
|
15
13
|
|
|
16
14
|
<template>
|
|
17
15
|
<a href="#" class="sidebar-button">
|
|
18
|
-
<img :src="image" class="img-fluid" />
|
|
16
|
+
<img :src="image" :alt="title" class="img-fluid" />
|
|
19
17
|
</a>
|
|
20
18
|
</template>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="p-1 rounded-corner relative hover:bg-primary dark:hover:bg-primary-dark group transition-all duration-300"
|
|
4
|
+
:class="customClass"
|
|
5
|
+
:title="tooltip"
|
|
6
|
+
@click="handleClick"
|
|
7
|
+
@mouseenter="showTooltip = true"
|
|
8
|
+
@mouseleave="showTooltip = false"
|
|
9
|
+
>
|
|
10
|
+
<img
|
|
11
|
+
:src="icon"
|
|
12
|
+
:alt="alt"
|
|
13
|
+
class="m-auto h-5.5 w-auto transition-all duration-300"
|
|
14
|
+
:class="iconClass"
|
|
15
|
+
/>
|
|
16
|
+
|
|
17
|
+
<!-- Badge/Counter -->
|
|
18
|
+
<div
|
|
19
|
+
v-if="badge !== null && badge > 0"
|
|
20
|
+
class="absolute bottom-1 right-1 bg-primary dark:bg-primary-dark text-white text-xs rounded-full w-4 h-4 flex items-center justify-center font-bold animate-bounce"
|
|
21
|
+
>
|
|
22
|
+
{{ badge }}
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<!-- Tooltip personalizado -->
|
|
26
|
+
<div
|
|
27
|
+
v-if="showCustomTooltip && customTooltipText"
|
|
28
|
+
class="absolute top-1 left-1/2 transform -translate-x-1/2 text-xs font-semibold p-1 rounded-corner transition-all duration-300 pointer-events-none background"
|
|
29
|
+
:class="[
|
|
30
|
+
tooltipClass,
|
|
31
|
+
{
|
|
32
|
+
'opacity-0 -translate-y-2': !showTooltip,
|
|
33
|
+
'opacity-100 translate-y-0': showTooltip
|
|
34
|
+
}
|
|
35
|
+
]"
|
|
36
|
+
>
|
|
37
|
+
{{ customTooltipText }}
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Slot para contenido adicional personalizado -->
|
|
41
|
+
<slot></slot>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<script setup lang="ts">
|
|
46
|
+
import { ref } from 'vue';
|
|
47
|
+
|
|
48
|
+
interface Props {
|
|
49
|
+
icon: string;
|
|
50
|
+
alt?: string;
|
|
51
|
+
tooltip?: string;
|
|
52
|
+
badge?: number | null;
|
|
53
|
+
iconClass?: string | Record<string, boolean>;
|
|
54
|
+
customClass?: string | Record<string, boolean>;
|
|
55
|
+
tooltipClass?: string | Record<string, boolean>;
|
|
56
|
+
showCustomTooltip?: boolean;
|
|
57
|
+
customTooltipText?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
withDefaults(defineProps<Props>(), {
|
|
61
|
+
alt: '',
|
|
62
|
+
tooltip: '',
|
|
63
|
+
badge: null,
|
|
64
|
+
iconClass: () => ({}),
|
|
65
|
+
customClass: '',
|
|
66
|
+
tooltipClass: '',
|
|
67
|
+
showCustomTooltip: false,
|
|
68
|
+
customTooltipText: '',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const emit = defineEmits<{
|
|
72
|
+
click: [];
|
|
73
|
+
}>();
|
|
74
|
+
|
|
75
|
+
const showTooltip = ref(false);
|
|
76
|
+
|
|
77
|
+
const handleClick = () => {
|
|
78
|
+
emit('click');
|
|
79
|
+
};
|
|
80
|
+
</script>
|