its_ui_vite 1.0.8 → 1.0.10

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
@@ -130,6 +130,22 @@ slots: ['без имени' <!-- есть дефольное значение (p
130
130
  slots: ['customIcon' <!-- есть дефольное значение -->]
131
131
  ```
132
132
 
133
+ * CDatepicker
134
+
135
+ ```
136
+ {
137
+ date?: string,
138
+ locale?: 'rus' | 'usa' | 'tur' | 'spa',
139
+ max?: string,
140
+ min?: string,
141
+ width?: string,
142
+ isOpen?: boolean,
143
+ modelValue?: any,
144
+ }
145
+
146
+ slots: ['customIcon' <!-- есть дефольное значение -->]
147
+ ```
148
+
133
149
  * CTooltip
134
150
 
135
151
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "its_ui_vite",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "main": "./src/libIndex.js",
5
5
  "module": "./src/libIndex.js",
6
6
  "files": [
@@ -23,6 +23,7 @@
23
23
  "compression": "^1.7.4",
24
24
  "dotenv": "^16.3.1",
25
25
  "express": "4.18.2",
26
+ "luxon": "^3.7.2",
26
27
  "vue": "^3.3.11",
27
28
  "vue3-perfect-scrollbar": "^2.0.0",
28
29
  "yargs": "^17.7.2"
@@ -75,6 +75,8 @@ export type TIcon = {
75
75
  'bx-volume-mute'?: boolean,
76
76
  /** ![](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTMgNS41QzEzIDMuNTcgMTEuNDMgMiA5LjUgMkM3LjQ2NiAyIDYuMjUgMy41MjUgNi4yNSA1SDguMjVDOC4yNSA0LjU4NSA4LjYzOCA0IDkuNSA0QzEwLjMyNyA0IDExIDQuNjczIDExIDUuNUMxMSA2LjMyNyAxMC4zMjcgNyA5LjUgN0gyVjlIOS41QzExLjQzIDkgMTMgNy40MyAxMyA1LjVaTTE1LjUgMTVIOFYxN0gxNS41QzE2LjMyNyAxNyAxNyAxNy42NzMgMTcgMTguNUMxNyAxOS4zMjcgMTYuMzI3IDIwIDE1LjUgMjBDMTQuNjM4IDIwIDE0LjI1IDE5LjQxNSAxNC4yNSAxOUgxMi4yNUMxMi4yNSAyMC40NzUgMTMuNDY2IDIyIDE1LjUgMjJDMTcuNDMgMjIgMTkgMjAuNDMgMTkgMTguNUMxOSAxNi41NyAxNy40MyAxNSAxNS41IDE1WiIgZmlsbD0id2hpdGUiLz48cGF0aCBkPSJNMTggNUMxNS43OTQgNSAxNCA2Ljc5NCAxNCA5SDE2QzE2IDcuODk3IDE2Ljg5NyA3IDE4IDdDMTkuMTAzIDcgMjAgNy44OTcgMjAgOUMyMCAxMC4xMDMgMTkuMTAzIDExIDE4IDExSDJWMTNIMThDMjAuMjA2IDEzIDIyIDExLjIwNiAyMiA5QzIyIDYuNzk0IDIwLjIwNiA1IDE4IDVaTTIgMTVINlYxN0gyVjE1WiIgZmlsbD0id2hpdGUiLz48L3N2Zz4=) */
77
77
  'bx-wind'?: boolean,
78
+ /** ![](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNyAxMUg5VjEzSDdWMTFaTTIxIDVWMTlDMjEgMjAuMTEgMjAuMTEgMjEgMTkgMjFINUMzLjg5IDIxIDMgMjAuMSAzIDE5VjVDMyAzLjkgMy45IDMgNSAzSDZWMUg4VjNIMTZWMUgxOFYzSDE5QzIwLjExIDMgMjEgMy45IDIxIDVaTTUgN0gxOVY1SDVWN1pNMTkgMTlWOUg1VjE5SDE5Wk0xNSAxM1YxMUgxN1YxM0gxNVpNMTEgMTNWMTFIMTNWMTNIMTFaTTcgMTVIOVYxN0g3VjE1Wk0xNSAxN1YxNUgxN1YxN0gxNVpNMTEgMTdWMTVIMTNWMTdIMTFaIiBmaWxsPSJ3aGl0ZSIvPjwvc3ZnPg==) */
79
+ 'calendar-month-outline'?: boolean,
78
80
  }
79
81
  export const iconContent = {
80
82
  'Bar-chart': `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 20V10" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 20V4" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6 20V14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
@@ -115,4 +117,5 @@ export const iconContent = {
115
117
  'bx-volume-full': `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16 21C19.527 19.453 21.999 16.091 21.999 12C21.999 7.909 19.527 4.547 16 3V5C18.387 6.386 19.999 9.047 19.999 12C19.999 14.953 18.387 17.614 16 19V21Z" fill="white"/><path d="M16 6.99997V17C17.225 15.9 18 13.771 18 12C18 10.229 17.225 8.09997 16 6.99997ZM4 17H6.697L12.445 20.832C12.5958 20.9321 12.7708 20.9896 12.9516 20.9984C13.1324 21.0072 13.3122 20.967 13.472 20.882C13.6316 20.7965 13.765 20.6693 13.858 20.514C13.951 20.3587 14.0001 20.181 14 20V3.99997C13.9999 3.81909 13.9508 3.64162 13.8578 3.48646C13.7648 3.3313 13.6315 3.20427 13.472 3.11889C13.3125 3.03351 13.1329 2.99299 12.9522 3.00163C12.7715 3.01027 12.5966 3.06776 12.446 3.16797L6.697 6.99997H4C2.897 6.99997 2 7.89697 2 8.99997V15C2 16.103 2.897 17 4 17ZM4 8.99997H7C7.033 8.99997 7.061 8.98397 7.093 8.98097C7.22601 8.96741 7.35509 8.928 7.473 8.86497C7.499 8.84997 7.53 8.84797 7.555 8.83197L12 5.86797V18.132L7.555 15.168C7.53 15.151 7.499 15.148 7.473 15.135C7.35491 15.0707 7.22491 15.0312 7.091 15.019C7.059 15.016 7.032 15 7 15H4V8.99997Z" fill="white"/></svg>`,
116
118
  'bx-volume-mute': `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M21.707 20.293L19.684 18.27C21.1845 16.5263 22.0063 14.3003 21.999 12C21.999 7.90897 19.527 4.54697 16 2.99997V4.99997C18.387 6.38597 19.999 9.04697 19.999 12C19.9946 13.7765 19.4072 15.5025 18.327 16.913L17.042 15.628C17.644 14.536 18 13.19 18 12C18 10.229 17.225 8.09997 16 6.99997V14.586L14 12.586V3.99997C13.9999 3.81908 13.9508 3.64161 13.8578 3.48645C13.7648 3.3313 13.6315 3.20426 13.472 3.11889C13.3125 3.03351 13.1329 2.99298 12.9522 3.00163C12.7715 3.01027 12.5966 3.06776 12.446 3.16797L7.727 6.31297L3.707 2.29297L2.293 3.70697L20.293 21.707L21.707 20.293ZM12 5.86797V10.586L9.169 7.75497L12 5.86797ZM4 17H6.697L12.445 20.832C12.5958 20.9321 12.7708 20.9896 12.9516 20.9984C13.1324 21.0072 13.3122 20.967 13.472 20.882C13.6316 20.7965 13.765 20.6693 13.858 20.514C13.951 20.3587 14.0001 20.181 14 20V18.121L12 16.121V18.132L7.555 15.168C7.53 15.151 7.499 15.148 7.473 15.135C7.35491 15.0707 7.22491 15.0312 7.091 15.019C7.059 15.016 7.032 15 7 15H4V8.99997H4.879L3.102 7.22297C2.77189 7.38821 2.4941 7.6418 2.29954 7.95553C2.10499 8.26926 2.00129 8.63081 2 8.99997V15C2 16.103 2.897 17 4 17Z" fill="white"/></svg>`,
117
119
  'bx-wind': `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13 5.5C13 3.57 11.43 2 9.5 2C7.466 2 6.25 3.525 6.25 5H8.25C8.25 4.585 8.638 4 9.5 4C10.327 4 11 4.673 11 5.5C11 6.327 10.327 7 9.5 7H2V9H9.5C11.43 9 13 7.43 13 5.5ZM15.5 15H8V17H15.5C16.327 17 17 17.673 17 18.5C17 19.327 16.327 20 15.5 20C14.638 20 14.25 19.415 14.25 19H12.25C12.25 20.475 13.466 22 15.5 22C17.43 22 19 20.43 19 18.5C19 16.57 17.43 15 15.5 15Z" fill="white"/><path d="M18 5C15.794 5 14 6.794 14 9H16C16 7.897 16.897 7 18 7C19.103 7 20 7.897 20 9C20 10.103 19.103 11 18 11H2V13H18C20.206 13 22 11.206 22 9C22 6.794 20.206 5 18 5ZM2 15H6V17H2V15Z" fill="white"/></svg>`,
120
+ 'calendar-month-outline': `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7 11H9V13H7V11ZM21 5V19C21 20.11 20.11 21 19 21H5C3.89 21 3 20.1 3 19V5C3 3.9 3.9 3 5 3H6V1H8V3H16V1H18V3H19C20.11 3 21 3.9 21 5ZM5 7H19V5H5V7ZM19 19V9H5V19H19ZM15 13V11H17V13H15ZM11 13V11H13V13H11ZM7 15H9V17H7V15ZM15 17V15H17V17H15ZM11 17V15H13V17H11Z" fill="white"/></svg>`,
118
121
  }
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M7 11H9V13H7V11ZM21 5V19C21 20.11 20.11 21 19 21H5C3.89 21 3 20.1 3 19V5C3 3.9 3.9 3 5 3H6V1H8V3H16V1H18V3H19C20.11 3 21 3.9 21 5ZM5 7H19V5H5V7ZM19 19V9H5V19H19ZM15 13V11H17V13H15ZM11 13V11H13V13H11ZM7 15H9V17H7V15ZM15 17V15H17V17H15ZM11 17V15H13V17H11Z" fill="white"/>
3
+ </svg>
@@ -0,0 +1,1006 @@
1
+ <template>
2
+ <div
3
+ ref="root"
4
+ :data-c-datepicker-id="datePickerId"
5
+ class="c-datepicker"
6
+ :style="`--transition: ${transition}ms; --width: ${width}; --picker-content-indent-y: ${pickerContentIndentY}px`"
7
+ >
8
+ <div
9
+ ref="inpWrap"
10
+ :class="['c-datepicker__inp_wrap', { open: isOpenPicker }]"
11
+ @click="setIsOpen(!isOpenPicker)"
12
+ >
13
+ <CInput
14
+ class="c-datepicker__inp"
15
+ size="sm"
16
+ :width="width"
17
+ :placeholder="activeDate.toLocaleString(DateTime.DATE_FULL)"
18
+ :disabled="true"
19
+ >
20
+ <template #customIcon>
21
+ <CIcon :name="{'calendar-month-outline': true}" :size="24" />
22
+ </template>
23
+ </CInput>
24
+ </div>
25
+
26
+
27
+ <div
28
+ ref="time"
29
+ :class="['c-datepicker__hours_wrap', { open: isOpenTime }]"
30
+ >
31
+ <div
32
+ class="c-datepicker__hours_inps"
33
+ @click="isOpenTime = true"
34
+ >
35
+ <input
36
+ type="text"
37
+ name="hours"
38
+ v-model="modelHour"
39
+ @input="setTime('hour', ($event.target as HTMLInputElement).value)"
40
+ @focus="isOpenTime = true, isOpenPicker = false"
41
+ @blur="dispatchEmit"
42
+ />
43
+ <span>:</span>
44
+ <input
45
+ type="text"
46
+ name="minute"
47
+ v-model="modelMinute"
48
+ @input="setTime('minute', ($event.target as HTMLInputElement).value)"
49
+ @focus="isOpenTime = true"
50
+ @blur="isOpenTime = false"
51
+ />
52
+ </div>
53
+ <Teleport to="body">
54
+ <div
55
+ class="c-datepicker__hours_list-wrap"
56
+ ref="timeContent"
57
+ :data-c-datepicker-id="datePickerId"
58
+ :style="timeStyle"
59
+ >
60
+ <div
61
+ :style="`--transition: ${transition}ms; --picker-time-indent-y: ${pickerContentIndentY}px`"
62
+ :class="['c-datepicker__hours_list', pickerContentPosition, {open: isOpenTime}]"
63
+ >
64
+ <div class="c-datepicker__hours_list-content">
65
+ <CScroll class="c-datepicker__hours_list-scroll">
66
+ <button
67
+ v-for="item in hoursOptions"
68
+ :key="item.value"
69
+ :class="['c-datepicker__hours_btn', {active: item.text === modelHour}]"
70
+ @click="setTime('hour', item.text)"
71
+ >
72
+ {{ item.text }}
73
+ </button>
74
+ </CScroll>
75
+ <div class="c-datepicker__hours_list-line"></div>
76
+ <CScroll class="c-datepicker__hours_list-scroll">
77
+ <button
78
+ v-for="item in minuteOptions"
79
+ :key="item.value"
80
+ :class="['c-datepicker__hours_btn', {active: item.text === modelMinute}]"
81
+ @click="setTime('minute', item.text)"
82
+ >
83
+ {{ item.text }}
84
+ </button>
85
+ </CScroll>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </Teleport>
90
+ </div>
91
+
92
+ <Teleport to="body">
93
+ <div
94
+ :style="pickerContentStyle"
95
+ :class="['c-datepicker__content_wrap', { open: isOpenPicker }]"
96
+ >
97
+ <div
98
+ ref="pickerContent"
99
+ :data-c-datepicker-id="datePickerId"
100
+ :class="['c-datepicker__content', pickerContentPosition, {open: isOpenPicker}]"
101
+ :style="`--transition: ${transition}ms; --width: ${width}; --picker-content-indent-y: ${pickerContentIndentY}px`"
102
+ >
103
+ <div class="c-datepicker__selects">
104
+ <CSelect
105
+ class="c-datepicker__select"
106
+ ref="monthSelect"
107
+ size="sm"
108
+ :options="monthsOption"
109
+ :transform-val="true"
110
+ :placeholder="monthsOption[activeMonth - 1].text"
111
+ :locale="locale"
112
+ :iconPosition="['right']"
113
+ width="100px"
114
+ :model-value="modelYear"
115
+ @change_cselect="setSelect('month', $event[0])"
116
+ />
117
+ <CSelect
118
+ class="c-datepicker__select"
119
+ ref="yearSelect"
120
+ size="sm"
121
+ :options="yearOption"
122
+ :transform-val="true"
123
+ :placeholder="String(activeYear)"
124
+ :locale="locale"
125
+ :iconPosition="['right']"
126
+ width="70px"
127
+ :model-value="modelYear"
128
+ @change_cselect="setSelect('year', $event[0])"
129
+ />
130
+ </div>
131
+ <div class="c-datepicker__table">
132
+ <div v-for="day in text.daysOfWeek" :key="day" class="c-datepicker__table_item c-datepicker__table_name">
133
+ {{ day }}
134
+ </div>
135
+
136
+ <button
137
+ v-for="day in monthDays"
138
+ :key="day.ISO"
139
+ class="c-datepicker__table_item"
140
+ :class="{
141
+ disable: isDisableDay(day.ISO),
142
+ active: isActiveDay(day.ISO),
143
+ }"
144
+
145
+ @click="setActiveDay(day.ISO)"
146
+ >
147
+ <span>{{ day.value }}</span>
148
+ </button>
149
+ </div>
150
+ <div class="c-datepicker__action">
151
+ <CButton
152
+ class="c-datepicker__action_btn"
153
+ variant="outlined"
154
+ size="md"
155
+ @click="setIsOpen(false)"
156
+ >
157
+ {{ text.cancel }}
158
+ </CButton>
159
+ <CButton
160
+ class="c-datepicker__action_btn"
161
+ size="md"
162
+ @click="dispatchEmit()"
163
+ >
164
+ {{ text.accept }}
165
+ </CButton>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ </Teleport>
170
+ </div>
171
+ </template>
172
+
173
+ <script setup lang="ts">
174
+ import { DateTime } from 'luxon'
175
+
176
+ import CIcon from './CIcons/CIcon.vue';
177
+ import CSelect from './CSelect.vue';
178
+ import CInput from './CInput.vue';
179
+ import CButton from './CButton.vue';
180
+ import CScroll from './CScroll.vue';
181
+
182
+ import { addTracingElement } from '../assets/js/helpers';
183
+ import { computed, ref, watch, onMounted, onUnmounted, Teleport } from 'vue';
184
+
185
+ type TProps = {
186
+ date?: string,
187
+ locale?: 'rus' | 'usa' | 'tur' | 'spa',
188
+ max?: string,
189
+ min?: string,
190
+ width?: string,
191
+ isOpen?: boolean,
192
+ modelValue?: any,
193
+ }
194
+
195
+ const props = withDefaults(defineProps<TProps>(), {
196
+ locale: 'rus',
197
+ isOpen: false,
198
+ width: '100%',
199
+ modelValue: () => [],
200
+ });
201
+
202
+ const defaultDate = DateTime.now()
203
+
204
+ type TMonthDay = {
205
+ ISO: string
206
+ value: number
207
+ }
208
+
209
+ type TSelectOption = {
210
+ text: string,
211
+ value: number,
212
+ }
213
+
214
+ const transition = 200;
215
+ const pickerContentIndentY = 12;
216
+ const datePickerId = Math.random() * 100_000_000
217
+
218
+ let monthDays = ref<TMonthDay[]>([]);
219
+
220
+ let root = ref();
221
+ let inpWrap = ref();
222
+ let pickerContent = ref();
223
+ let monthSelect = ref();
224
+ let yearSelect = ref();
225
+ let time = ref();
226
+ let timeContent = ref();
227
+
228
+ let isOpenTime = ref(false);
229
+ let isOpenPicker = ref(props.isOpen || false);
230
+ let pickerContentPosition = ref<'bottom' | 'top'>('bottom');
231
+
232
+ let oldDateISO = ref<any>(defaultDate.toISO())
233
+ let activeDate = ref<any>(defaultDate);
234
+ let activeMonth = ref<number>(defaultDate.month);
235
+ let activeYear = ref<number>(defaultDate.year);
236
+
237
+ let modelMonth: any = ref([]);
238
+ let modelYear: any = ref([]);
239
+ let modelHour = ref('00');
240
+ let modelMinute = ref('00');
241
+
242
+ let pickerContentStyle = ref({
243
+ left: '0px',
244
+ top: '0px',
245
+ width: '210px',
246
+ height: '38px'
247
+ });
248
+
249
+ let timeStyle = ref({
250
+ left: '0px',
251
+ top: '0px',
252
+ width: '50px',
253
+ height: '34px'
254
+ });
255
+
256
+ const TracingElement = addTracingElement(root, handlePicker);
257
+ const TracingTime = addTracingElement(time, handleTime);
258
+
259
+ onMounted(() => {
260
+ if (props.date) {
261
+ if (typeof props.date === 'string') {
262
+ activeDate.value = DateTime.fromISO(props.date)
263
+ }
264
+
265
+ if (!activeDate.value?.isValid) {
266
+ activeDate.value = defaultDate
267
+ }
268
+ }
269
+
270
+ oldDateISO.value = activeDate.value;
271
+
272
+ const currentDate = activeDate.value.toISO();
273
+
274
+ activeMonth.value = activeDate.value.month;
275
+ activeYear.value = activeDate.value.year;
276
+
277
+ fillMonthDays(currentDate);
278
+
279
+ modelHour.value = activeDate.value.toFormat('HH');
280
+ modelMinute.value = activeDate.value.toFormat('mm');
281
+
282
+ TracingElement.addListeners();
283
+ TracingTime.addListeners();
284
+ document.body.addEventListener('click', handleClickPicker);
285
+ document.body.addEventListener('click', handleClickTime);
286
+ });
287
+
288
+ onUnmounted(() => {
289
+ TracingElement.removeListeners();
290
+ TracingTime.removeListeners();
291
+ document.body.removeEventListener('click', handleClickPicker)
292
+ document.body.removeEventListener('click', handleClickTime);
293
+ });
294
+
295
+ const emit = defineEmits<{
296
+ (name: 'update:modelValue', value: string),
297
+ (name: 'change_cdatepicker', value: string),
298
+ (name: 'update:isOpen', value: boolean)
299
+ }>();
300
+
301
+ const text = computed(() => {
302
+ const lang = {
303
+ rus: {
304
+ daysOfWeek: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'],
305
+ months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
306
+ cancel: 'Отмена',
307
+ accept: 'OK',
308
+ },
309
+
310
+ usa: {
311
+ daysOfWeek: [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ],
312
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
313
+ cancel: 'Cancel',
314
+ accept: 'OK',
315
+ },
316
+
317
+ tur: {
318
+ daysOfWeek: [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ],
319
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
320
+ cancel: 'Cancel',
321
+ accept: 'OK',
322
+ },
323
+
324
+ spa: {
325
+ daysOfWeek: [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ],
326
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
327
+ cancel: 'Cancel',
328
+ accept: 'OK',
329
+ },
330
+ };
331
+
332
+ return lang[props.locale]
333
+ })
334
+
335
+ const monthsOption = computed(() => {
336
+ let options: TSelectOption[] = [];
337
+
338
+ text.value.months.forEach((item, index) => {
339
+ options.push({
340
+ text: item,
341
+ value: index + 1,
342
+ });
343
+ });
344
+
345
+ return options;
346
+ });
347
+
348
+ const yearOption = computed(() => {
349
+ let options: TSelectOption[] = [];
350
+
351
+ const range = 10;
352
+ const minYear = activeDate.value.year - range;
353
+ const maxYear = activeDate.value.year + range;
354
+
355
+ for(let i = minYear; i < maxYear; i++) {
356
+ options.push({
357
+ text: String(i),
358
+ value: i,
359
+ })
360
+ }
361
+
362
+ return options;
363
+ });
364
+
365
+ const hoursOptions = computed(() => {
366
+ let options: TSelectOption[] = [];
367
+
368
+ const max = 24;
369
+
370
+ for (let i = 0; i < max; i++) {
371
+ options.push({
372
+ text: getTimeStr(i),
373
+ value: i,
374
+ })
375
+ }
376
+
377
+ return options;
378
+ });
379
+
380
+ const minuteOptions = computed(() => {
381
+ let options: TSelectOption[] = [];
382
+
383
+ const max = 60;
384
+
385
+ for (let i = 0; i < max; i++) {
386
+ options.push({
387
+ text: getTimeStr(i),
388
+ value: i,
389
+ })
390
+ }
391
+
392
+ return options;
393
+ });
394
+
395
+ watch(() => isOpenPicker.value, (newValue) => {
396
+ emit('update:isOpen', newValue);
397
+ })
398
+
399
+ watch(() => activeDate.value, (newValue) => {
400
+ const value = typeof newValue === 'string' ? newValue : newValue.toISO();
401
+
402
+ emit('update:modelValue', value);
403
+ })
404
+
405
+ function handlePicker(positionInfo?: any) {
406
+ pickerContentStyle.value.left = `${positionInfo.position.x}px`;
407
+ pickerContentStyle.value.top = `${positionInfo.position.y}px`;
408
+ pickerContentStyle.value.width = `${positionInfo.position.width}px`;
409
+ pickerContentStyle.value.height = `${positionInfo.position.height}px`;
410
+ setPickerContentPosition();
411
+
412
+ if (positionInfo.isVisible) return;
413
+
414
+ setIsOpen(false);
415
+ }
416
+
417
+ function handleClickPicker(evt: MouseEvent) {
418
+ const target = (evt.target as HTMLElement);
419
+ const classesArr = [
420
+ '.c-datepicker__inp_wrap',
421
+ '.c-datepicker__content',
422
+ `[data-c-select-id="${monthSelect.value.selectId}"]`,
423
+ `[data-c-select-id="${yearSelect.value.selectId}"]`,
424
+ ];
425
+ const classesStr = classesArr.join(',');
426
+ const isTarget: HTMLElement | null = target.closest(classesStr);
427
+
428
+ if (isCurrent(isTarget)) return;
429
+
430
+ setIsOpen(false);
431
+ }
432
+
433
+ function isCurrent(target: HTMLElement | null) {
434
+ const isMatchMonthSelectId = String(monthSelect.value.selectId) === target?.dataset?.cSelectId;
435
+ const isMatchYearSelectId = String(yearSelect.value.selectId) === target?.dataset?.cSelectId;
436
+
437
+ const isMatchInpWrap = inpWrap.value === target;
438
+ const isMatchPickerContent = pickerContent.value === target;
439
+
440
+ return isMatchMonthSelectId || isMatchYearSelectId || isMatchInpWrap || isMatchPickerContent;
441
+ }
442
+
443
+ function handleClickTime(evt: MouseEvent) {
444
+ const target = (evt.target as HTMLElement);
445
+ const classesStr = '.c-datepicker__hours_wrap, .c-datepicker__hours_list-wrap';
446
+ const isTimeContent = target.closest(classesStr);
447
+
448
+ const isTarget = isTimeContent === time.value;
449
+ const isContent = isTimeContent === timeContent.value;
450
+
451
+ if (isTarget || isContent) return;
452
+
453
+ isOpenTime.value = false;
454
+ }
455
+
456
+ function handleTime(positionInfo?: any) {
457
+ timeStyle.value.left = `${positionInfo.position.x}px`;
458
+ timeStyle.value.top = `${positionInfo.position.y}px`;
459
+ timeStyle.value.width = `${positionInfo.position.width}px`;
460
+ timeStyle.value.height = `${positionInfo.position.height}px`;
461
+
462
+ if (positionInfo.isVisible) return;
463
+
464
+ isOpenTime.value = false;
465
+ }
466
+
467
+ function setPickerContentPosition() {
468
+ const windowHeight = window.innerHeight;
469
+ const inputHeight = root.value.clientHeight;
470
+ const listClientRect = pickerContent.value.getBoundingClientRect()
471
+ const isTop = pickerContentPosition.value === 'top'
472
+
473
+ const indentTop = (listClientRect.height + inputHeight) * (isTop ? 1 : 0) + pickerContentIndentY
474
+ const indent = windowHeight - listClientRect.y - indentTop
475
+
476
+ pickerContentPosition.value = indent > listClientRect.height ? 'bottom' : 'top'
477
+ }
478
+
479
+ function fillMonthDays(ISO: string | null) {
480
+ if (typeof ISO !== 'string') return
481
+
482
+ if (monthDays.value.length < 1) {
483
+ const currentDaysDate = DateTime.fromISO(ISO).startOf('month').startOf('week');
484
+ const monthDayObj: TMonthDay = {
485
+ ISO: (currentDaysDate.toISO() as string),
486
+ value: currentDaysDate.day
487
+ };
488
+ monthDays.value.push(monthDayObj);
489
+
490
+ return fillMonthDays(currentDaysDate.toISO());
491
+ }
492
+
493
+ const lastWeekDate = DateTime.fromISO(ISO).set({ month: activeMonth.value, year: activeYear.value }).endOf('month', {}).endOf('week', {});
494
+ // добавляем по дню до тех пор пока не дойдем до последней даты
495
+ const currentDate = DateTime.fromISO(ISO).plus({ day: 1 });
496
+
497
+ const monthDayObj: TMonthDay = {
498
+ ISO: (currentDate.toISO() as string),
499
+ value: currentDate.day,
500
+ };
501
+ monthDays.value.push(monthDayObj);
502
+
503
+ const isMismatchYear = lastWeekDate.year !== currentDate.year;
504
+ const isMismatchMonth = lastWeekDate.month !== currentDate.month;
505
+ const isMismatchDay = lastWeekDate.day !== currentDate.day;
506
+
507
+ // проверка на совпадение последней даты
508
+ if (isMismatchYear || isMismatchMonth || isMismatchDay) fillMonthDays(currentDate.toISO());
509
+ }
510
+
511
+ function isDisableDay(ISO: string): boolean {
512
+ if (!props.min && !props.max) return false;
513
+
514
+ const currentDate = DateTime.fromISO(ISO);
515
+ const maxDate = DateTime.fromISO(props.max as string);
516
+ const minDate = DateTime.fromISO(props.min as string);
517
+
518
+ const isMoreMax = maxDate.ts < currentDate.ts;
519
+ const isLessMin = minDate.ts > currentDate.ts;
520
+
521
+ return isMoreMax || isLessMin;
522
+ }
523
+
524
+ function setTime(name: 'hour' | 'minute', value: string) {
525
+ const numValue = value.replace(/[^0-9]/g, '');
526
+
527
+ let min = 0;
528
+ let max = 59;
529
+
530
+ if (name === 'hour') {
531
+ max = 23;
532
+ }
533
+ if (name === 'minute') {
534
+ max = 59;
535
+ }
536
+
537
+ const validValue = Math.min(Math.max(min, +numValue), max);
538
+ const strValue = getTimeStr(validValue);
539
+
540
+ if (name === 'hour') modelHour.value = strValue
541
+ else modelMinute.value = strValue
542
+
543
+ const currentDate = activeDate.value.toISO();
544
+
545
+ activeDate.value = DateTime.fromISO(currentDate).set({ hour: +modelHour.value, minute: +modelMinute.value });
546
+
547
+ dispatchEmit()
548
+ }
549
+
550
+ function getTimeStr(value: number) {
551
+ return `0${value}`.match(/.{2}$/)?.[0] || '00'
552
+ }
553
+
554
+ function setIsOpen(value: boolean) {
555
+ isOpenPicker.value = value;
556
+ setOldDate(value);
557
+ }
558
+
559
+ function setOldDate(value: boolean) {
560
+ if (value) {
561
+ oldDateISO.value = activeDate.value.toISO()
562
+ } else {
563
+ activeDate.value = DateTime.fromISO(oldDateISO.value);
564
+ activeMonth.value = activeDate.value.month;
565
+ activeYear.value = activeDate.value.year;
566
+
567
+ setSelect('month', activeMonth.value);
568
+ setSelect('year', activeYear.value);
569
+ }
570
+ }
571
+
572
+ function dispatchEmit() {
573
+ const currentDateISO = activeDate.value.toISO();
574
+ setIsOpen(false);
575
+
576
+ oldDateISO.value = currentDateISO;
577
+ activeDate.value = DateTime.fromISO(currentDateISO);
578
+ emit('change_cdatepicker', currentDateISO);
579
+ }
580
+
581
+ function setActiveDay(ISO: string) {
582
+ if (isDisableDay(ISO)) return;
583
+
584
+ const currentDate = DateTime.fromISO(ISO);
585
+
586
+ const currentActiveDate = DateTime.fromISO(activeDate.value.toISO()).set({
587
+ year: currentDate.year,
588
+ month: currentDate.month,
589
+ day: currentDate.day,
590
+ });
591
+
592
+ activeDate.value = currentActiveDate;
593
+ }
594
+
595
+ function setSelect(dateName: 'month' | 'year', value: number) {
596
+ if (!value) return;
597
+
598
+ const currentISO = activeDate.value.toISO();
599
+
600
+ if (dateName === 'month') {
601
+ activeMonth.value = value;
602
+ modelMonth.value[0] = monthsOption.value[value - 1]
603
+ }
604
+
605
+ if (dateName === 'year') {
606
+ activeYear.value = value;
607
+ modelYear.value[0] = {
608
+ value,
609
+ text: String(value)
610
+ }
611
+ }
612
+
613
+ activeDate.value = DateTime.fromISO(currentISO).set({ month: activeMonth.value, year: activeYear.value })
614
+ const currentDate = activeDate.value.toISO();
615
+
616
+ monthDays.value = [];
617
+ fillMonthDays(currentDate);
618
+ }
619
+
620
+ function isActiveDay(ISO: string): boolean {
621
+ const currentDate = DateTime.fromISO(ISO);
622
+
623
+ return activeDate.value.year === currentDate.year && activeDate.value.month === currentDate.month && activeDate.value.day === currentDate.day
624
+ }
625
+
626
+ defineExpose({
627
+ datePickerId
628
+ })
629
+
630
+ </script>
631
+
632
+ <style lang="scss">
633
+
634
+ $timeWidth: 60px;
635
+
636
+ .c-datepicker {
637
+ display: flex;
638
+ align-items: center;
639
+
640
+ gap: 15px;
641
+
642
+ &__inp {
643
+ &_wrap {
644
+ position: relative;
645
+
646
+ cursor: pointer;
647
+
648
+ &.open,
649
+ &:hover {
650
+ .c-input {
651
+ border: 1px solid var(--green-light);
652
+ }
653
+
654
+ .c-input__custom-icon svg path {
655
+ fill: var(--green-light);
656
+ }
657
+ }
658
+
659
+ &::after {
660
+ content: '';
661
+
662
+ position: absolute;
663
+ left: 0;
664
+ right: 0;
665
+ top: 0;
666
+ bottom: 0;
667
+
668
+ display: flex;
669
+ }
670
+
671
+ .c-input__wrap {
672
+ opacity: 1;
673
+ }
674
+ }
675
+
676
+ .c-input__custom-icon {
677
+ opacity: 1;
678
+
679
+ svg path {
680
+ transition: var(--transition);
681
+ }
682
+ }
683
+ }
684
+
685
+ &__hours {
686
+
687
+ &_inps {
688
+ display: flex;
689
+ justify-content: center;
690
+ align-items: center;
691
+
692
+ font-size: 14px;
693
+
694
+ color: var(--white);
695
+
696
+ input {
697
+ width: 16px;
698
+ padding: 0;
699
+
700
+ outline: none;
701
+ border: none;
702
+
703
+ color: inherit;
704
+ background: transparent;
705
+
706
+ -moz-appearance: textfield;
707
+
708
+ &::-webkit-outer-spin-button,
709
+ &::-webkit-inner-spin-button {
710
+ -webkit-appearance: none;
711
+ margin: 0;
712
+ }
713
+ }
714
+ }
715
+
716
+ &_wrap {
717
+ box-sizing: border-box;
718
+
719
+ display: flex;
720
+ justify-content: center;
721
+
722
+ width: $timeWidth;
723
+ height: 32px;
724
+
725
+ border: 1px solid var(--white);
726
+ border-radius: 5px;
727
+
728
+ background: var(--black-dark);
729
+
730
+ transition: var(--transition);
731
+
732
+ cursor: pointer;
733
+
734
+ &.open,
735
+ &:hover {
736
+ border: 1px solid var(--green-light);
737
+ }
738
+ }
739
+ }
740
+ }
741
+
742
+ .c-datepicker__hours_list-wrap {
743
+ --max-height: 112px;
744
+ --scroll-height: 100px;
745
+
746
+ position: fixed;
747
+ z-index: 999;
748
+ transition: var(--transition);
749
+ pointer-events: none;
750
+
751
+ .c-datepicker {
752
+ &__hours {
753
+ &_list-content {
754
+ box-sizing: border-box;
755
+
756
+ display: flex;
757
+
758
+ max-height: var(--scroll-height);
759
+ }
760
+
761
+ &_list {
762
+ position: absolute;
763
+ left: 0%;
764
+ transform: translate(0px, 0px);
765
+ z-index: 20;
766
+
767
+ box-sizing: border-box;
768
+
769
+ width: $timeWidth;
770
+ max-height: var(--max-height);
771
+ padding: 6px;
772
+
773
+ border: 1px solid var(--green-dark);
774
+ border-radius: 5px;
775
+
776
+ background: var(--black-dark);
777
+ box-shadow: var(--shadow);
778
+
779
+ transition: var(--transition);
780
+
781
+ opacity: 0;
782
+ pointer-events: none;
783
+
784
+ &.bottom {
785
+ --indent-y: var(--picker-time-indent-y);
786
+
787
+ top: 100%;
788
+ }
789
+
790
+ &.top {
791
+ --indent-y: calc(var(--picker-time-indent-y) * -1);
792
+
793
+ bottom: 100%;
794
+ }
795
+
796
+ &.open {
797
+ transform: translate(0, var(--indent-y));
798
+
799
+ opacity: 1;
800
+ pointer-events: all;
801
+ }
802
+ }
803
+
804
+ &_list-scroll {
805
+ .c-scroll__content {
806
+ background: transparent;
807
+ }
808
+
809
+ .ps__rail-x,
810
+ .ps__rail-y {
811
+ display: none;
812
+ }
813
+ }
814
+
815
+ &_list-line {
816
+ display: flex;
817
+
818
+ width: 1px;
819
+ height: var(--scroll-height);
820
+
821
+ margin: 0 3px;
822
+
823
+ background: var(--green-dark);
824
+ }
825
+
826
+ &_btn {
827
+ display: flex;
828
+ justify-content: center;
829
+ align-items: center;
830
+
831
+ width: 20px;
832
+ height: 20px;
833
+
834
+ font-size: 14px;
835
+
836
+ border: none;
837
+ outline: none;
838
+
839
+ background: transparent;
840
+ color: var(--white);
841
+
842
+ transition: var(--transition);
843
+
844
+ cursor: pointer;
845
+
846
+ &.active,
847
+ &:hover {
848
+ color: var(--green-light);
849
+ }
850
+ }
851
+ }
852
+ }
853
+ }
854
+
855
+ .c-datepicker__content_wrap {
856
+ position: fixed;
857
+ z-index: 999;
858
+ transition: var(--transition);
859
+ pointer-events: none;
860
+
861
+ .c-datepicker {
862
+ &__selects {
863
+ display: flex;
864
+ justify-content: space-between;
865
+
866
+ margin-bottom: 10px;
867
+ }
868
+
869
+ &__select {
870
+ .c-input {
871
+ padding: 5px;
872
+ padding-right: 30px !important;
873
+
874
+ border: none !important;
875
+ background: transparent;
876
+ }
877
+
878
+ .c-input__custom-icon {
879
+ right: 7px !important;
880
+
881
+ .c-select__inp_icon {
882
+ --svg-size: 18px !important;
883
+ }
884
+ }
885
+ }
886
+
887
+ &__content {
888
+ box-sizing: border-box;
889
+
890
+ position: absolute;
891
+ left: 50%;
892
+ transform: translate(-50%, 0px);
893
+ z-index: 20;
894
+
895
+ width: 100%;
896
+ min-width: 295px;
897
+ padding: 15px;
898
+
899
+ border: 1px solid var(--green-medium);
900
+ border-radius: 5px;
901
+
902
+ color: var(--white);
903
+ background: var(--black-dark);
904
+
905
+ pointer-events: none;
906
+ opacity: 0;
907
+
908
+ transition: var(--transition);
909
+
910
+ &.bottom {
911
+ --indent-y: var(--picker-content-indent-y);
912
+
913
+ top: 100%;
914
+ }
915
+
916
+ &.top {
917
+ --indent-y: calc(var(--picker-content-indent-y) * -1);
918
+
919
+ bottom: 100%;
920
+ }
921
+
922
+ &.open {
923
+ transform: translate(-50%, var(--indent-y));
924
+
925
+ opacity: 1;
926
+ pointer-events: all;
927
+ }
928
+
929
+ .c-input__custom-icon {
930
+ pointer-events: none;
931
+ }
932
+ }
933
+
934
+ &__table {
935
+ --item-size: 36px;
936
+
937
+ display: grid;
938
+ grid-template-columns: repeat(7, 36px);
939
+
940
+ gap: 2px;
941
+
942
+ font-size: 14px;
943
+
944
+ &_item {
945
+ width: var(--item-size);
946
+ height: var(--item-size);
947
+
948
+ display: flex;
949
+ align-items: center;
950
+ justify-content: center;
951
+
952
+ padding: 0;
953
+ border: none;
954
+
955
+ background: transparent;
956
+ color: var(--white);
957
+
958
+ cursor: pointer;
959
+
960
+ &.disable {
961
+ opacity: 0.5;
962
+ pointer-events: none;
963
+ }
964
+
965
+ &.active span {
966
+ background: var(--green-light);
967
+ }
968
+
969
+ span {
970
+ width: 26px;
971
+ height: 26px;
972
+
973
+ display: flex;
974
+ justify-content: center;
975
+ align-items: center;
976
+
977
+ border-radius: 6px;
978
+ }
979
+ }
980
+
981
+ &_name {
982
+ display: flex;
983
+
984
+ font-weight: 500;
985
+
986
+ cursor: auto;
987
+ }
988
+ }
989
+
990
+ &__action {
991
+ display: flex;
992
+ justify-content: center;
993
+ gap: 12px;
994
+
995
+ margin: 10px auto 0;
996
+
997
+ &_btn {
998
+ width: 84px;
999
+ padding-left: 0;
1000
+ padding-right: 0;
1001
+ }
1002
+ }
1003
+ }
1004
+ }
1005
+
1006
+ </style>
@@ -1,8 +1,9 @@
1
1
  <template>
2
2
  <div
3
3
  ref="root"
4
+ :data-c-select-id="selectId"
4
5
  :style="`--transition: ${transition}ms; --width: ${width}; --list-indent-y: ${listIndentY}px`"
5
- :class="[classes.root, variant, size, { disabled }]"
6
+ :class="[classes.root, variant, size, { disabled, open: isOpen }]"
6
7
  >
7
8
  <CInput
8
9
  :placeholder="activePlaceholder"
@@ -21,6 +22,7 @@
21
22
  </CInput>
22
23
  <Teleport to="body">
23
24
  <div
25
+ :data-c-select-id="selectId"
24
26
  :style="listStyle"
25
27
  :class="[classes.fixedContainer, variant, size, { open: isOpen, disabled }]"
26
28
  >
@@ -128,7 +130,8 @@ const emit = defineEmits<{
128
130
  }>();
129
131
 
130
132
  const transition = 200,
131
- listIndentY = 12
133
+ listIndentY = 12,
134
+ selectId = Math.random() * 100_000_000
132
135
 
133
136
  let findValue = ref(''),
134
137
  isOpen = ref(props.isOpen || false),
@@ -273,6 +276,7 @@ const handleOption: TDebounceEvent = function(option, evt) {
273
276
  setOption(option, evt);
274
277
 
275
278
  if (!isMultiple.value) dispatchEmit()
279
+ setActivePlaceholder()
276
280
  }
277
281
 
278
282
  const setOption: TDebounceEvent = function(option, evt) {
@@ -363,14 +367,15 @@ function setListPosition() {
363
367
 
364
368
  function handleClick(evt: MouseEvent) {
365
369
  const target = (evt.target as HTMLElement);
366
- const classesStr = `.${classes.input},.c-input__custom-icon${isMultiple.value ? ',.c-input__custom-icon,.c-select__list_btn' : ''}`
370
+ const classesStr = `.${classes.input},.c-input__custom-icon${isMultiple.value ? ',.c-input__custom-icon,.c-select__list' : ''}`
367
371
  const currentRoot = target.closest(`.${classes.root}`)
368
372
  const domElements = target.closest(classesStr)
369
373
 
370
374
  const isRoot = currentRoot === root.value
375
+ const isList = domElements === list.value.$el
371
376
  const isToggle = target.closest('.c-input__custom-icon')
372
377
 
373
- if (isRoot && domElements) {
378
+ if ((isRoot || isList) && domElements) {
374
379
  setListPosition()
375
380
  isOpen.value = isToggle ? !isOpen.value : true
376
381
  emit('update:isOpen', isOpen.value)
@@ -397,6 +402,10 @@ function dispatchEmit() {
397
402
  }, transition);
398
403
  }
399
404
 
405
+ defineExpose({
406
+ selectId
407
+ })
408
+
400
409
  </script>
401
410
 
402
411
  <style lang="scss">
package/src/libIndex.js CHANGED
@@ -7,6 +7,7 @@ import CTooltip from './components/CTooltip.vue';
7
7
  import CPopup from './components/CPopup.vue';
8
8
  import CAlert from './components/CAlert.vue';
9
9
  import CIcon from './components/CIcons/CIcon.vue';
10
+ import CDatepicker from './components/CDatepicker.vue';
10
11
  import CScroll from './components/CScroll.vue';
11
12
 
12
13
  import { CClasses } from './assets/js/helpers';
@@ -23,6 +24,7 @@ export {
23
24
  CPopup,
24
25
  CAlert,
25
26
  CIcon,
27
+ CDatepicker,
26
28
  CScroll,
27
29
 
28
30
  CClasses,
@@ -639,6 +639,40 @@
639
639
  </div>
640
640
  <!-- ./checkboxes -->
641
641
 
642
+ <!-- CDatepicker -->
643
+ <div class="table content">
644
+ <div class="table__wrap">
645
+ <div class="table__item table__title">
646
+ CDatepicker
647
+ </div>
648
+ <div class="table">
649
+ <!-- col -->
650
+ <div class="table__col checkboxes">
651
+ <div class="table__item">
652
+ </div>
653
+ </div>
654
+ <!-- ./col -->
655
+
656
+ <!-- col -->
657
+ <div class="table__col checkboxes">
658
+ <div class="table__item">
659
+ <CDatepicker @change_cdatepicker="(e) => console.log(e)" />
660
+ </div>
661
+ </div>
662
+ <!-- ./col -->
663
+
664
+ <!-- col -->
665
+ <div class="table__col checkboxes">
666
+ <div class="table__item">
667
+ <CDatepicker @change_cdatepicker="(e) => console.log(e)" />
668
+ </div>
669
+ </div>
670
+ <!-- ./col -->
671
+ </div>
672
+ </div>
673
+ </div>
674
+ <!-- ./CDatepicker -->
675
+
642
676
  <!-- select -->
643
677
  <div style="min-height: 400px;" class="table content">
644
678
  <div class="table__wrap">
@@ -955,6 +989,7 @@ import CTooltip from '../components/CTooltip.vue';
955
989
  import CPopup from '../components/CPopup.vue';
956
990
  import CAlert from '../components/CAlert.vue';
957
991
  import CIcon from '../components/CIcons/CIcon.vue';
992
+ import CDatepicker from '../components/CDatepicker.vue';
958
993
 
959
994
  import { iconContent } from '../assets/icon/icons-manifest';
960
995
 
@@ -1089,6 +1124,7 @@ export default {
1089
1124
  CPopup,
1090
1125
  CAlert,
1091
1126
  CIcon,
1127
+ CDatepicker,
1092
1128
  },
1093
1129
  }
1094
1130
  </script>