ketekny-ui-kit 1.0.39 → 1.0.41

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/index.js CHANGED
@@ -7,6 +7,7 @@ import kDialog from './src/ui/kDialog.vue'
7
7
  import kDrawer from './src/ui/kDrawer.vue'
8
8
  import kInput from './src/ui/kInput.vue'
9
9
  import kDateSelector from './src/ui/kDateSelector.vue'
10
+ import kDateSelectorV2 from './src/ui/kDateSelector_v2.vue'
10
11
  import kToolbar from './src/ui/kToolbar.vue'
11
12
  import kSelect from './src/ui/kSelect.vue'
12
13
  import kTable from './src/ui/kTable.vue'
@@ -50,7 +51,7 @@ export {
50
51
  kMessage, kCode, kToolbar, kTable, kTabs, kChip, kSpinner, kDatatable, kIcon, kMenu, kSkeleton, kProgressBar, kTree,
51
52
 
52
53
  // Form Components
53
- kButton, kSelect, kUploader, kToggle, kInput, kDateSelector, kEditor, kSelectButton, kTags, kSearch, kArrayList, kList, kTextArea,
54
+ kButton, kSelect, kUploader, kToggle, kInput, kDateSelector, kDateSelectorV2, kEditor, kSelectButton, kTags, kSearch, kArrayList, kList, kTextArea,
54
55
 
55
56
  // Dialogs
56
57
  kDialog, kDrawer,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ketekny-ui-kit",
3
3
  "type": "module",
4
- "version": "1.0.39",
4
+ "version": "1.0.41",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -2,9 +2,9 @@
2
2
  <Teleport to="body">
3
3
  <Transition name="fade">
4
4
  <div v-if="visible" class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-40" style="z-index: 1500">
5
- <div class="w-full max-w-md p-6 bg-white border rounded-lg shadow-xl border-primary/20 dark:bg-slate-800 dark:border-slate-700">
5
+ <div class="w-full max-w-md p-6 bg-white border rounded-lg shadow-xl border-primary/20 dark:bg-zinc-900 dark:border-zinc-800">
6
6
  <h2 class="mb-2 text-lg font-semibold text-primary">{{ title }}</h2>
7
- <div class="mb-6 text-gray-700 dark:text-slate-300"><span v-html="message" /></div>
7
+ <div class="mb-6 text-gray-700 dark:text-zinc-300"><span v-html="message" /></div>
8
8
  <div class="flex justify-end gap-3">
9
9
  <kButton :disabled="loading" secondary label="Άκυρο" @click="cancel" />
10
10
  <kButton :loading="loading" danger label="Συνέχεια" @click="confirm" />
@@ -8,7 +8,7 @@
8
8
  <transition name="dialog">
9
9
  <div
10
10
  ref="dialogPanel"
11
- class="relative bg-white dark:bg-slate-900 shadow-lg overflow-hidden rounded-2xl flex flex-col max-h-[calc(100dvh-1rem)] sm:max-h-[90vh] m-2 sm:m-4"
11
+ class="relative bg-white dark:bg-zinc-900 shadow-lg overflow-hidden rounded-2xl flex flex-col max-h-[calc(100dvh-1rem)] sm:max-h-[90vh] m-2 sm:m-4"
12
12
  :class="dialogClasses"
13
13
  :role="'dialog'"
14
14
  :aria-modal="'true'"
@@ -17,16 +17,16 @@
17
17
  v-show="visible"
18
18
  >
19
19
  <!-- Header -->
20
- <div class="flex flex-row items-center p-3 text-white bg-sky-800 shrink-0 sm:p-4">
21
- <div :id="titleId" class="text-base font-semibold !text-white sm:text-xl">{{ title }}</div>
20
+ <div class="flex flex-row items-center px-4 py-3 border-b border-primary/20 bg-primary/5 dark:bg-primary/10 shrink-0">
21
+ <div :id="titleId" class="text-sm font-semibold text-primary sm:text-base">{{ title }}</div>
22
22
  <div class="flex-1" />
23
23
  <button
24
24
  type="button"
25
- class="p-1 text-black transition duration-100 ease-in-out rounded-full cursor-pointer text-secondary hover:bg-white hover:text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70"
25
+ class="p-1.5 rounded-md text-zinc-400 hover:text-primary dark:text-zinc-500 dark:hover:text-primary hover:bg-primary/10 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
26
26
  aria-label="Close dialog"
27
27
  @click="close"
28
28
  >
29
- <X />
29
+ <X class="w-5 h-5" />
30
30
  </button>
31
31
  </div>
32
32
 
@@ -36,14 +36,14 @@
36
36
  </div>
37
37
 
38
38
  <!-- Scrollable slot content -->
39
- <div class="flex-1 p-3 overflow-auto bg-secondary dark:bg-slate-800/60 sm:p-4">
39
+ <div class="flex-1 p-3 overflow-auto sm:p-4">
40
40
  <slot></slot>
41
41
  </div>
42
42
 
43
43
  <!-- Footer -->
44
44
  <div
45
45
  v-if="$slots.footer"
46
- class="flex flex-col-reverse gap-2 p-3 border-t shrink-0 border-slate-100 dark:border-slate-700 sm:flex-row sm:justify-end sm:p-4"
46
+ class="flex flex-col-reverse gap-2 p-3 border-t shrink-0 border-zinc-200 dark:border-zinc-800 sm:flex-row sm:justify-end sm:p-4"
47
47
  >
48
48
  <slot name="footer"></slot>
49
49
  </div>
@@ -1,41 +1,50 @@
1
1
  <template>
2
- <div v-if="visible" class="fixed inset-0 z-50 flex">
3
- <transition name="fade ">
4
- <div class="fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm" @click="close"></div>
5
- </transition>
6
- <!-- Backdrop -->
7
- <!-- <div class="fixed inset-0 bg-black bg-opacity-50 backdrop-blur-sm" @click="close"></div> -->
8
-
9
- <!-- Drawer -->
10
- <div
11
- class="fixed top-0 bottom-0 z-50 flex w-[calc(100vw-1rem)] max-w-full flex-col bg-white shadow-xl transition-all duration-500 dark:bg-slate-900 sm:w-[400px]"
12
- :class="side === 'right' ? 'right-0' : 'left-0'">
13
- <!-- Header -->
14
- <div class="flex items-center justify-between p-3 text-white bg-primary dark:bg-sky-800 sm:p-4">
15
- <div class="text-base font-semibold sm:text-lg">{{ title }}</div>
16
- <button @click="close" class="text-white hover:text-gray-200">
17
- <X class="w-5 h-5" />
18
- </button>
19
- </div>
2
+ <div
3
+ v-if="visible"
4
+ class="fixed inset-0 z-50 flex overflow-hidden bg-black/50 backdrop-blur-sm"
5
+ :class="side === 'right' ? 'justify-end' : 'justify-start'"
6
+ @click.self="close"
7
+ >
8
+ <transition :name="side === 'right' ? 'drawer-right' : 'drawer-left'">
9
+ <div
10
+ class="relative m-2 flex h-[calc(100dvh-1rem)] w-[calc(100vw-1rem)] max-w-[calc(100vw-1rem)] flex-col overflow-hidden rounded-2xl bg-white shadow-lg dark:bg-zinc-900 sm:m-4 sm:h-[calc(100vh-2rem)] sm:w-[400px]"
11
+ role="dialog"
12
+ aria-modal="true"
13
+ @click.stop
14
+ >
15
+ <div class="flex flex-row items-center border-b border-primary/20 bg-primary/5 px-4 py-3 dark:bg-primary/10">
16
+ <div class="text-sm font-semibold text-primary sm:text-base">{{ title }}</div>
17
+ <div class="flex-1" />
18
+ <button
19
+ type="button"
20
+ class="cursor-pointer rounded-md p-1.5 text-zinc-400 transition-colors hover:bg-primary/10 hover:text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 dark:text-zinc-500 dark:hover:text-primary"
21
+ aria-label="Close drawer"
22
+ @click="close"
23
+ >
24
+ <X class="w-5 h-5" />
25
+ </button>
26
+ </div>
20
27
 
21
- <!-- Content -->
22
- <div class="flex-1 p-3 overflow-auto bg-secondary dark:bg-slate-800/60 sm:p-4">
23
- <slot></slot>
24
- </div>
28
+ <div class="flex-1 overflow-auto p-3 sm:p-4">
29
+ <slot></slot>
30
+ </div>
25
31
 
26
- <!-- Footer -->
27
- <div class="p-3 bg-gray-100 border-t border-slate-200 dark:bg-slate-800 dark:border-slate-700 sm:p-4">
28
- <slot name="footer"></slot>
32
+ <div
33
+ v-if="$slots.footer"
34
+ class="flex flex-col-reverse gap-2 border-t border-zinc-200 p-3 dark:border-zinc-800 sm:flex-row sm:justify-end sm:p-4"
35
+ >
36
+ <slot name="footer"></slot>
37
+ </div>
29
38
  </div>
30
- </div>
39
+ </transition>
31
40
  </div>
32
41
  </template>
33
42
 
34
43
  <script>
35
- import { X } from "lucide-vue-next";
44
+ import { X } from 'lucide-vue-next'
36
45
 
37
46
  export default {
38
- name: "KDrawer",
47
+ name: 'KDrawer',
39
48
  components: {
40
49
  X,
41
50
  },
@@ -46,28 +55,45 @@ export default {
46
55
  },
47
56
  title: {
48
57
  type: String,
49
- default: "",
58
+ default: '',
50
59
  },
51
60
  side: {
52
61
  type: String,
53
- default: "right", // "left" or "right"
54
- validator: (val) => ["left", "right"].includes(val),
62
+ default: 'right', // "left" or "right"
63
+ validator: (val) => ['left', 'right'].includes(val),
55
64
  },
56
65
  },
57
66
  watch: {
58
67
  visible(val) {
59
- document.body.style.overflow = val ? "hidden" : "";
68
+ document.body.style.overflow = val ? 'hidden' : ''
60
69
  },
61
70
  },
62
71
  beforeUnmount() {
63
- document.body.style.overflow = "";
72
+ document.body.style.overflow = ''
64
73
  },
65
74
  methods: {
66
75
  close() {
67
- this.$emit("update:visible", false);
76
+ this.$emit('update:visible', false)
68
77
  },
69
78
  },
70
- };
79
+ }
71
80
  </script>
72
81
 
73
- <style scoped></style>
82
+ <style scoped>
83
+ .drawer-right-enter-active,
84
+ .drawer-right-leave-active,
85
+ .drawer-left-enter-active,
86
+ .drawer-left-leave-active {
87
+ transition: transform 0.25s ease;
88
+ }
89
+
90
+ .drawer-right-enter-from,
91
+ .drawer-right-leave-to {
92
+ transform: translateX(100%);
93
+ }
94
+
95
+ .drawer-left-enter-from,
96
+ .drawer-left-leave-to {
97
+ transform: translateX(-100%);
98
+ }
99
+ </style>
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <div :class="labelStyle === 'inline' ? 'flex items-center gap-2' : ''">
3
+ <div
4
+ v-if="showLabel && labelStyle !== 'inline'"
5
+ :class="[
6
+ 'block mb-1 text-sm font-bold',
7
+ disabled ? 'text-slate-500' : 'text-primary/90',
8
+ ]"
9
+ >
10
+ {{ label }}
11
+ </div>
12
+
13
+ <el-switch
14
+ :model-value="resolvedValue"
15
+ :disabled="disabled"
16
+ :active-text="activeText"
17
+ :inactive-text="inactiveText"
18
+ :active-color="activeColor"
19
+ :inactive-color="inactiveColor"
20
+ :size="size"
21
+ :width="width"
22
+ :inline-prompt="inlinePrompt"
23
+ @update:model-value="emitModelUpdate"
24
+ @change="$emit('change', $event)"
25
+ />
26
+
27
+ <div
28
+ v-if="showLabel && labelStyle === 'inline'"
29
+ :class="[
30
+ 'text-sm font-bold cursor-pointer',
31
+ disabled ? 'text-slate-500 cursor-not-allowed' : 'text-primary/90',
32
+ ]"
33
+ @click="!disabled && emitModelUpdate(!resolvedValue)"
34
+ >
35
+ {{ label }}
36
+ </div>
37
+ </div>
38
+ </template>
39
+
40
+ <script setup>
41
+ import { computed, getCurrentInstance } from 'vue'
42
+ import { ElSwitch } from 'element-plus'
43
+
44
+ const props = defineProps({
45
+ modelValue: { type: Boolean, default: false },
46
+ value: { type: Boolean, default: undefined },
47
+ label: { type: String, default: '' },
48
+ labelStyle: { type: String, default: null },
49
+ disabled: { type: Boolean, default: false },
50
+ size: {
51
+ type: String,
52
+ default: 'default',
53
+ validator: (val) => ['large', 'default', 'small'].includes(val),
54
+ },
55
+ width: { type: [String, Number], default: undefined },
56
+ activeText: { type: String, default: '' },
57
+ inactiveText: { type: String, default: '' },
58
+ activeColor: { type: String, default: '' },
59
+ inactiveColor: { type: String, default: '' },
60
+ inlinePrompt: { type: Boolean, default: false },
61
+ })
62
+
63
+ const emit = defineEmits(['update:modelValue', 'update:value', 'change'])
64
+
65
+ const instance = getCurrentInstance()
66
+ const passedProps = new Set(Object.keys(instance?.vnode.props || {}))
67
+ const hasProp = (...names) => names.some((name) => passedProps.has(name))
68
+
69
+ const resolvedValue = computed(() => (hasProp('value') ? props.value : props.modelValue))
70
+
71
+ const showLabel = computed(() => !!props.label && props.label.trim() !== '')
72
+
73
+ function emitModelUpdate(val) {
74
+ emit('update:modelValue', val)
75
+ emit('update:value', val)
76
+ }
77
+ </script>
@@ -0,0 +1,132 @@
1
+ <template>
2
+ <div :class="labelStyle === 'inline' ? 'flex items-center gap-2' : ''">
3
+ <div
4
+ v-if="showLabel && labelStyle !== 'inline'"
5
+ :class="[
6
+ 'block mb-1 text-sm font-bold',
7
+ disabled ? 'text-slate-500' : 'text-primary/90',
8
+ ]"
9
+ >
10
+ {{ label }}
11
+ </div>
12
+
13
+ <SwitchRoot
14
+ :checked="resolvedValue"
15
+ :disabled="disabled"
16
+ class="k-toggle-v3-track"
17
+ :data-state="resolvedValue ? 'checked' : 'unchecked'"
18
+ @update:checked="emitModelUpdate"
19
+ >
20
+ <SwitchThumb class="k-toggle-v3-thumb" />
21
+ </SwitchRoot>
22
+
23
+ <div
24
+ v-if="showLabel && labelStyle === 'inline'"
25
+ :class="[
26
+ 'text-sm font-bold cursor-pointer',
27
+ disabled ? 'text-slate-500 cursor-not-allowed' : 'text-primary/90',
28
+ ]"
29
+ @click="!disabled && emitModelUpdate(!resolvedValue)"
30
+ >
31
+ {{ label }}
32
+ </div>
33
+ </div>
34
+ </template>
35
+
36
+ <script setup>
37
+ import { computed, getCurrentInstance } from 'vue'
38
+ import { SwitchRoot, SwitchThumb } from 'reka-ui'
39
+
40
+ const props = defineProps({
41
+ modelValue: { type: Boolean, default: false },
42
+ value: { type: Boolean, default: undefined },
43
+ label: { type: String, default: '' },
44
+ labelStyle: { type: String, default: null },
45
+ disabled: { type: Boolean, default: false },
46
+ })
47
+
48
+ const emit = defineEmits(['update:modelValue', 'update:value'])
49
+
50
+ const instance = getCurrentInstance()
51
+ const passedProps = new Set(Object.keys(instance?.vnode.props || {}))
52
+ const hasProp = (...names) => names.some((name) => passedProps.has(name))
53
+
54
+ const resolvedValue = computed(() => (hasProp('value') ? props.value : props.modelValue))
55
+
56
+ const showLabel = computed(() => !!props.label && props.label.trim() !== '')
57
+
58
+ function emitModelUpdate(val) {
59
+ emit('update:modelValue', val)
60
+ emit('update:value', val)
61
+ }
62
+ </script>
63
+
64
+ <style scoped>
65
+ .k-toggle-v3-track {
66
+ position: relative;
67
+ display: inline-flex;
68
+ align-items: center;
69
+ width: 3rem;
70
+ height: 1.75rem;
71
+ padding: 0.15rem;
72
+ border-radius: 9999px;
73
+ border: none;
74
+ cursor: pointer;
75
+ flex-shrink: 0;
76
+ background: #94a3b8;
77
+ transition: background-color 0.25s ease, box-shadow 0.25s ease;
78
+ outline: none;
79
+ }
80
+
81
+ .k-toggle-v3-track:focus-visible {
82
+ box-shadow: 0 0 0 2px #fff, 0 0 0 4px #10b981;
83
+ }
84
+
85
+ .k-toggle-v3-track[data-state='checked'] {
86
+ background: #10b981;
87
+ box-shadow: 0 0 12px rgba(16, 185, 129, 0.35);
88
+ }
89
+
90
+ .k-toggle-v3-track[data-state='unchecked'] {
91
+ background: #94a3b8;
92
+ }
93
+
94
+ .k-toggle-v3-track[data-disabled] {
95
+ cursor: not-allowed;
96
+ opacity: 0.45;
97
+ }
98
+
99
+ .k-toggle-v3-thumb {
100
+ display: block;
101
+ width: 1.35rem;
102
+ height: 1.35rem;
103
+ border-radius: 9999px;
104
+ background: #fff;
105
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.04);
106
+ transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);
107
+ pointer-events: none;
108
+ }
109
+
110
+ .k-toggle-v3-track[data-state='checked'] .k-toggle-v3-thumb {
111
+ transform: translateX(1.25rem);
112
+ }
113
+
114
+ .k-toggle-v3-track[data-state='unchecked'] .k-toggle-v3-thumb {
115
+ transform: translateX(0);
116
+ }
117
+
118
+ /* Dark mode */
119
+ :global(html.dark) .k-toggle-v3-track[data-state='unchecked'] {
120
+ background: #475569;
121
+ }
122
+
123
+ :global(html.dark) .k-toggle-v3-track[data-state='checked'] {
124
+ background: #059669;
125
+ box-shadow: 0 0 14px rgba(5, 150, 105, 0.4);
126
+ }
127
+
128
+ :global(html.dark) .k-toggle-v3-thumb {
129
+ background: #f1f5f9;
130
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
131
+ }
132
+ </style>
@@ -0,0 +1,122 @@
1
+ <template>
2
+ <div :class="labelStyle === 'inline' ? 'flex items-center gap-2' : ''">
3
+ <div
4
+ v-if="showLabel && labelStyle !== 'inline'"
5
+ :class="[
6
+ 'block mb-1 text-sm font-bold',
7
+ disabled ? 'text-slate-500' : 'text-primary/90',
8
+ ]"
9
+ >
10
+ {{ label }}
11
+ </div>
12
+
13
+ <button
14
+ type="button"
15
+ role="switch"
16
+ :aria-checked="resolvedValue"
17
+ :aria-label="label || 'Toggle'"
18
+ :disabled="disabled"
19
+ :class="cn(toggleVariants({ state: resolvedValue ? 'checked' : 'unchecked', size }), $attrs.class)"
20
+ @click="!disabled && emitModelUpdate(!resolvedValue)"
21
+ >
22
+ <span
23
+ :class="cn(thumbVariants({ state: resolvedValue ? 'checked' : 'unchecked', size }))"
24
+ />
25
+ </button>
26
+
27
+ <div
28
+ v-if="showLabel && labelStyle === 'inline'"
29
+ :class="[
30
+ 'text-sm font-bold cursor-pointer',
31
+ disabled ? 'text-slate-500 cursor-not-allowed' : 'text-primary/90',
32
+ ]"
33
+ @click="!disabled && emitModelUpdate(!resolvedValue)"
34
+ >
35
+ {{ label }}
36
+ </div>
37
+ </div>
38
+ </template>
39
+
40
+ <script setup>
41
+ import { computed, getCurrentInstance } from 'vue'
42
+ import { cva } from 'class-variance-authority'
43
+ import { cn } from '../lib/utils'
44
+
45
+ const toggleVariants = cva(
46
+ 'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-slate-300 dark:focus-visible:ring-offset-slate-950 disabled:cursor-not-allowed disabled:opacity-50',
47
+ {
48
+ variants: {
49
+ state: {
50
+ checked: 'bg-slate-900 dark:bg-slate-50',
51
+ unchecked: 'bg-slate-200 dark:bg-slate-800',
52
+ },
53
+ size: {
54
+ sm: 'h-4 w-7',
55
+ default: 'h-5 w-9',
56
+ lg: 'h-6 w-11',
57
+ },
58
+ },
59
+ defaultVariants: {
60
+ state: 'unchecked',
61
+ size: 'default',
62
+ },
63
+ }
64
+ )
65
+
66
+ const thumbVariants = cva(
67
+ 'pointer-events-none block rounded-full bg-white shadow-sm ring-0 transition-transform duration-200 dark:bg-slate-950',
68
+ {
69
+ variants: {
70
+ state: {
71
+ checked: '',
72
+ unchecked: '',
73
+ },
74
+ size: {
75
+ sm: 'h-3 w-3',
76
+ default: 'h-4 w-4',
77
+ lg: 'h-5 w-5',
78
+ },
79
+ },
80
+ compoundVariants: [
81
+ { state: 'checked', size: 'sm', class: 'translate-x-3' },
82
+ { state: 'unchecked', size: 'sm', class: 'translate-x-0.5' },
83
+ { state: 'checked', size: 'default', class: 'translate-x-4' },
84
+ { state: 'unchecked', size: 'default', class: 'translate-x-0.5' },
85
+ { state: 'checked', size: 'lg', class: 'translate-x-5' },
86
+ { state: 'unchecked', size: 'lg', class: 'translate-x-0.5' },
87
+ ],
88
+ defaultVariants: {
89
+ state: 'unchecked',
90
+ size: 'default',
91
+ },
92
+ }
93
+ )
94
+
95
+ const props = defineProps({
96
+ modelValue: { type: Boolean, default: false },
97
+ value: { type: Boolean, default: undefined },
98
+ label: { type: String, default: '' },
99
+ labelStyle: { type: String, default: null },
100
+ disabled: { type: Boolean, default: false },
101
+ size: {
102
+ type: String,
103
+ default: 'default',
104
+ validator: (val) => ['sm', 'default', 'lg'].includes(val),
105
+ },
106
+ })
107
+
108
+ const emit = defineEmits(['update:modelValue', 'update:value'])
109
+
110
+ const instance = getCurrentInstance()
111
+ const passedProps = new Set(Object.keys(instance?.vnode.props || {}))
112
+ const hasProp = (...names) => names.some((name) => passedProps.has(name))
113
+
114
+ const resolvedValue = computed(() => (hasProp('value') ? props.value : props.modelValue))
115
+
116
+ const showLabel = computed(() => !!props.label && props.label.trim() !== '')
117
+
118
+ function emitModelUpdate(val) {
119
+ emit('update:modelValue', val)
120
+ emit('update:value', val)
121
+ }
122
+ </script>