@koslibs/components 1.11.1 → 1.13.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/Button/index_module.css +4 -0
  3. package/dist/components/Collapse/collapse.d.ts +10 -0
  4. package/dist/components/Collapse/collapse.js +2 -1
  5. package/dist/components/Collapse/index_module.css +6 -5
  6. package/dist/components/Input/index_module.css +8 -7
  7. package/dist/components/Input/input.d.ts +1 -0
  8. package/dist/components/Input/input.js +2 -1
  9. package/dist/components/Input/types.d.ts +5 -0
  10. package/dist/components/Modal/components/modal-surface/index.js +1 -1
  11. package/dist/components/Select/components/select-dropdown.d.ts +2 -2
  12. package/dist/components/Select/components/select-dropdown.js +2 -1
  13. package/dist/components/Select/index_module.css +5 -4
  14. package/dist/components/Select/select.js +3 -1
  15. package/dist/components/Select/types.d.ts +1 -1
  16. package/dist/components/Sidepanel/components/content/index.d.ts +13 -0
  17. package/dist/components/Sidepanel/components/content/index.js +21 -0
  18. package/dist/components/Sidepanel/components/content/index.module.js +5 -0
  19. package/dist/components/Sidepanel/components/content/index_module.css +14 -0
  20. package/dist/components/Sidepanel/components/footer/index.d.ts +66 -0
  21. package/dist/components/Sidepanel/components/footer/index.js +25 -0
  22. package/dist/components/Sidepanel/components/footer/index.module.js +19 -0
  23. package/dist/components/Sidepanel/components/footer/index_module.css +57 -0
  24. package/dist/components/Sidepanel/components/header/index.d.ts +33 -0
  25. package/dist/components/Sidepanel/components/header/index.js +45 -0
  26. package/dist/components/Sidepanel/components/header/index.module.js +8 -0
  27. package/dist/components/Sidepanel/components/header/index_module.css +32 -0
  28. package/dist/components/Sidepanel/components/sidepanel-surface/index.d.ts +4 -0
  29. package/dist/components/Sidepanel/components/sidepanel-surface/index.js +92 -0
  30. package/dist/components/Sidepanel/context.d.ts +6 -0
  31. package/dist/components/Sidepanel/context.js +3 -0
  32. package/dist/components/Sidepanel/hooks/use-dialog-transition.d.ts +5 -0
  33. package/dist/components/Sidepanel/hooks/use-dialog-transition.js +83 -0
  34. package/dist/components/Sidepanel/index.d.ts +8 -0
  35. package/dist/components/Sidepanel/index.js +4 -0
  36. package/dist/components/Sidepanel/index.module.js +8 -0
  37. package/dist/components/Sidepanel/index_module.css +68 -0
  38. package/dist/components/Sidepanel/scrollbar.module.js +5 -0
  39. package/dist/components/Sidepanel/scrollbar_module.css +41 -0
  40. package/dist/components/Sidepanel/sidepanel.d.ts +9 -0
  41. package/dist/components/Sidepanel/sidepanel.js +28 -0
  42. package/dist/components/Sidepanel/types.d.ts +36 -0
  43. package/dist/components/Sidepanel/types.js +0 -0
  44. package/dist/components/Sidepanel/vars.css +12 -0
  45. package/dist/components/Tabs/index_module.css +1 -1
  46. package/dist/components/Textarea/index_module.css +3 -2
  47. package/dist/components/Textarea/textarea.d.ts +1 -0
  48. package/dist/components/Textarea/textarea.js +2 -1
  49. package/dist/components/Textarea/types.d.ts +5 -0
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.js +1 -0
  52. package/dist/utils/lock-scroll.d.ts +1 -0
  53. package/dist/utils/lock-scroll.js +49 -0
  54. package/package.json +1 -1
  55. package/dist/components/Modal/utils/lock-scroll.d.ts +0 -1
  56. package/dist/components/Modal/utils/lock-scroll.js +0 -31
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @koslibs/components
2
2
 
3
+ ## 1.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#15](https://github.com/koslibs/components/pull/15) [`061457d`](https://github.com/koslibs/components/commit/061457d29c63e8fc4ad1929d7c7ca2b9fffae075) Thanks [@holypower777](https://github.com/holypower777)! - Установлен курсор `not-allowed` для disabled-состояний Button и Tab. У Input, Textarea и Select курсор применяется ко всему компоненту, включая внешние подписи и подсказки. У Select disabled имеет приоритет над readOnly.
8
+
9
+ Добавлен Sidepanel: панель шириной 500 px с выездом и уходом справа, бекдропом Modal, Header с необязательным заголовком и hasCloser, прокручиваемым Content и Footer только для Button. Поддержаны строковая и вертикальная раскладки кнопок, управляемое закрытие, удержание и возврат фокуса, Portal и блокировка прокрутки до завершения анимации. Добавлены Storybook, типы, тесты и отдельный импорт.
10
+
11
+ Общая блокировка прокрутки Modal и Sidepanel вынесена в src/utils/lock-scroll.ts. Она сохраняет положение страницы при временном снятии scrollbar-gutter, чтобы панель доходила до края окна.
12
+
13
+ Пустой Header Sidepanel не занимает места. Скроллбар показывает только слайдер без стрелок и фона. Footer поддерживает sticky с дефолтом true; при false кнопки прокручиваются вместе с содержимым. Переключение режима выполнено через классы CSS Modules без служебного data-атрибута.
14
+
15
+ Footer Sidepanel принимает компоненты ComponentType<ButtonProps>: обязательный primary справа и необязательный secondary слева. Внешний вид кнопок задаётся снаружи. gap принимает 12/16/24/32 с дефолтом 12; layout — start/end/center/space-between/column с дефолтом space-between.
16
+
17
+ ## 1.12.0
18
+
19
+ ### Minor Changes
20
+
21
+ - [#14](https://github.com/koslibs/components/pull/14) [`242a16c`](https://github.com/koslibs/components/commit/242a16ca5c8ba04bdfab319bf75752a9c4b62826) Thanks [@holypower777](https://github.com/holypower777)! - Добавлен проп `hasOutline` для Input, Textarea, Select и Collapse. Outline при фокусе и раскрытии Select теперь включается явно через `hasOutline={true}`; по умолчанию он выключен. Для отключения используется `hasOutline={false}`. Select передаёт настройку поисковому Input. В Storybook добавлены переключатели и примеры.
22
+
3
23
  ## 1.11.1
4
24
 
5
25
  ### Patch Changes
@@ -21,6 +21,10 @@
21
21
  transition: background-color .1s ease-in-out, color .1s ease-in-out, box-shadow .1s ease-in-out;
22
22
  display: inline-flex;
23
23
  position: relative;
24
+
25
+ &:disabled {
26
+ cursor: not-allowed;
27
+ }
24
28
  }
25
29
 
26
30
  .size-32-oD8MDp {
@@ -27,6 +27,11 @@ export type CollapseProps = Omit<HTMLAttributes<HTMLDivElement>, 'onTransitionEn
27
27
  * @default false
28
28
  */
29
29
  showLeftChevron?: boolean;
30
+ /**
31
+ * Показывает outline кнопки при клавиатурном фокусе.
32
+ * @default false
33
+ */
34
+ hasOutline?: boolean;
30
35
  /**
31
36
  * Запрос изменения состояния. В controlled-режиме обновите expanded.
32
37
  */
@@ -72,6 +77,11 @@ export declare const Collapse: import("react").ForwardRefExoticComponent<Omit<HT
72
77
  * @default false
73
78
  */
74
79
  showLeftChevron?: boolean;
80
+ /**
81
+ * Показывает outline кнопки при клавиатурном фокусе.
82
+ * @default false
83
+ */
84
+ hasOutline?: boolean;
75
85
  /**
76
86
  * Запрос изменения состояния. В controlled-режиме обновите expanded.
77
87
  */
@@ -4,7 +4,7 @@ import classnames from "classnames";
4
4
  import { forwardRef, useId, useLayoutEffect, useRef, useState } from "react";
5
5
  import { TypographyText } from "../Typography/text.js";
6
6
  import index_module from "./index.module.js";
7
- const Collapse = /*#__PURE__*/ forwardRef(({ expanded, defaultExpanded = false, collapsedLabel, expandedLabel, showRightChevron = true, showLeftChevron = false, onExpandedChange, onTransitionEnd, expandedContentClassName, dataTestId, className, children, ...restProps }, ref)=>{
7
+ const Collapse = /*#__PURE__*/ forwardRef(({ expanded, defaultExpanded = false, collapsedLabel, expandedLabel, showRightChevron = true, showLeftChevron = false, hasOutline = false, onExpandedChange, onTransitionEnd, expandedContentClassName, dataTestId, className, children, ...restProps }, ref)=>{
8
8
  const [expandedState, setExpandedState] = useState(defaultExpanded);
9
9
  const isExpanded = expanded ?? expandedState;
10
10
  const contentId = `collapse-${useId()}`;
@@ -51,6 +51,7 @@ const Collapse = /*#__PURE__*/ forwardRef(({ expanded, defaultExpanded = false,
51
51
  ...restProps,
52
52
  ref: ref,
53
53
  className: classnames(index_module.component, className),
54
+ "data-has-outline": hasOutline || void 0,
54
55
  "data-test-id": dataTestId,
55
56
  children: [
56
57
  label && /*#__PURE__*/ jsxs("button", {
@@ -37,6 +37,7 @@
37
37
  cursor: pointer;
38
38
  background: none;
39
39
  border: 0;
40
+ outline: none;
40
41
  display: inline-flex;
41
42
 
42
43
  &:hover {
@@ -47,12 +48,12 @@
47
48
  text-underline-offset: 3px;
48
49
  text-decoration: underline;
49
50
  }
51
+ }
50
52
 
51
- &:focus-visible {
52
- outline: 2px solid var(--collapse-focus-color);
53
- outline-offset: 2px;
54
- border-radius: var(--border-radius-4, 4px);
55
- }
53
+ .component-tPiInR[data-has-outline] .toggle-YA8uJB:focus-visible {
54
+ outline: 2px solid var(--collapse-focus-color);
55
+ outline-offset: 2px;
56
+ border-radius: var(--border-radius-4, 4px);
56
57
  }
57
58
 
58
59
  .icon-wFeaQN {
@@ -42,6 +42,7 @@
42
42
  --field-label-color: var(--input-disabled-color);
43
43
  --field-placeholder-color: var(--input-disabled-color);
44
44
  --field-icon-color: var(--input-disabled-color);
45
+ cursor: not-allowed;
45
46
  }
46
47
  }
47
48
 
@@ -100,13 +101,13 @@
100
101
  }
101
102
  }
102
103
 
103
- .component-aUdiT5:not([data-disabled]) .field-MFnvYv:focus-within {
104
+ .component-aUdiT5[data-has-outline]:not([data-disabled]) .field-MFnvYv:focus-within {
104
105
  box-shadow: none;
105
106
  outline: 2px solid var(--input-focus-border-color);
106
107
  outline-offset: 0;
107
108
  }
108
109
 
109
- .component-aUdiT5[data-error]:not([data-disabled]) .field-MFnvYv:focus-within {
110
+ .component-aUdiT5[data-has-outline][data-error]:not([data-disabled]) .field-MFnvYv:focus-within {
110
111
  outline-color: var(--input-error-border-color);
111
112
  }
112
113
 
@@ -274,11 +275,6 @@
274
275
  color: var(--field-icon-color);
275
276
  }
276
277
 
277
- &:focus-visible {
278
- outline: 2px solid var(--input-focus-border-color);
279
- outline-offset: 2px;
280
- }
281
-
282
278
  & svg {
283
279
  width: var(--input-icon-size);
284
280
  height: var(--input-icon-size);
@@ -286,6 +282,11 @@
286
282
  }
287
283
  }
288
284
 
285
+ .component-aUdiT5[data-has-outline] .clear-A5Krz8:focus-visible {
286
+ outline: 2px solid var(--input-focus-border-color);
287
+ outline-offset: 2px;
288
+ }
289
+
289
290
  .component-aUdiT5 .message-ArUnJU {
290
291
  color: var(--input-hint-color);
291
292
  overflow-wrap: anywhere;
@@ -7,6 +7,7 @@ export declare const Input: import("react").ForwardRefExoticComponent<Omit<impor
7
7
  view?: "primary" | "secondary";
8
8
  size?: 40 | 48 | 56 | 64 | 72;
9
9
  block?: boolean;
10
+ hasOutline?: boolean;
10
11
  label?: import("react").ReactNode;
11
12
  labelView?: "outer" | "inner";
12
13
  hint?: import("react").ReactNode;
@@ -6,7 +6,7 @@ import { useDataTestId } from "../../hooks/use-data-test-id.js";
6
6
  import { Button } from "../Button/index.js";
7
7
  import { TypographyText } from "../Typography/text.js";
8
8
  import index_module from "./index.module.js";
9
- const Input = /*#__PURE__*/ forwardRef(({ view = 'primary', size = 40, block = false, label, labelView = 'outer', hint, error, leftAddon, rightAddon, clear = false, clearLabel = 'Очистить поле', value, defaultValue, maxLength, showValueLength = false, onChange, onClear, disabled = false, readOnly = false, type = 'text', id, className, style, fieldClassName, inputClassName, labelClassName, messageClassName, dataTestId, ...restProps }, ref)=>{
9
+ const Input = /*#__PURE__*/ forwardRef(({ view = 'primary', size = 40, block = false, hasOutline = false, label, labelView = 'outer', hint, error, leftAddon, rightAddon, clear = false, clearLabel = 'Очистить поле', value, defaultValue, maxLength, showValueLength = false, onChange, onClear, disabled = false, readOnly = false, type = 'text', id, className, style, fieldClassName, inputClassName, labelClassName, messageClassName, dataTestId, ...restProps }, ref)=>{
10
10
  const generatedId = useId();
11
11
  const inputId = id ?? generatedId;
12
12
  const inputRef = useRef(null);
@@ -92,6 +92,7 @@ const Input = /*#__PURE__*/ forwardRef(({ view = 'primary', size = 40, block = f
92
92
  style: style,
93
93
  "data-view": view,
94
94
  "data-block": block || void 0,
95
+ "data-has-outline": hasOutline || void 0,
95
96
  "data-disabled": disabled || void 0,
96
97
  "data-read-only": readOnly || void 0,
97
98
  "data-error": hasError || void 0,
@@ -35,6 +35,11 @@ export type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'v
35
35
  * @default false
36
36
  */
37
37
  block?: boolean;
38
+ /**
39
+ * Показывает outline при фокусе.
40
+ * @default false
41
+ */
42
+ hasOutline?: boolean;
38
43
  /**
39
44
  * Название поля. Связывается с input через htmlFor и id.
40
45
  */
@@ -1,8 +1,8 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import classnames from "classnames";
3
3
  import { forwardRef, useCallback, useLayoutEffect, useMemo, useRef } from "react";
4
+ import { lockScroll } from "../../../../utils/lock-scroll.js";
4
5
  import { ModalContext } from "../../context.js";
5
- import { lockScroll } from "../../utils/lock-scroll.js";
6
6
  import colors_module from "../../colors.module.js";
7
7
  import index_module from "../../index.module.js";
8
8
  const ModalSurface = /*#__PURE__*/ forwardRef(({ onClose, size = 720, escapeKeyDown = true, backdropClick = true, lockScroll: shouldLockScroll = true, className, dataTestId, children, onPointerDown, onClick, ...restProps }, ref)=>{
@@ -1,7 +1,7 @@
1
1
  import type { SelectState } from '../hooks/use-select-state.js';
2
2
  import type { SelectProps } from '../types.js';
3
- type Props = Pick<SelectProps, 'multiple' | 'searchable' | 'searchPlaceholder' | 'noResultsText' | 'dataTestId'> & {
3
+ type Props = Pick<SelectProps, 'multiple' | 'searchable' | 'searchPlaceholder' | 'noResultsText' | 'dataTestId' | 'hasOutline'> & {
4
4
  state: SelectState;
5
5
  };
6
- export declare function SelectDropdown({ state, multiple, searchable, searchPlaceholder, noResultsText, dataTestId, }: Props): import("react").JSX.Element;
6
+ export declare function SelectDropdown({ state, multiple, searchable, hasOutline, searchPlaceholder, noResultsText, dataTestId, }: Props): import("react").JSX.Element;
7
7
  export {};
@@ -7,7 +7,7 @@ import { useDataTestId } from "../../../hooks/use-data-test-id.js";
7
7
  import { Input } from "../../Input/index.js";
8
8
  import { TypographyText } from "../../Typography/text.js";
9
9
  import index_module from "../index.module.js";
10
- function SelectDropdown({ state, multiple, searchable, searchPlaceholder = 'Поиск по списку', noResultsText = 'Ничего не найдено', dataTestId }) {
10
+ function SelectDropdown({ state, multiple, searchable, hasOutline, searchPlaceholder = 'Поиск по списку', noResultsText = 'Ничего не найдено', dataTestId }) {
11
11
  const getDataTestId = useDataTestId(dataTestId);
12
12
  const { options, selectedIds, search, activeId, setSearch, handleKeyDown, select } = state;
13
13
  const activeOptionRef = useRef(null);
@@ -23,6 +23,7 @@ function SelectDropdown({ state, multiple, searchable, searchPlaceholder = 'По
23
23
  searchable && /*#__PURE__*/ jsx(Input, {
24
24
  block: true,
25
25
  autoFocus: true,
26
+ hasOutline: hasOutline,
26
27
  size: 40,
27
28
  view: "secondary",
28
29
  value: search,
@@ -41,6 +41,7 @@
41
41
  --field-icon-color: var(--select-disabled-color);
42
42
  --field-placeholder-color: var(--select-disabled-color);
43
43
  --field-border: transparent;
44
+ cursor: not-allowed;
44
45
  }
45
46
  }
46
47
 
@@ -89,13 +90,13 @@
89
90
  }
90
91
  }
91
92
 
92
- .component-cM8DTg:not(.disabled-fZIeN_) .field-jGXYY5:focus-within, .component-cM8DTg.open-CCkiBd .field-jGXYY5 {
93
+ .component-cM8DTg[data-has-outline]:not(.disabled-fZIeN_) .field-jGXYY5:focus-within, .component-cM8DTg[data-has-outline].open-CCkiBd .field-jGXYY5 {
93
94
  box-shadow: none;
94
95
  outline: 2px solid var(--select-focus-border-color);
95
96
  outline-offset: 0;
96
97
  }
97
98
 
98
- .component-cM8DTg.error-TrKhzv:not(.disabled-fZIeN_) .field-jGXYY5:focus-within, .component-cM8DTg.error-TrKhzv.open-CCkiBd .field-jGXYY5 {
99
+ .component-cM8DTg[data-has-outline].error-TrKhzv:not(.disabled-fZIeN_) .field-jGXYY5:focus-within, .component-cM8DTg[data-has-outline].error-TrKhzv.open-CCkiBd .field-jGXYY5 {
99
100
  outline-color: var(--select-error-border-color);
100
101
  }
101
102
 
@@ -204,11 +205,11 @@
204
205
  }
205
206
  }
206
207
 
207
- .component-cM8DTg.disabled-fZIeN_ .trigger-nKdmww {
208
+ .component-cM8DTg.disabled-fZIeN_ .trigger-nKdmww, .component-cM8DTg.disabled-fZIeN_ .label-rrakc7 {
208
209
  cursor: not-allowed;
209
210
  }
210
211
 
211
- .component-cM8DTg.readOnly-zwkIeK .trigger-nKdmww {
212
+ .component-cM8DTg.readOnly-zwkIeK:not(.disabled-fZIeN_) .trigger-nKdmww {
212
213
  cursor: default;
213
214
  }
214
215
 
@@ -22,7 +22,7 @@ const dropdownOffset = [
22
22
  8
23
23
  ];
24
24
  const Select = /*#__PURE__*/ forwardRef((props, ref)=>{
25
- const { view = 'primary', size = 40, label, labelView = 'outer', block = false, placeholder = 'Выберите значение', hint, error, clear = false, disabled = false, readOnly = false, className, style, labelClassName, messageClassName, dropdownClassName, dataTestId, id, name, form, onClear: _onClear, getPortalContainer, options: _options, multiple, value: _value, defaultValue: _defaultValue, onChange: _onChange, searchable, searchPlaceholder, open: _open, defaultOpen: _defaultOpen, onOpenChange: _onOpenChange, noResultsText, ...buttonProps } = props;
25
+ const { view = 'primary', size = 40, label, labelView = 'outer', block = false, hasOutline = false, placeholder = 'Выберите значение', hint, error, clear = false, disabled = false, readOnly = false, className, style, labelClassName, messageClassName, dropdownClassName, dataTestId, id, name, form, onClear: _onClear, getPortalContainer, options: _options, multiple, value: _value, defaultValue: _defaultValue, onChange: _onChange, searchable, searchPlaceholder, open: _open, defaultOpen: _defaultOpen, onOpenChange: _onOpenChange, noResultsText, ...buttonProps } = props;
26
26
  const generatedId = useId();
27
27
  const triggerId = id ?? generatedId;
28
28
  const getDataTestId = useDataTestId(dataTestId);
@@ -48,6 +48,7 @@ const Select = /*#__PURE__*/ forwardRef((props, ref)=>{
48
48
  [index_module.labelRaised]: innerLabel && raisedLabel
49
49
  }, className),
50
50
  style: style,
51
+ "data-has-outline": hasOutline || void 0,
51
52
  "data-test-id": getDataTestId('form-control'),
52
53
  children: [
53
54
  hasLabel && 'outer' === labelView && /*#__PURE__*/ jsx("label", {
@@ -149,6 +150,7 @@ const Select = /*#__PURE__*/ forwardRef((props, ref)=>{
149
150
  state: state,
150
151
  multiple: multiple,
151
152
  searchable: searchable,
153
+ hasOutline: hasOutline,
152
154
  searchPlaceholder: searchPlaceholder,
153
155
  noResultsText: noResultsText,
154
156
  dataTestId: dataTestId
@@ -27,7 +27,7 @@ export type SelectChangePayload<Value = string | null> = {
27
27
  */
28
28
  selectedOptions: SelectOption[];
29
29
  };
30
- type CommonProps = Pick<InputProps, 'view' | 'size' | 'block' | 'hint' | 'error' | 'clear' | 'className' | 'style' | 'labelClassName' | 'messageClassName'> & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'value' | 'defaultValue' | 'onChange' | 'children' | 'name' | 'type' | 'className' | 'style'> & {
30
+ type CommonProps = Pick<InputProps, 'view' | 'size' | 'block' | 'hasOutline' | 'hint' | 'error' | 'clear' | 'className' | 'style' | 'labelClassName' | 'messageClassName'> & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'value' | 'defaultValue' | 'onChange' | 'children' | 'name' | 'type' | 'className' | 'style'> & {
31
31
  /**
32
32
  * Варианты с уникальными id. Порядок строк сохраняется при выборе.
33
33
  */
@@ -0,0 +1,13 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ export type ContentProps = HTMLAttributes<HTMLDivElement> & {
3
+ /**
4
+ * Базовый идентификатор части -content. По умолчанию наследуется от Sidepanel.
5
+ */
6
+ dataTestId?: string;
7
+ };
8
+ export declare const Content: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
9
+ /**
10
+ * Базовый идентификатор части -content. По умолчанию наследуется от Sidepanel.
11
+ */
12
+ dataTestId?: string;
13
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import classnames from "classnames";
3
+ import { forwardRef, useContext } from "react";
4
+ import { useDataTestId } from "../../../../hooks/use-data-test-id.js";
5
+ import { SidepanelContext } from "../../context.js";
6
+ import index_module from "../../index.module.js";
7
+ import scrollbar_module from "../../scrollbar.module.js";
8
+ import index_module_0 from "./index.module.js";
9
+ const Content = /*#__PURE__*/ forwardRef(({ className, dataTestId, children, ...restProps }, ref)=>{
10
+ const { dataTestId: panelDataTestId } = useContext(SidepanelContext);
11
+ const getDataTestId = useDataTestId(dataTestId ?? panelDataTestId);
12
+ return /*#__PURE__*/ jsx("div", {
13
+ ...restProps,
14
+ ref: ref,
15
+ className: classnames(index_module_0.component, index_module.content, scrollbar_module.scrollbar, className),
16
+ "data-test-id": getDataTestId('content'),
17
+ children: children
18
+ });
19
+ });
20
+ Content.displayName = 'SidepanelContent';
21
+ export { Content };
@@ -0,0 +1,5 @@
1
+ import "./index_module.css";
2
+ const index_module = {
3
+ component: "component-vYcjBo"
4
+ };
5
+ export default index_module;
@@ -0,0 +1,14 @@
1
+ .component-vYcjBo {
2
+ box-sizing: border-box;
3
+ gap: var(--size-16);
4
+ min-width: 0;
5
+ min-height: 0;
6
+ padding: var(--sidepanel-padding);
7
+ overscroll-behavior: contain;
8
+ overflow-wrap: anywhere;
9
+ flex-direction: column;
10
+ flex: auto;
11
+ display: flex;
12
+ overflow: auto;
13
+ }
14
+
@@ -0,0 +1,66 @@
1
+ import { type ComponentType, type HTMLAttributes } from 'react';
2
+ import type { ButtonProps } from '../../../Button/index.js';
3
+ export type FooterProps = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'dangerouslySetInnerHTML'> & {
4
+ /**
5
+ * Компонент основной кнопки. Обязателен; располагается после secondary (справа в строке).
6
+ * Внешний вид и обработчики задаются внутри переданного компонента.
7
+ */
8
+ primary: ComponentType<ButtonProps>;
9
+ /**
10
+ * Компонент вторичной кнопки. Располагается перед primary (слева в строке).
11
+ * Не выводится, если проп не передан.
12
+ */
13
+ secondary?: ComponentType<ButtonProps>;
14
+ /**
15
+ * Минимальное расстояние между кнопками в пикселях из общей шкалы отступов.
16
+ * @default 12
17
+ */
18
+ gap?: 12 | 16 | 24 | 32;
19
+ /**
20
+ * Оставляет кнопки видимыми при прокрутке. При false Footer прокручивается с Content.
21
+ * @default true
22
+ */
23
+ sticky?: boolean;
24
+ /**
25
+ * Выравнивание кнопок. В space-between кнопки находятся у противоположных краёв,
26
+ * единственная primary — справа. В column secondary находится над primary.
27
+ * @default 'space-between'
28
+ */
29
+ layout?: 'start' | 'end' | 'center' | 'space-between' | 'column';
30
+ /**
31
+ * Базовый идентификатор части -footer. По умолчанию наследуется от Sidepanel.
32
+ */
33
+ dataTestId?: string;
34
+ };
35
+ export declare const Footer: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children" | "dangerouslySetInnerHTML"> & {
36
+ /**
37
+ * Компонент основной кнопки. Обязателен; располагается после secondary (справа в строке).
38
+ * Внешний вид и обработчики задаются внутри переданного компонента.
39
+ */
40
+ primary: ComponentType<ButtonProps>;
41
+ /**
42
+ * Компонент вторичной кнопки. Располагается перед primary (слева в строке).
43
+ * Не выводится, если проп не передан.
44
+ */
45
+ secondary?: ComponentType<ButtonProps>;
46
+ /**
47
+ * Минимальное расстояние между кнопками в пикселях из общей шкалы отступов.
48
+ * @default 12
49
+ */
50
+ gap?: 12 | 16 | 24 | 32;
51
+ /**
52
+ * Оставляет кнопки видимыми при прокрутке. При false Footer прокручивается с Content.
53
+ * @default true
54
+ */
55
+ sticky?: boolean;
56
+ /**
57
+ * Выравнивание кнопок. В space-between кнопки находятся у противоположных краёв,
58
+ * единственная primary — справа. В column secondary находится над primary.
59
+ * @default 'space-between'
60
+ */
61
+ layout?: "start" | "end" | "center" | "space-between" | "column";
62
+ /**
63
+ * Базовый идентификатор части -footer. По умолчанию наследуется от Sidepanel.
64
+ */
65
+ dataTestId?: string;
66
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,25 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import classnames from "classnames";
3
+ import { forwardRef, useContext } from "react";
4
+ import { useDataTestId } from "../../../../hooks/use-data-test-id.js";
5
+ import { SidepanelContext } from "../../context.js";
6
+ import index_module from "../../index.module.js";
7
+ import index_module_0 from "./index.module.js";
8
+ const Footer = /*#__PURE__*/ forwardRef(({ primary: Primary, secondary: Secondary, gap = 12, sticky = true, layout = 'space-between', className, dataTestId, ...restProps }, ref)=>{
9
+ const { dataTestId: panelDataTestId } = useContext(SidepanelContext);
10
+ const getDataTestId = useDataTestId(dataTestId ?? panelDataTestId);
11
+ return /*#__PURE__*/ jsxs("div", {
12
+ ...restProps,
13
+ ref: ref,
14
+ className: classnames(index_module_0.component, index_module_0[layout], index_module_0[`gap-${gap}`], {
15
+ [index_module.nonStickyFooter]: !sticky
16
+ }, className),
17
+ "data-test-id": getDataTestId('footer'),
18
+ children: [
19
+ Secondary && /*#__PURE__*/ jsx(Secondary, {}),
20
+ /*#__PURE__*/ jsx(Primary, {})
21
+ ]
22
+ });
23
+ });
24
+ Footer.displayName = 'SidepanelFooter';
25
+ export { Footer };
@@ -0,0 +1,19 @@
1
+ import "./index_module.css";
2
+ const index_module = {
3
+ component: "component-skRGIM",
4
+ column: "column-eFrOSV",
5
+ start: "start-AsGQua",
6
+ end: "end-GUmizA",
7
+ center: "center-ZCtISA",
8
+ "space-between": "space-between-JgT7jT",
9
+ spaceBetween: "space-between-JgT7jT",
10
+ "gap-12": "gap-12-vpXLHP",
11
+ gap12: "gap-12-vpXLHP",
12
+ "gap-16": "gap-16-YnuaQb",
13
+ gap16: "gap-16-YnuaQb",
14
+ "gap-24": "gap-24-Z4lCD9",
15
+ gap24: "gap-24-Z4lCD9",
16
+ "gap-32": "gap-32-dJPD9x",
17
+ gap32: "gap-32-dJPD9x"
18
+ };
19
+ export default index_module;
@@ -0,0 +1,57 @@
1
+ .component-skRGIM {
2
+ box-sizing: border-box;
3
+ padding: var(--sidepanel-padding);
4
+ flex-shrink: 0;
5
+ align-items: center;
6
+ display: flex;
7
+ }
8
+
9
+ .component-skRGIM:not(.column-eFrOSV) > button[data-block="true"] {
10
+ flex: 1 1 0;
11
+ }
12
+
13
+ .start-AsGQua {
14
+ justify-content: flex-start;
15
+ }
16
+
17
+ .end-GUmizA {
18
+ justify-content: flex-end;
19
+ }
20
+
21
+ .center-ZCtISA {
22
+ justify-content: center;
23
+ }
24
+
25
+ .space-between-JgT7jT {
26
+ justify-content: space-between;
27
+ }
28
+
29
+ .space-between-JgT7jT > :only-child {
30
+ margin-left: auto;
31
+ }
32
+
33
+ .column-eFrOSV {
34
+ flex-direction: column;
35
+ align-items: stretch;
36
+ }
37
+
38
+ .column-eFrOSV > button {
39
+ width: 100%;
40
+ }
41
+
42
+ .gap-12-vpXLHP {
43
+ gap: var(--size-12);
44
+ }
45
+
46
+ .gap-16-YnuaQb {
47
+ gap: var(--size-16);
48
+ }
49
+
50
+ .gap-24-Z4lCD9 {
51
+ gap: var(--size-24);
52
+ }
53
+
54
+ .gap-32-dJPD9x {
55
+ gap: var(--size-32);
56
+ }
57
+
@@ -0,0 +1,33 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ export type HeaderProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'children'> & {
3
+ /**
4
+ * Необязательный заголовок панели. Без заголовка и крестика Header не выводится.
5
+ */
6
+ title?: string;
7
+ /**
8
+ * Показывает крестик, который вызывает onClose ближайшего Sidepanel.
9
+ * @default true
10
+ */
11
+ hasCloser?: boolean;
12
+ /**
13
+ * Базовый идентификатор частей -header и -closer.
14
+ * По умолчанию наследуется от Sidepanel.
15
+ */
16
+ dataTestId?: string;
17
+ };
18
+ export declare const Header: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children" | "title"> & {
19
+ /**
20
+ * Необязательный заголовок панели. Без заголовка и крестика Header не выводится.
21
+ */
22
+ title?: string;
23
+ /**
24
+ * Показывает крестик, который вызывает onClose ближайшего Sidepanel.
25
+ * @default true
26
+ */
27
+ hasCloser?: boolean;
28
+ /**
29
+ * Базовый идентификатор частей -header и -closer.
30
+ * По умолчанию наследуется от Sidepanel.
31
+ */
32
+ dataTestId?: string;
33
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,45 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { CrossHeavyMIcon } from "@koslibs/icons/CrossHeavyMIcon";
3
+ import classnames from "classnames";
4
+ import { forwardRef, useContext } from "react";
5
+ import { useDataTestId } from "../../../../hooks/use-data-test-id.js";
6
+ import { Button } from "../../../Button/index.js";
7
+ import { TypographyText } from "../../../Typography/index.js";
8
+ import { SidepanelContext } from "../../context.js";
9
+ import index_module from "../../index.module.js";
10
+ import index_module_0 from "./index.module.js";
11
+ const Header = /*#__PURE__*/ forwardRef(({ title, hasCloser = true, className, dataTestId, ...restProps }, ref)=>{
12
+ const { onClose, dataTestId: panelDataTestId } = useContext(SidepanelContext);
13
+ const getDataTestId = useDataTestId(dataTestId ?? panelDataTestId);
14
+ if (!title?.trim() && !hasCloser) return null;
15
+ return /*#__PURE__*/ jsxs("div", {
16
+ ...restProps,
17
+ ref: ref,
18
+ className: classnames(index_module_0.component, index_module.header, className),
19
+ "data-test-id": getDataTestId('header'),
20
+ children: [
21
+ /*#__PURE__*/ jsx("div", {
22
+ className: index_module_0.title,
23
+ children: title && /*#__PURE__*/ jsx(TypographyText, {
24
+ size: 20,
25
+ weight: "semibold",
26
+ children: title
27
+ })
28
+ }),
29
+ hasCloser && /*#__PURE__*/ jsx(Button, {
30
+ size: 32,
31
+ view: "transparent",
32
+ className: index_module_0.closer,
33
+ labelClassName: index_module_0.closerLabel,
34
+ onClick: onClose,
35
+ dataTestId: getDataTestId('closer'),
36
+ children: /*#__PURE__*/ jsx(CrossHeavyMIcon, {
37
+ width: 24,
38
+ height: 24
39
+ })
40
+ })
41
+ ]
42
+ });
43
+ });
44
+ Header.displayName = 'SidepanelHeader';
45
+ export { Header };
@@ -0,0 +1,8 @@
1
+ import "./index_module.css";
2
+ const index_module = {
3
+ component: "component-pnHhxm",
4
+ title: "title-nolsR9",
5
+ closer: "closer-PhFI6O",
6
+ closerLabel: "closerLabel-q4m2AK"
7
+ };
8
+ export default index_module;