@hh.ru/magritte-ui-chat-input 1.0.1

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 (67) hide show
  1. package/index.css +286 -0
  2. package/index.d.ts +7 -0
  3. package/index.js +38 -0
  4. package/index.js.map +1 -0
  5. package/index.mock.d.ts +7 -0
  6. package/index.mock.js +47 -0
  7. package/index.mock.js.map +1 -0
  8. package/internal/ChatInputContext.d.ts +13 -0
  9. package/internal/ChatInputContext.js +9 -0
  10. package/internal/ChatInputContext.js.map +1 -0
  11. package/internal/ChatInputSlotButton.d.ts +16 -0
  12. package/internal/ChatInputSlotButton.js +25 -0
  13. package/internal/ChatInputSlotButton.js.map +1 -0
  14. package/internal/FocusTarget.d.ts +14 -0
  15. package/internal/FocusTarget.js +17 -0
  16. package/internal/FocusTarget.js.map +1 -0
  17. package/internal/ModeTransition.d.ts +12 -0
  18. package/internal/ModeTransition.js +29 -0
  19. package/internal/ModeTransition.js.map +1 -0
  20. package/internal/TextInput.d.ts +18 -0
  21. package/internal/TextInput.js +149 -0
  22. package/internal/TextInput.js.map +1 -0
  23. package/internal/VoiceRecord.d.ts +24 -0
  24. package/internal/VoiceRecord.js +45 -0
  25. package/internal/VoiceRecord.js.map +1 -0
  26. package/internal/VoiceRecordIndicator.d.ts +4 -0
  27. package/internal/VoiceRecordIndicator.js +22 -0
  28. package/internal/VoiceRecordIndicator.js.map +1 -0
  29. package/internal/focusGroup.d.ts +22 -0
  30. package/internal/focusGroup.js +36 -0
  31. package/internal/focusGroup.js.map +1 -0
  32. package/internal/useHasSound.d.ts +7 -0
  33. package/internal/useHasSound.js +77 -0
  34. package/internal/useHasSound.js.map +1 -0
  35. package/internal/useRovingFocusGroup.d.ts +19 -0
  36. package/internal/useRovingFocusGroup.js +151 -0
  37. package/internal/useRovingFocusGroup.js.map +1 -0
  38. package/internal/useSendOnEnter.d.ts +13 -0
  39. package/internal/useSendOnEnter.js +42 -0
  40. package/internal/useSendOnEnter.js.map +1 -0
  41. package/internal/useVoiceRecordSession.d.ts +51 -0
  42. package/internal/useVoiceRecordSession.js +245 -0
  43. package/internal/useVoiceRecordSession.js.map +1 -0
  44. package/internal/voiceSlotRegistry.d.ts +6 -0
  45. package/internal/voiceSlotRegistry.js +10 -0
  46. package/internal/voiceSlotRegistry.js.map +1 -0
  47. package/package.json +44 -0
  48. package/public/ChatInput.d.ts +47 -0
  49. package/public/ChatInput.js +122 -0
  50. package/public/ChatInput.js.map +1 -0
  51. package/public/ChatInputChips.d.ts +6 -0
  52. package/public/ChatInputChips.js +16 -0
  53. package/public/ChatInputChips.js.map +1 -0
  54. package/public/ChatInputPreview.d.ts +12 -0
  55. package/public/ChatInputPreview.js +14 -0
  56. package/public/ChatInputPreview.js.map +1 -0
  57. package/public/ChatInputSendButton.d.ts +14 -0
  58. package/public/ChatInputSendButton.js +38 -0
  59. package/public/ChatInputSendButton.js.map +1 -0
  60. package/public/ChatInputVoiceButton.d.ts +17 -0
  61. package/public/ChatInputVoiceButton.js +91 -0
  62. package/public/ChatInputVoiceButton.js.map +1 -0
  63. package/styles-CQQdqOFh.js +5 -0
  64. package/styles-CQQdqOFh.js.map +1 -0
  65. package/types.d.ts +46 -0
  66. package/types.js +3 -0
  67. package/types.js.map +1 -0
package/index.css ADDED
@@ -0,0 +1,286 @@
1
+ :root{
2
+ --magritte-color-background-primary-v24-8-0:#ffffff;
3
+ --magritte-color-stroke-neutral-v24-8-0:#DCE3EB;
4
+ --magritte-color-stroke-state-focused-accessible-v24-8-0:#0070ff7a;
5
+ --magritte-color-stroke-state-neutral-hovered-v24-8-0:#DCE3EB;
6
+ --magritte-color-text-primary-v24-8-0:#000000;
7
+ --magritte-color-text-secondary-v24-8-0:#768694;
8
+ --magritte-color-text-tertiary-v24-8-0:#AABBCA;
9
+ --magritte-color-icon-tertiary-v24-8-0:#AABBCA;
10
+ --magritte-gradient-component-chat-input-background-fade-v24-8-0:#ffffff00 0%, #ffffff 100%;
11
+ --magritte-shadow-level-1-color-v24-8-0:#7090b029;
12
+ --magritte-shadow-level-1-x-v24-8-0:0px;
13
+ --magritte-shadow-level-1-y-v24-8-0:4px;
14
+ --magritte-shadow-level-1-blur-v24-8-0:12px;
15
+ --magritte-shadow-level-1-spread-v24-8-0:0px;
16
+ }
17
+
18
+ :root{
19
+ --magritte-semantic-animation-time-s-duration-v24-8-0:200ms;
20
+ --magritte-semantic-animation-ease-base-timing-function-v24-8-0:cubic-bezier(0.4, 0.24, 0, 1);
21
+ }
22
+
23
+ .magritte-night-theme{
24
+ --magritte-color-background-primary-v24-8-0:#1B1B1B;
25
+ --magritte-color-stroke-neutral-v24-8-0:#303030;
26
+ --magritte-color-stroke-state-neutral-hovered-v24-8-0:#303030;
27
+ --magritte-color-text-primary-v24-8-0:#ffffff;
28
+ --magritte-color-text-secondary-v24-8-0:#ABABAB;
29
+ --magritte-color-text-tertiary-v24-8-0:#767676;
30
+ --magritte-color-icon-tertiary-v24-8-0:#767676;
31
+ --magritte-gradient-component-chat-input-background-fade-v24-8-0:#1b1b1b00 0%, #1b1b1b 100%;
32
+ --magritte-shadow-level-1-color-v24-8-0:#00000029;
33
+ }
34
+ .magritte-chat-input___P69oN_1-0-1{
35
+ --chat-input-stroke-color:var(--magritte-color-stroke-neutral-v24-8-0);
36
+ --chat-input-focus-shadow:0 0 0 transparent;
37
+ box-shadow:inset 0 0 0 1px var(--chat-input-stroke-color), var(--chat-input-focus-shadow);
38
+ border-radius:24px;
39
+ padding:12px;
40
+ overflow:hidden;
41
+ background-color:var(--magritte-color-background-primary-v24-8-0);
42
+ --magritte-ui-scrollbar-offset-right:-12px;
43
+ }
44
+ .magritte-chat-input___P69oN_1-0-1.magritte-no-background___eiIY1_1-0-1{
45
+ background:none;
46
+ --chat-input-stroke-color:transparent !important;
47
+ }
48
+ .magritte-chat-input___P69oN_1-0-1:has(*:focus){
49
+ --chat-input-focus-shadow:var(--magritte-shadow-level-1-x-v24-8-0) var(--magritte-shadow-level-1-y-v24-8-0) var(--magritte-shadow-level-1-blur-v24-8-0) var(--magritte-shadow-level-1-spread-v24-8-0) var(--magritte-shadow-level-1-color-v24-8-0);
50
+ }
51
+ .magritte-chat-input___P69oN_1-0-1:has(.magritte-text-input-focused___TfyLu_1-0-1){
52
+ outline:4px solid var(--magritte-color-stroke-state-focused-accessible-v24-8-0);
53
+ }
54
+ @media (min-width: 1020px){
55
+ body.magritte-old-layout .magritte-chat-input___P69oN_1-0-1:hover{
56
+ --chat-input-stroke-color:var(--magritte-color-stroke-state-neutral-hovered-v24-8-0);
57
+ }
58
+ }
59
+ @media (min-width: 1024px){
60
+ body:not(.magritte-old-layout) .magritte-chat-input___P69oN_1-0-1:hover{
61
+ --chat-input-stroke-color:var(--magritte-color-stroke-state-neutral-hovered-v24-8-0);
62
+ }
63
+ }
64
+ .magritte-content-grid___g03sT_1-0-1{
65
+ display:grid;
66
+ grid-template-columns:auto minmax(0, 1fr) auto;
67
+ }
68
+ .magritte-content-grid___g03sT_1-0-1 .magritte-preview___baXKi_1-0-1{
69
+ grid-area:1 / 1 / 2 / -1;
70
+ }
71
+ .magritte-content-grid___g03sT_1-0-1 .magritte-left___7JZYp_1-0-1{
72
+ grid-area:3 / 1;
73
+ align-self:end;
74
+ }
75
+ .magritte-content-grid___g03sT_1-0-1 .magritte-central___zkguj_1-0-1{
76
+ grid-area:3 / 2;
77
+ align-self:center;
78
+ }
79
+ .magritte-content-grid___g03sT_1-0-1 .magritte-right___-tGcO_1-0-1{
80
+ grid-area:3 / 3;
81
+ align-self:end;
82
+ }
83
+ .magritte-content-grid___g03sT_1-0-1 .magritte-scrollbar-cell___--M2x_1-0-1{
84
+ grid-area:2 / 1 / 3 / -1;
85
+ position:relative;
86
+ pointer-events:none;
87
+ }
88
+ .magritte-content-grid___g03sT_1-0-1 .magritte-scrollbar-cell___--M2x_1-0-1 > div{
89
+ pointer-events:auto;
90
+ }
91
+ .magritte-content-grid___g03sT_1-0-1 .magritte-text-input___3AFO4_1-0-1{
92
+ grid-area:3 / 2;
93
+ }
94
+ .magritte-content-grid___g03sT_1-0-1 .magritte-text-input___3AFO4_1-0-1.magritte-layout-vertical___mVTr-_1-0-1,
95
+ .magritte-content-grid___g03sT_1-0-1.magritte-has-central___VRZik_1-0-1 .magritte-text-input___3AFO4_1-0-1{
96
+ grid-area:2 / 1 / 3 / -1;
97
+ }
98
+ .magritte-content-grid___g03sT_1-0-1.magritte-no-left-controls___7r6yy_1-0-1:not(.magritte-has-central___VRZik_1-0-1) .magritte-text-input___3AFO4_1-0-1{
99
+ grid-area:2 / 1 / 4 / 3;
100
+ }
101
+ .magritte-content-grid___g03sT_1-0-1.magritte-no-right-controls___HBSag_1-0-1:not(.magritte-has-central___VRZik_1-0-1) .magritte-text-input___3AFO4_1-0-1{
102
+ grid-area:2 / 2 / 4 / -1;
103
+ }
104
+ .magritte-content-grid___g03sT_1-0-1.magritte-no-left-controls___7r6yy_1-0-1:not(.magritte-has-central___VRZik_1-0-1){
105
+ grid-template-rows:auto minmax(0, 1fr) auto;
106
+ }
107
+ .magritte-right___-tGcO_1-0-1,
108
+ .magritte-left___7JZYp_1-0-1{
109
+ display:flex;
110
+ gap:8px;
111
+ }
112
+ .magritte-chips___gLao5_1-0-1{
113
+ display:flex;
114
+ flex-wrap:nowrap;
115
+ width:max-content;
116
+ gap:8px;
117
+ }
118
+ .magritte-chips-container___-bS5O_1-0-1{
119
+ padding:0 12px;
120
+ }
121
+ .magritte-no-left-controls___7r6yy_1-0-1 .magritte-chips-container___-bS5O_1-0-1{
122
+ padding-left:0;
123
+ }
124
+ .magritte-no-right-controls___HBSag_1-0-1 .magritte-chips-container___-bS5O_1-0-1{
125
+ padding-right:0;
126
+ }
127
+ .magritte-preview-block___iiPdS_1-0-1{
128
+ display:flex;
129
+ align-items:center;
130
+ gap:12px;
131
+ padding-bottom:8px;
132
+ }
133
+ .magritte-preview-side___ymR-u_1-0-1{
134
+ display:flex;
135
+ flex-shrink:0;
136
+ }
137
+ .magritte-preview-content___ZgyBD_1-0-1{
138
+ display:flex;
139
+ flex:1;
140
+ flex-direction:column;
141
+ min-width:0;
142
+ }
143
+ .magritte-preview-title___SkFeZ_1-0-1{
144
+ font-family:"hh sans";
145
+ font-weight:500;
146
+ line-height:18px;
147
+ font-size:12px;
148
+ letter-spacing:0.01em;
149
+ text-indent:0px;
150
+ text-transform:none;
151
+ text-decoration:none;
152
+ color:var(--magritte-color-text-primary-v24-8-0);
153
+ }
154
+ .magritte-preview-subtitle___58qfR_1-0-1{
155
+ font-family:"hh sans";
156
+ font-weight:400;
157
+ line-height:20px;
158
+ font-size:14px;
159
+ letter-spacing:0.005em;
160
+ text-indent:0px;
161
+ text-transform:none;
162
+ text-decoration:none;
163
+ overflow:hidden;
164
+ color:var(--magritte-color-text-secondary-v24-8-0);
165
+ white-space:nowrap;
166
+ text-overflow:ellipsis;
167
+ }
168
+ .magritte-text-input-wrapper___TPbwC_1-0-1{
169
+ display:contents;
170
+ }
171
+ .magritte-text-input___3AFO4_1-0-1{
172
+ box-sizing:content-box;
173
+ padding:0 12px;
174
+ border:none;
175
+ background:none;
176
+ outline:none;
177
+ scrollbar-width:none;
178
+ width:calc(100% - 24px);
179
+ resize:none;
180
+ align-self:center;
181
+ --fade-top:linear-gradient(#000, #000);
182
+ --fade-bottom:linear-gradient(#000, #000);
183
+ scroll-padding-top:16px;
184
+ scroll-padding-bottom:16px;
185
+ font-family:"hh sans";
186
+ font-weight:400;
187
+ line-height:20px;
188
+ font-size:14px;
189
+ letter-spacing:0.005em;
190
+ text-indent:0px;
191
+ text-transform:none;
192
+ text-decoration:none;
193
+ color:var(--magritte-color-text-primary-v24-8-0);
194
+ }
195
+ .magritte-text-input___3AFO4_1-0-1::placeholder{
196
+ color:var(--magritte-color-text-tertiary-v24-8-0);
197
+ opacity:1;
198
+ }
199
+ .magritte-text-input___3AFO4_1-0-1:focus,
200
+ .magritte-text-input___3AFO4_1-0-1:focus-visible{
201
+ border:none;
202
+ outline:none;
203
+ }
204
+ .magritte-text-input___3AFO4_1-0-1.magritte-with-top-fade___okgqo_1-0-1,
205
+ .magritte-text-input___3AFO4_1-0-1.magritte-with-bottom-fade___82gr8_1-0-1{
206
+ -webkit-mask:var(--fade-top) top / 100% 16px no-repeat, linear-gradient(#000, #000) center / 100% calc(100% - 32px) no-repeat, var(--fade-bottom) bottom / 100% 16px no-repeat;
207
+ mask:var(--fade-top) top / 100% 16px no-repeat, linear-gradient(#000, #000) center / 100% calc(100% - 32px) no-repeat, var(--fade-bottom) bottom / 100% 16px no-repeat;
208
+ }
209
+ .magritte-text-input___3AFO4_1-0-1.magritte-with-top-fade___okgqo_1-0-1{
210
+ --fade-top:linear-gradient(180deg, var(--magritte-gradient-component-chat-input-background-fade-v24-8-0));
211
+ }
212
+ .magritte-text-input___3AFO4_1-0-1.magritte-with-bottom-fade___82gr8_1-0-1{
213
+ --fade-bottom:linear-gradient(0deg, var(--magritte-gradient-component-chat-input-background-fade-v24-8-0));
214
+ }
215
+ .magritte-text-input___3AFO4_1-0-1.magritte-measure___xNKc0_1-0-1{
216
+ height:20px !important;
217
+ padding:0 !important;
218
+ margin:0 !important;
219
+ }
220
+ .magritte-voice-record___p64PV_1-0-1{
221
+ display:flex;
222
+ align-items:center;
223
+ justify-content:space-between;
224
+ width:100%;
225
+ box-sizing:border-box;
226
+ padding-left:12px;
227
+ min-height:40px;
228
+ gap:8px;
229
+ }
230
+ .magritte-voice-record-left___njEt-_1-0-1{
231
+ display:flex;
232
+ align-items:center;
233
+ gap:8px;
234
+ min-width:0;
235
+ }
236
+ .magritte-voice-record-right___UTv-U_1-0-1{
237
+ display:flex;
238
+ align-items:center;
239
+ gap:8px;
240
+ }
241
+ .magritte-voice-record-right___UTv-U_1-0-1.magritte-voice-processing___C0xnX_1-0-1{
242
+ padding:8px;
243
+ }
244
+ .magritte-voice-record-indicator___V8ybb_1-0-1{
245
+ display:flex;
246
+ flex-shrink:0;
247
+ align-items:center;
248
+ gap:2px;
249
+ }
250
+ .magritte-voice-record-indicator-bar___jgyqz_1-0-1{
251
+ width:2px;
252
+ min-height:2px;
253
+ max-height:2px;
254
+ border-radius:1000px;
255
+ background-color:var(--magritte-color-icon-tertiary-v24-8-0);
256
+ transition:max-height var(--magritte-semantic-animation-time-s-duration-v24-8-0) var(--magritte-semantic-animation-ease-base-timing-function-v24-8-0);
257
+ animation:magritte-voice-record-indicator-wave___4VsyE_1-0-1 400ms linear infinite alternate paused;
258
+ }
259
+ .magritte-voice-record-indicator-has-sound___67t5g_1-0-1 .magritte-voice-record-indicator-bar___jgyqz_1-0-1{
260
+ max-height:27px;
261
+ animation-play-state:running;
262
+ }
263
+ @media (prefers-reduced-motion: reduce){
264
+ .magritte-voice-record-indicator-has-sound___67t5g_1-0-1 .magritte-voice-record-indicator-bar___jgyqz_1-0-1{
265
+ animation-play-state:paused;
266
+ }
267
+ }
268
+ @keyframes magritte-voice-record-indicator-wave___4VsyE_1-0-1{
269
+ from{
270
+ height:calc(var(--target-height) * 0.62px);
271
+ }
272
+ to{
273
+ height:calc(var(--target-height) * 1.22px);
274
+ }
275
+ }
276
+ .magritte-mode-container___nATVD_1-0-1{
277
+ position:relative;
278
+ display:grid;
279
+ align-content:end;
280
+ }
281
+ .magritte-voice-overlay___LbxAI_1-0-1{
282
+ position:absolute;
283
+ right:0;
284
+ bottom:0;
285
+ left:0;
286
+ }
package/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { ChatInput } from '@hh.ru/magritte-ui-chat-input/public/ChatInput';
2
+ export { type ChatInputProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInput';
3
+ export { type ChatInputVoiceRecordConfig } from '@hh.ru/magritte-ui-chat-input/types';
4
+ export { type ChatInputChipsProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputChips';
5
+ export { type ChatInputPreviewProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputPreview';
6
+ export { type ChatInputSendButtonProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputSendButton';
7
+ export { type ChatInputVoiceButtonProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputVoiceButton';
package/index.js ADDED
@@ -0,0 +1,38 @@
1
+ import './index.css';
2
+ export { ChatInput } from './public/ChatInput.js';
3
+ import 'react/jsx-runtime';
4
+ import 'react';
5
+ import 'classnames';
6
+ import '@hh.ru/magritte-common-use-disabled';
7
+ import '@hh.ru/magritte-common-use-multiple-refs';
8
+ import './internal/ChatInputContext.js';
9
+ import './internal/ModeTransition.js';
10
+ import 'motion/react';
11
+ import '@hh.ru/magritte-design-tokens/types';
12
+ import './styles-CQQdqOFh.js';
13
+ import './internal/TextInput.js';
14
+ import './internal/useSendOnEnter.js';
15
+ import '@hh.ru/magritte-common-keyboard';
16
+ import '@hh.ru/magritte-ui-scrollbar';
17
+ import './internal/VoiceRecord.js';
18
+ import '@hh.ru/magritte-common-use-translation';
19
+ import './internal/FocusTarget.js';
20
+ import './internal/focusGroup.js';
21
+ import './internal/VoiceRecordIndicator.js';
22
+ import './internal/useHasSound.js';
23
+ import '@hh.ru/magritte-ui-icon/icon';
24
+ import '@hh.ru/magritte-ui-loader';
25
+ import '@hh.ru/magritte-ui-typography';
26
+ import './internal/useRovingFocusGroup.js';
27
+ import './internal/useVoiceRecordSession.js';
28
+ import './internal/voiceSlotRegistry.js';
29
+ import '@hh.ru/magritte-internal-slot-registry';
30
+ import './public/ChatInputChips.js';
31
+ import '@hh.ru/magritte-ui-chips';
32
+ import '@hh.ru/magritte-ui-scrollable-container';
33
+ import './public/ChatInputPreview.js';
34
+ import '@hh.ru/magritte-ui-icon';
35
+ import './public/ChatInputSendButton.js';
36
+ import './internal/ChatInputSlotButton.js';
37
+ import './public/ChatInputVoiceButton.js';
38
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,7 @@
1
+ export { type ChatInputProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInput';
2
+ export { type ChatInputVoiceRecordConfig } from '@hh.ru/magritte-ui-chat-input/types';
3
+ export { type ChatInputChipsProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputChips';
4
+ export { type ChatInputPreviewProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputPreview';
5
+ export { type ChatInputSendButtonProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputSendButton';
6
+ export { type ChatInputVoiceButtonProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputVoiceButton';
7
+ export declare const ChatInput: import("react").ForwardRefExoticComponent<Record<string, unknown>>;
package/index.mock.js ADDED
@@ -0,0 +1,47 @@
1
+ import './index.css';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { forwardRef } from 'react';
4
+
5
+ function enhance(mockComponent, staticProps) {
6
+ // удаляем внутренний свойства реакта, чтобы не перезаписать ими mockComponent
7
+ delete staticProps.type;
8
+ delete staticProps.$$typeof;
9
+ delete staticProps.render;
10
+ for (const prop in staticProps) {
11
+ mockComponent[prop] = staticProps[prop];
12
+ }
13
+ }
14
+ const MockComponent = ({ style, ...props }) => (
15
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
16
+ // @ts-ignore
17
+ // eslint-disable-next-line react/jsx-no-undef
18
+ jsx("magritte-component-mock", { "style-data": style, ...props }));
19
+ /**
20
+ * Возвращает mock React компонента
21
+ */
22
+ const mockComponent = (componentName, staticProps = {}, { withChildren, innerDependence, WrapperComponent } = {}) => {
23
+ const mockWrapper = forwardRef(({ children, ...props }, ref) => {
24
+ const mock = (
25
+ // eslint-disable-next-line react/jsx-no-undef
26
+ jsxs(MockComponent, { ...props, _component: componentName, children: [innerDependence ? innerDependence(props, ref) : null, withChildren ? children : null] }));
27
+ return WrapperComponent ? jsx(WrapperComponent, { ...props, children: mock }) : mock;
28
+ });
29
+ staticProps && enhance(mockWrapper, staticProps);
30
+ mockWrapper.displayName = 'magritte-ref';
31
+ return mockWrapper;
32
+ };
33
+
34
+ const staticProps = {
35
+ Chips: mockComponent('Chips', undefined, {
36
+ withChildren: true,
37
+ }),
38
+ Preview: mockComponent('Preview'),
39
+ SendButton: mockComponent('SendButton'),
40
+ VoiceButton: mockComponent('VoiceButton'),
41
+ };
42
+ const ChatInput = mockComponent('ChatInput', staticProps, {
43
+ withChildren: true,
44
+ });
45
+
46
+ export { ChatInput };
47
+ //# sourceMappingURL=index.mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mock.js","sources":["../mock-component/lib/setupMockComponent.js","src/index.mock.ts"],"sourcesContent":["import { jsxs, jsx } from 'react/jsx-runtime';\nimport { forwardRef } from 'react';\n\nfunction enhance(mockComponent, staticProps) {\n // удаляем внутренний свойства реакта, чтобы не перезаписать ими mockComponent\n delete staticProps.type;\n delete staticProps.$$typeof;\n delete staticProps.render;\n for (const prop in staticProps) {\n mockComponent[prop] = staticProps[prop];\n }\n}\nconst MockComponent = ({ style, ...props }) => (\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\n// eslint-disable-next-line react/jsx-no-undef\njsx(\"magritte-component-mock\", { \"style-data\": style, ...props }));\nconst MockComponentRenderProp = ({ style, ...props }) => (\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\n// eslint-disable-next-line react/jsx-no-undef\njsx(\"magritte-component-mock-render-prop\", { \"style-data\": style, ...props }));\n/**\n * Возвращает mock React компонента\n */\nconst mockComponent = (componentName, staticProps = {}, { withChildren, innerDependence, WrapperComponent } = {}) => {\n const mockWrapper = forwardRef(({ children, ...props }, ref) => {\n const mock = (\n // eslint-disable-next-line react/jsx-no-undef\n jsxs(MockComponent, { ...props, _component: componentName, children: [innerDependence ? innerDependence(props, ref) : null, withChildren ? children : null] }));\n return WrapperComponent ? jsx(WrapperComponent, { ...props, children: mock }) : mock;\n });\n staticProps && enhance(mockWrapper, staticProps);\n mockWrapper.displayName = 'magritte-ref';\n return mockWrapper;\n};\n/**\n * Возвращает mock React компонента, который рендерит переданный в него пропс render\n *\n * @param {String} componentName наименование компонента\n * @param {Object} [staticProps] объект со свойствами, которыми необходимо усилить mock\n * @param {Object} [options] параметры мока\n * @param {boolean} [options.withChildren] флаг отвечает за рендеринг children компонента\n * @param {React.Component} [options.innerDependence] Реакт компонент, который рендерится внутри мока.\n * Необходим, когда mock компоненту требуется иметь\n * дополнительные элементы\n */\nconst mockComponentRenderProp = (componentName, staticProps, { withChildren, innerDependence, renderArguments } = { renderArguments: [] }) => {\n const mockWrapper = ({ render, children, ...props }) => (jsxs(MockComponentRenderProp, { ...props, _component: componentName, children: [innerDependence ? innerDependence(props) : null, withChildren ? children : null, render?.(...(renderArguments || []))] }));\n staticProps && enhance(mockWrapper, staticProps);\n mockWrapper.displayName = 'magritte-component-mock-render-prop';\n return mockWrapper;\n};\n\nexport { mockComponent, mockComponentRenderProp };\n//# sourceMappingURL=setupMockComponent.js.map\n","import { mockComponent } from '@hh.ru/magritte-ui-mock-component';\n\nexport { type ChatInputProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInput';\nexport { type ChatInputVoiceRecordConfig } from '@hh.ru/magritte-ui-chat-input/types';\nexport { type ChatInputChipsProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputChips';\nexport { type ChatInputPreviewProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputPreview';\nexport { type ChatInputSendButtonProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputSendButton';\nexport { type ChatInputVoiceButtonProps } from '@hh.ru/magritte-ui-chat-input/public/ChatInputVoiceButton';\n\nconst staticProps = {\n Chips: mockComponent('Chips', undefined, {\n withChildren: true,\n }),\n Preview: mockComponent('Preview'),\n SendButton: mockComponent('SendButton'),\n VoiceButton: mockComponent('VoiceButton'),\n};\n\nexport const ChatInput = mockComponent('ChatInput', staticProps, {\n withChildren: true,\n});\n"],"names":[],"mappings":";;;AAGA,SAAS,OAAO,CAAC,aAAa,EAAE,WAAW,EAAE;AAC7C;AACA,IAAI,OAAO,WAAW,CAAC,IAAI,CAAC;AAC5B,IAAI,OAAO,WAAW,CAAC,QAAQ,CAAC;AAChC,IAAI,OAAO,WAAW,CAAC,MAAM,CAAC;AAC9B,IAAI,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE;AACpC,QAAQ,aAAa,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;AAChD,KAAK;AACL,CAAC;AACD,MAAM,aAAa,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE;AAC1C;AACA;AACA;AACA,GAAG,CAAC,yBAAyB,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;AAMnE;AACA;AACA;AACA,MAAM,aAAa,GAAG,CAAC,aAAa,EAAE,WAAW,GAAG,EAAE,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,GAAG,EAAE,KAAK;AACrH,IAAI,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,KAAK;AACpE,QAAQ,MAAM,IAAI;AAClB;AACA,QAAQ,IAAI,CAAC,aAAa,EAAE,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,eAAe,GAAG,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AACxK,QAAQ,OAAO,gBAAgB,GAAG,GAAG,CAAC,gBAAgB,EAAE,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;AAC7F,KAAK,CAAC,CAAC;AACP,IAAI,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AACrD,IAAI,WAAW,CAAC,WAAW,GAAG,cAAc,CAAC;AAC7C,IAAI,OAAO,WAAW,CAAC;AACvB,CAAC;;AC1BD,MAAM,WAAW,GAAG;AAChB,IAAA,KAAK,EAAE,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE;AACrC,QAAA,YAAY,EAAE,IAAI;KACrB,CAAC;AACF,IAAA,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC;AACjC,IAAA,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC;AACvC,IAAA,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC;CAC5C,CAAC;MAEW,SAAS,GAAG,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE;AAC7D,IAAA,YAAY,EAAE,IAAI;AACrB,CAAA;;;;"}
@@ -0,0 +1,13 @@
1
+ import { type VoiceRecordStatus } from '@hh.ru/magritte-ui-chat-input/types';
2
+ interface ChatInputContextValue {
3
+ /** Хендлер отправки; SendButton вызывает его по клику */
4
+ onSend?: VoidFunction;
5
+ /** Старт записи голоса; VoiceButton вызывает его по клику. Есть внутри ChatInput
6
+ * (снаружи контекст пустой, кнопка неактивна) */
7
+ startVoiceRecord?: VoidFunction;
8
+ /** Статус сессии записи — VoiceButton показывает по нему ожидание доступа к микрофону */
9
+ voiceRecordStatus?: VoiceRecordStatus;
10
+ }
11
+ export declare const ChatInputContextProvider: import("react").Provider<ChatInputContextValue>;
12
+ export declare const useChatInputContext: () => ChatInputContextValue;
13
+ export {};
@@ -0,0 +1,9 @@
1
+ import './../index.css';
2
+ import { createContext, useContext } from 'react';
3
+
4
+ const ChatInputContext = createContext({});
5
+ const ChatInputContextProvider = ChatInputContext.Provider;
6
+ const useChatInputContext = () => useContext(ChatInputContext);
7
+
8
+ export { ChatInputContextProvider, useChatInputContext };
9
+ //# sourceMappingURL=ChatInputContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatInputContext.js","sources":["src/internal/ChatInputContext.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\nimport { type VoiceRecordStatus } from '@hh.ru/magritte-ui-chat-input/types';\n\ninterface ChatInputContextValue {\n /** Хендлер отправки; SendButton вызывает его по клику */\n onSend?: VoidFunction;\n /** Старт записи голоса; VoiceButton вызывает его по клику. Есть внутри ChatInput\n * (снаружи контекст пустой, кнопка неактивна) */\n startVoiceRecord?: VoidFunction;\n /** Статус сессии записи — VoiceButton показывает по нему ожидание доступа к микрофону */\n voiceRecordStatus?: VoiceRecordStatus;\n}\n\nconst ChatInputContext = createContext<ChatInputContextValue>({});\n\nexport const ChatInputContextProvider = ChatInputContext.Provider;\n\nexport const useChatInputContext = (): ChatInputContextValue => useContext(ChatInputContext);\n"],"names":[],"mappings":";;AAcA,MAAM,gBAAgB,GAAG,aAAa,CAAwB,EAAE,CAAC,CAAC;AAErD,MAAA,wBAAwB,GAAG,gBAAgB,CAAC,SAAS;AAErD,MAAA,mBAAmB,GAAG,MAA6B,UAAU,CAAC,gBAAgB;;;;"}
@@ -0,0 +1,16 @@
1
+ import { type FC, type MouseEventHandler, type ReactElement, type ReactNode } from 'react';
2
+ import { type IconProps } from '@hh.ru/magritte-ui-icon/types';
3
+ interface ChatInputSlotButtonProps {
4
+ /** Встроенная иконка со всеми пропсами — рендерится, когда children не переданы */
5
+ icon: ReactElement;
6
+ /** Те же пропсы иконки — уходят в IconDefaultPropsContext, чтобы children-иконка получила
7
+ * то же оформление/aria и (за счёт onClick) стала интерактивной кнопкой */
8
+ iconProps: Partial<IconProps>;
9
+ /** Обработчик клика (тот же, что в `iconProps.onClick`) — им ловим всплывший клик от
10
+ * произвольных (не-иконочных) children */
11
+ onClick?: MouseEventHandler<HTMLButtonElement>;
12
+ /** Кастомный контент вместо встроенной иконки */
13
+ children?: ReactNode;
14
+ }
15
+ export declare const ChatInputSlotButton: FC<ChatInputSlotButtonProps>;
16
+ export {};
@@ -0,0 +1,25 @@
1
+ import './../index.css';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { useRef, Children } from 'react';
4
+ import { useDisabled } from '@hh.ru/magritte-common-use-disabled';
5
+ import { IconDefaultPropsContext } from '@hh.ru/magritte-ui-icon';
6
+
7
+ const ChatInputSlotButton = ({ icon, iconProps, onClick, children }) => {
8
+ const handledEventRef = useRef(null);
9
+ const isDisabled = useDisabled();
10
+ if (Children.toArray(children).length === 0) {
11
+ return icon;
12
+ }
13
+ const handleClickOnce = (event) => {
14
+ if (handledEventRef.current === event.nativeEvent || isDisabled) {
15
+ return;
16
+ }
17
+ handledEventRef.current = event.nativeEvent;
18
+ onClick?.(event);
19
+ };
20
+ return (jsx(IconDefaultPropsContext, { props: { ...iconProps, onClick: handleClickOnce }, children: jsx("div", { style: { display: 'contents' }, onClick: handleClickOnce, children: children }) }));
21
+ };
22
+ ChatInputSlotButton.displayName = 'ChatInputSlotButton';
23
+
24
+ export { ChatInputSlotButton };
25
+ //# sourceMappingURL=ChatInputSlotButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatInputSlotButton.js","sources":["src/internal/ChatInputSlotButton.tsx"],"sourcesContent":["import {\n Children,\n type FC,\n type MouseEvent as ReactMouseEvent,\n type MouseEventHandler,\n type ReactElement,\n type ReactNode,\n useRef,\n} from 'react';\n\nimport { useDisabled } from '@hh.ru/magritte-common-use-disabled';\nimport { IconDefaultPropsContext } from '@hh.ru/magritte-ui-icon';\nimport { type IconProps } from '@hh.ru/magritte-ui-icon/types';\n\ninterface ChatInputSlotButtonProps {\n /** Встроенная иконка со всеми пропсами — рендерится, когда children не переданы */\n icon: ReactElement;\n /** Те же пропсы иконки — уходят в IconDefaultPropsContext, чтобы children-иконка получила\n * то же оформление/aria и (за счёт onClick) стала интерактивной кнопкой */\n iconProps: Partial<IconProps>;\n /** Обработчик клика (тот же, что в `iconProps.onClick`) — им ловим всплывший клик от\n * произвольных (не-иконочных) children */\n onClick?: MouseEventHandler<HTMLButtonElement>;\n /** Кастомный контент вместо встроенной иконки */\n children?: ReactNode;\n}\n\nexport const ChatInputSlotButton: FC<ChatInputSlotButtonProps> = ({ icon, iconProps, onClick, children }) => {\n const handledEventRef = useRef<Event | null>(null);\n const isDisabled = useDisabled();\n\n if (Children.toArray(children).length === 0) {\n return icon;\n }\n\n const handleClickOnce = (event: ReactMouseEvent<HTMLElement>) => {\n if (handledEventRef.current === event.nativeEvent || isDisabled) {\n return;\n }\n handledEventRef.current = event.nativeEvent;\n onClick?.(event as ReactMouseEvent<HTMLButtonElement>);\n };\n\n return (\n <IconDefaultPropsContext props={{ ...iconProps, onClick: handleClickOnce }}>\n <div style={{ display: 'contents' }} onClick={handleClickOnce}>\n {children}\n </div>\n </IconDefaultPropsContext>\n );\n};\n\nChatInputSlotButton.displayName = 'ChatInputSlotButton';\n"],"names":["_jsx"],"mappings":";;;;;AA2BO,MAAM,mBAAmB,GAAiC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAI;AACxG,IAAA,MAAM,eAAe,GAAG,MAAM,CAAe,IAAI,CAAC,CAAC;AACnD,IAAA,MAAM,UAAU,GAAG,WAAW,EAAE,CAAC;IAEjC,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;AACzC,QAAA,OAAO,IAAI,CAAC;KACf;AAED,IAAA,MAAM,eAAe,GAAG,CAAC,KAAmC,KAAI;QAC5D,IAAI,eAAe,CAAC,OAAO,KAAK,KAAK,CAAC,WAAW,IAAI,UAAU,EAAE;YAC7D,OAAO;SACV;AACD,QAAA,eAAe,CAAC,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC;AAC5C,QAAA,OAAO,GAAG,KAA2C,CAAC,CAAC;AAC3D,KAAC,CAAC;AAEF,IAAA,QACIA,GAAA,CAAC,uBAAuB,EAAA,EAAC,KAAK,EAAE,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE,EACtE,QAAA,EAAAA,GAAA,CAAA,KAAA,EAAA,EAAK,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,eAAe,EACxD,QAAA,EAAA,QAAQ,EACP,CAAA,EAAA,CACgB,EAC5B;AACN,EAAE;AAEF,mBAAmB,CAAC,WAAW,GAAG,qBAAqB;;;;"}
@@ -0,0 +1,14 @@
1
+ import { type FC, type ReactNode } from 'react';
2
+ interface FocusTargetProps {
3
+ /** Приоритет обхода стрелками в roving-группе (меньше — раньше), см. FOCUS_PRIORITY */
4
+ priority: number;
5
+ children: ReactNode;
6
+ }
7
+ /**
8
+ * Обёртка-маркер вокруг наших кнопок для roving-группы (useRovingFocusGroup). `display:contents` —
9
+ * не создаёт бокс, так что раскладка слота/блока записи не меняется, а менеджер находит кнопку
10
+ * внутри по data-атрибуту. Маркер вешаем на обёртку, а не на саму иконку: кастомный data-* на
11
+ * иконке уезжает на внутренний скрытый <svg> (не на фокусируемый <button>) и не проходит по типам.
12
+ */
13
+ export declare const FocusTarget: FC<FocusTargetProps>;
14
+ export {};
@@ -0,0 +1,17 @@
1
+ import './../index.css';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { focusPriorityProps } from './focusGroup.js';
4
+
5
+ // стиль обёртки-маркера: не создаёт бокс, дети остаются flex-элементами родительского слота
6
+ const TARGET_STYLE = { display: 'contents' };
7
+ /**
8
+ * Обёртка-маркер вокруг наших кнопок для roving-группы (useRovingFocusGroup). `display:contents` —
9
+ * не создаёт бокс, так что раскладка слота/блока записи не меняется, а менеджер находит кнопку
10
+ * внутри по data-атрибуту. Маркер вешаем на обёртку, а не на саму иконку: кастомный data-* на
11
+ * иконке уезжает на внутренний скрытый <svg> (не на фокусируемый <button>) и не проходит по типам.
12
+ */
13
+ const FocusTarget = ({ priority, children }) => (jsx("span", { style: TARGET_STYLE, ...focusPriorityProps(priority), children: children }));
14
+ FocusTarget.displayName = 'ChatInputFocusTarget';
15
+
16
+ export { FocusTarget };
17
+ //# sourceMappingURL=FocusTarget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FocusTarget.js","sources":["src/internal/FocusTarget.tsx"],"sourcesContent":["import { type FC, type ReactNode } from 'react';\n\nimport { focusPriorityProps } from '@hh.ru/magritte-ui-chat-input/internal/focusGroup';\n\n// стиль обёртки-маркера: не создаёт бокс, дети остаются flex-элементами родительского слота\nconst TARGET_STYLE = { display: 'contents' } as const;\n\ninterface FocusTargetProps {\n /** Приоритет обхода стрелками в roving-группе (меньше — раньше), см. FOCUS_PRIORITY */\n priority: number;\n children: ReactNode;\n}\n\n/**\n * Обёртка-маркер вокруг наших кнопок для roving-группы (useRovingFocusGroup). `display:contents` —\n * не создаёт бокс, так что раскладка слота/блока записи не меняется, а менеджер находит кнопку\n * внутри по data-атрибуту. Маркер вешаем на обёртку, а не на саму иконку: кастомный data-* на\n * иконке уезжает на внутренний скрытый <svg> (не на фокусируемый <button>) и не проходит по типам.\n */\nexport const FocusTarget: FC<FocusTargetProps> = ({ priority, children }) => (\n <span style={TARGET_STYLE} {...focusPriorityProps(priority)}>\n {children}\n </span>\n);\n\nFocusTarget.displayName = 'ChatInputFocusTarget';\n"],"names":["_jsx"],"mappings":";;;AAIA;AACA,MAAM,YAAY,GAAG,EAAE,OAAO,EAAE,UAAU,EAAW,CAAC;AAQtD;;;;;AAKG;AACU,MAAA,WAAW,GAAyB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MACpEA,GAAA,CAAA,MAAA,EAAA,EAAM,KAAK,EAAE,YAAY,EAAA,GAAM,kBAAkB,CAAC,QAAQ,CAAC,EACtD,QAAA,EAAA,QAAQ,EACN,CAAA,EACT;AAEF,WAAW,CAAC,WAAW,GAAG,sBAAsB;;;;"}
@@ -0,0 +1,12 @@
1
+ import { type FC, type ReactNode } from 'react';
2
+ type ChatInputMode = 'text' | 'voice';
3
+ export interface ModeTransitionProps {
4
+ /** Активный режим; смена запускает кроссфейд контента и анимацию высоты оболочки */
5
+ mode: ChatInputMode;
6
+ /** Контент текстового режима (сетка со слотами и вводом) */
7
+ text: ReactNode;
8
+ /** Контент режима записи голоса */
9
+ voice: ReactNode;
10
+ }
11
+ export declare const ModeTransition: FC<ModeTransitionProps>;
12
+ export {};
@@ -0,0 +1,29 @@
1
+ import './../index.css';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useReducedMotion, motion } from 'motion/react';
4
+ import { tokenSemanticAnimationEaseBaseX1, tokenSemanticAnimationEaseBaseY1, tokenSemanticAnimationEaseBaseX2, tokenSemanticAnimationEaseBaseY2, tokenSemanticAnimationTimeSDuration } from '@hh.ru/magritte-design-tokens/types';
5
+ import { s as styles } from '../styles-CQQdqOFh.js';
6
+
7
+ const EASE_BASE = [
8
+ tokenSemanticAnimationEaseBaseX1,
9
+ tokenSemanticAnimationEaseBaseY1,
10
+ tokenSemanticAnimationEaseBaseX2,
11
+ tokenSemanticAnimationEaseBaseY2,
12
+ ];
13
+ const MODE_TRANSITION_DURATION_MS = tokenSemanticAnimationTimeSDuration;
14
+ const INSTANT_TRANSITION = { duration: 0 };
15
+ const MODE_CONTENT_TRANSITION = {
16
+ duration: MODE_TRANSITION_DURATION_MS / 1000,
17
+ ease: EASE_BASE,
18
+ };
19
+ const VISIBLE_LAYER = { opacity: 1, visibility: 'visible' };
20
+ const HIDDEN_LAYER = { opacity: 0, transitionEnd: { visibility: 'hidden' } };
21
+ const ModeTransition = ({ mode, text, voice }) => {
22
+ const transition = useReducedMotion() ? INSTANT_TRANSITION : MODE_CONTENT_TRANSITION;
23
+ const isVoice = mode === 'voice';
24
+ return (jsxs(motion.div, { className: styles.modeContainer, animate: { height: isVoice ? 40 : 'auto' }, transition: transition, children: [jsx(motion.div, { initial: false, animate: isVoice ? HIDDEN_LAYER : VISIBLE_LAYER, transition: transition, "aria-hidden": isVoice, children: text }), jsx(motion.div, { className: styles.voiceOverlay, initial: false, animate: isVoice ? VISIBLE_LAYER : HIDDEN_LAYER, transition: transition, "aria-hidden": !isVoice, children: voice })] }));
25
+ };
26
+ ModeTransition.displayName = 'ChatInputModeTransition';
27
+
28
+ export { ModeTransition };
29
+ //# sourceMappingURL=ModeTransition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModeTransition.js","sources":["src/internal/ModeTransition.tsx"],"sourcesContent":["import { type FC, type ReactNode } from 'react';\nimport { motion, type TargetAndTransition, useReducedMotion } from 'motion/react';\n\nimport {\n tokenSemanticAnimationEaseBaseX1,\n tokenSemanticAnimationEaseBaseX2,\n tokenSemanticAnimationEaseBaseY1,\n tokenSemanticAnimationEaseBaseY2,\n tokenSemanticAnimationTimeSDuration,\n} from '@hh.ru/magritte-design-tokens/types';\n\nimport styles from '../styles.less';\n\ntype ChatInputMode = 'text' | 'voice';\n\nexport interface ModeTransitionProps {\n /** Активный режим; смена запускает кроссфейд контента и анимацию высоты оболочки */\n mode: ChatInputMode;\n /** Контент текстового режима (сетка со слотами и вводом) */\n text: ReactNode;\n /** Контент режима записи голоса */\n voice: ReactNode;\n}\n\nconst EASE_BASE = [\n tokenSemanticAnimationEaseBaseX1,\n tokenSemanticAnimationEaseBaseY1,\n tokenSemanticAnimationEaseBaseX2,\n tokenSemanticAnimationEaseBaseY2,\n] as const;\n\nconst MODE_TRANSITION_DURATION_MS = tokenSemanticAnimationTimeSDuration;\nconst INSTANT_TRANSITION = { duration: 0 } as const;\n\nconst MODE_CONTENT_TRANSITION = {\n duration: MODE_TRANSITION_DURATION_MS / 1000,\n ease: EASE_BASE,\n} as const;\n\nconst VISIBLE_LAYER: TargetAndTransition = { opacity: 1, visibility: 'visible' };\nconst HIDDEN_LAYER: TargetAndTransition = { opacity: 0, transitionEnd: { visibility: 'hidden' } };\n\nexport const ModeTransition: FC<ModeTransitionProps> = ({ mode, text, voice }) => {\n const transition = useReducedMotion() ? INSTANT_TRANSITION : MODE_CONTENT_TRANSITION;\n const isVoice = mode === 'voice';\n\n return (\n <motion.div\n className={styles.modeContainer}\n animate={{ height: isVoice ? 40 : 'auto' }}\n transition={transition}\n >\n <motion.div\n initial={false}\n animate={isVoice ? HIDDEN_LAYER : VISIBLE_LAYER}\n transition={transition}\n aria-hidden={isVoice}\n >\n {text}\n </motion.div>\n <motion.div\n className={styles.voiceOverlay}\n initial={false}\n animate={isVoice ? VISIBLE_LAYER : HIDDEN_LAYER}\n transition={transition}\n aria-hidden={!isVoice}\n >\n {voice}\n </motion.div>\n </motion.div>\n );\n};\n\nModeTransition.displayName = 'ChatInputModeTransition';\n"],"names":["_jsxs","_jsx"],"mappings":";;;;;AAwBA,MAAM,SAAS,GAAG;IACd,gCAAgC;IAChC,gCAAgC;IAChC,gCAAgC;IAChC,gCAAgC;CAC1B,CAAC;AAEX,MAAM,2BAA2B,GAAG,mCAAmC,CAAC;AACxE,MAAM,kBAAkB,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAW,CAAC;AAEpD,MAAM,uBAAuB,GAAG;IAC5B,QAAQ,EAAE,2BAA2B,GAAG,IAAI;AAC5C,IAAA,IAAI,EAAE,SAAS;CACT,CAAC;AAEX,MAAM,aAAa,GAAwB,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AACjF,MAAM,YAAY,GAAwB,EAAE,OAAO,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,CAAC;AAE3F,MAAM,cAAc,GAA4B,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAI;AAC7E,IAAA,MAAM,UAAU,GAAG,gBAAgB,EAAE,GAAG,kBAAkB,GAAG,uBAAuB,CAAC;AACrF,IAAA,MAAM,OAAO,GAAG,IAAI,KAAK,OAAO,CAAC;IAEjC,QACIA,KAAC,MAAM,CAAC,GAAG,EACP,EAAA,SAAS,EAAE,MAAM,CAAC,aAAa,EAC/B,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,EAAE,GAAG,MAAM,EAAE,EAC1C,UAAU,EAAE,UAAU,EAEtB,QAAA,EAAA,CAAAC,GAAA,CAAC,MAAM,CAAC,GAAG,EAAA,EACP,OAAO,EAAE,KAAK,EACd,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,aAAa,EAC/C,UAAU,EAAE,UAAU,iBACT,OAAO,EAAA,QAAA,EAEnB,IAAI,EAAA,CACI,EACbA,GAAA,CAAC,MAAM,CAAC,GAAG,IACP,SAAS,EAAE,MAAM,CAAC,YAAY,EAC9B,OAAO,EAAE,KAAK,EACd,OAAO,EAAE,OAAO,GAAG,aAAa,GAAG,YAAY,EAC/C,UAAU,EAAE,UAAU,iBACT,CAAC,OAAO,YAEpB,KAAK,EAAA,CACG,CACJ,EAAA,CAAA,EACf;AACN,EAAE;AAEF,cAAc,CAAC,WAAW,GAAG,yBAAyB;;;;"}
@@ -0,0 +1,18 @@
1
+ import { type TextareaHTMLAttributes, type FocusEventHandler } from 'react';
2
+ export declare const useFocus: <T extends HTMLTextAreaElement = HTMLTextAreaElement>() => {
3
+ focusHandler: FocusEventHandler<T>;
4
+ blurHandler: FocusEventHandler<T>;
5
+ isFocused: boolean;
6
+ };
7
+ export declare const TextInput: import("react").ForwardRefExoticComponent<Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "value"> & {
8
+ value: string;
9
+ maxHeight?: number;
10
+ layout: "vertical" | "horizontal";
11
+ onSend?: VoidFunction;
12
+ /** Ширина контейнера; её изменение пере-измеряет layout (перенос текста зависит от ширины) */
13
+ containerWidth?: number;
14
+ /** Есть центральный слот - ввод всегда на своём ряду, паддинги spacious */
15
+ hasCentral: boolean;
16
+ /** Контролы с обеих сторон - в вертикальном лейауте ввод уезжает на свой ряд, паддинги spacious */
17
+ hasBothSideControls: boolean;
18
+ } & import("react").RefAttributes<HTMLTextAreaElement>>;