ketekny-ui-kit 1.0.40 → 1.0.42

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.40",
4
+ "version": "1.0.42",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -34,7 +34,7 @@ import { ElDatePicker } from 'element-plus'
34
34
  const props = defineProps({
35
35
  modelValue: { type: [String, Date, Array], default: null },
36
36
  id: {
37
- type: String,
37
+ type: [String, Array],
38
38
  default: () => `datepicker-${Math.random().toString(36).slice(2, 11)}`,
39
39
  },
40
40
  label: { type: String, default: '' },
@@ -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>