ketekny-ui-kit 1.0.9 → 1.0.11
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 +4 -2
- package/package.json +1 -1
- package/src/ui/kProgressBar.vue +102 -0
- package/src/ui/kTable.vue +2 -2
- package/src/ui/kTextArea.vue +116 -0
package/index.js
CHANGED
|
@@ -23,6 +23,8 @@ import kTags from './src/ui/kTags.vue'
|
|
|
23
23
|
import kSearch from './src/ui/kSearch.vue'
|
|
24
24
|
import kArrayList from './src/ui/kArrayList.vue'
|
|
25
25
|
import kSkeleton from './src/ui/kSkeleton.vue'
|
|
26
|
+
import kProgressBar from './src/ui/kProgressBar.vue'
|
|
27
|
+
import kTextArea from './src/ui/kTextArea.vue'
|
|
26
28
|
|
|
27
29
|
// Layout Components
|
|
28
30
|
import kAppHeader from './src/layout/kAppHeader.vue'
|
|
@@ -43,10 +45,10 @@ import tailwindPreset from './tailwind-preset.js'
|
|
|
43
45
|
// Named exports (tree-shaking friendly)
|
|
44
46
|
export {
|
|
45
47
|
// UI Components
|
|
46
|
-
kMessage, kCode, kToolbar, kTable, kTabs, kChip, kSpinner, kDatatable, kIcon, kMenu, kSkeleton,
|
|
48
|
+
kMessage, kCode, kToolbar, kTable, kTabs, kChip, kSpinner, kDatatable, kIcon, kMenu, kSkeleton, kProgressBar,
|
|
47
49
|
|
|
48
50
|
// Form Components
|
|
49
|
-
kButton, kSelect, kUploader, kToggle, kInput, kDateSelector, kEditor, kSelectButton, kTags, kSearch, kArrayList,
|
|
51
|
+
kButton, kSelect, kUploader, kToggle, kInput, kDateSelector, kEditor, kSelectButton, kTags, kSearch, kArrayList, kTextArea,
|
|
50
52
|
|
|
51
53
|
// Dialogs
|
|
52
54
|
kDialog, kDrawer,
|
package/package.json
CHANGED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="w-full">
|
|
3
|
+
<div
|
|
4
|
+
class="relative w-full overflow-hidden rounded-full bg-slate-200"
|
|
5
|
+
:style="{ height: resolvedHeight }"
|
|
6
|
+
role="progressbar"
|
|
7
|
+
:aria-valuemin="0"
|
|
8
|
+
:aria-valuemax="max"
|
|
9
|
+
:aria-valuenow="safeValue"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
class="h-full transition-all duration-300 ease-out"
|
|
13
|
+
:class="barClass"
|
|
14
|
+
:style="{ width: `${percentage}%` }"
|
|
15
|
+
/>
|
|
16
|
+
<span
|
|
17
|
+
v-if="showValue"
|
|
18
|
+
class="absolute inset-0 flex items-center justify-center text-xs font-semibold text-slate-700"
|
|
19
|
+
>
|
|
20
|
+
{{ Math.round(percentage) }}%
|
|
21
|
+
</span>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script>
|
|
27
|
+
export default {
|
|
28
|
+
name: 'kProgressBar',
|
|
29
|
+
props: {
|
|
30
|
+
value: { type: Number, default: 0 },
|
|
31
|
+
max: { type: Number, default: 100 },
|
|
32
|
+
height: { type: [String, Number], default: 8 },
|
|
33
|
+
color: {
|
|
34
|
+
type: String,
|
|
35
|
+
default: 'primary',
|
|
36
|
+
validator: (v) => ['primary', 'success', 'warning', 'danger', 'info'].includes(v),
|
|
37
|
+
},
|
|
38
|
+
striped: { type: Boolean, default: false },
|
|
39
|
+
animated: { type: Boolean, default: false },
|
|
40
|
+
showValue: { type: Boolean, default: false },
|
|
41
|
+
},
|
|
42
|
+
computed: {
|
|
43
|
+
safeMax() {
|
|
44
|
+
return Number(this.max) > 0 ? Number(this.max) : 100
|
|
45
|
+
},
|
|
46
|
+
safeValue() {
|
|
47
|
+
const inVal = Number(this.value)
|
|
48
|
+
if (!Number.isFinite(inVal)) return 0
|
|
49
|
+
return Math.min(Math.max(inVal, 0), this.safeMax)
|
|
50
|
+
},
|
|
51
|
+
percentage() {
|
|
52
|
+
return (this.safeValue / this.safeMax) * 100
|
|
53
|
+
},
|
|
54
|
+
resolvedHeight() {
|
|
55
|
+
return typeof this.height === 'number' ? `${this.height}px` : this.height
|
|
56
|
+
},
|
|
57
|
+
barClass() {
|
|
58
|
+
const palette = {
|
|
59
|
+
primary: 'bg-primary',
|
|
60
|
+
success: 'bg-green-600',
|
|
61
|
+
warning: 'bg-amber-500',
|
|
62
|
+
danger: 'bg-red-600',
|
|
63
|
+
info: 'bg-sky-600',
|
|
64
|
+
}
|
|
65
|
+
return [
|
|
66
|
+
palette[this.color] || palette.primary,
|
|
67
|
+
this.striped ? 'k-progress-striped' : '',
|
|
68
|
+
this.animated ? 'k-progress-animated' : '',
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<style scoped>
|
|
76
|
+
.k-progress-striped {
|
|
77
|
+
background-image: linear-gradient(
|
|
78
|
+
45deg,
|
|
79
|
+
rgba(255, 255, 255, 0.25) 25%,
|
|
80
|
+
transparent 25%,
|
|
81
|
+
transparent 50%,
|
|
82
|
+
rgba(255, 255, 255, 0.25) 50%,
|
|
83
|
+
rgba(255, 255, 255, 0.25) 75%,
|
|
84
|
+
transparent 75%,
|
|
85
|
+
transparent
|
|
86
|
+
);
|
|
87
|
+
background-size: 1rem 1rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.k-progress-animated {
|
|
91
|
+
animation: k-progress-bar-stripes 0.9s linear infinite;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@keyframes k-progress-bar-stripes {
|
|
95
|
+
from {
|
|
96
|
+
background-position: 1rem 0;
|
|
97
|
+
}
|
|
98
|
+
to {
|
|
99
|
+
background-position: 0 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
</style>
|
package/src/ui/kTable.vue
CHANGED
|
@@ -115,7 +115,7 @@ export default {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
.table-custom--card th {
|
|
118
|
-
@apply px-6 py-4 text-
|
|
118
|
+
@apply px-6 py-4 text-lg font-semibold text-left text-white bg-primary border-b border-primary;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
.table-custom--plain th {
|
|
@@ -127,7 +127,7 @@ export default {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
.table-custom--card td {
|
|
130
|
-
@apply h-[
|
|
130
|
+
@apply h-[3.2rem] px-6 py-4 text-base text-slate-800;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
.table-custom--plain td {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="w-full">
|
|
3
|
+
<label
|
|
4
|
+
v-if="label != null"
|
|
5
|
+
:for="inputId"
|
|
6
|
+
class="inputLabel"
|
|
7
|
+
:class="hasError ? 'text-red-500' : 'text-gray-700'"
|
|
8
|
+
>
|
|
9
|
+
{{ label }}
|
|
10
|
+
</label>
|
|
11
|
+
|
|
12
|
+
<textarea
|
|
13
|
+
:id="inputId"
|
|
14
|
+
:value="computedModelValue"
|
|
15
|
+
@input="handleInput"
|
|
16
|
+
:rows="rows"
|
|
17
|
+
:disabled="disabled"
|
|
18
|
+
:placeholder="placeholder"
|
|
19
|
+
:autofocus="autofocus"
|
|
20
|
+
:class="textareaClass"
|
|
21
|
+
:aria-invalid="hasError ? 'true' : 'false'"
|
|
22
|
+
:aria-describedby="describedById"
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
<div :id="errorId" class="mt-1 text-red-500" v-if="hasError && error !== true && error !== ''">
|
|
26
|
+
{{ error }}
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div :id="infoId" class="mt-1 text-gray-500" v-if="info != null">
|
|
30
|
+
{{ info }}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
export default {
|
|
37
|
+
name: 'kTextArea',
|
|
38
|
+
props: {
|
|
39
|
+
modelValue: [String, Number],
|
|
40
|
+
label: { type: String, default: null },
|
|
41
|
+
info: { type: String, default: null },
|
|
42
|
+
error: { type: [String, Boolean], default: null },
|
|
43
|
+
placeholder: { type: String, default: null },
|
|
44
|
+
disabled: { type: Boolean, default: false },
|
|
45
|
+
id: { type: String, default: null },
|
|
46
|
+
autofocus: { type: Boolean, default: false },
|
|
47
|
+
rows: { type: Number, default: 4 },
|
|
48
|
+
capsOnly: { type: Boolean, default: false },
|
|
49
|
+
},
|
|
50
|
+
data() {
|
|
51
|
+
return {
|
|
52
|
+
computedModelValue: '',
|
|
53
|
+
generatedId: `textarea-${Math.random().toString(36).slice(2, 11)}`,
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
computed: {
|
|
57
|
+
hasError() {
|
|
58
|
+
return this.error != null && this.error !== false
|
|
59
|
+
},
|
|
60
|
+
inputId() {
|
|
61
|
+
return this.id || this.generatedId
|
|
62
|
+
},
|
|
63
|
+
errorId() {
|
|
64
|
+
return `${this.inputId}-error`
|
|
65
|
+
},
|
|
66
|
+
infoId() {
|
|
67
|
+
return `${this.inputId}-info`
|
|
68
|
+
},
|
|
69
|
+
describedById() {
|
|
70
|
+
const ids = []
|
|
71
|
+
if (this.hasError && this.error !== true && this.error !== '') ids.push(this.errorId)
|
|
72
|
+
if (this.info != null) ids.push(this.infoId)
|
|
73
|
+
return ids.length ? ids.join(' ') : null
|
|
74
|
+
},
|
|
75
|
+
textareaClass() {
|
|
76
|
+
return [
|
|
77
|
+
'w-full px-3 py-2 border rounded-lg transition shadow-sm focus:outline-none text-gray-700 focus:ring-0 focus:border-green-500 bg-white placeholder-gray-400',
|
|
78
|
+
this.hasError ? 'border-red-500 focus:ring focus:ring-red-300' : '',
|
|
79
|
+
this.disabled ? '!bg-gray-100 !text-gray-400 !cursor-not-allowed' : '',
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
created() {
|
|
84
|
+
this.init(this.modelValue)
|
|
85
|
+
},
|
|
86
|
+
watch: {
|
|
87
|
+
modelValue(val) {
|
|
88
|
+
this.init(val)
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
methods: {
|
|
92
|
+
init(val) {
|
|
93
|
+
this.computedModelValue = val ?? ''
|
|
94
|
+
},
|
|
95
|
+
handleInput(event) {
|
|
96
|
+
let val = event.target.value
|
|
97
|
+
|
|
98
|
+
if (typeof val === 'string' && this.capsOnly) {
|
|
99
|
+
val = val.normalize('NFD').replace(/\p{Diacritic}/gu, '')
|
|
100
|
+
val = val
|
|
101
|
+
.replace(/[άαΆΑ]/g, 'Α')
|
|
102
|
+
.replace(/[έεΈΕ]/g, 'Ε')
|
|
103
|
+
.replace(/[ήηΉΗ]/g, 'Η')
|
|
104
|
+
.replace(/[ίιΊΙϊΐ]/g, 'Ι')
|
|
105
|
+
.replace(/[όοΌΟ]/g, 'Ο')
|
|
106
|
+
.replace(/[ύυΎΫΰϋ]/g, 'Υ')
|
|
107
|
+
.replace(/[ώωΏΩ]/g, 'Ω')
|
|
108
|
+
.toUpperCase()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.computedModelValue = val
|
|
112
|
+
this.$emit('update:modelValue', val)
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
</script>
|