@koslibs/components 1.15.0 → 1.17.0
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/CHANGELOG.md +14 -0
- package/dist/components/Chips/chips.d.ts +2 -0
- package/dist/components/Chips/chips.js +48 -0
- package/dist/components/Chips/hooks/use-chips-state.d.ts +7 -0
- package/dist/components/Chips/hooks/use-chips-state.js +56 -0
- package/dist/components/Chips/index.d.ts +2 -0
- package/dist/components/Chips/index.js +1 -0
- package/dist/components/Chips/index.module.js +9 -0
- package/dist/components/Chips/index_module.css +99 -0
- package/dist/components/Chips/types.d.ts +86 -0
- package/dist/components/Chips/types.js +0 -0
- package/dist/components/Chips/utils/normalize-value.d.ts +2 -0
- package/dist/components/Chips/utils/normalize-value.js +9 -0
- package/dist/components/Chips/vars.css +19 -0
- package/dist/components/Datepicker/components/calendar-month.d.ts +8 -0
- package/dist/components/Datepicker/components/calendar-month.js +68 -0
- package/dist/components/Datepicker/components/range-calendar.d.ts +5 -0
- package/dist/components/Datepicker/components/range-calendar.js +78 -0
- package/dist/components/Datepicker/datepicker-panel.d.ts +2 -0
- package/dist/components/Datepicker/datepicker-panel.js +187 -0
- package/dist/components/Datepicker/datepicker.d.ts +2 -0
- package/dist/components/Datepicker/datepicker.js +194 -0
- package/dist/components/Datepicker/hooks/use-datepicker-draft.d.ts +22 -0
- package/dist/components/Datepicker/hooks/use-datepicker-draft.js +105 -0
- package/dist/components/Datepicker/hooks/use-datepicker-state.d.ts +23 -0
- package/dist/components/Datepicker/hooks/use-datepicker-state.js +106 -0
- package/dist/components/Datepicker/hooks/use-datepicker-value.d.ts +8 -0
- package/dist/components/Datepicker/hooks/use-datepicker-value.js +31 -0
- package/dist/components/Datepicker/hooks/use-range-scroll.d.ts +15 -0
- package/dist/components/Datepicker/hooks/use-range-scroll.js +219 -0
- package/dist/components/Datepicker/index.d.ts +3 -0
- package/dist/components/Datepicker/index.js +2 -0
- package/dist/components/Datepicker/index.module.js +37 -0
- package/dist/components/Datepicker/index_module.css +251 -0
- package/dist/components/Datepicker/panel.module.js +39 -0
- package/dist/components/Datepicker/panel_module.css +375 -0
- package/dist/components/Datepicker/types.d.ts +133 -0
- package/dist/components/Datepicker/types.js +0 -0
- package/dist/components/Datepicker/utils/date-utils.d.ts +17 -0
- package/dist/components/Datepicker/utils/date-utils.js +80 -0
- package/dist/components/Datepicker/utils/range-scroll-geometry.d.ts +15 -0
- package/dist/components/Datepicker/utils/range-scroll-geometry.js +29 -0
- package/dist/components/Datepicker/vars.css +26 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @koslibs/components
|
|
2
2
|
|
|
3
|
+
## 1.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#20](https://github.com/koslibs/components/pull/20) [`ea31f0a`](https://github.com/koslibs/components/commit/ea31f0a1ece96d62c21f943de86459946fb1a5fe) Thanks [@holypower777](https://github.com/holypower777)! - Добавлен Chips: одиночный и множественный выбор, управляемые и внутренние значения, блокировка группы и пунктов, состояния из Figma с плавными переходами, перенос полных подписей, нативные формы, Storybook и публичные типы. All обрабатывается как обычный пункт.
|
|
8
|
+
|
|
9
|
+
## 1.16.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#19](https://github.com/koslibs/components/pull/19) [`0f37bcd`](https://github.com/koslibs/components/commit/0f37bcdcecf865db07cd16ed4d711473b142b87c) Thanks [@holypower777](https://github.com/holypower777)! - Добавлены Datepicker и самостоятельная DatepickerPanel для выбора одной даты или включительного диапазона: ручной ввод, проверка дат, черновик с Apply/Cancel, непрерывная прокрутка календаря со связанной навигацией месяцев, формы и документация Storybook.
|
|
14
|
+
|
|
15
|
+
Обновлён @koslibs/icons до 1.3.0: Datepicker использует CalendarIcon, ChevronLeftIcon и ChevronRightIcon из библиотеки. У полос прокрутки убраны кнопки-стрелки.
|
|
16
|
+
|
|
3
17
|
## 1.15.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import { forwardRef, useId, useImperativeHandle } from "react";
|
|
4
|
+
import { useDataTestId } from "../../hooks/use-data-test-id.js";
|
|
5
|
+
import { TypographyText } from "../Typography/text.js";
|
|
6
|
+
import { useChipsState } from "./hooks/use-chips-state.js";
|
|
7
|
+
import index_module from "./index.module.js";
|
|
8
|
+
const Chips = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
9
|
+
const { items, multiselect = false, value: _value, defaultValue: _defaultValue, onChange: _onChange, disabled = false, form, className, listClassName, itemClassName, dataTestId, ...restProps } = props;
|
|
10
|
+
const groupName = useId();
|
|
11
|
+
const getId = useDataTestId(dataTestId);
|
|
12
|
+
const state = useChipsState(props);
|
|
13
|
+
useImperativeHandle(ref, ()=>state.groupRef.current, []);
|
|
14
|
+
return /*#__PURE__*/ jsx("fieldset", {
|
|
15
|
+
...restProps,
|
|
16
|
+
ref: state.groupRef,
|
|
17
|
+
form: form,
|
|
18
|
+
disabled: disabled,
|
|
19
|
+
className: classnames(index_module.component, className),
|
|
20
|
+
"data-test-id": dataTestId,
|
|
21
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
22
|
+
className: classnames(index_module.list, listClassName),
|
|
23
|
+
children: items.map((item)=>/*#__PURE__*/ jsxs("label", {
|
|
24
|
+
className: index_module.item,
|
|
25
|
+
children: [
|
|
26
|
+
/*#__PURE__*/ jsx("input", {
|
|
27
|
+
type: multiselect ? 'checkbox' : 'radio',
|
|
28
|
+
name: groupName,
|
|
29
|
+
form: groupName,
|
|
30
|
+
value: item.value,
|
|
31
|
+
checked: state.selected.has(item.value),
|
|
32
|
+
disabled: disabled || item.disabled,
|
|
33
|
+
onChange: state.handleChange,
|
|
34
|
+
className: index_module.input,
|
|
35
|
+
"data-test-id": getId(item.value)
|
|
36
|
+
}),
|
|
37
|
+
/*#__PURE__*/ jsx(TypographyText, {
|
|
38
|
+
size: 14,
|
|
39
|
+
className: classnames(index_module.chip, itemClassName),
|
|
40
|
+
children: item.label
|
|
41
|
+
})
|
|
42
|
+
]
|
|
43
|
+
}, item.value))
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
Chips.displayName = 'Chips';
|
|
48
|
+
export { Chips };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ChangeEvent } from 'react';
|
|
2
|
+
import type { ChipsProps } from '../types.js';
|
|
3
|
+
export declare function useChipsState(props: ChipsProps): {
|
|
4
|
+
groupRef: import("react").RefObject<HTMLFieldSetElement>;
|
|
5
|
+
selected: Set<string>;
|
|
6
|
+
handleChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
7
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { normalizeValue } from "../utils/normalize-value.js";
|
|
3
|
+
function useChipsState(props) {
|
|
4
|
+
const { items, multiselect = false, defaultValue } = props;
|
|
5
|
+
const controlled = void 0 !== props.value;
|
|
6
|
+
const groupRef = useRef(null);
|
|
7
|
+
const [localValue, setLocalValue] = useState(defaultValue);
|
|
8
|
+
const value = normalizeValue(items, multiselect, controlled ? props.value : localValue);
|
|
9
|
+
const selected = new Set(Array.isArray(value) ? value : [
|
|
10
|
+
value
|
|
11
|
+
].filter((item)=>null !== item));
|
|
12
|
+
useEffect(()=>{
|
|
13
|
+
const form = groupRef.current?.form;
|
|
14
|
+
let active = true;
|
|
15
|
+
const handleReset = (event)=>{
|
|
16
|
+
queueMicrotask(()=>{
|
|
17
|
+
if (active && !event.defaultPrevented && !controlled) setLocalValue(defaultValue);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
form?.addEventListener('reset', handleReset);
|
|
21
|
+
return ()=>{
|
|
22
|
+
active = false;
|
|
23
|
+
form?.removeEventListener('reset', handleReset);
|
|
24
|
+
};
|
|
25
|
+
}, [
|
|
26
|
+
props.form,
|
|
27
|
+
controlled,
|
|
28
|
+
defaultValue
|
|
29
|
+
]);
|
|
30
|
+
const handleChange = (event)=>{
|
|
31
|
+
const item = items.find((candidate)=>candidate.value === event.currentTarget.value);
|
|
32
|
+
if (props.disabled || !item || item.disabled) return;
|
|
33
|
+
if (props.multiselect) {
|
|
34
|
+
const current = Array.isArray(value) ? value : [];
|
|
35
|
+
const next = event.currentTarget.checked ? [
|
|
36
|
+
...current,
|
|
37
|
+
item.value
|
|
38
|
+
] : current.filter((candidate)=>candidate !== item.value);
|
|
39
|
+
if (!controlled) setLocalValue(next);
|
|
40
|
+
props.onChange?.(event, {
|
|
41
|
+
value: next
|
|
42
|
+
});
|
|
43
|
+
} else if (value !== item.value) {
|
|
44
|
+
if (!controlled) setLocalValue(item.value);
|
|
45
|
+
props.onChange?.(event, {
|
|
46
|
+
value: item.value
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
groupRef,
|
|
52
|
+
selected,
|
|
53
|
+
handleChange
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export { useChipsState };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Chips } from "./chips.js";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
@import "../../styles/theme.css";
|
|
2
|
+
@import "./vars.css";
|
|
3
|
+
|
|
4
|
+
.component-YzqK3S {
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
min-width: 0;
|
|
7
|
+
color: var(--chips-color);
|
|
8
|
+
font-family: var(--font-primary);
|
|
9
|
+
border: 0;
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.list-Dlab5I {
|
|
15
|
+
gap: var(--chips-gap);
|
|
16
|
+
flex-wrap: wrap;
|
|
17
|
+
align-items: flex-start;
|
|
18
|
+
display: flex;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.item-Q7WaXH {
|
|
22
|
+
min-width: 0;
|
|
23
|
+
max-width: 100%;
|
|
24
|
+
display: inline-flex;
|
|
25
|
+
position: relative;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.input-UTMIEW {
|
|
29
|
+
z-index: 1;
|
|
30
|
+
opacity: 0;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
width: 100%;
|
|
33
|
+
height: 100%;
|
|
34
|
+
margin: 0;
|
|
35
|
+
position: absolute;
|
|
36
|
+
inset: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.chip-jqwYZi {
|
|
40
|
+
box-sizing: border-box;
|
|
41
|
+
min-width: 0;
|
|
42
|
+
min-height: var(--chips-height);
|
|
43
|
+
padding: var(--chips-padding-y) var(--chips-padding-x);
|
|
44
|
+
border-radius: var(--chips-radius);
|
|
45
|
+
background: var(--chips-background);
|
|
46
|
+
color: var(--chips-color);
|
|
47
|
+
text-align: center;
|
|
48
|
+
overflow-wrap: anywhere;
|
|
49
|
+
line-height: 20px;
|
|
50
|
+
transition: background-color .1s ease-in-out, color .1s ease-in-out;
|
|
51
|
+
display: block;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.input-UTMIEW:checked + .chip-jqwYZi {
|
|
55
|
+
background: var(--chips-selected-background);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@media (hover: hover) {
|
|
59
|
+
.input-UTMIEW:not(:disabled):hover + .chip-jqwYZi {
|
|
60
|
+
background: var(--chips-hover-background);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.input-UTMIEW:not(:disabled):checked:hover + .chip-jqwYZi {
|
|
64
|
+
background: var(--chips-selected-hover-background);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.input-UTMIEW:not(:disabled):active + .chip-jqwYZi {
|
|
69
|
+
background: var(--chips-active-background);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.input-UTMIEW:not(:disabled):checked:active + .chip-jqwYZi {
|
|
73
|
+
background: var(--chips-selected-active-background);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.input-UTMIEW:focus-visible + .chip-jqwYZi {
|
|
77
|
+
outline: 2px solid var(--chips-focus-color);
|
|
78
|
+
outline-offset: 2px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.input-UTMIEW:disabled {
|
|
82
|
+
cursor: not-allowed;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.input-UTMIEW:disabled + .chip-jqwYZi {
|
|
86
|
+
background: var(--chips-disabled-background);
|
|
87
|
+
color: var(--chips-disabled-color);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.input-UTMIEW:disabled:checked + .chip-jqwYZi {
|
|
91
|
+
background: var(--chips-disabled-selected-background);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@media (prefers-reduced-motion: reduce) {
|
|
95
|
+
.chip-jqwYZi {
|
|
96
|
+
transition: none;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ChangeEvent, FieldsetHTMLAttributes } from 'react';
|
|
2
|
+
export type ChipsItem = {
|
|
3
|
+
/**
|
|
4
|
+
* Уникальное значение пункта.
|
|
5
|
+
*/
|
|
6
|
+
value: string;
|
|
7
|
+
/**
|
|
8
|
+
* Полная текстовая подпись пункта.
|
|
9
|
+
*/
|
|
10
|
+
label: string;
|
|
11
|
+
/**
|
|
12
|
+
* Блокирует переключение пункта, сохраняя его выбранное состояние.
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type ChipsChangePayload<Value = string | null> = {
|
|
18
|
+
/**
|
|
19
|
+
* Выбранное значение или массив значений в порядке выбора.
|
|
20
|
+
*/
|
|
21
|
+
value: Value;
|
|
22
|
+
};
|
|
23
|
+
type CommonProps = Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, 'onChange' | 'defaultValue' | 'children' | 'name' | 'disabled'> & {
|
|
24
|
+
/**
|
|
25
|
+
* Пункты с уникальными value. All обрабатывается как обычный пункт.
|
|
26
|
+
*/
|
|
27
|
+
items: readonly ChipsItem[];
|
|
28
|
+
/**
|
|
29
|
+
* Блокирует все пункты группы, сохраняя выбор.
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Дополнительный CSS-класс списка пунктов.
|
|
35
|
+
*/
|
|
36
|
+
listClassName?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Дополнительный CSS-класс каждого chip.
|
|
39
|
+
*/
|
|
40
|
+
itemClassName?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Идентификатор группы для тестов. Пункты получают суффикс с их value.
|
|
43
|
+
*/
|
|
44
|
+
dataTestId?: string;
|
|
45
|
+
};
|
|
46
|
+
type SingleProps = {
|
|
47
|
+
/**
|
|
48
|
+
* Выбирает один пункт. Повторное нажатие сохраняет выбор.
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
multiselect?: false;
|
|
52
|
+
/**
|
|
53
|
+
* Управляемое значение; null означает пустой выбор.
|
|
54
|
+
*/
|
|
55
|
+
value?: string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Начальное значение неуправляемой группы.
|
|
58
|
+
* @default null
|
|
59
|
+
*/
|
|
60
|
+
defaultValue?: string | null;
|
|
61
|
+
/**
|
|
62
|
+
* Изменение пользователем выбранного значения.
|
|
63
|
+
*/
|
|
64
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>, payload: ChipsChangePayload) => void;
|
|
65
|
+
};
|
|
66
|
+
type MultipleProps = {
|
|
67
|
+
/**
|
|
68
|
+
* Независимое переключение нескольких пунктов.
|
|
69
|
+
*/
|
|
70
|
+
multiselect: true;
|
|
71
|
+
/**
|
|
72
|
+
* Управляемый массив выбранных значений.
|
|
73
|
+
*/
|
|
74
|
+
value?: readonly string[];
|
|
75
|
+
/**
|
|
76
|
+
* Начальный массив значений неуправляемой группы.
|
|
77
|
+
* @default []
|
|
78
|
+
*/
|
|
79
|
+
defaultValue?: readonly string[];
|
|
80
|
+
/**
|
|
81
|
+
* Изменение пользователем набора выбранных значений.
|
|
82
|
+
*/
|
|
83
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>, payload: ChipsChangePayload<string[]>) => void;
|
|
84
|
+
};
|
|
85
|
+
export type ChipsProps = CommonProps & (SingleProps | MultipleProps);
|
|
86
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
function normalizeValue(items, multiselect, value) {
|
|
2
|
+
const available = new Set(items.map((item)=>item.value));
|
|
3
|
+
if (!multiselect) return 'string' == typeof value && available.has(value) ? value : null;
|
|
4
|
+
if (!Array.isArray(value)) return [];
|
|
5
|
+
return [
|
|
6
|
+
...new Set(value.filter((item)=>'string' == typeof item && available.has(item)))
|
|
7
|
+
];
|
|
8
|
+
}
|
|
9
|
+
export { normalizeValue };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--chips-gap: var(--size-8);
|
|
3
|
+
--chips-height: var(--size-32);
|
|
4
|
+
--chips-padding-x: var(--size-12);
|
|
5
|
+
--chips-padding-y: var(--size-6);
|
|
6
|
+
--chips-radius: var(--border-radius-pill);
|
|
7
|
+
--chips-color: #1c1a18;
|
|
8
|
+
--chips-background: transparent;
|
|
9
|
+
--chips-hover-background: #e9e4d9;
|
|
10
|
+
--chips-active-background: #dcd4c5;
|
|
11
|
+
--chips-selected-background: #fdde9b;
|
|
12
|
+
--chips-selected-hover-background: #f4d38d;
|
|
13
|
+
--chips-selected-active-background: #e9c479;
|
|
14
|
+
--chips-disabled-background: #cfd1cb;
|
|
15
|
+
--chips-disabled-selected-background: #f7e6c2;
|
|
16
|
+
--chips-disabled-color: #858981;
|
|
17
|
+
--chips-focus-color: #ff6b0a;
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DatepickerDraft } from '../hooks/use-datepicker-draft.js';
|
|
2
|
+
export declare function Weekdays(): import("react").JSX.Element;
|
|
3
|
+
export declare function CalendarMonth({ month, state, range, dataTestId, }: {
|
|
4
|
+
month: number;
|
|
5
|
+
state: DatepickerDraft;
|
|
6
|
+
range: boolean;
|
|
7
|
+
dataTestId?: string;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import { useDataTestId } from "../../../hooks/use-data-test-id.js";
|
|
4
|
+
import { TypographyText } from "../../Typography/text.js";
|
|
5
|
+
import { getMonthDays, monthLabel, parseDate } from "../utils/date-utils.js";
|
|
6
|
+
import panel_module from "../panel.module.js";
|
|
7
|
+
function Weekdays() {
|
|
8
|
+
return /*#__PURE__*/ jsx("div", {
|
|
9
|
+
className: panel_module.weekdays,
|
|
10
|
+
children: [
|
|
11
|
+
'Mo',
|
|
12
|
+
'Tu',
|
|
13
|
+
'We',
|
|
14
|
+
'Th',
|
|
15
|
+
'Fr',
|
|
16
|
+
'Sa',
|
|
17
|
+
'Su'
|
|
18
|
+
].map((day)=>/*#__PURE__*/ jsx("span", {
|
|
19
|
+
children: day
|
|
20
|
+
}, day))
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function CalendarMonth({ month, state, range, dataTestId }) {
|
|
24
|
+
const id = useDataTestId(dataTestId);
|
|
25
|
+
const from = parseDate(state.draft.from) ? state.draft.from : '';
|
|
26
|
+
const to = 'object' == typeof state.value ? state.value?.to ?? '' : '';
|
|
27
|
+
const end = to || state.preview;
|
|
28
|
+
const handleMouseLeave = ()=>state.hover(null);
|
|
29
|
+
return /*#__PURE__*/ jsxs("section", {
|
|
30
|
+
className: panel_module.month,
|
|
31
|
+
title: monthLabel(month),
|
|
32
|
+
children: [
|
|
33
|
+
range && /*#__PURE__*/ jsx(TypographyText, {
|
|
34
|
+
tag: "div",
|
|
35
|
+
size: 14,
|
|
36
|
+
weight: "semibold",
|
|
37
|
+
className: panel_module.monthTitle,
|
|
38
|
+
children: monthLabel(month)
|
|
39
|
+
}),
|
|
40
|
+
/*#__PURE__*/ jsx("div", {
|
|
41
|
+
className: panel_module.days,
|
|
42
|
+
onMouseLeave: handleMouseLeave,
|
|
43
|
+
children: getMonthDays(month).map((date, index)=>{
|
|
44
|
+
if (!date) return /*#__PURE__*/ jsx("span", {}, `empty-${index}`);
|
|
45
|
+
const handleClick = ()=>state.select(date);
|
|
46
|
+
const handleMouseEnter = ()=>state.hover(date);
|
|
47
|
+
return /*#__PURE__*/ jsx("button", {
|
|
48
|
+
type: "button",
|
|
49
|
+
className: classnames(panel_module.day, {
|
|
50
|
+
[panel_module.selected]: !range && date === from,
|
|
51
|
+
[panel_module.rangeStart]: range && date === from,
|
|
52
|
+
[panel_module.rangeEnd]: range && date === to,
|
|
53
|
+
[panel_module.oneDay]: range && date === from && date === to,
|
|
54
|
+
[panel_module.inRange]: range && from && end && date > from && date < end,
|
|
55
|
+
[panel_module.previewEnd]: range && !to && date === state.preview
|
|
56
|
+
}),
|
|
57
|
+
title: date,
|
|
58
|
+
"data-test-id": id(`day-${date}`),
|
|
59
|
+
onClick: handleClick,
|
|
60
|
+
onMouseEnter: handleMouseEnter,
|
|
61
|
+
children: Number(date.slice(-2))
|
|
62
|
+
}, date);
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export { CalendarMonth, Weekdays };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import { Fragment } from "react";
|
|
4
|
+
import { useDataTestId } from "../../../hooks/use-data-test-id.js";
|
|
5
|
+
import { useRangeScroll } from "../hooks/use-range-scroll.js";
|
|
6
|
+
import { MONTH_NAMES, monthLabel } from "../utils/date-utils.js";
|
|
7
|
+
import { CalendarMonth, Weekdays } from "./calendar-month.js";
|
|
8
|
+
import panel_module from "../panel.module.js";
|
|
9
|
+
function RangeCalendar({ state, dataTestId }) {
|
|
10
|
+
const id = useDataTestId(dataTestId);
|
|
11
|
+
const scroll = useRangeScroll(state.month, state.navigationVersion);
|
|
12
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
13
|
+
className: panel_module.rangeCalendar,
|
|
14
|
+
children: [
|
|
15
|
+
/*#__PURE__*/ jsxs("div", {
|
|
16
|
+
ref: scroll.monthsRef,
|
|
17
|
+
className: panel_module.months,
|
|
18
|
+
"data-test-id": id('months'),
|
|
19
|
+
onScroll: scroll.monthsScroll,
|
|
20
|
+
children: [
|
|
21
|
+
/*#__PURE__*/ jsx("div", {
|
|
22
|
+
ref: scroll.windowRef,
|
|
23
|
+
className: panel_module.monthWindow,
|
|
24
|
+
"data-test-id": id('month-window')
|
|
25
|
+
}),
|
|
26
|
+
scroll.months.map((month, index)=>{
|
|
27
|
+
const handleClick = ()=>state.navigate(month);
|
|
28
|
+
const registerMonth = (element)=>scroll.registerMonth(month, element);
|
|
29
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
30
|
+
children: [
|
|
31
|
+
(0 === index || month % 12 === 0) && /*#__PURE__*/ jsx("div", {
|
|
32
|
+
className: panel_module.year,
|
|
33
|
+
children: Math.floor(month / 12) + 1
|
|
34
|
+
}),
|
|
35
|
+
/*#__PURE__*/ jsx("button", {
|
|
36
|
+
type: "button",
|
|
37
|
+
ref: registerMonth,
|
|
38
|
+
className: classnames(panel_module.monthButton, month >= scroll.visible.first && month <= scroll.visible.last && panel_module.monthVisible),
|
|
39
|
+
title: monthLabel(month),
|
|
40
|
+
"data-test-id": id(`month-${month}`),
|
|
41
|
+
onClick: handleClick,
|
|
42
|
+
children: MONTH_NAMES[month % 12]
|
|
43
|
+
})
|
|
44
|
+
]
|
|
45
|
+
}, month);
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
}),
|
|
49
|
+
/*#__PURE__*/ jsxs("div", {
|
|
50
|
+
className: panel_module.calendars,
|
|
51
|
+
"data-test-id": id('calendar'),
|
|
52
|
+
children: [
|
|
53
|
+
/*#__PURE__*/ jsx(Weekdays, {}),
|
|
54
|
+
/*#__PURE__*/ jsx("div", {
|
|
55
|
+
ref: scroll.calendarRef,
|
|
56
|
+
className: panel_module.calendarMonths,
|
|
57
|
+
"data-test-id": id('calendar-months'),
|
|
58
|
+
onScroll: scroll.calendarScroll,
|
|
59
|
+
children: scroll.calendars.map((month)=>{
|
|
60
|
+
const registerCalendarMonth = (element)=>scroll.registerCalendarMonth(month, element);
|
|
61
|
+
return /*#__PURE__*/ jsx("div", {
|
|
62
|
+
ref: registerCalendarMonth,
|
|
63
|
+
className: panel_module.monthBlock,
|
|
64
|
+
children: /*#__PURE__*/ jsx(CalendarMonth, {
|
|
65
|
+
month: month,
|
|
66
|
+
state: state,
|
|
67
|
+
range: true,
|
|
68
|
+
dataTestId: dataTestId
|
|
69
|
+
})
|
|
70
|
+
}, month);
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
]
|
|
74
|
+
})
|
|
75
|
+
]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export { RangeCalendar };
|