vueless 0.0.169 → 0.0.171

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.
@@ -1,14 +1,22 @@
1
1
  import { unref } from "vue";
2
2
 
3
3
  function clickOutside(target, handler, options) {
4
- const { capture = true } = options;
4
+ const { capture = true, ignore = [] } = options;
5
5
 
6
6
  let shouldListen = true;
7
7
 
8
+ const ignoreList = unref(ignore).map((item) => unref(item));
8
9
  const el = unref(target);
9
10
 
10
11
  function onClick(event) {
11
- if (!el || el === event.target || event.composedPath().includes(el)) return;
12
+ if (
13
+ !el ||
14
+ el === event.target ||
15
+ event.composedPath().includes(el) ||
16
+ ignoreList.includes(event.target)
17
+ ) {
18
+ return;
19
+ }
12
20
 
13
21
  if (!shouldListen) {
14
22
  shouldListen = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.169",
3
+ "version": "0.0.171",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless Component Framework.",
6
6
  "homepage": "https://vueless.com",
@@ -8,7 +8,13 @@ export function useAttrs(
8
8
  { tableRows, isNesting, isShownActionsHeader, isHeaderSticky, isFooterSticky },
9
9
  ) {
10
10
  const { config, getAttrs, hasSlotContent } = useUI(defaultConfig, () => props.config);
11
- const { stickyHeaderCell, headerCell } = config.value;
11
+ const { stickyHeaderCell, headerCell, footerRow, bodyCell, headerCellGeneral } = config.value;
12
+
13
+ const cvaHeaderCellGeneral = cva({
14
+ base: headerCellGeneral.base,
15
+ variants: headerCellGeneral.variants,
16
+ compoundVariants: headerCellGeneral.compoundVariants,
17
+ });
12
18
 
13
19
  const cvaCustomHeaderItem = cva({
14
20
  base: stickyHeaderCell.base,
@@ -22,9 +28,23 @@ export function useAttrs(
22
28
  compoundVariants: headerCell.compoundVariants,
23
29
  });
24
30
 
25
- const stickyHeaderCellClasses = computed(() => cvaCustomHeaderItem({ compact: props.compact }));
31
+ const cvaFooterRow = cva({
32
+ base: footerRow.base,
33
+ variants: footerRow.variants,
34
+ compoundVariants: footerRow.compoundVariants,
35
+ });
36
+
37
+ const cvaBodyCell = cva({
38
+ base: bodyCell.base,
39
+ variants: bodyCell.variants,
40
+ compoundVariants: bodyCell.compoundVariants,
41
+ });
26
42
 
43
+ const stickyHeaderCellClasses = computed(() => cvaCustomHeaderItem({ compact: props.compact }));
27
44
  const headerCellClasses = computed(() => cvaHeaderCell({ compact: props.compact }));
45
+ const footerRowClasses = computed(() => cvaFooterRow({ compact: props.compact }));
46
+ const bodyCellClasses = computed(() => cvaBodyCell({ compact: props.compact }));
47
+ const headerCellGeneralClasses = computed(() => cvaHeaderCellGeneral({ compact: props.compact }));
28
48
 
29
49
  const wrapperAttrs = getAttrs("wrapper");
30
50
  const stickyHeaderAttrsRaw = getAttrs("stickyHeader");
@@ -53,7 +73,7 @@ export function useAttrs(
53
73
  isComponent: true,
54
74
  });
55
75
  const bodyAttrs = getAttrs("body");
56
- const bodyCellAttrsRaw = getAttrs("bodyCell", { isComponent: true });
76
+ const bodyCellAttrsRaw = getAttrs("bodyCell", { classes: bodyCellClasses });
57
77
  const bodyCheckboxAttrs = getAttrs("bodyCheckbox", { isComponent: true });
58
78
  const bodyCellDateSeparatorAttrs = getAttrs("bodyCellDateSeparator");
59
79
  const bodyDateSeparatorAttrs = getAttrs("bodyDateSeparator", { isComponent: true });
@@ -61,14 +81,13 @@ export function useAttrs(
61
81
  const bodyCellSecondaryAttrs = getAttrs("bodyCellSecondary");
62
82
  const bodyCellSecondaryEmptyAttrs = getAttrs("bodyCellSecondaryEmpty");
63
83
  const footerAttrsRaw = getAttrs("footer");
64
- const footerRowAttrs = getAttrs("footerRow");
65
- const footerCellAttrs = getAttrs("footerCell", { isComponent: true });
84
+ const footerRowAttrs = getAttrs("footerRow", { classes: footerRowClasses });
66
85
  const stickyFooterRowAttrs = getAttrs("stickyFooterRow");
67
86
  const bodyEmptyStateAttrs = getAttrs("bodyEmptyState", { isComponent: true });
68
87
  const bodyRowBeforeAttrsRaw = getAttrs("bodyRowBefore");
69
- const bodyRowBeforeCellAttrs = getAttrs("bodyRowBeforeCell", { isComponent: true });
88
+ const bodyRowBeforeCellAttrsRaw = getAttrs("bodyRowBeforeCell");
70
89
  const bodyRowAfterAttrs = getAttrs("bodyRowAfter");
71
- const bodyRowAfterCellAttrs = getAttrs("bodyRowAfterCell", { isComponent: true });
90
+ const bodyRowAfterCellAttrsRaw = getAttrs("bodyRowAfterCell");
72
91
  const bodyCellNestedWrapperAttrsRaw = getAttrs("bodyCellNestedWrapper");
73
92
 
74
93
  const bodyRowAttrsRaw = getAttrs("bodyRow");
@@ -78,6 +97,16 @@ export function useAttrs(
78
97
  class: cx([bodyCellAttrsRaw.value.class, classes || ""]),
79
98
  }));
80
99
 
100
+ const bodyRowBeforeCellAttrs = computed(() => ({
101
+ ...bodyRowBeforeAttrsRaw.value,
102
+ class: cx([bodyCellAttrs.value().class, bodyRowBeforeCellAttrsRaw.value.class]),
103
+ }));
104
+
105
+ const bodyRowAfterCellAttrs = computed(() => ({
106
+ ...bodyRowAfterCellAttrsRaw.value,
107
+ class: cx([bodyCellAttrs.value().class, bodyRowAfterCellAttrsRaw.value.class]),
108
+ }));
109
+
81
110
  const bodyRowAttrs = computed(() => (row) => ({
82
111
  ...bodyRowAttrsRaw.value,
83
112
  class: cx([bodyRowAttrsRaw.value.class, row]),
@@ -85,7 +114,7 @@ export function useAttrs(
85
114
 
86
115
  const headerCellAttrs = computed(() => (classes) => ({
87
116
  ...headerCellAttrsRaw.value,
88
- class: cx([config.value.headerCellGeneral, headerCellAttrsRaw.value.class, classes]),
117
+ class: cx([headerCellGeneralClasses.value, headerCellAttrsRaw.value.class, classes]),
89
118
  }));
90
119
 
91
120
  const stickyHeaderActionsCounterAttrs = computed(() => ({
@@ -151,7 +180,7 @@ export function useAttrs(
151
180
 
152
181
  const stickyHeaderCellAttrs = computed(() => (classes) => ({
153
182
  ...stickyHeaderCellAttrsRaw.value,
154
- class: cx([config.value.headerCellGeneral, stickyHeaderCellAttrsRaw.value.class, classes]),
183
+ class: cx([headerCellGeneralClasses.value, stickyHeaderCellAttrsRaw.value.class, classes]),
155
184
  }));
156
185
 
157
186
  const bodyRowDateSeparatorAttrs = (rowIndex) => {
@@ -188,7 +217,6 @@ export function useAttrs(
188
217
  bodyRowDateSeparatorAttrs,
189
218
  bodyCellNestedWrapperAttrs,
190
219
  bodyCellAttrs,
191
- footerCellAttrs,
192
220
  stickyHeaderCounterAttrs,
193
221
  stickyHeaderActionsCounterAttrs,
194
222
  headerCounterAttrs,
@@ -1,6 +1,13 @@
1
1
  export default /*tw*/ {
2
2
  wrapper: "relative",
3
- headerCellGeneral: "p-4 text-sm font-normal text-gray-500 text-left",
3
+ headerCellGeneral: {
4
+ base: "p-4 first:p-5 text-sm font-normal text-gray-500 text-left",
5
+ variants: {
6
+ compact: {
7
+ true: "px-4 py-3 last:px-4 last:py-3 first:px-4 first:py-3",
8
+ },
9
+ },
10
+ },
4
11
  headerCounterGeneral: "mr-3 font-medium text-sm text-gray-900",
5
12
  stickyHeader: "fixed top-0 flex items-center z-30 overflow-hidden rounded-none border",
6
13
  stickyHeaderRow: "border-gray-200 bg-white",
@@ -28,7 +35,14 @@ export default /*tw*/ {
28
35
  bodyRowAfter: "!p-0",
29
36
  bodyRowAfterCell: "py-1",
30
37
  bodyRowDateSeparator: "",
31
- bodyCell: "",
38
+ bodyCell: {
39
+ base: "p-[1.125rem] py-5 first:p-5 truncate align-top last:p-5",
40
+ variants: {
41
+ compact: {
42
+ true: "px-4 py-3 last:px-4 last:py-3 first:px-4 first:py-3",
43
+ },
44
+ },
45
+ },
32
46
  bodyCellSecondary: "mt-1 text-xs text-gray-500",
33
47
  bodyCellSecondaryEmpty: "inline-block",
34
48
  bodyCellCheckbox: "first:px-4", // try to remove first
@@ -43,8 +57,14 @@ export default /*tw*/ {
43
57
  bodyDateSeparator: "",
44
58
  bodyEmptyState: "my-8",
45
59
  footer: "group/footer border-t border-solid border-gray-200",
46
- footerRow: "",
47
- footerCell: "",
60
+ footerRow: {
61
+ base: "[&_td]:p-[1.125rem] [&_td]:py-5 first:[&_td]:p-5",
62
+ variants: {
63
+ compact: {
64
+ true: "[&_td]:p-4",
65
+ },
66
+ },
67
+ },
48
68
  stickyFooter: "relative group/footer-fixed",
49
69
  stickyFooterRow: `
50
70
  fixed bottom-0 -ml-px border border-b border-gray-200 bg-white
@@ -2,7 +2,6 @@ import { getArgTypes, getSlotNames } from "../service.storybook";
2
2
  import { getRandomId } from "../service.ui";
3
3
 
4
4
  import UTable from "../ui.data-table";
5
- import UTableCell from "../ui.data-table-cell";
6
5
  import UButton from "../ui.button";
7
6
  import ULink from "../ui.button-link";
8
7
  import UMoney from "../ui.text-money";
@@ -110,7 +109,7 @@ const EmptyTemplate = (args) => ({
110
109
  });
111
110
 
112
111
  const SlotTemplate = (args) => ({
113
- components: { UTable, UButton, UTableCell, ULink, UMoney, UBadge, URow },
112
+ components: { UTable, UButton, ULink, UMoney, UBadge, URow },
114
113
  setup() {
115
114
  return { args };
116
115
  },
@@ -166,12 +165,12 @@ stickyFooter.args = {
166
165
  stickyFooter: true,
167
166
  slotTemplate: `
168
167
  <template #footer>
169
- <UTableCell>
168
+ <td>
170
169
  <p>
171
170
  🤘🤘🤘
172
171
  Lorem ipsum dolor sit amet.
173
172
  </p>
174
- </UTableCell>
173
+ </td>
175
174
  </template>
176
175
  `,
177
176
  };
@@ -201,8 +200,8 @@ slotDefault.args = {
201
200
  `,
202
201
  };
203
202
 
204
- export const slotTheadActions = SlotTemplate.bind({});
205
- slotTheadActions.args = {
203
+ export const slotHeaderActions = SlotTemplate.bind({});
204
+ slotHeaderActions.args = {
206
205
  numberOfRows: 50,
207
206
  stickyHeader: true,
208
207
  selectable: true,
@@ -227,8 +226,8 @@ slotTheadActions.args = {
227
226
  `,
228
227
  };
229
228
 
230
- export const slotbodyRowBefore = SlotTemplate.bind({});
231
- slotbodyRowBefore.args = {
229
+ export const slotBeforeFirstRow = SlotTemplate.bind({});
230
+ slotBeforeFirstRow.args = {
232
231
  slotTemplate: `
233
232
  <template #before-first-row>
234
233
  <p>
@@ -241,8 +240,8 @@ slotbodyRowBefore.args = {
241
240
  `,
242
241
  };
243
242
 
244
- export const slotbodyRowAfter = SlotTemplate.bind({});
245
- slotbodyRowAfter.args = {
243
+ export const slotAfterLastRow = SlotTemplate.bind({});
244
+ slotAfterLastRow.args = {
246
245
  slotTemplate: `
247
246
  <template #after-last-row>
248
247
  <p>
@@ -255,18 +254,18 @@ slotbodyRowAfter.args = {
255
254
  `,
256
255
  };
257
256
 
258
- export const slotTfoot = SlotTemplate.bind({});
259
- slotTfoot.args = {
257
+ export const slotFooter = SlotTemplate.bind({});
258
+ slotFooter.args = {
260
259
  slotTemplate: `
261
- <template #tfoot>
262
- <UTableCell>
260
+ <template #footer>
261
+ <td>
263
262
  <p>
264
263
  Lorem ipsum dolor sit amet, consectetur adipiscing elit,
265
264
  sed do eiusmod tempor incididunt ut labore et dolore magna
266
265
  aliqua. Ut enim ad minim veniam, quis nostrud exercitation
267
266
  ullamco laboris nisi ut aliquip ex ea commodo consequat.
268
267
  </p>
269
- </UTableCell>
268
+ </td>
270
269
  </template>
271
270
  `,
272
271
  };
@@ -76,15 +76,14 @@
76
76
  <table v-bind="tableAttrs">
77
77
  <thead v-bind="headerAttrs" :style="tableRowWidthStyle">
78
78
  <tr v-if="hasSlotContent($slots['before-header'])" v-bind="headerRowAttrs">
79
- <UTableCell
79
+ <td
80
80
  v-if="hasSlotContent($slots['before-header'])"
81
81
  :colspan="colsCount"
82
- :compact="compact"
83
82
  v-bind="headerCellAttrs()"
84
83
  >
85
84
  <!-- @slot Use it to add something before header row. -->
86
85
  <slot name="before-header" :cols-count="colsCount" />
87
- </UTableCell>
86
+ </td>
88
87
  </tr>
89
88
 
90
89
  <tr v-if="hasSlotContent($slots['before-header'])" v-bind="headerRowAttrs"></tr>
@@ -132,18 +131,14 @@
132
131
 
133
132
  <tbody v-if="tableRows.length" v-bind="bodyAttrs">
134
133
  <template v-for="(row, rowIndex) in tableRows" :key="row.id">
135
- <tr v-if="rowIndex === firstRow" v-bind="bodyRowBeforeAttrs">
136
- <UTableCell
137
- v-if="hasSlotContent($slots['before-first-row'])"
138
- :colspan="colsCount"
139
- :compact="compact"
140
- v-bind="bodyRowBeforeCellAttrs"
141
- >
134
+ <tr
135
+ v-if="rowIndex === firstRow && hasSlotContent($slots['before-first-row'])"
136
+ v-bind="bodyRowBeforeAttrs"
137
+ >
138
+ <td :colspan="colsCount" v-bind="bodyRowBeforeCellAttrs">
142
139
  <!-- @slot Use it to add something before first row. -->
143
140
  <slot name="before-first-row" />
144
- </UTableCell>
145
-
146
- <td v-else :colspan="colsCount" v-bind="bodyRowBeforeCellAttrs" />
141
+ </td>
147
142
  </tr>
148
143
 
149
144
  <tr
@@ -165,11 +160,7 @@
165
160
  :data-cy="`${dataCy}-row`"
166
161
  @click="onClickRow(row)"
167
162
  >
168
- <UTableCell
169
- v-if="selectable"
170
- :compact="compact"
171
- v-bind="bodyCellAttrs(config.bodyCellCheckbox)"
172
- >
163
+ <td v-if="selectable" v-bind="bodyCellAttrs(config.bodyCellCheckbox)">
173
164
  <UCheckbox
174
165
  v-model="selectedRows"
175
166
  :data-id="row.id"
@@ -179,12 +170,11 @@
179
170
  v-bind="bodyCheckboxAttrs"
180
171
  @click.stop
181
172
  />
182
- </UTableCell>
173
+ </td>
183
174
 
184
- <UTableCell
175
+ <td
185
176
  v-for="(value, key, index) in TableService.getFilteredRow(row, columns)"
186
177
  :key="index"
187
- :compact="compact"
188
178
  v-bind="bodyCellAttrs(getCellClasses(key))"
189
179
  >
190
180
  <template v-if="hasSlotContent($slots[`cell-${key}`])">
@@ -245,21 +235,17 @@
245
235
  {{ value || HYPHEN_SYMBOL }}
246
236
  </div>
247
237
  </template>
248
- </UTableCell>
238
+ </td>
249
239
  </tr>
250
240
 
251
- <tr v-if="rowIndex === lastRow" v-bind="bodyRowAfterAttrs">
252
- <UTableCell
253
- v-if="hasSlotContent($slots['after-last-row'])"
254
- :compact="compact"
255
- :colspan="colsCount"
256
- v-bind="bodyRowAfterCellAttrs"
257
- >
241
+ <tr
242
+ v-if="rowIndex === lastRow && hasSlotContent($slots['after-last-row'])"
243
+ v-bind="bodyRowAfterAttrs"
244
+ >
245
+ <td :colspan="colsCount" v-bind="bodyRowAfterCellAttrs">
258
246
  <!-- @slot Use it to add something after last row. -->
259
247
  <slot name="after-last-row" />
260
- </UTableCell>
261
-
262
- <td v-else :colspan="colsCount" v-bind="bodyRowAfterCellAttrs" />
248
+ </td>
263
249
  </tr>
264
250
  </template>
265
251
  </tbody>
@@ -282,14 +268,14 @@
282
268
 
283
269
  <tfoot v-if="hasSlotContent($slots['footer'])" v-bind="footerClassesAttrs">
284
270
  <tr ref="footerRowRef" v-bind="footerRowAttrs">
285
- <UTableCell v-if="selectable" :compact="compact" v-bind="footerCellAttrs" />
271
+ <td v-if="selectable" />
286
272
 
287
273
  <!-- @slot Use it to add something in table footer. -->
288
274
  <slot name="footer" :cols-count="colsCount" />
289
275
  </tr>
290
276
 
291
277
  <tr ref="stickyFooterRowRef" :style="tableRowWidthStyle" v-bind="stickyFooterRowAttrs">
292
- <UTableCell v-if="selectable" :compact="compact" v-bind="footerCellAttrs" />
278
+ <td v-if="selectable" />
293
279
 
294
280
  <!-- @slot Use it to add something in table footer. -->
295
281
  <slot name="footer" :cols-count="colsCount" />
@@ -319,7 +305,6 @@ import UEmpty from "../ui.text-empty";
319
305
  import UDivider from "../ui.container-divider";
320
306
  import UCheckbox from "../ui.form-checkbox";
321
307
  import ULoaderTop from "../ui.loader-top";
322
- import UTableCell from "../ui.data-table-cell";
323
308
 
324
309
  import UIService from "../service.ui";
325
310
 
@@ -523,7 +508,6 @@ const {
523
508
  bodyCellNestedWrapperAttrs,
524
509
  headerCellAttrs,
525
510
  bodyCellAttrs,
526
- footerCellAttrs,
527
511
  stickyHeaderActionsCheckboxAttrs,
528
512
  stickyHeaderCheckboxAttrs,
529
513
  headerCheckboxAttrs,
@@ -595,7 +579,7 @@ onBeforeUnmount(() => {
595
579
  });
596
580
 
597
581
  function onWindowResize() {
598
- tableWidth.value = tableWrapperRef.value.offsetWidth;
582
+ tableWidth.value = tableWrapperRef.value?.offsetWidth || 0;
599
583
 
600
584
  setHeaderCellWidth();
601
585
  setFooterCellWidth();
@@ -21,6 +21,7 @@
21
21
  <script setup>
22
22
  import { computed } from "vue";
23
23
 
24
+ import { cx } from "../../service.ui";
24
25
  import { formatDate, dateIsOutOfRange } from "../services/calendar.service";
25
26
  import {
26
27
  isToday,
@@ -210,33 +211,40 @@ function getDay(date, dayNumber) {
210
211
  }
211
212
 
212
213
  function getDayClasses(day) {
214
+ const isDayInRange =
215
+ props.range &&
216
+ localSelectedDate.value &&
217
+ props.selectedDateTo &&
218
+ !dateIsOutOfRange(
219
+ day,
220
+ props.selectedDate,
221
+ props.selectedDateTo,
222
+ props.locale,
223
+ props.dateFormat,
224
+ );
225
+
213
226
  const isNotSelectedDate =
214
227
  (!isSelectedDay(day) && !isSelectedToDay(day)) || props.selectedDate === null;
215
228
 
216
229
  if (isToday(day) && isNotSelectedDate) {
217
- return [currentDayAttrs.value.class];
230
+ return cx([isDayInRange && inRangeDayAttrs.value.class, currentDayAttrs.value.class]);
218
231
  }
219
232
 
220
233
  if (props.range && isSelectedDay(day)) {
221
- return [
222
- inRangeFirstDayAttrs.value.class,
234
+ return cx([
223
235
  isAnotherMothDay(day, activeMonthDate.value) && anotherMonthDayAttrs.value.class,
224
- ];
236
+ inRangeFirstDayAttrs.value.class,
237
+ ]);
225
238
  }
226
239
 
227
240
  if (props.range && isSelectedToDay(day)) {
228
- return [
229
- inRangeLastDayAttrs.value.class,
241
+ return cx([
230
242
  isAnotherMothDay(day, activeMonthDate.value) && anotherMonthDayAttrs.value.class,
231
- ];
243
+ inRangeLastDayAttrs.value.class,
244
+ ]);
232
245
  }
233
246
 
234
- if (
235
- props.range &&
236
- localSelectedDate.value &&
237
- props.selectedDateTo &&
238
- !dateIsOutOfRange(day, props.selectedDate, props.selectedDateTo, props.locale, props.dateFormat)
239
- ) {
247
+ if (isDayInRange) {
240
248
  return [inRangeDayAttrs.value.class];
241
249
  }
242
250
 
@@ -64,7 +64,7 @@ Timepicker.args = { value: new Date(2024, 2, 14, 12, 24, 14), timepicker: true }
64
64
 
65
65
  export const MinMax = DefaultTemplate.bind({});
66
66
  MinMax.args = {
67
- minDate: "2022-02-22",
67
+ minDate: new Date(2022, 2, 22),
68
68
  maxDate: new Date(2022, 2, 26),
69
69
  value: new Date(2022, 2, 24),
70
70
  };
@@ -579,17 +579,23 @@ function onInputDate(newDate) {
579
579
  const isFullReset =
580
580
  localValue.value.to || !localValue.value.from || date <= localValue.value.from;
581
581
 
582
- localValue.value = isFullReset
582
+ const updatedValue = isFullReset
583
583
  ? { from: date, to: null }
584
584
  : { from: localValue.value.from, to: date };
585
+
586
+ localValue.value = updatedValue;
587
+
588
+ emit("input", updatedValue);
585
589
  } else {
586
590
  localValue.value = date;
587
- }
588
591
 
589
- activeDate.value = null;
590
- activeMonth.value = null;
592
+ emit("input", localValue.value);
593
+ }
591
594
 
592
- emit("input", localValue.value);
595
+ if (!props.range) {
596
+ activeDate.value = null;
597
+ activeMonth.value = null;
598
+ }
593
599
 
594
600
  wrapperRef.value.focus();
595
601
  }
@@ -102,7 +102,6 @@ export default /*tw*/ {
102
102
  },
103
103
  },
104
104
  day: "font-medium w-full h-10 text-sm mb-0.5",
105
- currentDay: "text-white bg-brand-900 hover:bg-brand-900 hover:text-white",
106
105
  weekDay: "text-sm size-10",
107
106
  month: "font-medium",
108
107
  selectedMonth: "bg-zinc-100 text-brand-900 hover:text-white",
@@ -119,6 +118,8 @@ export default /*tw*/ {
119
118
  dateFormatWithDot: "Date should be in format 'dd.mm.yyyy'.",
120
119
  notCorrectMonthNumber: "Wrong month number.",
121
120
  notCorrectDayNumber: "Wrong day in month.",
121
+ fromDateGraterThanSecond: "The first date should be less than the second.",
122
+ toDateSmallerThanFirst: "The second date should be greater than the first.",
122
123
  weekdays: {
123
124
  shorthand: {
124
125
  sunday: "Sun",
@@ -29,6 +29,7 @@
29
29
 
30
30
  <div v-if="isVariant.button" v-bind="buttonWrapperAttrs">
31
31
  <UButton
32
+ ref="buttonPrevRef"
32
33
  square
33
34
  :size="size"
34
35
  :disabled="disabled"
@@ -40,6 +41,7 @@
40
41
 
41
42
  <UButton
42
43
  :id="id"
44
+ ref="buttonRef"
43
45
  :size="size"
44
46
  :disabled="disabled"
45
47
  :label="userFormatDate"
@@ -48,6 +50,7 @@
48
50
  />
49
51
 
50
52
  <UButton
53
+ ref="buttonNextRef"
51
54
  square
52
55
  :size="size"
53
56
  :disabled="disabled"
@@ -62,11 +65,10 @@
62
65
  <div
63
66
  v-if="isShownMenu"
64
67
  ref="menuRef"
68
+ v-click-outside="[deactivate, clickOutsideOptions]"
65
69
  tabindex="-1"
66
70
  v-bind="menuAttrs"
67
- @blur="onBlur"
68
71
  @keydown.esc="deactivate"
69
- @mouseleave="onMouseoutMenu"
70
72
  >
71
73
  <div v-bind="periodsRowAttrs">
72
74
  <div
@@ -146,28 +148,26 @@
146
148
  <UInput
147
149
  ref="rangeInputStartRef"
148
150
  v-model="rangeStart"
149
- :error="inputRangeStartError"
151
+ :error="inputRangeFromError"
150
152
  size="sm"
151
153
  v-bind="rangeInputAttrs"
152
154
  :name="rangeInputName"
153
155
  @input="onInputRangeInput($event, INPUT_RANGE_TYPE.start)"
154
- @blur="onBlurRangeInput"
155
156
  />
156
157
 
157
158
  <UInput
158
159
  ref="rangeInputEndRef"
159
160
  v-model="rangeEnd"
160
- :error="inputRangeEndError"
161
+ :error="inputRangeToError"
161
162
  size="sm"
162
163
  v-bind="rangeInputAttrs"
163
164
  :name="rangeInputName"
164
165
  @input="onInputRangeInput($event, INPUT_RANGE_TYPE.end)"
165
- @blur="onBlurRangeInput"
166
166
  />
167
167
  </div>
168
168
 
169
169
  <div v-bind="inputRangeErrorAttrs">
170
- {{ inputRangeEndError || inputRangeStartError }}
170
+ {{ inputRangeToError || inputRangeFromError }}
171
171
  </div>
172
172
 
173
173
  <UCalendar
@@ -179,6 +179,7 @@
179
179
  :date-format="dateFormat"
180
180
  range
181
181
  @mouseenter="onMouseoverCalendar"
182
+ @input="onInputCalendar"
182
183
  />
183
184
  </div>
184
185
  </Transition>
@@ -195,6 +196,8 @@ import UButton from "../ui.button";
195
196
  import UCalendar from "../ui.form-calendar";
196
197
  import { LOCALE_TYPE } from "../ui.form-calendar/constants";
197
198
 
199
+ import vClickOutside from "../directive.clickOutside";
200
+
198
201
  import UIService, { getRandomId } from "../service.ui";
199
202
 
200
203
  import {
@@ -227,7 +230,11 @@ import {
227
230
  getMonthsDateList,
228
231
  } from "./services/dateRange.service";
229
232
 
230
- import { wrongDateFormat, wrongMonthNumber, wrongDayNumber } from "./services/validation.service";
233
+ import {
234
+ isWrongDateFormat,
235
+ isWrongMonthNumber,
236
+ isWrongDayNumber,
237
+ } from "./services/validation.service";
231
238
  import useAttrs from "./composables/attrs.composable";
232
239
  import { useLocale } from "../composable.locale";
233
240
  import useBreakpoint from "../composable.breakpoint";
@@ -395,6 +402,8 @@ const props = defineProps({
395
402
  },
396
403
  });
397
404
 
405
+ const inputRangeFormat = "d.m.Y";
406
+
398
407
  const emit = defineEmits(["update:modelValue"]);
399
408
 
400
409
  const isShownMenu = ref(false);
@@ -402,6 +411,9 @@ const wrapperRef = ref(null);
402
411
  const menuRef = ref(null);
403
412
  const rangeInputStartRef = ref(null);
404
413
  const rangeInputEndRef = ref(null);
414
+ const buttonRef = ref(null);
415
+ const buttonPrevRef = ref(null);
416
+ const buttonNextRef = ref(null);
405
417
 
406
418
  const { isTop, isRight, adjustPositionY, adjustPositionX } = useAdjustElementPosition(
407
419
  wrapperRef,
@@ -444,6 +456,10 @@ const i18nGlobal = tm(UDatePickerRange);
444
456
 
445
457
  const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
446
458
 
459
+ const clickOutsideOptions = computed(() => ({
460
+ ignore: [buttonRef.value.buttonRef, buttonPrevRef.value.buttonRef, buttonNextRef.value.buttonRef],
461
+ }));
462
+
447
463
  const locale = computed(() => {
448
464
  const { months, weekdays } = currentLocale.value;
449
465
 
@@ -470,9 +486,9 @@ const activeDate = ref(
470
486
  const period = ref(PERIOD.ownRange);
471
487
  const rangeStart = ref("");
472
488
  const rangeEnd = ref("");
473
- const inputRangeStartError = ref("");
474
- const inputRangeEndError = ref("");
475
- const isHoverEvent = ref(false);
489
+ const inputRangeFromError = ref("");
490
+ const inputRangeToError = ref("");
491
+ const calendarInnerValue = ref({ from: "", to: "" });
476
492
  const periodDateList = ref(null);
477
493
 
478
494
  const { isMobileBreakpoint } = useBreakpoint();
@@ -611,9 +627,19 @@ const userFormatDate = computed(() => {
611
627
  startYear = "";
612
628
  }
613
629
 
614
- const fromTitle = isSameMonth(from, to)
615
- ? from.getDate()
616
- : `${from.getDate()} ${startMonthName} ${startYear}`;
630
+ const isDatesToSameMonth = isSameMonth(from, to);
631
+ const isDatesToSameYear = from.getFullYear() === to.getFullYear();
632
+
633
+ let fromTitle = `${from.getDate()} ${startMonthName} ${startYear}`;
634
+
635
+ if (isDatesToSameMonth && isDatesToSameYear) {
636
+ fromTitle = from.getDate();
637
+ }
638
+
639
+ if (!isDatesToSameMonth && isDatesToSameYear) {
640
+ fromTitle = `${from.getDate()} ${startMonthName}`;
641
+ }
642
+
617
643
  const toTitle = to ? `${to.getDate()} ${endMonthName} ${endYear}` : "";
618
644
 
619
645
  title = `${fromTitle} – ${toTitle}`;
@@ -680,13 +706,15 @@ watch(
680
706
  const parsedDateTo = parseDate(props.modelValue.to, props.dateFormat, locale.value);
681
707
 
682
708
  rangeStart.value = props.modelValue.from
683
- ? formatDate(parsedDateFrom, "d.m.Y", locale.value)
709
+ ? formatDate(parsedDateFrom, inputRangeFormat, locale.value)
684
710
  : "";
685
711
 
686
- rangeEnd.value = props.modelValue.to ? formatDate(parsedDateTo, "d.m.Y", locale.value) : "";
712
+ rangeEnd.value = props.modelValue.to
713
+ ? formatDate(parsedDateTo, inputRangeFormat, locale.value)
714
+ : "";
687
715
 
688
- inputRangeStartError.value = "";
689
- inputRangeEndError.value = "";
716
+ inputRangeFromError.value = "";
717
+ inputRangeToError.value = "";
690
718
  },
691
719
  { deep: true, immediate: true },
692
720
  );
@@ -820,72 +848,75 @@ function activate() {
820
848
 
821
849
  function deactivate() {
822
850
  isShownMenu.value = false;
823
-
824
- isHoverEvent.value = false;
825
851
  }
826
852
 
827
- function onBlur(event) {
828
- const { relatedTarget } = event;
853
+ function isGraterThanTo(value) {
854
+ if (!value) return false;
829
855
 
830
- if (!menuRef.value?.contains(relatedTarget)) {
831
- deactivate();
832
- }
833
- }
856
+ const parsedValue = parseDate(value, inputRangeFormat, locale.value);
857
+ const parsedTo = parseDate(localValue.value.to, props.dateFormat, locale.value);
834
858
 
835
- function onBlurRangeInput(event) {
836
- const { relatedTarget } = event;
837
- const isRangeInputFocus = relatedTarget?.name === rangeInputName.value;
859
+ return parsedValue > parsedTo;
860
+ }
838
861
 
839
- if (!isRangeInputFocus) {
840
- menuRef.value.focus();
841
- }
862
+ function isSmallerThanFrom(value) {
863
+ if (!value) return false;
842
864
 
843
- if (!menuRef.value?.contains(relatedTarget) && !isHoverEvent.value) {
844
- deactivate();
845
- }
865
+ const parsedValue = parseDate(value, inputRangeFormat, locale.value);
866
+ const parsedFrom = parseDate(localValue.value.from, props.dateFormat, locale.value);
846
867
 
847
- isHoverEvent.value = false;
868
+ return parsedValue < parsedFrom;
848
869
  }
849
870
 
850
871
  function onInputRangeInput(value, type) {
851
- const isInvalidDateFormat = !wrongDateFormat(value);
852
- const isInvalidMonthNumber = !wrongMonthNumber(value);
853
- const isInvalidDayNumber = !wrongDayNumber(value);
872
+ const isInvalidDateFormat = isWrongDateFormat(value);
854
873
 
855
874
  let error = "";
856
875
 
857
876
  if (isInvalidDateFormat && value) {
858
877
  error = locale.value.dateFormatWithDot;
859
- } else if (isInvalidMonthNumber && value) {
878
+ } else if (isWrongMonthNumber(value) && value) {
860
879
  error = locale.value.notCorrectMonthNumber;
861
- } else if (isInvalidDayNumber && value) {
880
+ } else if (isWrongDayNumber(value) && value) {
862
881
  error = locale.value.notCorrectDayNumber;
882
+ } else if (isGraterThanTo(value) && type === INPUT_RANGE_TYPE.start) {
883
+ error = locale.value.fromDateGraterThanSecond;
884
+ } else if (isSmallerThanFrom(value) && type === INPUT_RANGE_TYPE.end) {
885
+ error = locale.value.toDateSmallerThanFirst;
863
886
  }
864
887
 
865
888
  if (type === INPUT_RANGE_TYPE.start) {
866
- inputRangeStartError.value = error;
889
+ inputRangeFromError.value = error;
867
890
  }
868
891
 
869
892
  if (type === INPUT_RANGE_TYPE.end) {
870
- inputRangeEndError.value = error;
893
+ inputRangeToError.value = error;
871
894
  }
872
895
 
873
- const parsedValue = parseDate(value || new Date(), props.dateFormat, locale.value);
874
- const isOutOfRange = dateIsOutOfRange(
875
- parsedValue,
876
- props.minDate,
877
- props.maxDate,
878
- locale.value,
879
- props.dateFormat,
880
- );
881
- const isToLessThanFrom = parsedValue <= localValue.value.from;
896
+ if (!isInvalidDateFormat) {
897
+ const parsedValue = parseDate(value || new Date(), inputRangeFormat, locale.value);
882
898
 
883
- if (type === INPUT_RANGE_TYPE.start && !error && !isOutOfRange) {
884
- localValue.value.from = value ? parsedValue : "";
885
- }
899
+ const isOutOfRange = dateIsOutOfRange(
900
+ parsedValue,
901
+ props.minDate,
902
+ props.maxDate,
903
+ locale.value,
904
+ props.dateFormat,
905
+ );
906
+
907
+ if (type === INPUT_RANGE_TYPE.start && !error && !isOutOfRange) {
908
+ localValue.value = {
909
+ from: value ? parsedValue : "",
910
+ to: localValue.value.to,
911
+ };
912
+ }
886
913
 
887
- if (type === INPUT_RANGE_TYPE.end && !error && !isOutOfRange && !isToLessThanFrom) {
888
- localValue.value.to = value ? parsedValue : "";
914
+ if (type === INPUT_RANGE_TYPE.end && !error && !isOutOfRange) {
915
+ localValue.value = {
916
+ from: localValue.value.from,
917
+ to: value ? parsedValue : "",
918
+ };
919
+ }
889
920
  }
890
921
  }
891
922
 
@@ -1009,26 +1040,25 @@ function onMouseoverCalendar() {
1009
1040
 
1010
1041
  if (isRangeInputFocus || !rangeInputStartRef.value || !rangeInputEndRef.value) return;
1011
1042
 
1012
- if ((rangeStart.value && rangeEnd.value && !inputRangeEndError.value) || !rangeStart.value) {
1043
+ const hasValues =
1044
+ calendarInnerValue.value.from && calendarInnerValue.value.to && !inputRangeToError.value;
1045
+ const hasOnlyFromValue =
1046
+ calendarInnerValue.value.from && !calendarInnerValue.value.to && !inputRangeFromError.value;
1047
+
1048
+ if (hasValues || !rangeStart.value) {
1013
1049
  rangeInputStartRef.value.input.focus();
1014
1050
 
1015
1051
  return;
1016
1052
  }
1017
1053
 
1018
- if (rangeStart.value && !rangeEnd.value && !inputRangeStartError.value) {
1054
+ if (hasOnlyFromValue) {
1019
1055
  rangeInputEndRef.value.input.focus();
1020
1056
 
1021
1057
  return;
1022
1058
  }
1023
1059
  }
1024
1060
 
1025
- function onMouseoutMenu() {
1026
- const isRangeInputFocus = document.activeElement.name === rangeInputName.value;
1027
-
1028
- if (isRangeInputFocus) {
1029
- isHoverEvent.value = true;
1030
-
1031
- document.activeElement.blur();
1032
- }
1061
+ function onInputCalendar(value) {
1062
+ calendarInnerValue.value = value;
1033
1063
  }
1034
1064
  </script>
@@ -1,22 +1,22 @@
1
1
  import { getDaysInMonth } from "../../ui.form-calendar/services/date.service";
2
2
 
3
- const DATE_WITH_DOT_FORMAT_REG_EXP = /^\d{2}([./-])\d{2}\1\d{4}$/;
3
+ const datePattern = /^\d{1,2}\.\d{1,2}\.\d{4}$/;
4
4
 
5
- export function wrongDateFormat(value) {
6
- return !!value.match(DATE_WITH_DOT_FORMAT_REG_EXP);
5
+ export function isWrongDateFormat(value) {
6
+ return !datePattern.test(value);
7
7
  }
8
8
 
9
- export function wrongMonthNumber(value) {
9
+ export function isWrongMonthNumber(value) {
10
10
  const splitDate = value.split(".");
11
11
  const month = splitDate[1];
12
12
 
13
- return Number(month) >= 1 && Number(month) <= 12;
13
+ return !(Number(month) >= 1 && Number(month) <= 12);
14
14
  }
15
15
 
16
- export function wrongDayNumber(value) {
16
+ export function isWrongDayNumber(value) {
17
17
  const [day, month, year] = value.split(".");
18
18
 
19
19
  const daysInMonth = getDaysInMonth(new Date(year, month - 1));
20
20
 
21
- return Number(day) >= 1 && Number(day) <= Number(daysInMonth);
21
+ return !(Number(day) >= 1 && Number(day) <= Number(daysInMonth));
22
22
  }
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "vueless",
4
- "version": "0.0.169",
4
+ "version": "0.0.171",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",
@@ -6730,48 +6730,6 @@
6730
6730
  "symbol": "default"
6731
6731
  }
6732
6732
  },
6733
- {
6734
- "name": "UTableCell",
6735
- "description": "",
6736
- "attributes": [
6737
- {
6738
- "name": "compact",
6739
- "description": "Makes the cell more narrow.",
6740
- "value": {
6741
- "kind": "expression",
6742
- "type": "boolean"
6743
- },
6744
- "default": "false"
6745
- },
6746
- {
6747
- "name": "config",
6748
- "description": "Sets component ui config object.",
6749
- "value": {
6750
- "kind": "expression",
6751
- "type": "object"
6752
- },
6753
- "default": "{}"
6754
- },
6755
- {
6756
- "name": "dataCy",
6757
- "description": "Sets data-cy attribute for automated testing.",
6758
- "value": {
6759
- "kind": "expression",
6760
- "type": "string"
6761
- },
6762
- "default": "\"\""
6763
- }
6764
- ],
6765
- "slots": [
6766
- {
6767
- "name": "default"
6768
- }
6769
- ],
6770
- "source": {
6771
- "module": "./src/ui.data-table-cell/index.vue",
6772
- "symbol": "default"
6773
- }
6774
- },
6775
6733
  {
6776
6734
  "name": "UTabs",
6777
6735
  "description": "",
@@ -1,24 +0,0 @@
1
- import useUI from "../../composable.ui";
2
- import { cva } from "../../service.ui";
3
-
4
- import defaultConfig from "../configs/default.config";
5
- import { computed } from "vue";
6
-
7
- export function useAttrs(props) {
8
- const { config, getAttrs } = useUI(defaultConfig, () => props.config);
9
- const { tableCell } = config.value;
10
-
11
- const cvaTableCell = cva({
12
- base: tableCell.base,
13
- variants: tableCell.variants,
14
- compoundVariants: tableCell.compoundVariants,
15
- });
16
-
17
- const tableCellClasses = computed(() => cvaTableCell({ compact: props.compact }));
18
-
19
- const tableCellAttrs = getAttrs("tableCell", { classes: tableCellClasses });
20
-
21
- return {
22
- tableCellAttrs,
23
- };
24
- }
@@ -1,19 +0,0 @@
1
- export default /*tw*/ {
2
- tableCell: {
3
- base: `
4
- p-[1.125rem] py-5 first:p-5 group-[]/body:truncate group-[]/body:align-top
5
- group-[]/body:last:p-5 !overflow-visible
6
- `,
7
- variants: {
8
- compact: {
9
- true: `
10
- group-[]/body:px-4 group-[]/body:py-3 group-[]/body:last:px-4 group-[]/body:last:py-3
11
- group-[]/body:first:px-4 group-[]/body:first:py-3 group-[]/footer:p-4:
12
- `,
13
- },
14
- },
15
- },
16
- defaultVariants: {
17
- compact: false,
18
- },
19
- };
@@ -1,5 +0,0 @@
1
- /*
2
- This const is needed to prevent the issue in script setup:
3
- `defineProps` is referencing locally declared variables. (vue/valid-define-props)
4
- */
5
- export const UTableCell = "UTableCell";
@@ -1,44 +0,0 @@
1
- <template>
2
- <td :data-cy="dataCy" v-bind="tableCellAttrs">
3
- <slot />
4
- </td>
5
- </template>
6
-
7
- <script setup>
8
- import UIService from "../service.ui";
9
-
10
- import { UTableCell } from "./constants";
11
- import defaultConfig from "./configs/default.config";
12
- import { useAttrs } from "./composables/attrs.composable";
13
-
14
- /* Should be a string for correct web-types gen */
15
- defineOptions({ name: "UTableCell", inheritAttrs: false });
16
-
17
- const props = defineProps({
18
- /**
19
- * Makes the cell more narrow.
20
- */
21
- compact: {
22
- type: Boolean,
23
- default: UIService.get(defaultConfig, UTableCell).default.compact,
24
- },
25
-
26
- /**
27
- * Sets component ui config object.
28
- */
29
- config: {
30
- type: Object,
31
- default: () => ({}),
32
- },
33
-
34
- /**
35
- * Sets data-cy attribute for automated testing.
36
- */
37
- dataCy: {
38
- type: String,
39
- default: "",
40
- },
41
- });
42
-
43
- const { tableCellAttrs } = useAttrs(props);
44
- </script>