ketekny-ui-kit 1.0.137 → 1.0.138

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/README.md CHANGED
@@ -42,6 +42,7 @@ import "ketekny-ui-kit/styles.css";
42
42
  import {
43
43
  kButton,
44
44
  kInput,
45
+ kRange,
45
46
  kSelect,
46
47
  kDateSelector,
47
48
  kToggle,
@@ -58,6 +59,7 @@ const app = createApp(App);
58
59
 
59
60
  app.component("kButton", kButton);
60
61
  app.component("kInput", kInput);
62
+ app.component("kRange", kRange);
61
63
  app.component("kSelect", kSelect);
62
64
  app.component("kDateSelector", kDateSelector);
63
65
  app.component("kToggle", kToggle);
@@ -194,6 +196,49 @@ module.exports = {
194
196
 
195
197
  Note: exact option keys may vary by component/plugin implementation version.
196
198
 
199
+ ## Integer Range
200
+
201
+ `kRange` is a dual-handle slider that selects two integers. Its `v-model` is a
202
+ two-item array in `[from, to]` order.
203
+
204
+ ```vue
205
+ <template>
206
+ <kRange
207
+ v-model="range"
208
+ label="Allowed age"
209
+ :min="18"
210
+ :max="80"
211
+ :step="1"
212
+ />
213
+ </template>
214
+
215
+ <script setup>
216
+ import { ref } from "vue";
217
+ import { kRange } from "ketekny-ui-kit";
218
+
219
+ const range = ref([25, 65]);
220
+ </script>
221
+ ```
222
+
223
+ The configured minimum and maximum appear at the slider ends. Selected values
224
+ appear below their handles.
225
+
226
+ Props:
227
+
228
+ - `modelValue` (`Number[]`, default `[0, 100]`): selected `[from, to]` integers.
229
+ - `min` (`Number`, default `0`): minimum selectable value.
230
+ - `max` (`Number`, default `100`): maximum selectable value.
231
+ - `step` (`Number`, default `1`): positive integer increment.
232
+ - `label`, `info`, `error`, `id`: standard field metadata.
233
+ - `disabled`: disables the slider.
234
+ - `showStops`: shows every step along the slider.
235
+ - `showTooltip`: shows handle tooltips while dragging.
236
+
237
+ Events:
238
+
239
+ - `update:modelValue`: emitted while either handle moves.
240
+ - `change`: emitted when the selected range changes.
241
+
197
242
  ## Exports
198
243
 
199
244
  Components:
@@ -201,7 +246,7 @@ Components:
201
246
  - `kMessage`, `kCode`, `kToolbar`, `kTable`, `kTabs`, `kChip`
202
247
  - `kSpinner`, `kDatatable`, `kIcon`, `kMenu`, `kSkeleton`
203
248
  - `kProgressBar`, `kPagination`, `kTree`, `kButton`, `kSelect`, `kUploader`
204
- - `kToggle`, `kCheckbox`, `kInput`, `kDateSelector`, `kEditor`
249
+ - `kToggle`, `kCheckbox`, `kInput`, `kRange`, `kDateSelector`, `kEditor`
205
250
  - `kSelectButton`, `kTags`, `kSearch`, `kArrayList`, `kList`, `kTextArea`
206
251
  - `kDialog`, `kDrawer`
207
252
  - `kAppHeader`, `kAppFooter`, `kAppMain`, `kHero`
package/index.js CHANGED
@@ -9,6 +9,7 @@ import kCode from './src/ui/kCode.vue'
9
9
  import kDialog from './src/ui/kDialog.vue'
10
10
  import kDrawer from './src/ui/kDrawer.vue'
11
11
  import kInput from './src/ui/kInput.vue'
12
+ import kRange from './src/ui/kRange.vue'
12
13
  import kDateSelector from './src/ui/kDateSelector.vue'
13
14
  import kToolbar from './src/ui/kToolbar.vue'
14
15
  import kTooltip from './src/ui/kTooltip.vue'
@@ -63,7 +64,7 @@ export {
63
64
  kMessage, kCode, kToolbar, kTooltip, kTable, kTabs, kCard, kChip, kClipboard, kSpinner, kDatatable, kIcon, kMenu, kSkeleton, kProgressBar, kPagination, kTree,
64
65
 
65
66
  // Form Components
66
- kButton, kCheckbox, kSelect, kUploader, kToggle, kInput, kDateSelector, kEditor, kSelectButton, kTags, kSearch, kArrayList, kList, kTextArea, kForm, kOptionGroup,
67
+ kButton, kCheckbox, kSelect, kUploader, kToggle, kInput, kRange, kDateSelector, kEditor, kSelectButton, kTags, kSearch, kArrayList, kList, kTextArea, kForm, kOptionGroup,
67
68
 
68
69
  // Dialogs
69
70
  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.137",
4
+ "version": "1.0.138",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -0,0 +1,130 @@
1
+ <template>
2
+ <div class="w-full text-primary/90">
3
+ <label v-if="label != null" :for="rangeId" class="inputLabel" :class="labelClass">
4
+ {{ label }}
5
+ </label>
6
+
7
+ <div class="flex items-start gap-4">
8
+ <span class="min-w-8 pt-2 text-right text-sm tabular-nums">{{ min }}</span>
9
+ <div class="relative min-w-0 flex-1 pb-7">
10
+ <el-slider
11
+ :id="rangeId"
12
+ :model-value="rangeValue"
13
+ :min="min"
14
+ :max="max"
15
+ :step="step"
16
+ :disabled="disabled"
17
+ :show-stops="showStops"
18
+ :show-tooltip="showTooltip"
19
+ range
20
+ @update:model-value="updateValue"
21
+ @change="$emit('change', $event)"
22
+ />
23
+ <span
24
+ v-for="marker in selectedMarkers"
25
+ :key="marker.key"
26
+ class="absolute top-8 text-sm tabular-nums"
27
+ :class="marker.alignment"
28
+ :style="{ left: `${marker.position}%` }"
29
+ >
30
+ {{ marker.label }}
31
+ </span>
32
+ </div>
33
+ <span class="min-w-8 pt-2 text-sm tabular-nums">{{ max }}</span>
34
+ </div>
35
+
36
+ <div :id="errorId" :class="theme.errorText" v-if="hasError && error !== true && error !== ''">
37
+ {{ error }}
38
+ </div>
39
+ <div :id="infoId" :class="theme.infoText" v-else-if="info != null">
40
+ {{ info }}
41
+ </div>
42
+ </div>
43
+ </template>
44
+
45
+ <script>
46
+ import { ElSlider } from "element-plus";
47
+ import { K_INPUT_THEME } from "./themes/kInput.theme";
48
+
49
+ export default {
50
+ name: "kRange",
51
+ components: { ElSlider },
52
+ props: {
53
+ modelValue: {
54
+ type: Array,
55
+ default: () => [0, 100],
56
+ validator: (value) => value.length === 2,
57
+ },
58
+ min: { type: Number, default: 0 },
59
+ max: { type: Number, default: 100 },
60
+ step: {
61
+ type: Number,
62
+ default: 1,
63
+ validator: (value) => Number.isInteger(value) && value > 0,
64
+ },
65
+ label: { type: String, default: null },
66
+ info: { type: String, default: null },
67
+ error: { type: [String, Boolean], default: null },
68
+ disabled: { type: Boolean, default: false },
69
+ showStops: { type: Boolean, default: false },
70
+ showTooltip: { type: Boolean, default: true },
71
+ id: { type: String, default: null },
72
+ },
73
+ emits: ["update:modelValue", "change"],
74
+ data() {
75
+ return {
76
+ theme: K_INPUT_THEME,
77
+ generatedId: `range-${Math.random().toString(36).slice(2, 11)}`,
78
+ };
79
+ },
80
+ computed: {
81
+ rangeId() {
82
+ return this.id || this.generatedId;
83
+ },
84
+ errorId() {
85
+ return `${this.rangeId}-error`;
86
+ },
87
+ infoId() {
88
+ return `${this.rangeId}-info`;
89
+ },
90
+ rangeValue() {
91
+ const values = this.modelValue?.length === 2 ? this.modelValue : [this.min, this.max];
92
+ return values.map((value, index) => {
93
+ const fallback = index === 0 ? this.min : this.max;
94
+ const integer = Number.isFinite(Number(value)) ? Math.round(Number(value)) : fallback;
95
+ return Math.min(this.max, Math.max(this.min, integer));
96
+ });
97
+ },
98
+ selectedMarkers() {
99
+ if (this.rangeValue[0] === this.rangeValue[1]) {
100
+ return [this.createMarker(this.rangeValue[0], `${this.rangeValue[0]} – ${this.rangeValue[1]}`, "range")];
101
+ }
102
+ return [
103
+ this.createMarker(this.rangeValue[0], this.rangeValue[0], "from"),
104
+ this.createMarker(this.rangeValue[1], this.rangeValue[1], "to"),
105
+ ];
106
+ },
107
+ hasError() {
108
+ return this.error != null && this.error !== false;
109
+ },
110
+ labelClass() {
111
+ if (this.hasError) return this.theme.labelError;
112
+ if (this.disabled) return this.theme.labelDisabled;
113
+ return this.theme.label;
114
+ },
115
+ },
116
+ methods: {
117
+ createMarker(value, label, key) {
118
+ const span = this.max - this.min;
119
+ const position = span === 0 ? 0 : ((value - this.min) / span) * 100;
120
+ let alignment = "-translate-x-1/2";
121
+ if (position <= 0) alignment = "translate-x-0";
122
+ if (position >= 100) alignment = "-translate-x-full";
123
+ return { key, label, position, alignment };
124
+ },
125
+ updateValue(value) {
126
+ this.$emit("update:modelValue", value.map((item) => Math.round(item)));
127
+ },
128
+ },
129
+ };
130
+ </script>