ketekny-ui-kit 1.0.67 → 1.0.69

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ketekny-ui-kit",
3
3
  "type": "module",
4
- "version": "1.0.67",
4
+ "version": "1.0.69",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -16,19 +16,21 @@
16
16
  </button>
17
17
 
18
18
  <aside
19
- class="flex h-screen w-full flex-col overflow-y-auto overflow-x-hidden bg-[radial-gradient(circle_at_0_0,#ffffff0f,#0000_34%),linear-gradient(#101a2d_0%,#0a1120_52%,#070d19_100%)] text-white shadow-2xl transition-all duration-300 ease-out lg:w-[360px] lg:shrink-0 lg:border-r lg:border-white/10 lg:will-change-transform"
19
+ class="flex h-screen w-full flex-col overflow-y-auto overflow-x-hidden bg-[radial-gradient(circle_at_0_0,#ffffff0f,#0000_34%),linear-gradient(#101a2d_0%,#0a1120_52%,#070d19_100%)] text-white shadow-2xl transition-all duration-300 ease-out lg:w-[360px] lg:shrink-0 lg:will-change-transform"
20
20
  :class="
21
21
  isSidebarCollapsed
22
22
  ? 'lg:relative lg:z-30 lg:w-[76px]'
23
23
  : 'lg:relative lg:z-30 lg:translate-x-0'
24
24
  "
25
25
  >
26
- <div class="relative border-b border-white/10">
26
+ <div class="relative ">
27
27
  <button
28
28
  v-if="logoSrc"
29
29
  type="button"
30
30
  class="block w-full text-left"
31
- v-tooltip.right.html="isSidebarCollapsed ? collapsedContactTooltip : logoAlt"
31
+ v-tooltip.right.html="
32
+ isSidebarCollapsed ? collapsedContactTooltip : logoAlt
33
+ "
32
34
  @click="$emit('logo-click')"
33
35
  >
34
36
  <img
@@ -37,25 +39,12 @@
37
39
  class="cursor-pointer px-4 py-4 transition-all duration-300"
38
40
  />
39
41
  </button>
40
- <div v-if="title && !isSidebarCollapsed" class="py-3 mt-3 text-lg text-center bg-[#da0000]">
42
+ <div
43
+ v-if="title && !isSidebarCollapsed"
44
+ class="py-3 text-xl font-bold text-center"
45
+ >
41
46
  {{ title }}
42
47
  </div>
43
- <div v-if="showSponsors && !isSidebarCollapsed" class="flex flex-col gap-3 px-4 py-4 bg-[#0f4e8d]">
44
- <div>
45
- <img
46
- src="https://s3.ketekny.gr/public/web-apps/sponsors/Greece 2.0_NextGeneration_White_gr.png"
47
- alt="Tameio Anakampsis"
48
- class="mx-auto max-h-16 w-auto object-contain"
49
- />
50
- </div>
51
- <div>
52
- <img
53
- src="https://s3.ketekny.gr/public/web-apps/sponsors/yp_ygeias_dark.png"
54
- alt="Ypourgeio Ygeias"
55
- class="mx-auto max-h-16 w-auto object-contain"
56
- />
57
- </div>
58
- </div>
59
48
 
60
49
  <slot name="sidebar-header" />
61
50
 
@@ -68,17 +57,18 @@
68
57
  @click.stop="toggleSidebar"
69
58
  >
70
59
  <slot name="toggle-icon">
71
- <kIcon
72
- :name="collapseIcon"
73
- size="16"
74
- class="text-white"
75
- />
60
+ <kIcon :name="collapseIcon" size="16" class="text-white" />
76
61
  </slot>
77
62
  </button>
78
63
  </div>
79
64
 
65
+
66
+
80
67
  <div class="flex-1 pt-4">
81
- <div v-if="isSidebarCollapsed" class="mx-1 mt-3 flex flex-col items-center gap-2">
68
+ <div
69
+ v-if="isSidebarCollapsed"
70
+ class="mx-1 mt-3 flex flex-col items-center gap-2"
71
+ >
82
72
  <SidebarIconLink
83
73
  v-for="item in normalizedMainMenu"
84
74
  :key="item.key"
@@ -125,44 +115,74 @@
125
115
  </div>
126
116
  </div>
127
117
 
128
- <div v-if="account " class="shrink-0 border-t border-white/10 bg-slate-900/80 px-1 py-4 text-white/80">
118
+ <div
119
+ v-if="account"
120
+ class="shrink-0 border-b border-white/10 bg-slate-900/80 px-1 pb-4 text-white/80"
121
+ >
129
122
  <div v-if="isSidebarCollapsed" class="flex flex-col gap-3">
130
123
  <div v-if="account.loggedIn" class="flex flex-col items-center gap-2">
131
- <SidebarIconLink icon="UserRoundCog" :on-click="() => $emit('edit-profile')" :title="'Επεξεργασία προφίλ'" v-tooltip.right="'Επεξεργασία προφίλ'" />
132
- <SidebarIconLink icon="LogOut" warning :on-click="() => $emit('signout')" :title="'Αποσύνδεση'" v-tooltip.right="'Αποσύνδεση'" />
124
+ <SidebarIconLink
125
+ icon="UserRoundCog"
126
+ :on-click="() => $emit('edit-profile')"
127
+ :title="'Επεξεργασία προφίλ'"
128
+ v-tooltip.right="'Επεξεργασία προφίλ'"
129
+ />
130
+ <SidebarIconLink
131
+ icon="LogOut"
132
+ warning
133
+ :on-click="() => $emit('signout')"
134
+ :title="'Αποσύνδεση'"
135
+ v-tooltip.right="'Αποσύνδεση'"
136
+ />
133
137
  </div>
134
138
  <div v-else class="flex flex-col items-center gap-2">
135
- <SidebarIconLink icon="LogIn" :on-click="() => $emit('signin')" :title="'Sign in'" v-tooltip.right="'Sign in'" />
136
- <SidebarIconLink icon="UserPlus" :on-click="() => $emit('signup')" :title="'Sign up'" v-tooltip.right="'Sign up'" />
139
+ <SidebarIconLink
140
+ icon="LogIn"
141
+ :on-click="() => $emit('signin')"
142
+ :title="'Sign in'"
143
+ v-tooltip.right="'Sign in'"
144
+ />
145
+ <SidebarIconLink
146
+ icon="UserPlus"
147
+ :on-click="() => $emit('signup')"
148
+ :title="'Sign up'"
149
+ v-tooltip.right="'Sign up'"
150
+ />
137
151
  </div>
138
152
  </div>
139
153
  <div v-else class="flex flex-col gap-3 px-3">
140
154
  <div class="min-w-0">
141
- <p v-if="account.loggedIn" class="mt-2 truncate text-lg font-semibold text-white">
155
+ <p
156
+ v-if="account.loggedIn"
157
+ class="mt-2 truncate text-lg font-semibold text-white"
158
+ >
142
159
  {{ accountDisplayName }}
143
160
  </p>
144
- <p v-if="account.loggedIn && displayEmail" class="truncate text-sm text-slate-300">
161
+ <p
162
+ v-if="account.loggedIn && displayEmail"
163
+ class="truncate text-sm text-slate-300"
164
+ >
145
165
  {{ displayEmail }}
146
166
  </p>
147
167
  </div>
148
- <div class="flex flex-wrap gap-2">
168
+ <div class="flex flex-wrap justify-between gap-2">
149
169
  <kButton
150
170
  v-if="!account.loggedIn"
151
171
  secondary
152
172
  icon="LogIn"
153
- label="Sign in"
173
+ label="Σύνδεση"
154
174
  @click="$emit('signin')"
155
175
  />
156
176
  <kButton
157
177
  v-if="!account.loggedIn"
158
178
  secondary
159
179
  icon="UserPlus"
160
- label="Sign up"
180
+ label="Εγγραφή"
161
181
  @click="$emit('signup')"
162
182
  />
163
183
  <kButton
164
184
  v-if="account.loggedIn"
165
- secondary
185
+ variant="soft"
166
186
  icon="UserRoundCog"
167
187
  label="Επεξεργασία προφίλ"
168
188
  @click="$emit('edit-profile')"
@@ -177,21 +197,32 @@
177
197
  </div>
178
198
  </div>
179
199
  </div>
200
+
180
201
 
181
202
  <div
182
- v-if="showContactInfo && !isSidebarCollapsed"
183
- class="shrink-0 border-t border-white/10 p-4 text-white/50"
203
+ v-if="!isSidebarCollapsed"
204
+ class="shrink-0 border-t border-white/10 text-white/50"
184
205
  >
185
- <div class="flex flex-col gap-2">
186
- <p v-if="!isSidebarCollapsed" class="text-sm font-semibold uppercase tracking-[0.25em] text-white/60">
206
+ <div class="flex flex-col gap-2 px-4" v-if="showContactInfo">
207
+ <p
208
+ class="text-sm font-semibold uppercase tracking-[0.25em] text-white/60"
209
+ >
187
210
  KETEKNY AE
188
211
  </p>
189
- <div class="flex flex-wrap items-center gap-x-3 text-sm leading-relaxed" :class="isSidebarCollapsed ? 'justify-center' : ''">
190
- <span v-if="isSidebarCollapsed" class="sr-only">KETEKNY AE contact info</span>
212
+ <div
213
+ class="flex flex-wrap items-center gap-x-3 text-sm"
214
+ :class="isSidebarCollapsed ? 'justify-center' : ''"
215
+ >
216
+ <span v-if="isSidebarCollapsed" class="sr-only"
217
+ >KETEKNY AE contact info</span
218
+ >
191
219
  <span class="whitespace-nowrap">Βερανζέρου 13, 10677, Αθήνα</span>
192
220
  <span class="whitespace-nowrap">210 3648 337</span>
193
221
  <span class="whitespace-nowrap">
194
- <a :href="`mailto:${contactEmail}`" class="whitespace-nowrap hover:underline">
222
+ <a
223
+ :href="`mailto:${contactEmail}`"
224
+ class="whitespace-nowrap hover:underline"
225
+ >
195
226
  {{ contactEmail }}
196
227
  </a>
197
228
  </span>
@@ -205,11 +236,32 @@
205
236
  </a>
206
237
  </div>
207
238
  </div>
208
- </div>
209
239
 
240
+ <div
241
+ v-if="showSponsors && !isSidebarCollapsed"
242
+ class="flex flex-col gap-3 px-4 py-1 bg-[#0d4b87] mt-2"
243
+ >
244
+ <div>
245
+ <img
246
+ src="https://s3.ketekny.gr/public/web-apps/sponsors/Greece 2.0_NextGeneration_White_gr.png"
247
+ alt="Tameio Anakampsis"
248
+ class="mx-auto max-h-14 w-auto object-contain"
249
+ />
250
+ </div>
251
+ <div>
252
+ <img
253
+ src="https://s3.ketekny.gr/public/web-apps/sponsors/yp_ygeias_dark.png"
254
+ alt="Ypourgeio Ygeias"
255
+ class="mx-auto max-h-14 w-auto object-contain"
256
+ />
257
+ </div>
258
+ </div>
259
+ </div>
210
260
  </aside>
211
261
 
212
- <main class="min-h-screen min-w-0 flex-1 overflow-y-auto overflow-x-hidden lg:h-full lg:min-h-0">
262
+ <main
263
+ class="min-h-screen min-w-0 flex-1 overflow-y-auto overflow-x-hidden lg:h-full lg:min-h-0"
264
+ >
213
265
  <div class="w-full">
214
266
  <slot />
215
267
  </div>
@@ -218,10 +270,10 @@
218
270
  </template>
219
271
 
220
272
  <script>
221
- import { h } from 'vue'
222
- import kButton from '../ui/kButton.vue'
223
- import kIcon from '../ui/kIcon.vue'
224
- import { RouterLink } from 'vue-router'
273
+ import { h } from "vue";
274
+ import kButton from "../ui/kButton.vue";
275
+ import kIcon from "../ui/kIcon.vue";
276
+ import { RouterLink } from "vue-router";
225
277
 
226
278
  const SidebarIconLink = {
227
279
  props: {
@@ -229,22 +281,22 @@ const SidebarIconLink = {
229
281
  active: { type: Boolean, default: false },
230
282
  to: { type: [String, Object], default: null },
231
283
  href: { type: String, default: null },
232
- title: { type: String, default: '' },
284
+ title: { type: String, default: "" },
233
285
  isExternal: { type: Boolean, default: false },
234
- target: { type: String, default: '_blank' },
235
- rel: { type: String, default: 'noreferrer' },
286
+ target: { type: String, default: "_blank" },
287
+ rel: { type: String, default: "noreferrer" },
236
288
  onClick: { type: Function, default: null },
237
289
  warning: { type: Boolean, default: false },
238
- tooltip: { type: String, default: '' },
290
+ tooltip: { type: String, default: "" },
239
291
  },
240
292
  methods: {
241
293
  handleClick(event) {
242
- if (this.onClick) this.onClick(event)
294
+ if (this.onClick) this.onClick(event);
243
295
  },
244
296
  },
245
297
  render() {
246
- const isLink = Boolean(this.to)
247
- const tag = isLink ? RouterLink : this.isExternal ? 'a' : 'button'
298
+ const isLink = Boolean(this.to);
299
+ const tag = isLink ? RouterLink : this.isExternal ? "a" : "button";
248
300
 
249
301
  return h(
250
302
  tag,
@@ -253,24 +305,24 @@ const SidebarIconLink = {
253
305
  href: this.href,
254
306
  target: this.isExternal ? this.target : null,
255
307
  rel: this.isExternal ? this.rel : null,
256
- type: isLink || this.isExternal ? null : 'button',
308
+ type: isLink || this.isExternal ? null : "button",
257
309
  title: this.title,
258
310
  class: [
259
- 'group relative flex h-14 w-14 items-center justify-center rounded-xl border border-white/15 transition',
311
+ "group relative flex h-14 w-14 items-center justify-center rounded-xl border border-white/15 transition",
260
312
  this.active
261
- ? 'bg-slate-950 text-white'
313
+ ? "bg-slate-950 text-white"
262
314
  : this.warning
263
- ? 'bg-[#d97706] text-white hover:bg-[#b45309]'
264
- : 'bg-white/5 text-white hover:border-cyan-400/40 hover:bg-white/10',
315
+ ? "bg-[#d97706] text-white hover:bg-[#b45309]"
316
+ : "bg-white/5 text-white hover:border-cyan-400/40 hover:bg-white/10",
265
317
  ],
266
318
  onClick: this.handleClick,
267
319
  },
268
- [h(kIcon, { name: this.icon, size: '20', color: '#ffffff' })]
269
- )
320
+ [h(kIcon, { name: this.icon, size: "20", color: "#ffffff" })]
321
+ );
270
322
  },
271
- }
323
+ };
272
324
 
273
- const DEFAULT_STORAGE_KEY = 'two-col-layout-sidebar-collapsed'
325
+ const DEFAULT_STORAGE_KEY = "two-col-layout-sidebar-collapsed";
274
326
 
275
327
  export default {
276
328
  components: {
@@ -279,97 +331,110 @@ export default {
279
331
  RouterLink,
280
332
  SidebarIconLink,
281
333
  },
282
- emits: ['logo-click', 'signin', 'signup', 'signout', 'edit-profile'],
334
+ emits: ["logo-click", "signin", "signup", "signout", "edit-profile"],
283
335
  props: {
284
- title: { type: String, default: '' },
336
+ title: { type: String, default: "" },
285
337
  collapsedLogoSrc: {
286
338
  type: String,
287
- default: 'https://s3.ketekny.gr/public/web-apps/logos/logo-icon-dark.png',
339
+ default: "https://s3.ketekny.gr/public/web-apps/logos/logo-icon-dark.png",
288
340
  },
289
- logoSrc: { type: String, default: '' },
290
- logoAlt: { type: String, default: 'Logo' },
291
- collapseLabel: { type: String, default: 'Collapse sidebar' },
292
- expandLabel: { type: String, default: 'Expand sidebar' },
293
- collapseIcon: { type: String, default: 'ArrowLeftToLine' },
294
- expandIcon: { type: String, default: 'ArrowRightToLine' },
341
+ logoSrc: { type: String, default: "" },
342
+ logoAlt: { type: String, default: "Logo" },
343
+ collapseLabel: { type: String, default: "Collapse sidebar" },
344
+ expandLabel: { type: String, default: "Expand sidebar" },
345
+ collapseIcon: { type: String, default: "ArrowLeftToLine" },
346
+ expandIcon: { type: String, default: "ArrowRightToLine" },
295
347
  showSponsors: { type: Boolean, default: false },
296
348
  storageKey: { type: String, default: DEFAULT_STORAGE_KEY },
297
349
  defaultCollapsed: { type: Boolean, default: false },
298
350
  mainMenu: { type: Array, default: () => [] },
299
351
  showContactInfo: { type: Boolean, default: false },
300
352
  account: { type: Object, default: null },
301
- contactEmail: { type: String, default: 'grammateia@ketekny.gr' },
353
+ contactEmail: { type: String, default: "grammateia@ketekny.gr" },
302
354
  },
303
355
  data() {
304
356
  return {
305
357
  isSidebarCollapsed: false,
306
358
  mdCollapseQuery: null,
307
- }
359
+ };
308
360
  },
309
361
  created() {
310
- this.restoreSidebarState()
362
+ this.restoreSidebarState();
311
363
  },
312
364
  mounted() {
313
- this.mdCollapseQuery = window.matchMedia('(min-width: 768px) and (max-width: 1023px)')
314
- this.syncSidebarForViewport()
315
- this.mdCollapseQuery.addEventListener('change', this.syncSidebarForViewport)
365
+ this.mdCollapseQuery = window.matchMedia(
366
+ "(min-width: 768px) and (max-width: 1023px)"
367
+ );
368
+ this.syncSidebarForViewport();
369
+ this.mdCollapseQuery.addEventListener(
370
+ "change",
371
+ this.syncSidebarForViewport
372
+ );
316
373
  },
317
374
  beforeUnmount() {
318
- this.mdCollapseQuery?.removeEventListener('change', this.syncSidebarForViewport)
375
+ this.mdCollapseQuery?.removeEventListener(
376
+ "change",
377
+ this.syncSidebarForViewport
378
+ );
319
379
  },
320
380
  methods: {
321
381
  restoreSidebarState() {
322
- if (typeof window === 'undefined') {
323
- this.isSidebarCollapsed = this.defaultCollapsed
324
- return
382
+ if (typeof window === "undefined") {
383
+ this.isSidebarCollapsed = this.defaultCollapsed;
384
+ return;
325
385
  }
326
386
 
327
- const stored = window.localStorage.getItem(this.storageKey)
387
+ const stored = window.localStorage.getItem(this.storageKey);
328
388
  if (stored == null) {
329
- this.isSidebarCollapsed = this.defaultCollapsed
330
- return
389
+ this.isSidebarCollapsed = this.defaultCollapsed;
390
+ return;
331
391
  }
332
392
 
333
- this.isSidebarCollapsed = stored === 'true'
393
+ this.isSidebarCollapsed = stored === "true";
334
394
  },
335
395
  syncSidebarForViewport() {
336
- if (typeof window === 'undefined') return
396
+ if (typeof window === "undefined") return;
337
397
 
338
- const isMd = window.matchMedia('(min-width: 768px) and (max-width: 1023px)').matches
398
+ const isMd = window.matchMedia(
399
+ "(min-width: 768px) and (max-width: 1023px)"
400
+ ).matches;
339
401
  if (isMd) {
340
- this.isSidebarCollapsed = true
341
- return
402
+ this.isSidebarCollapsed = true;
403
+ return;
342
404
  }
343
405
 
344
- this.restoreSidebarState()
406
+ this.restoreSidebarState();
345
407
  },
346
408
  persistSidebarState() {
347
- if (typeof window === 'undefined') return
409
+ if (typeof window === "undefined") return;
348
410
 
349
- window.localStorage.setItem(this.storageKey, this.isSidebarCollapsed ? 'true' : 'false')
411
+ window.localStorage.setItem(
412
+ this.storageKey,
413
+ this.isSidebarCollapsed ? "true" : "false"
414
+ );
350
415
  },
351
416
  toggleSidebar() {
352
- this.isSidebarCollapsed = !this.isSidebarCollapsed
353
- this.persistSidebarState()
417
+ this.isSidebarCollapsed = !this.isSidebarCollapsed;
418
+ this.persistSidebarState();
354
419
  },
355
420
  menuItemTag(item) {
356
- if (item?.isLink) return 'RouterLink'
357
- if (item?.isExternal) return 'a'
358
- return 'button'
421
+ if (item?.isLink) return "RouterLink";
422
+ if (item?.isExternal) return "a";
423
+ return "button";
359
424
  },
360
425
  },
361
426
  computed: {
362
427
  normalizedMainMenu() {
363
428
  return this.mainMenu.map((item, index) => {
364
- const isExternal = Boolean(item?.href)
365
- const isLink = Boolean(item?.to)
366
- const active = Boolean(item?.active)
429
+ const isExternal = Boolean(item?.href);
430
+ const isLink = Boolean(item?.to);
431
+ const active = Boolean(item?.active);
367
432
 
368
433
  return {
369
434
  key: item?.key || item?.label || index,
370
- label: item?.label || '',
371
- description: item?.description || '',
372
- icon: item?.icon || 'Dot',
435
+ label: item?.label || "",
436
+ description: item?.description || "",
437
+ icon: item?.icon || "Dot",
373
438
  active,
374
439
  isExternal,
375
440
  isLink,
@@ -377,31 +442,36 @@ export default {
377
442
  ? {
378
443
  to: item.to,
379
444
  title: item.title || item.label,
380
- 'aria-current': active ? 'page' : null,
445
+ "aria-current": active ? "page" : null,
381
446
  }
382
447
  : isExternal
383
- ? {
384
- href: item.href,
385
- target: item.target || '_blank',
386
- rel: item.rel || 'noreferrer',
387
- title: item.title || item.label,
388
- }
389
- : {
390
- type: 'button',
391
- title: item.title || item.label,
392
- },
448
+ ? {
449
+ href: item.href,
450
+ target: item.target || "_blank",
451
+ rel: item.rel || "noreferrer",
452
+ title: item.title || item.label,
453
+ }
454
+ : {
455
+ type: "button",
456
+ title: item.title || item.label,
457
+ },
393
458
  classes: active
394
- ? 'border-white/70 bg-white/90 text-slate-950 shadow-lg shadow-black/20'
395
- : 'border-white/10 bg-white/5 text-white hover:border-cyan-400/40 hover:bg-white/10',
396
- }
397
- })
459
+ ? "border-white/70 bg-white/90 text-slate-950 shadow-lg shadow-black/20"
460
+ : "border-white/10 bg-white/5 text-white hover:border-cyan-400/40 hover:bg-white/10",
461
+ };
462
+ });
398
463
  },
399
464
  accountDisplayName() {
400
- if (!this.account) return ''
401
- return [this.account.firstName, this.account.lastName].filter(Boolean).join(' ').trim() || 'Account'
465
+ if (!this.account) return "";
466
+ return (
467
+ [this.account.firstName, this.account.lastName]
468
+ .filter(Boolean)
469
+ .join(" ")
470
+ .trim() || "Account"
471
+ );
402
472
  },
403
473
  displayEmail() {
404
- return this.account?.email || ''
474
+ return this.account?.email || "";
405
475
  },
406
476
  collapsedContactTooltip() {
407
477
  return `
@@ -416,8 +486,8 @@ export default {
416
486
  <div>https://ketekny.gr</div>
417
487
  </div>
418
488
  </div>
419
- `
489
+ `;
420
490
  },
421
491
  },
422
- }
492
+ };
423
493
  </script>