ketekny-ui-kit 1.0.78 → 1.0.80
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/directives/v-tooltip.js +69 -27
- package/src/layout/twoColSidebarPanel.vue +5 -5
- package/src/ui/componentDescription.vue +3 -3
- package/src/ui/componentShowcase.vue +2 -2
- package/src/ui/kSearch.vue +15 -1
- package/src/ui/kTags.vue +5 -2
- package/tailwind-preset.js +1 -0
package/package.json
CHANGED
|
@@ -32,18 +32,23 @@ function createNodes() {
|
|
|
32
32
|
|
|
33
33
|
const bubble = document.createElement('div')
|
|
34
34
|
bubble.className = `
|
|
35
|
-
px-3 py-2 rounded-lg text-
|
|
35
|
+
px-3 py-2 rounded-lg text-base font-medium leading-5
|
|
36
36
|
text-slate-50 bg-slate-900 shadow-xl ring-1 ring-black/20
|
|
37
37
|
opacity-0 scale-95 transition duration-150 ease-out
|
|
38
38
|
`.replace(/\s+/g, ' ').trim()
|
|
39
|
+
bubble.style.color = '#f8fafc'
|
|
40
|
+
bubble.style.backgroundColor = '#0f172a'
|
|
41
|
+
bubble.style.borderColor = 'rgba(0, 0, 0, 0.2)'
|
|
39
42
|
bubble.style.whiteSpace = 'normal' // allow wrap when needed
|
|
40
43
|
bubble.style.maxWidth = 'min(36rem, calc(100vw - 1.5rem))' // clamp width
|
|
41
44
|
bubble.style.wordBreak = 'break-word'
|
|
42
45
|
bubble.style.letterSpacing = '0.01em'
|
|
43
46
|
bubble.style.position = 'relative'
|
|
47
|
+
bubble.setAttribute('role', 'tooltip')
|
|
48
|
+
bubble.setAttribute('aria-hidden', 'true')
|
|
44
49
|
|
|
45
50
|
const arrow = document.createElement('div')
|
|
46
|
-
arrow.className = 'absolute w-2 h-2 rotate-45 bg-slate-900'
|
|
51
|
+
arrow.className = 'absolute w-2 h-2 rotate-45 bg-slate-900 opacity-0 transition-opacity duration-150 ease-out'
|
|
47
52
|
arrow.setAttribute('aria-hidden', 'true')
|
|
48
53
|
|
|
49
54
|
wrapper.appendChild(bubble)
|
|
@@ -184,25 +189,35 @@ export default {
|
|
|
184
189
|
(content.mode === 'component' && !content.component)
|
|
185
190
|
) return
|
|
186
191
|
|
|
187
|
-
const { wrapper, bubble, arrow } = createNodes()
|
|
188
|
-
document.body.appendChild(wrapper)
|
|
189
|
-
|
|
190
|
-
// Render content
|
|
191
|
-
let app
|
|
192
|
-
if (content.mode === 'text') bubble.textContent = content.text
|
|
193
|
-
if (content.mode === 'html') bubble.innerHTML = content.html
|
|
194
|
-
if (content.mode === 'component') {
|
|
195
|
-
app = createApp({ render: () => h(content.component, content.props || {}) })
|
|
196
|
-
app.mount(bubble)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
192
|
const state = {
|
|
200
193
|
placement: placementPref,
|
|
201
194
|
pad: 8, // viewport padding
|
|
202
|
-
gap: 8
|
|
195
|
+
gap: 8, // distance from trigger
|
|
196
|
+
visible: false,
|
|
197
|
+
nodes: null,
|
|
198
|
+
app: null,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function ensureNodes() {
|
|
202
|
+
if (state.nodes) return state.nodes
|
|
203
|
+
|
|
204
|
+
const { wrapper, bubble, arrow } = createNodes()
|
|
205
|
+
document.body.appendChild(wrapper)
|
|
206
|
+
|
|
207
|
+
if (content.mode === 'text') bubble.textContent = content.text
|
|
208
|
+
if (content.mode === 'html') bubble.innerHTML = content.html
|
|
209
|
+
if (content.mode === 'component') {
|
|
210
|
+
state.app = createApp({ render: () => h(content.component, content.props || {}) })
|
|
211
|
+
state.app.mount(bubble)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
state.nodes = { wrapper, bubble, arrow }
|
|
215
|
+
return state.nodes
|
|
203
216
|
}
|
|
204
217
|
|
|
205
218
|
function positionAndShow() {
|
|
219
|
+
const nodes = ensureNodes()
|
|
220
|
+
const { wrapper, bubble, arrow } = nodes
|
|
206
221
|
const elRect = el.getBoundingClientRect()
|
|
207
222
|
const bSize = measureBubble(bubble)
|
|
208
223
|
const pos = computePosition({ elRect, bSize, placement: state.placement, gap: state.gap, pad: state.pad })
|
|
@@ -215,18 +230,42 @@ export default {
|
|
|
215
230
|
placeArrow({ arrow, bubbleRect: { width: bSize.width, height: bSize.height }, elRect, pos })
|
|
216
231
|
|
|
217
232
|
// Show
|
|
233
|
+
bubble.setAttribute('aria-hidden', 'false')
|
|
218
234
|
bubble.classList.remove('opacity-0', 'scale-95')
|
|
219
235
|
bubble.classList.add('opacity-100', 'scale-100')
|
|
220
236
|
arrow.style.display = 'block'
|
|
237
|
+
arrow.classList.remove('opacity-100')
|
|
238
|
+
arrow.classList.add('opacity-0')
|
|
239
|
+
window.setTimeout(() => {
|
|
240
|
+
if (!state.visible || !state.nodes) return
|
|
241
|
+
arrow.classList.remove('opacity-0')
|
|
242
|
+
arrow.classList.add('opacity-100')
|
|
243
|
+
}, 150)
|
|
221
244
|
|
|
222
245
|
// Save current placement (may be flipped)
|
|
223
246
|
state.placement = pos.placement
|
|
247
|
+
state.visible = true
|
|
224
248
|
}
|
|
225
249
|
|
|
226
250
|
function hide() {
|
|
251
|
+
if (!state.nodes) return
|
|
252
|
+
|
|
253
|
+
const { bubble, wrapper } = state.nodes
|
|
254
|
+
bubble.setAttribute('aria-hidden', 'true')
|
|
227
255
|
bubble.classList.remove('opacity-100', 'scale-100')
|
|
228
256
|
bubble.classList.add('opacity-0', 'scale-95')
|
|
229
|
-
|
|
257
|
+
state.visible = false
|
|
258
|
+
const { arrow } = state.nodes
|
|
259
|
+
arrow.classList.remove('opacity-100')
|
|
260
|
+
arrow.classList.add('opacity-0')
|
|
261
|
+
|
|
262
|
+
window.setTimeout(() => {
|
|
263
|
+
if (state.visible || !state.nodes) return
|
|
264
|
+
state.app?.unmount?.()
|
|
265
|
+
wrapper.remove()
|
|
266
|
+
state.nodes = null
|
|
267
|
+
state.app = null
|
|
268
|
+
}, 160)
|
|
230
269
|
}
|
|
231
270
|
|
|
232
271
|
// Events
|
|
@@ -244,7 +283,7 @@ export default {
|
|
|
244
283
|
window.addEventListener('scroll', onScrollOrResize, true)
|
|
245
284
|
window.addEventListener('resize', onScrollOrResize)
|
|
246
285
|
|
|
247
|
-
el._tooltip = {
|
|
286
|
+
el._tooltip = { onEnter, onLeave, onScrollOrResize, state }
|
|
248
287
|
},
|
|
249
288
|
|
|
250
289
|
updated(el, binding) {
|
|
@@ -255,27 +294,30 @@ export default {
|
|
|
255
294
|
if (nextPlacement !== undefined) {
|
|
256
295
|
// Update preferred placement; flip may still occur on show
|
|
257
296
|
// We store it on a small state on the element:
|
|
258
|
-
t.
|
|
297
|
+
t.state.placement = nextPlacement
|
|
259
298
|
}
|
|
260
299
|
|
|
261
300
|
const content = normalizeValue(binding)
|
|
262
|
-
if (
|
|
263
|
-
|
|
301
|
+
if (!t.state.nodes) return
|
|
302
|
+
|
|
303
|
+
const { bubble } = t.state.nodes
|
|
304
|
+
if (content.mode === 'text') bubble.textContent = content.text || ''
|
|
305
|
+
else if (content.mode === 'html') bubble.innerHTML = content.html || ''
|
|
264
306
|
else if (content.mode === 'component') {
|
|
265
|
-
|
|
307
|
+
t.state.app?.unmount?.()
|
|
266
308
|
const app = createApp({ render: () => h(content.component, content.props || {}) })
|
|
267
|
-
app.mount(
|
|
268
|
-
t.app = app
|
|
309
|
+
app.mount(bubble)
|
|
310
|
+
t.state.app = app
|
|
269
311
|
}
|
|
270
312
|
},
|
|
271
313
|
|
|
272
314
|
unmounted(el) {
|
|
273
315
|
const t = el._tooltip
|
|
274
316
|
if (!t) return
|
|
275
|
-
if (t.app) t.app.unmount()
|
|
276
|
-
t.
|
|
277
|
-
|
|
278
|
-
|
|
317
|
+
if (t.state.app) t.state.app.unmount()
|
|
318
|
+
if (t.state.nodes) {
|
|
319
|
+
t.state.nodes.wrapper.remove()
|
|
320
|
+
}
|
|
279
321
|
el.removeEventListener('mouseenter', t.onEnter)
|
|
280
322
|
el.removeEventListener('mouseleave', t.onLeave)
|
|
281
323
|
el.removeEventListener('focus', t.onEnter)
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
<span
|
|
75
75
|
v-if="item.description"
|
|
76
76
|
class="block mt-1 text-sm"
|
|
77
|
-
:class="item.active ? 'text-slate-700' : 'text-slate-
|
|
77
|
+
:class="item.active ? 'text-slate-700' : 'text-slate-200'"
|
|
78
78
|
>
|
|
79
79
|
{{ item.description }}
|
|
80
80
|
</span>
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
|
|
86
86
|
<div
|
|
87
87
|
v-if="account"
|
|
88
|
-
class="px-1 pb-4 border-b shrink-0 border-white/10 bg-slate-900/80 text-white/
|
|
88
|
+
class="px-1 pb-4 border-b shrink-0 border-white/10 bg-slate-900/80 text-white/90"
|
|
89
89
|
>
|
|
90
90
|
<div v-if="collapsed" class="flex flex-col gap-3">
|
|
91
91
|
<div v-if="account.loggedIn" class="flex flex-col items-center gap-2">
|
|
@@ -179,10 +179,10 @@
|
|
|
179
179
|
|
|
180
180
|
<div
|
|
181
181
|
v-if="!collapsed"
|
|
182
|
-
class="border-t shrink-0 border-white/10 text-white/
|
|
182
|
+
class="border-t shrink-0 border-white/10 text-white/70"
|
|
183
183
|
>
|
|
184
184
|
<div v-if="showContactInfo" class="flex flex-col gap-2 px-4">
|
|
185
|
-
<p class="text-sm font-semibold uppercase tracking-[0.25em] text-white/
|
|
185
|
+
<p class="text-sm font-semibold uppercase tracking-[0.25em] text-white/80">
|
|
186
186
|
KETEKNY AE
|
|
187
187
|
</p>
|
|
188
188
|
<div class="flex flex-wrap items-center text-sm gap-x-3">
|
|
@@ -240,7 +240,7 @@ const LABELS = {
|
|
|
240
240
|
signOut: "Αποσύνδεση",
|
|
241
241
|
signIn: "Σύνδεση",
|
|
242
242
|
signUp: "Εγγραφή",
|
|
243
|
-
contactAddress: "
|
|
243
|
+
contactAddress: "Βερανζέρου 13, 10677, Αθήνα",
|
|
244
244
|
};
|
|
245
245
|
|
|
246
246
|
export default {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="overflow-hidden border rounded-lg shadow-sm component-card cp-card cp-border">
|
|
3
3
|
<div class="flex items-center justify-between px-4 py-3 border-b cp-border cp-muted-50">
|
|
4
|
-
<
|
|
4
|
+
<h2 class="text-lg font-semibold cp-text">{{ componentName }} Description</h2>
|
|
5
5
|
|
|
6
6
|
<div class="flex items-center gap-1">
|
|
7
7
|
<button
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
</td>
|
|
35
35
|
<td class="px-4 py-3"><span class="font-mono text-sm text-blue-600">{{ prop.type }}</span></td>
|
|
36
36
|
<td class="px-4 py-3">
|
|
37
|
-
<code v-if="prop.default !== undefined" class="font-mono text-sm text-
|
|
37
|
+
<code v-if="prop.default !== undefined" class="font-mono text-sm text-slate-700">{{ prop.default }}</code>
|
|
38
38
|
<span v-else class="cp-text-muted">-</span>
|
|
39
39
|
</td>
|
|
40
40
|
<td class="px-4 py-3 cp-text-muted">{{ prop.description }}</td>
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
<code class="text-sm font-mono px-1.5 py-0.5 rounded cp-chip cp-chip-text">{{ slot.name }}</code>
|
|
66
66
|
</td>
|
|
67
67
|
<td class="px-4 py-3">
|
|
68
|
-
<span :class="['inline-flex items-center px-2 py-0.5 rounded-full text-
|
|
68
|
+
<span :class="['inline-flex items-center px-2 py-0.5 rounded-full text-sm font-medium', slot.scoped ? 'bg-emerald-100 text-emerald-700' : 'cp-badge-muted']">
|
|
69
69
|
{{ slot.scoped ? "Yes" : "No" }}
|
|
70
70
|
</span>
|
|
71
71
|
</td>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="overflow-hidden border rounded-lg shadow-sm component-card cp-card cp-border">
|
|
3
3
|
<div class="flex items-center justify-between px-4 py-3 border-b cp-border cp-muted-50">
|
|
4
|
-
<
|
|
4
|
+
<h2 class="text-lg font-semibold cp-text">{{ componentName }} Preview</h2>
|
|
5
5
|
|
|
6
6
|
<div class="flex items-center gap-1">
|
|
7
7
|
<button
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
v-for="vp in previewViewports"
|
|
23
23
|
:key="vp.id"
|
|
24
24
|
@click="activeViewport = vp.id"
|
|
25
|
-
:class="['px-3 py-1.5 text-
|
|
25
|
+
:class="['px-3 py-1.5 text-sm font-semibold rounded-md transition-colors', activeViewport === vp.id ? 'cp-tab-active' : 'cp-tab']">
|
|
26
26
|
{{ vp.label }}
|
|
27
27
|
</button>
|
|
28
28
|
</div>
|
package/src/ui/kSearch.vue
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="relative w-full">
|
|
3
|
-
<
|
|
3
|
+
<label v-if="label" :for="inputId" class="mb-1 block text-sm font-medium text-gray-700 dark:text-slate-300">
|
|
4
|
+
{{ label }}
|
|
5
|
+
</label>
|
|
6
|
+
<input
|
|
7
|
+
:id="inputId"
|
|
8
|
+
type="text"
|
|
9
|
+
v-model="localValue"
|
|
10
|
+
@input="onInput"
|
|
11
|
+
:class="[defaultStyle, disabled ? disabledStyle : '']"
|
|
12
|
+
:placeholder="placeholder"
|
|
13
|
+
:aria-label="!label ? placeholder : null"
|
|
14
|
+
:disabled="disabled"
|
|
15
|
+
/>
|
|
4
16
|
<Search class="absolute w-4 h-4 text-primary/70 -translate-y-1/2 pointer-events-none right-3 top-1/2" />
|
|
5
17
|
</div>
|
|
6
18
|
</template>
|
|
@@ -16,10 +28,12 @@ export default {
|
|
|
16
28
|
modelValue: { type: String, default: "" },
|
|
17
29
|
debounce: { type: [String, Number], default: 300 },
|
|
18
30
|
placeholder: { type: String, default: "Αναζήτηση..." },
|
|
31
|
+
label: { type: String, default: "" },
|
|
19
32
|
disabled: { type: Boolean, default: false },
|
|
20
33
|
},
|
|
21
34
|
data() {
|
|
22
35
|
return {
|
|
36
|
+
inputId: `ksearch-${Math.random().toString(36).slice(2, 10)}`,
|
|
23
37
|
defaultStyle: "w-full px-3 py-2 border rounded-lg transition shadow-sm focus:outline-none text-gray-700 focus:ring-2 focus:ring-primary/20 focus:border-primary bg-white placeholder-gray-400 dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500",
|
|
24
38
|
disabledStyle: "bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-slate-900 dark:text-slate-500",
|
|
25
39
|
|
package/src/ui/kTags.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="w-full">
|
|
3
|
-
<
|
|
3
|
+
<label v-if="label != null" :for="inputId" class="inputLabel" :class="hasError ? 'text-rose-800 dark:text-rose-400' : 'text-primary/90'">
|
|
4
4
|
{{ label }}
|
|
5
|
-
</
|
|
5
|
+
</label>
|
|
6
6
|
<div
|
|
7
7
|
:class="[
|
|
8
8
|
'flex flex-wrap items-center gap-2 p-2 border rounded-xl shadow-sm min-h-[3rem] transition',
|
|
@@ -19,11 +19,13 @@
|
|
|
19
19
|
</button>
|
|
20
20
|
</span>
|
|
21
21
|
<input
|
|
22
|
+
:id="inputId"
|
|
22
23
|
v-model="newTag"
|
|
23
24
|
@keydown.enter.prevent="addTag"
|
|
24
25
|
@keydown.backspace="handleBackspace"
|
|
25
26
|
@blur="addTag"
|
|
26
27
|
:placeholder="placeholder"
|
|
28
|
+
:aria-label="!label ? placeholder || 'Add tag' : null"
|
|
27
29
|
:disabled="disabled"
|
|
28
30
|
:class="['flex-1 p-1 bg-transparent border-none focus:outline-none dark:text-slate-100 dark:placeholder-slate-500', disabled ? 'text-gray-400 cursor-not-allowed dark:text-slate-500' : '']"
|
|
29
31
|
/>
|
|
@@ -56,6 +58,7 @@ export default {
|
|
|
56
58
|
data() {
|
|
57
59
|
return {
|
|
58
60
|
newTag: "",
|
|
61
|
+
inputId: `ktags-${Math.random().toString(36).slice(2, 10)}`,
|
|
59
62
|
};
|
|
60
63
|
},
|
|
61
64
|
computed: {
|