vueless 0.0.153 → 0.0.154

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.
@@ -0,0 +1,98 @@
1
+ import { ref, computed, toValue } from "vue";
2
+
3
+ export const POSITION = {
4
+ left: "left",
5
+ right: "right",
6
+ top: "top",
7
+ bottom: "bottom",
8
+ auto: "auto",
9
+ };
10
+
11
+ export function useAdjustElementPosition(
12
+ anchorElement,
13
+ targetElement,
14
+ position,
15
+ preferredPosition,
16
+ ) {
17
+ const preferredOpenDirectionY = ref(preferredPosition?.y || POSITION.bottom);
18
+ const preferredOpenDirectionX = ref(preferredPosition?.x || POSITION.left);
19
+
20
+ const localAnchorElement = computed(() => toValue(anchorElement));
21
+ const localTargetElement = computed(() => toValue(targetElement));
22
+ const localPosition = computed(() => toValue(position));
23
+ const localPreferredPosition = computed(() => toValue(preferredPosition));
24
+
25
+ const isTop = computed(() => {
26
+ if (localPosition.value.y !== POSITION.auto) {
27
+ return localPosition.value.y === POSITION.top;
28
+ }
29
+
30
+ return preferredOpenDirectionY.value === POSITION.top;
31
+ });
32
+
33
+ const isLeft = computed(() => {
34
+ if (localPosition.value.x !== POSITION.auto) {
35
+ return localPosition.value.x === POSITION.left;
36
+ }
37
+
38
+ return preferredOpenDirectionX.value === POSITION.right;
39
+ });
40
+
41
+ const isBottom = computed(() => {
42
+ if (localPosition.value.y !== POSITION.auto) {
43
+ return localPosition.value.y === POSITION.bottom;
44
+ }
45
+
46
+ return preferredOpenDirectionY.value === POSITION.bottom;
47
+ });
48
+
49
+ const isRight = computed(() => {
50
+ if (localPosition.value.x !== POSITION.auto) {
51
+ return localPosition.value.y === POSITION.right;
52
+ }
53
+
54
+ return preferredOpenDirectionX.value === POSITION.right;
55
+ });
56
+
57
+ function adjustPositionY() {
58
+ if (typeof window === "undefined") return;
59
+
60
+ const spaceAbove = localAnchorElement.value.getBoundingClientRect().top;
61
+ const spaceBelow = window.innerHeight - localAnchorElement.value.getBoundingClientRect().bottom;
62
+ const hasEnoughSpaceBelow =
63
+ spaceBelow > localTargetElement.value.getBoundingClientRect().height;
64
+ const hasEnoughSpaceAbove =
65
+ spaceAbove > localTargetElement.value.getBoundingClientRect().height;
66
+
67
+ if (localPreferredPosition.value.y === POSITION.top) {
68
+ preferredOpenDirectionY.value =
69
+ hasEnoughSpaceBelow || spaceBelow > spaceAbove ? POSITION.bottom : POSITION.top;
70
+ }
71
+
72
+ if (localPreferredPosition.value.y === POSITION.bottom) {
73
+ preferredOpenDirectionY.value =
74
+ hasEnoughSpaceAbove || spaceAbove > spaceBelow ? POSITION.top : POSITION.bottom;
75
+ }
76
+ }
77
+
78
+ function adjustPositionX() {
79
+ if (typeof window === "undefined") return;
80
+
81
+ const spaceRight = localAnchorElement.value.getBoundingClientRect().right;
82
+ const spaceLeft = window.innerWidth - localAnchorElement.value.getBoundingClientRect().left;
83
+ const hasEnoughSpaceLeft = spaceLeft > localTargetElement.value.getBoundingClientRect().width;
84
+ const hasEnoughSpaceRight = spaceRight > localTargetElement.value.getBoundingClientRect().width;
85
+
86
+ if (localPreferredPosition.value.x === POSITION.left) {
87
+ preferredOpenDirectionX.value =
88
+ hasEnoughSpaceRight || spaceRight > spaceLeft ? POSITION.right : POSITION.left;
89
+ }
90
+
91
+ if (localPreferredPosition.value.x === POSITION.right) {
92
+ preferredOpenDirectionX.value =
93
+ hasEnoughSpaceLeft || spaceLeft > spaceRight ? POSITION.left : POSITION.right;
94
+ }
95
+ }
96
+
97
+ return { isTop, isRight, isBottom, isLeft, adjustPositionY, adjustPositionX };
98
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.153",
3
+ "version": "0.0.154",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless Component Framework.",
6
6
  "homepage": "https://vueless.com",
@@ -1,13 +1,17 @@
1
1
  import useUI from "../../composable.ui";
2
2
  import { cva } from "../../service.ui";
3
+ import { computed, watchEffect } from "vue";
3
4
 
4
5
  import defaultConfig from "../configs/default.config";
5
- import { computed, watchEffect } from "vue";
6
+ import { POSITION } from "../../composable.adjustElementPosition";
6
7
 
7
- export default function useAttrs(props, { isShownCalendar }) {
8
+ export default function useAttrs(props, { isShownCalendar, isTop, isRight }) {
8
9
  const { config, getAttrs } = useUI(defaultConfig, () => props.config);
9
10
  const { calendar } = config.value;
10
11
 
12
+ const openDirectionY = computed(() => (isTop.value ? POSITION.top : POSITION.bottom));
13
+ const openDirectionX = computed(() => (isRight.value ? POSITION.right : POSITION.left));
14
+
11
15
  const cvaCalendarWrapper = cva({
12
16
  base: calendar.wrapper.base,
13
17
  variants: calendar.wrapper.variants,
@@ -15,7 +19,10 @@ export default function useAttrs(props, { isShownCalendar }) {
15
19
  });
16
20
 
17
21
  const calendarWrapperClasses = computed(() =>
18
- cvaCalendarWrapper({ openDirection: props.openDirection }),
22
+ cvaCalendarWrapper({
23
+ openDirectionY: openDirectionY.value,
24
+ openDirectionX: openDirectionX.value,
25
+ }),
19
26
  );
20
27
 
21
28
  const calendarAttrs = getAttrs("calendar", {
@@ -8,12 +8,15 @@ export default /*tw*/ {
8
8
  },
9
9
  calendar: {
10
10
  wrapper: {
11
- base: "absolute z-40 mt-2",
11
+ base: "absolute z-40 my-2",
12
12
  variants: {
13
- openDirection: {
13
+ openDirectionX: {
14
14
  left: "left-0",
15
15
  right: "right-0",
16
16
  },
17
+ openDirectionY: {
18
+ top: "bottom-full mt-0",
19
+ },
17
20
  },
18
21
  },
19
22
  },
@@ -109,7 +112,8 @@ export default /*tw*/ {
109
112
  dateFormat: "Y-m-d",
110
113
  userFormat: "F j, Y",
111
114
  size: "md",
112
- openDirection: "left",
115
+ openDirectionX: "auto",
116
+ openDirectionY: "auto",
113
117
  timepicker: false,
114
118
  disabled: false,
115
119
  maxDate: undefined,
@@ -76,11 +76,29 @@ const OpenDirectionTemplate = (args, { argTypes } = {}) => ({
76
76
  <URow class="!flex-col">
77
77
  <UDatePicker
78
78
  class="w-full"
79
- v-for="(direction, index) in directions"
80
- :open-direction="direction"
79
+ open-direction-y="top"
80
+ v-bind="args"
81
+ v-model="args.value"
82
+ />
83
+ <UDatePicker
84
+ class="w-full"
85
+ open-direction-y="bottom"
86
+ open-direction-x="right"
87
+ v-bind="args"
88
+ v-model="args.value"
89
+ />
90
+ <UDatePicker
91
+ class="w-full"
92
+ open-direction-y="bottom"
93
+ v-bind="args"
94
+ v-model="args.value"
95
+ />
96
+ <UDatePicker
97
+ class="w-full"
98
+ open-direction-y="bottom"
99
+ open-direction-x="left"
81
100
  v-bind="args"
82
101
  v-model="args.value"
83
- :key="index"
84
102
  />
85
103
  </URow>
86
104
  `,
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div v-bind="wrapperAttrs">
2
+ <div v-bind="wrapperAttrs" ref="wrapperRef">
3
3
  <UInput
4
4
  :id="id"
5
5
  :key="isShownCalendar"
@@ -69,6 +69,7 @@ import {
69
69
 
70
70
  import useAttrs from "./composables/attrs.composable";
71
71
  import { useLocale } from "../composable.locale";
72
+ import { useAdjustElementPosition } from "../composable.adjustElementPosition";
72
73
 
73
74
  import defaultConfig from "./configs/default.config";
74
75
  import { UDatePicker } from "./constants";
@@ -102,12 +103,21 @@ const props = defineProps({
102
103
  },
103
104
 
104
105
  /**
105
- * Datepicker open direction.
106
- * @values left, right
106
+ * Datepicker open direction on x-axis.
107
+ * @values auto, left, right
107
108
  */
108
- openDirection: {
109
+ openDirectionX: {
109
110
  type: String,
110
- default: UIService.get(defaultConfig, UDatePicker).default.openDirection,
111
+ default: UIService.get(defaultConfig, UDatePicker).default.openDirectionX,
112
+ },
113
+
114
+ /**
115
+ * Datepicker open direction on y-axis.
116
+ * @values auto, top, bottom
117
+ */
118
+ openDirectionY: {
119
+ type: String,
120
+ default: UIService.get(defaultConfig, UDatePicker).default.openDirectionY,
111
121
  },
112
122
 
113
123
  /**
@@ -220,8 +230,24 @@ const userFormatDate = ref("");
220
230
  const formattedDate = ref("");
221
231
  const customView = ref(VIEW.day);
222
232
 
233
+ const wrapperRef = ref(null);
223
234
  const calendarRef = ref(null);
224
235
 
236
+ const calendarWrapperRef = computed(() => calendarRef?.value?.wrapperRef);
237
+
238
+ const { isTop, isRight, adjustPositionY, adjustPositionX } = useAdjustElementPosition(
239
+ wrapperRef,
240
+ calendarWrapperRef,
241
+ {
242
+ x: props.openDirectionX,
243
+ y: props.openDirectionY,
244
+ },
245
+ {
246
+ x: "left",
247
+ y: "bottom",
248
+ },
249
+ );
250
+
225
251
  defineExpose({
226
252
  calendarRef,
227
253
  userFormatDate,
@@ -233,12 +259,21 @@ const localValue = computed({
233
259
  set: (value) => emit("update:modelValue", value),
234
260
  });
235
261
 
236
- const { config, inputAttrs, calendarAttrs, wrapperAttrs } = useAttrs(props, { isShownCalendar });
262
+ const { config, inputAttrs, calendarAttrs, wrapperAttrs } = useAttrs(props, {
263
+ isShownCalendar,
264
+ isTop,
265
+ isRight,
266
+ });
237
267
 
238
268
  function activate() {
239
269
  isShownCalendar.value = true;
240
270
 
241
- nextTick(() => calendarRef.value?.wrapperRef?.focus());
271
+ nextTick(() => {
272
+ adjustPositionY();
273
+ adjustPositionX();
274
+
275
+ calendarRef.value.wrapperRef.focus();
276
+ });
242
277
  }
243
278
 
244
279
  function deactivate() {
@@ -4,18 +4,24 @@ import { cx, cva } from "../../service.ui";
4
4
  import { computed, watchEffect } from "vue";
5
5
 
6
6
  import defaultConfig from "../configs/default.config";
7
+ import { POSITION } from "../../composable.adjustElementPosition";
7
8
 
8
- export default function useAttrs(props, { isShownMenu }) {
9
+ export default function useAttrs(props, { isShownMenu, isTop, isRight }) {
9
10
  const { config, getAttrs } = useUI(defaultConfig, () => props.config);
10
11
  const { menu } = config.value;
11
12
 
13
+ const openDirectionY = computed(() => (isTop.value ? POSITION.top : POSITION.bottom));
14
+ const openDirectionX = computed(() => (isRight.value ? POSITION.right : POSITION.left));
15
+
12
16
  const cvaMenu = cva({
13
17
  base: menu.base,
14
18
  variants: menu.variants,
15
19
  compoundVariants: menu.compoundVariants,
16
20
  });
17
21
 
18
- const menuClasses = computed(() => cvaMenu({ openDirection: props.openDirection }));
22
+ const menuClasses = computed(() =>
23
+ cvaMenu({ openDirectionY: openDirectionY.value, openDirectionX: openDirectionX.value }),
24
+ );
19
25
 
20
26
  const wrapperAttrs = getAttrs("wrapper");
21
27
  const buttonWrapperAttrsRaw = getAttrs("buttonWrapper");
@@ -20,12 +20,15 @@ export default /*tw*/ {
20
20
  disabled:cursor-not-allowed last:rounded-l-none last:rounded-r-lg first:rounded-l-lg first:rounded-r-none
21
21
  `,
22
22
  menu: {
23
- base: "absolute z-40 mt-2 w-80 overflow-hidden rounded-lg border border-brand-300 bg-white p-2 shadow focus:outline-none",
23
+ base: "absolute z-40 my-2 w-80 overflow-hidden rounded-lg border border-brand-300 bg-white p-2 shadow focus:outline-none",
24
24
  variants: {
25
- openDirection: {
25
+ openDirectionX: {
26
26
  left: "left-0",
27
27
  right: "right-0",
28
28
  },
29
+ openDirectionY: {
30
+ top: "bottom-full mt-0",
31
+ },
29
32
  },
30
33
  },
31
34
  menuTransition: {
@@ -193,7 +196,8 @@ export default /*tw*/ {
193
196
  defaultVariants: {
194
197
  size: "md",
195
198
  variant: "button",
196
- openDirection: "left",
199
+ openDirectionX: "auto",
200
+ openDirectionY: "auto",
197
201
  timepicker: false,
198
202
  disabled: false,
199
203
  dateFormat: "d.m.Y",
@@ -91,11 +91,29 @@ const OpenDirectionTemplate = (args, { argTypes } = {}) => ({
91
91
  <URow class="!flex-col">
92
92
  <UDatePickerRange
93
93
  class="w-full"
94
- v-for="(direction, index) in directions"
95
- :open-direction="direction"
94
+ open-direction-y="top"
95
+ v-bind="args"
96
+ v-model="args.value"
97
+ />
98
+ <UDatePickerRange
99
+ class="w-full"
100
+ open-direction-y="bottom"
101
+ open-direction-x="right"
102
+ v-bind="args"
103
+ v-model="args.value"
104
+ />
105
+ <UDatePickerRange
106
+ class="w-full"
107
+ open-direction-y="bottom"
108
+ v-bind="args"
109
+ v-model="args.value"
110
+ />
111
+ <UDatePickerRange
112
+ class="w-full"
113
+ open-direction-y="bottom"
114
+ open-direction-x="left"
96
115
  v-bind="args"
97
116
  v-model="args.value"
98
- :key="index"
99
117
  />
100
118
  </URow>
101
119
  `,
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div v-bind="wrapperAttrs">
2
+ <div v-bind="wrapperAttrs" ref="wrapperRef">
3
3
  <UInput
4
4
  v-if="isVariant.input"
5
5
  :id="id"
@@ -235,6 +235,7 @@ import { wrongDateFormat, wrongMonthNumber, wrongDayNumber } from "./services/va
235
235
  import useAttrs from "./composables/attrs.composable";
236
236
  import { useLocale } from "../composable.locale";
237
237
  import useBreakpoint from "../composable.breakpoint";
238
+ import { useAdjustElementPosition } from "../composable.adjustElementPosition";
238
239
 
239
240
  import defaultConfig from "./configs/default.config";
240
241
  import {
@@ -273,12 +274,21 @@ const props = defineProps({
273
274
  },
274
275
 
275
276
  /**
276
- * Datepicker open direction.
277
- * @values left, right
277
+ * Datepicker open direction on x-axis.
278
+ * @values auto, left, right
278
279
  */
279
- openDirection: {
280
+ openDirectionX: {
280
281
  type: String,
281
- default: UIService.get(defaultConfig, UDatePickerRange).default.openDirection,
282
+ default: UIService.get(defaultConfig, UDatePickerRange).default.openDirectionX,
283
+ },
284
+
285
+ /**
286
+ * Datepicker open direction on y-axis.
287
+ * @values auto, top, bottom
288
+ */
289
+ openDirectionY: {
290
+ type: String,
291
+ default: UIService.get(defaultConfig, UDatePickerRange).default.openDirectionY,
282
292
  },
283
293
 
284
294
  /**
@@ -392,6 +402,23 @@ const props = defineProps({
392
402
  const emit = defineEmits(["update:modelValue"]);
393
403
 
394
404
  const isShownMenu = ref(false);
405
+ const wrapperRef = ref(null);
406
+ const menuRef = ref(null);
407
+ const rangeInputStartRef = ref(null);
408
+ const rangeInputEndRef = ref(null);
409
+
410
+ const { isTop, isRight, adjustPositionY, adjustPositionX } = useAdjustElementPosition(
411
+ wrapperRef,
412
+ menuRef,
413
+ {
414
+ x: props.openDirectionX,
415
+ y: props.openDirectionY,
416
+ },
417
+ {
418
+ x: "left",
419
+ y: "bottom",
420
+ },
421
+ );
395
422
 
396
423
  const {
397
424
  config,
@@ -414,7 +441,7 @@ const {
414
441
  rangeInputAttrs,
415
442
  rangeInputWrapperAttrs,
416
443
  inputRangeErrorAttrs,
417
- } = useAttrs(props, { isShownMenu });
444
+ } = useAttrs(props, { isShownMenu, isTop, isRight });
418
445
  const { tm } = useLocale();
419
446
 
420
447
  const calendarValue = ref(props.modelValue);
@@ -422,16 +449,15 @@ const activeDate = ref(
422
449
  props.modelValue.from !== null ? getDateFromUnixTimestamp(props.modelValue.from) : new Date(),
423
450
  );
424
451
  const period = ref(PERIOD.ownRange);
425
- const periodDateList = ref(null);
426
452
  const rangeStart = ref("");
427
453
  const rangeEnd = ref("");
428
454
  const inputRangeStartError = ref("");
429
455
  const inputRangeEndError = ref("");
430
456
  const isHoverEvent = ref(false);
457
+ const periodDateList = ref(null);
431
458
 
432
- const menuRef = ref(null);
433
- const rangeInputStartRef = ref(null);
434
- const rangeInputEndRef = ref(null);
459
+ const { isMobileBreakpoint } = useBreakpoint();
460
+ const i18nGlobal = tm(UDatePickerRange);
435
461
 
436
462
  const localValue = computed({
437
463
  get: () => props.modelValue,
@@ -444,9 +470,6 @@ const localValue = computed({
444
470
  },
445
471
  });
446
472
 
447
- const { isMobileBreakpoint } = useBreakpoint();
448
- const i18nGlobal = tm(UDatePickerRange);
449
-
450
473
  const rangeInputName = computed(() => `rangeInput-${props.id}`);
451
474
  const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
452
475
 
@@ -602,7 +625,7 @@ const userFormatDate = computed(() => {
602
625
  const endMonth = String(to?.getMonth())?.padStart(2, "0");
603
626
 
604
627
  const fromTitle = `${startDay}.${startMonth}`;
605
- const toTitle = to ? `${endDay}.${endMonth} / ${to.getFullYear}` : "";
628
+ const toTitle = to ? `${endDay}.${endMonth} / ${to.getFullYear()}` : "";
606
629
 
607
630
  title = `${fromTitle} – ${toTitle}`;
608
631
  }
@@ -791,7 +814,12 @@ function getPeriodDateListClasses() {
791
814
  function activate() {
792
815
  isShownMenu.value = true;
793
816
 
794
- nextTick(() => menuRef.value.focus());
817
+ nextTick(() => {
818
+ adjustPositionY();
819
+ adjustPositionX();
820
+
821
+ menuRef.value.focus();
822
+ });
795
823
  }
796
824
 
797
825
  function deactivate() {
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "vueless",
4
- "version": "0.0.153",
4
+ "version": "0.0.154",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",
@@ -1531,13 +1531,22 @@
1531
1531
  "default": "null"
1532
1532
  },
1533
1533
  {
1534
- "name": "openDirection",
1535
- "description": "Datepicker open direction.",
1534
+ "name": "openDirectionX",
1535
+ "description": "Datepicker open direction on x-axis.",
1536
1536
  "value": {
1537
1537
  "kind": "expression",
1538
- "type": "'left' | 'right'"
1538
+ "type": "'auto' | 'left' | 'right'"
1539
1539
  },
1540
- "default": "left"
1540
+ "default": "auto"
1541
+ },
1542
+ {
1543
+ "name": "openDirectionY",
1544
+ "description": "Datepicker open direction on y-axis.",
1545
+ "value": {
1546
+ "kind": "expression",
1547
+ "type": "'auto' | 'top' | 'bottom'"
1548
+ },
1549
+ "default": "auto"
1541
1550
  },
1542
1551
  {
1543
1552
  "name": "minDate",
@@ -1693,13 +1702,22 @@
1693
1702
  "default": "{\n range: { from: 0, to: 0 },\n label: \"\",\n description: \"\"\n}"
1694
1703
  },
1695
1704
  {
1696
- "name": "openDirection",
1697
- "description": "Datepicker open direction.",
1705
+ "name": "openDirectionX",
1706
+ "description": "Datepicker open direction on x-axis.",
1698
1707
  "value": {
1699
1708
  "kind": "expression",
1700
- "type": "'left' | 'right'"
1709
+ "type": "'auto' | 'left' | 'right'"
1701
1710
  },
1702
- "default": "left"
1711
+ "default": "auto"
1712
+ },
1713
+ {
1714
+ "name": "openDirectionY",
1715
+ "description": "Datepicker open direction on y-axis.",
1716
+ "value": {
1717
+ "kind": "expression",
1718
+ "type": "'auto' | 'top' | 'bottom'"
1719
+ },
1720
+ "default": "auto"
1703
1721
  },
1704
1722
  {
1705
1723
  "name": "variant",