ketekny-ui-kit 1.0.134 → 1.0.136
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 +1 -1
- package/src/ui/kCard.vue +92 -16
- package/src/ui/kTabs.vue +34 -7
- package/src/ui/themes/kButton.theme.js +4 -4
package/package.json
CHANGED
package/src/ui/kCard.vue
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<article
|
|
2
|
+
<article
|
|
3
|
+
class="self-start overflow-hidden rounded-xl border shadow-sm"
|
|
4
|
+
:class="[cardClasses, { dark: isDark }]"
|
|
5
|
+
:data-theme="resolvedTheme"
|
|
6
|
+
>
|
|
3
7
|
<header
|
|
4
8
|
v-if="hasHeader"
|
|
5
|
-
class="flex justify-between gap-3
|
|
6
|
-
:class="
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
class="flex justify-between gap-3 px-5 py-4"
|
|
10
|
+
:class="[
|
|
11
|
+
headerClasses,
|
|
12
|
+
{
|
|
13
|
+
'border-b': !isCollapsed,
|
|
14
|
+
'group cursor-pointer select-none transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand/30': collapsible,
|
|
15
|
+
'items-start': subtitle,
|
|
16
|
+
'items-center': !subtitle,
|
|
17
|
+
},
|
|
18
|
+
]"
|
|
12
19
|
:role="collapsible ? 'button' : undefined"
|
|
13
20
|
:tabindex="collapsible ? 0 : undefined"
|
|
14
21
|
:aria-label="collapsible ? (isCollapsed ? 'Expand card body' : 'Collapse card body') : undefined"
|
|
@@ -22,15 +29,16 @@
|
|
|
22
29
|
<div class="flex items-center gap-3">
|
|
23
30
|
<span
|
|
24
31
|
v-if="icon"
|
|
25
|
-
class="inline-flex size-8 shrink-0 items-center justify-center rounded-lg
|
|
32
|
+
class="inline-flex size-8 shrink-0 items-center justify-center rounded-lg"
|
|
33
|
+
:class="iconClasses"
|
|
26
34
|
>
|
|
27
|
-
<kIcon :name="icon" :size="18" />
|
|
35
|
+
<kIcon :name="icon" :size="18" :color="iconColor" />
|
|
28
36
|
</span>
|
|
29
37
|
<div class="min-w-0">
|
|
30
|
-
<p v-if="title" class="text-sm font-semibold
|
|
38
|
+
<p v-if="title" class="text-sm font-semibold" :class="titleClasses">
|
|
31
39
|
{{ title }}
|
|
32
40
|
</p>
|
|
33
|
-
<p v-if="subtitle" :class="
|
|
41
|
+
<p v-if="subtitle" class="text-sm" :class="[subtitleClasses, { 'mt-1': title }]">
|
|
34
42
|
{{ subtitle }}
|
|
35
43
|
</p>
|
|
36
44
|
</div>
|
|
@@ -40,7 +48,8 @@
|
|
|
40
48
|
|
|
41
49
|
<span
|
|
42
50
|
v-if="collapsible"
|
|
43
|
-
class="-mr-1 inline-flex size-8 shrink-0 items-center justify-center
|
|
51
|
+
class="-mr-1 inline-flex size-8 shrink-0 items-center justify-center transition-colors"
|
|
52
|
+
:class="collapseIconClasses"
|
|
44
53
|
>
|
|
45
54
|
<ChevronDown
|
|
46
55
|
class="size-5 transition-transform duration-200"
|
|
@@ -52,15 +61,15 @@
|
|
|
52
61
|
|
|
53
62
|
<div
|
|
54
63
|
v-show="!isCollapsed"
|
|
55
|
-
class="
|
|
56
|
-
:class="dense ? 'p-0' : 'px-5 py-4'"
|
|
64
|
+
:class="[bodyClasses, dense ? 'p-0' : 'px-5 py-4']"
|
|
57
65
|
>
|
|
58
66
|
<slot />
|
|
59
67
|
</div>
|
|
60
68
|
|
|
61
69
|
<footer
|
|
62
70
|
v-if="$slots.footer"
|
|
63
|
-
class="border-t
|
|
71
|
+
class="border-t px-5 py-4"
|
|
72
|
+
:class="footerClasses"
|
|
64
73
|
>
|
|
65
74
|
<slot name="footer" />
|
|
66
75
|
</footer>
|
|
@@ -100,10 +109,16 @@ export default {
|
|
|
100
109
|
type: Boolean,
|
|
101
110
|
default: false,
|
|
102
111
|
},
|
|
112
|
+
theme: {
|
|
113
|
+
type: String,
|
|
114
|
+
default: 'auto',
|
|
115
|
+
validator: (value) => ['light', 'dark', 'auto', 'invert'].includes(value),
|
|
116
|
+
},
|
|
103
117
|
},
|
|
104
118
|
data() {
|
|
105
119
|
return {
|
|
106
120
|
internalCollapsed: this.collapsed,
|
|
121
|
+
appTheme: 'light',
|
|
107
122
|
}
|
|
108
123
|
},
|
|
109
124
|
computed: {
|
|
@@ -113,13 +128,74 @@ export default {
|
|
|
113
128
|
isCollapsed() {
|
|
114
129
|
return this.collapsible && this.internalCollapsed
|
|
115
130
|
},
|
|
131
|
+
resolvedTheme() {
|
|
132
|
+
if (this.theme === 'auto') return this.appTheme
|
|
133
|
+
if (this.theme === 'invert') return this.appTheme === 'dark' ? 'light' : 'dark'
|
|
134
|
+
return this.theme
|
|
135
|
+
},
|
|
136
|
+
isDark() {
|
|
137
|
+
return this.resolvedTheme === 'dark'
|
|
138
|
+
},
|
|
139
|
+
cardClasses() {
|
|
140
|
+
return this.isDark
|
|
141
|
+
? 'border-slate-700 bg-slate-900 text-slate-100'
|
|
142
|
+
: 'border-slate-200 bg-white text-slate-900'
|
|
143
|
+
},
|
|
144
|
+
headerClasses() {
|
|
145
|
+
if (this.isDark) {
|
|
146
|
+
return `border-slate-700 bg-slate-800/70${this.collapsible ? ' hover:bg-slate-800' : ''}`
|
|
147
|
+
}
|
|
148
|
+
return `border-slate-200 bg-slate-50/70${this.collapsible ? ' hover:bg-slate-100' : ''}`
|
|
149
|
+
},
|
|
150
|
+
titleClasses() {
|
|
151
|
+
return this.isDark ? 'text-slate-100' : 'text-slate-900'
|
|
152
|
+
},
|
|
153
|
+
subtitleClasses() {
|
|
154
|
+
return this.isDark ? 'text-slate-400' : 'text-slate-500'
|
|
155
|
+
},
|
|
156
|
+
iconClasses() {
|
|
157
|
+
return this.isDark
|
|
158
|
+
? 'bg-white/10 text-slate-100'
|
|
159
|
+
: 'bg-primary/10 text-primary'
|
|
160
|
+
},
|
|
161
|
+
iconColor() {
|
|
162
|
+
return this.isDark ? '#f1f5f9' : 'currentColor'
|
|
163
|
+
},
|
|
164
|
+
collapseIconClasses() {
|
|
165
|
+
return this.isDark
|
|
166
|
+
? 'text-slate-400 group-hover:text-slate-200'
|
|
167
|
+
: 'text-slate-500 group-hover:text-slate-700'
|
|
168
|
+
},
|
|
169
|
+
bodyClasses() {
|
|
170
|
+
return this.isDark ? 'bg-slate-900' : 'bg-white'
|
|
171
|
+
},
|
|
172
|
+
footerClasses() {
|
|
173
|
+
return this.isDark
|
|
174
|
+
? 'border-slate-700 bg-slate-800'
|
|
175
|
+
: 'border-slate-200 bg-slate-50'
|
|
176
|
+
},
|
|
116
177
|
},
|
|
117
178
|
watch: {
|
|
118
179
|
collapsed(value) {
|
|
119
180
|
this.internalCollapsed = value
|
|
120
181
|
},
|
|
121
182
|
},
|
|
183
|
+
mounted() {
|
|
184
|
+
this.syncAppTheme()
|
|
185
|
+
this.themeObserver = new MutationObserver(this.syncAppTheme)
|
|
186
|
+
this.themeObserver.observe(document.documentElement, {
|
|
187
|
+
attributes: true,
|
|
188
|
+
attributeFilter: ['class', 'data-theme'],
|
|
189
|
+
})
|
|
190
|
+
},
|
|
191
|
+
beforeUnmount() {
|
|
192
|
+
this.themeObserver?.disconnect()
|
|
193
|
+
},
|
|
122
194
|
methods: {
|
|
195
|
+
syncAppTheme() {
|
|
196
|
+
const root = document.documentElement
|
|
197
|
+
this.appTheme = root.dataset.theme === 'dark' || root.classList.contains('dark') ? 'dark' : 'light'
|
|
198
|
+
},
|
|
123
199
|
toggleCollapsed() {
|
|
124
200
|
this.internalCollapsed = !this.internalCollapsed
|
|
125
201
|
this.$emit('update:collapsed', this.internalCollapsed)
|
package/src/ui/kTabs.vue
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="w-full
|
|
3
|
-
<div
|
|
2
|
+
<div class="w-full">
|
|
3
|
+
<div
|
|
4
|
+
class="k-tabs-scroll flex overflow-x-auto overflow-y-hidden border-b border-slate-200 dark:border-slate-700"
|
|
5
|
+
role="tablist"
|
|
6
|
+
>
|
|
4
7
|
<button
|
|
5
8
|
v-for="tab in tabs"
|
|
6
9
|
:key="tab.id"
|
|
10
|
+
type="button"
|
|
11
|
+
role="tab"
|
|
12
|
+
:aria-selected="modelValue === tab.id"
|
|
13
|
+
:tabindex="modelValue === tab.id ? 0 : -1"
|
|
7
14
|
@click="selectTab(tab.id)"
|
|
8
15
|
:class="[
|
|
9
|
-
'shrink-0 whitespace-nowrap px-4
|
|
16
|
+
'-mb-px inline-flex h-11 shrink-0 items-center gap-2 whitespace-nowrap border-b-2 px-4 text-base font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary/30',
|
|
10
17
|
modelValue === tab.id
|
|
11
|
-
? '
|
|
12
|
-
: 'text-
|
|
18
|
+
? 'border-primary text-primary'
|
|
19
|
+
: 'border-transparent text-slate-600 hover:border-slate-300 hover:text-slate-900 dark:text-slate-400 dark:hover:border-slate-600 dark:hover:text-slate-100',
|
|
13
20
|
]"
|
|
14
21
|
>
|
|
22
|
+
<component
|
|
23
|
+
:is="iconComponent(tab.icon)"
|
|
24
|
+
v-if="tab.icon && iconComponent(tab.icon)"
|
|
25
|
+
class="size-4 shrink-0"
|
|
26
|
+
aria-hidden="true"
|
|
27
|
+
/>
|
|
15
28
|
{{ tab.title }}
|
|
16
29
|
</button>
|
|
17
30
|
</div>
|
|
@@ -19,8 +32,10 @@
|
|
|
19
32
|
</template>
|
|
20
33
|
|
|
21
34
|
<script>
|
|
35
|
+
import * as icons from "@lucide/vue";
|
|
36
|
+
|
|
22
37
|
export default {
|
|
23
|
-
name: "
|
|
38
|
+
name: "kTabs",
|
|
24
39
|
props: {
|
|
25
40
|
modelValue: { type: [String, Number], required: true },
|
|
26
41
|
tabs: {
|
|
@@ -33,6 +48,14 @@ export default {
|
|
|
33
48
|
},
|
|
34
49
|
emits: ["update:modelValue"],
|
|
35
50
|
methods: {
|
|
51
|
+
iconComponent(name) {
|
|
52
|
+
if (!name) return null;
|
|
53
|
+
const normalizedName = name
|
|
54
|
+
.split("-")
|
|
55
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
56
|
+
.join("");
|
|
57
|
+
return icons[normalizedName] || null;
|
|
58
|
+
},
|
|
36
59
|
selectTab(id) {
|
|
37
60
|
this.$emit("update:modelValue", id);
|
|
38
61
|
},
|
|
@@ -43,6 +66,10 @@ export default {
|
|
|
43
66
|
<style scoped>
|
|
44
67
|
.k-tabs-scroll {
|
|
45
68
|
-webkit-overflow-scrolling: touch;
|
|
46
|
-
scrollbar-width:
|
|
69
|
+
scrollbar-width: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.k-tabs-scroll::-webkit-scrollbar {
|
|
73
|
+
display: none;
|
|
47
74
|
}
|
|
48
75
|
</style>
|
|
@@ -5,8 +5,8 @@ export const K_BUTTON_BASE = "inline-flex flex-row items-center justify-center f
|
|
|
5
5
|
|
|
6
6
|
export const K_BUTTON_SIZE_STYLES = {
|
|
7
7
|
normal: {
|
|
8
|
-
regular: "h-
|
|
9
|
-
iconOnly: "h-
|
|
8
|
+
regular: "h-10 px-4 text-sm",
|
|
9
|
+
iconOnly: "h-10 w-10 p-0",
|
|
10
10
|
icon: "w-4 h-4",
|
|
11
11
|
loader: "w-4 h-4",
|
|
12
12
|
gap: "mr-1.5",
|
|
@@ -27,8 +27,8 @@ export const K_BUTTON_SIZE_STYLES = {
|
|
|
27
27
|
},
|
|
28
28
|
// Backward compatibility aliases
|
|
29
29
|
default: {
|
|
30
|
-
regular: "h-
|
|
31
|
-
iconOnly: "h-
|
|
30
|
+
regular: "h-10 px-4 text-sm",
|
|
31
|
+
iconOnly: "h-10 w-10 p-0",
|
|
32
32
|
icon: "w-4 h-4",
|
|
33
33
|
loader: "w-4 h-4",
|
|
34
34
|
gap: "mr-1.5",
|