endfield-vision 2.1.0 → 2.2.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 (60) hide show
  1. package/README.md +31 -9
  2. package/README.ru.md +31 -9
  3. package/dist/components/CommandPalette.d.ts +24 -1
  4. package/dist/components/CommandPalette.d.ts.map +1 -1
  5. package/dist/components/CommandPalette.js +22 -10
  6. package/dist/components/CommandPalette.js.map +1 -1
  7. package/dist/components/DataTable.d.ts +38 -1
  8. package/dist/components/DataTable.d.ts.map +1 -1
  9. package/dist/components/DataTable.js +135 -25
  10. package/dist/components/DataTable.js.map +1 -1
  11. package/dist/components/Modal.d.ts +26 -0
  12. package/dist/components/Modal.d.ts.map +1 -1
  13. package/dist/components/Modal.js +92 -0
  14. package/dist/components/Modal.js.map +1 -1
  15. package/dist/components/Toast.d.ts +3 -0
  16. package/dist/components/Toast.d.ts.map +1 -1
  17. package/dist/components/Toast.js +5 -1
  18. package/dist/components/Toast.js.map +1 -1
  19. package/dist/index.d.ts +4 -3
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +1 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/lib/accent-preset.d.ts +40 -0
  24. package/dist/lib/accent-preset.d.ts.map +1 -0
  25. package/dist/lib/accent-preset.js +71 -0
  26. package/dist/lib/accent-preset.js.map +1 -0
  27. package/dist/lib/i18n-messages.d.ts +7 -0
  28. package/dist/lib/i18n-messages.d.ts.map +1 -1
  29. package/dist/lib/i18n-messages.js +10 -2
  30. package/dist/lib/i18n-messages.js.map +1 -1
  31. package/dist/lib/theme-script.d.ts +10 -4
  32. package/dist/lib/theme-script.d.ts.map +1 -1
  33. package/dist/lib/theme-script.js +8 -3
  34. package/dist/lib/theme-script.js.map +1 -1
  35. package/dist/lib/theme.d.ts +10 -6
  36. package/dist/lib/theme.d.ts.map +1 -1
  37. package/dist/lib/theme.js +1 -1
  38. package/dist/lib/theme.js.map +1 -1
  39. package/dist/styles/accents.css +6 -6
  40. package/dist/styles/components/data.css +120 -0
  41. package/dist/styles/components/navigation.css +6 -0
  42. package/dist/styles/components/overlay.css +43 -0
  43. package/dist/styles/shell.css +2 -0
  44. package/dist/styles/tokens.css +27 -27
  45. package/package.json +1 -1
  46. package/src/components/CommandPalette.tsx +68 -10
  47. package/src/components/DataTable.tsx +258 -41
  48. package/src/components/Modal.tsx +148 -1
  49. package/src/components/Toast.tsx +21 -1
  50. package/src/index.ts +4 -0
  51. package/src/lib/accent-preset.tsx +109 -0
  52. package/src/lib/i18n-messages.ts +17 -3
  53. package/src/lib/theme-script.tsx +19 -5
  54. package/src/lib/theme.ts +13 -8
  55. package/src/styles/accents.css +6 -6
  56. package/src/styles/components/data.css +120 -0
  57. package/src/styles/components/navigation.css +6 -0
  58. package/src/styles/components/overlay.css +43 -0
  59. package/src/styles/shell.css +2 -0
  60. package/src/styles/tokens.css +27 -27
@@ -1,11 +1,11 @@
1
- import { type Accent, type Style, type Theme, type ThemePreference } from './theme-script.js';
1
+ import { type Accent, type AccentName, type Style, type Theme, type ThemePreference } from './theme-script.js';
2
2
  /** Тема: 'dark' | 'light' или 'system' - по настройке ОС. */
3
3
  export declare function setTheme(pref: ThemePreference, { persist, storageKey }?: {
4
4
  persist?: boolean | undefined;
5
5
  storageKey?: string | undefined;
6
6
  }): void;
7
- /** Акцент из пресетов (styles/accents.css). 'indigo' - базовый, атрибут снимается. */
8
- export declare function setAccent(accent: Accent, { persist, storageKey }?: {
7
+ /** Акцент: встроенный пресет (styles/accents.css) или свой (AccentPreset). 'indigo' - базовый, атрибут снимается. */
8
+ export declare function setAccent(accent: AccentName, { persist, storageKey }?: {
9
9
  persist?: boolean | undefined;
10
10
  storageKey?: string | undefined;
11
11
  }): void;
@@ -14,16 +14,20 @@ export declare function setStyle(style: Style, { persist, storageKey }?: {
14
14
  persist?: boolean | undefined;
15
15
  storageKey?: string | undefined;
16
16
  }): void;
17
- export interface ThemeState {
17
+ /**
18
+ * Состояние темы. A - имена акцентов приложения: по умолчанию встроенные;
19
+ * со своими пресетами - useTheme<Accent | 'brand'>().
20
+ */
21
+ export interface ThemeState<A extends string = Accent> {
18
22
  /** Действующая тема. */
19
23
  theme: Theme;
20
24
  /** Выбор пользователя (может быть 'system'). */
21
25
  preference: ThemePreference;
22
- accent: Accent;
26
+ accent: A;
23
27
  style: Style;
24
28
  setTheme: typeof setTheme;
25
29
  setAccent: typeof setAccent;
26
30
  setStyle: typeof setStyle;
27
31
  }
28
- export declare function useTheme(): ThemeState;
32
+ export declare function useTheme<A extends string = Accent>(): ThemeState<A>;
29
33
  //# sourceMappingURL=theme.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/lib/theme.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,MAAM,EACX,KAAK,KAAK,EACV,KAAK,KAAK,EACV,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAA;AAgDvB,6DAA6D;AAC7D,wBAAgB,QAAQ,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,OAAc,EAAE,UAA8B,EAAE;;;CAAK,GAAG,IAAI,CAM7G;AAED,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAc,EAAE,UAA+B,EAAE;;;CAAK,GAAG,IAAI,CAMxG;AAED,wFAAwF;AACxF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,OAAc,EAAE,UAA8B,EAAE;;;CAAK,GAAG,IAAI,CAMpG;AAED,MAAM,WAAW,UAAU;IACzB,wBAAwB;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,gDAAgD;IAChD,UAAU,EAAE,eAAe,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,KAAK,CAAA;IACZ,QAAQ,EAAE,OAAO,QAAQ,CAAA;IACzB,SAAS,EAAE,OAAO,SAAS,CAAA;IAC3B,QAAQ,EAAE,OAAO,QAAQ,CAAA;CAC1B;AAED,wBAAgB,QAAQ,IAAI,UAAU,CAmBrC"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/lib/theme.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,KAAK,EACV,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAA;AAgDvB,6DAA6D;AAC7D,wBAAgB,QAAQ,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,OAAc,EAAE,UAA8B,EAAE;;;CAAK,GAAG,IAAI,CAM7G;AAED,qHAAqH;AACrH,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,OAAc,EAAE,UAA+B,EAAE;;;CAAK,GAAG,IAAI,CAM5G;AAED,wFAAwF;AACxF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,OAAc,EAAE,UAA8B,EAAE;;;CAAK,GAAG,IAAI,CAMpG;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACnD,wBAAwB;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,gDAAgD;IAChD,UAAU,EAAE,eAAe,CAAA;IAC3B,MAAM,EAAE,CAAC,CAAA;IACT,KAAK,EAAE,KAAK,CAAA;IACZ,QAAQ,EAAE,OAAO,QAAQ,CAAA;IACzB,SAAS,EAAE,OAAO,SAAS,CAAA;IAC3B,QAAQ,EAAE,OAAO,QAAQ,CAAA;CAC1B;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,CAmBnE"}
package/dist/lib/theme.js CHANGED
@@ -48,7 +48,7 @@ export function setTheme(pref, { persist = true, storageKey = THEME_STORAGE_KEY
48
48
  store(storageKey, pref);
49
49
  emit();
50
50
  }
51
- /** Акцент из пресетов (styles/accents.css). 'indigo' - базовый, атрибут снимается. */
51
+ /** Акцент: встроенный пресет (styles/accents.css) или свой (AccentPreset). 'indigo' - базовый, атрибут снимается. */
52
52
  export function setAccent(accent, { persist = true, storageKey = ACCENT_STORAGE_KEY } = {}) {
53
53
  const root = document.documentElement;
54
54
  if (accent === 'indigo')
@@ -1 +1 @@
1
- {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/lib/theme.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/lib/theme.ts"],"names":[],"mappings":"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"}
@@ -115,7 +115,7 @@
115
115
  }
116
116
 
117
117
  /*
118
- * Стиль Slim: те же пресеты, приглушённые под тёплые нейтрали.
118
+ * Стиль Slim: те же пресеты, приглушённые под нейтрали стиля.
119
119
  * indigo - без атрибута data-accent.
120
120
  */
121
121
  :root[data-style='slim']:not([data-accent]) {
@@ -167,11 +167,11 @@
167
167
  }
168
168
 
169
169
  :root[data-style='slim'][data-accent='ink'] {
170
- --ev-accent-rgb: 200, 196, 186;
171
- --ev-accent-text: #e2dfd8;
172
- --ev-accent-strong: #e8e6df;
173
- --ev-accent-strong-hover: #f4f2ec;
174
- --ev-text-on-accent: #1c1b18;
170
+ --ev-accent-rgb: 200, 198, 192;
171
+ --ev-accent-text: #e2e0db;
172
+ --ev-accent-strong: #e9e8e3;
173
+ --ev-accent-strong-hover: #f5f4f0;
174
+ --ev-text-on-accent: #181817;
175
175
  }
176
176
 
177
177
  :root[data-style='slim'][data-theme='light']:not([data-accent]) {
@@ -131,6 +131,20 @@
131
131
  box-shadow: inset 0 1px 0 var(--ev-selected-edge), inset 0 -1px 0 var(--ev-selected-edge);
132
132
  }
133
133
 
134
+ /* Тон строки (rowTone): тонированный фон, наведение - тинт поверх него. */
135
+ .ev-table tbody tr[data-tone],
136
+ .ev-table-card[data-tone] {
137
+ --ev-tone-row: color-mix(in srgb, var(--ev-tone-soft) 65%, transparent);
138
+ }
139
+
140
+ .ev-table tbody tr[data-tone] > td {
141
+ background: var(--ev-tone-row);
142
+ }
143
+
144
+ .ev-table tbody tr[data-tone]:hover > td {
145
+ background: linear-gradient(var(--ev-hover), var(--ev-hover)), var(--ev-tone-row);
146
+ }
147
+
134
148
  .ev-table tbody tr[data-selected] td {
135
149
  background: var(--ev-selected-bg-subtle);
136
150
  }
@@ -139,6 +153,89 @@
139
153
  color: var(--ev-text-muted);
140
154
  }
141
155
 
156
+ /* --- Раскрывающиеся строки --- */
157
+ .ev-table .ev-table-expand {
158
+ width: 40px;
159
+ padding-right: 0;
160
+ }
161
+
162
+ .ev-table-expand-btn {
163
+ display: inline-flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ width: 24px;
167
+ height: 24px;
168
+ margin: 0 -4px;
169
+ border-radius: var(--ev-radius-xs);
170
+ color: var(--ev-text-muted);
171
+ vertical-align: middle;
172
+ }
173
+
174
+ .ev-table-expand-btn:hover {
175
+ background: var(--ev-hover);
176
+ color: var(--ev-text);
177
+ }
178
+
179
+ .ev-table-expand-btn svg {
180
+ transition: transform var(--ev-dur-fast) var(--ev-ease);
181
+ }
182
+
183
+ .ev-table-expand-btn[aria-expanded='true'] svg {
184
+ transform: rotate(90deg);
185
+ }
186
+
187
+ .ev-table tbody tr[data-expanded]:not([data-tone], [data-selected]) > td {
188
+ background: var(--ev-tint-1);
189
+ }
190
+
191
+ .ev-table tbody tr[data-expanded] > td {
192
+ border-bottom-color: transparent;
193
+ }
194
+
195
+ .ev-table .ev-table-detail-row > td {
196
+ height: auto;
197
+ padding: 0;
198
+ white-space: normal;
199
+ background: var(--ev-tint-1);
200
+ }
201
+
202
+ .ev-table-detail {
203
+ padding: var(--ev-space-2) var(--ev-space-5) var(--ev-space-5);
204
+ }
205
+
206
+ .ev-card-body-flush .ev-table-detail {
207
+ padding-inline: var(--ev-space-6);
208
+ }
209
+
210
+ /* --- Итоговая строка --- */
211
+ .ev-table tfoot td {
212
+ position: sticky;
213
+ bottom: 0;
214
+ z-index: var(--ev-z-sticky);
215
+ height: 40px;
216
+ background: var(--ev-surface-2);
217
+ border-top: 1px solid var(--ev-border);
218
+ border-bottom: 0;
219
+ font-weight: var(--ev-fw-medium);
220
+ }
221
+
222
+ :is(.ev-card, .ev-panel, .ev-modal, .ev-drawer) .ev-table tfoot td {
223
+ background: var(--ev-surface-3);
224
+ }
225
+
226
+ .ev-card-body-flush .ev-table tfoot td {
227
+ background: var(--ev-surface-2);
228
+ }
229
+
230
+ /* --- Шапка, липнущая к странице (stickyHeader): сдвиг считает DataTable --- */
231
+ .ev-table[data-sticky-page] thead th {
232
+ transform: translateY(var(--ev-thead-shift, 0px));
233
+ }
234
+
235
+ .ev-table[data-stuck] thead th {
236
+ box-shadow: var(--ev-shadow-sm);
237
+ }
238
+
142
239
  .ev-table-select {
143
240
  width: 44px;
144
241
  padding-right: 0 !important;
@@ -258,6 +355,29 @@
258
355
  opacity: 0.7;
259
356
  }
260
357
 
358
+ .ev-table-card[data-tone]:not([data-selected]) {
359
+ background-image: linear-gradient(var(--ev-tone-row), var(--ev-tone-row));
360
+ }
361
+
362
+ .ev-table-card[data-tone][data-clickable]:not([data-selected]):active {
363
+ background-image: linear-gradient(var(--ev-active), var(--ev-active)), linear-gradient(var(--ev-tone-row), var(--ev-tone-row));
364
+ }
365
+
366
+ .ev-table-card-head .ev-table-expand-btn {
367
+ flex: 0 0 auto;
368
+ margin: -2px -4px 0 0;
369
+ }
370
+
371
+ .ev-table-card-detail {
372
+ padding-top: var(--ev-space-3);
373
+ border-top: 1px solid var(--ev-border-soft);
374
+ min-width: 0;
375
+ }
376
+
377
+ .ev-table-card-total {
378
+ background: var(--ev-surface-2);
379
+ }
380
+
261
381
  .ev-table-card-head {
262
382
  display: flex;
263
383
  align-items: flex-start;
@@ -34,6 +34,12 @@
34
34
  color: var(--ev-text-muted);
35
35
  }
36
36
 
37
+ .ev-cmdk-spinner {
38
+ display: inline-flex;
39
+ width: 17px;
40
+ justify-content: center;
41
+ }
42
+
37
43
  .ev-cmdk-input {
38
44
  flex: 1 1 auto;
39
45
  min-width: 0;
@@ -276,6 +276,12 @@
276
276
  padding: 0;
277
277
  }
278
278
 
279
+ .ev-modal-prompt {
280
+ display: flex;
281
+ flex-direction: column;
282
+ gap: var(--ev-space-4);
283
+ }
284
+
279
285
  .ev-modal-message {
280
286
  color: var(--ev-text-secondary);
281
287
  line-height: 1.55;
@@ -462,6 +468,43 @@
462
468
  font-weight: var(--ev-fw-semibold);
463
469
  }
464
470
 
471
+ /* Карточка с onClick: заголовок - кнопка, растянутая на карточку; кнопки карточки - поверх неё. */
472
+ .ev-toast-open {
473
+ display: block;
474
+ padding: 0;
475
+ color: inherit;
476
+ text-align: left;
477
+ }
478
+
479
+ .ev-toast-open::after {
480
+ content: '';
481
+ position: absolute;
482
+ inset: 0;
483
+ border-radius: inherit;
484
+ }
485
+
486
+ .ev-toast-open:focus-visible {
487
+ outline: none;
488
+ box-shadow: none;
489
+ }
490
+
491
+ .ev-toast-open:focus-visible::after {
492
+ box-shadow: var(--ev-focus-ring);
493
+ }
494
+
495
+ .ev-toast[data-clickable] {
496
+ cursor: pointer;
497
+ }
498
+
499
+ .ev-toast[data-clickable]:hover {
500
+ background-image: linear-gradient(var(--ev-tint-2), var(--ev-tint-2));
501
+ }
502
+
503
+ .ev-toast[data-clickable] :is(.ev-toast-action, .ev-toast-actions, .ev-toast-close) {
504
+ position: relative;
505
+ z-index: 1;
506
+ }
507
+
465
508
  .ev-toast-desc {
466
509
  margin-top: 2px;
467
510
  font-size: var(--ev-fs-sm);
@@ -2,6 +2,8 @@
2
2
 
3
3
  @layer ev.shell {
4
4
  .ev-shell {
5
+ /* Шапка таблицы со stickyHeader липнет под шапкой приложения. */
6
+ --ev-table-sticky-top: var(--ev-topbar-h);
5
7
  display: flex;
6
8
  min-height: 100dvh;
7
9
  width: 100%;
@@ -295,39 +295,39 @@
295
295
 
296
296
  /*
297
297
  * Стиль Slim («Утончённый»): <html data-style="slim">. Плоский и спокойный:
298
- * тёплые нейтрали, без свечения, выбранное - нейтральным тоном, подписи без капса.
298
+ * нейтрали с лёгким тёплым оттенком, без свечения, выбранное - нейтральным тоном, подписи без капса.
299
299
  * Размеры (высоты контролов, отступы, кегль) те же, что в Default.
300
300
  * Правила компонентов - в slim.css (слой ev.styles).
301
301
  *
302
- * Контраст (тёмная): --ev-text-muted 5.6 к --ev-surface-1, 4.6 к --ev-surface-3;
303
- * --ev-border-control 3.5 к фону поля. Светлая: --ev-text-muted 5.8 / 4.7, рамка поля 3.4.
302
+ * Контраст (тёмная): --ev-text-muted 5.8 к --ev-surface-1, 4.8 к --ev-surface-3;
303
+ * --ev-border-control 3.6 к фону поля. Светлая: --ev-text-muted 5.8 / 4.7, рамка поля 3.4.
304
304
  */
305
305
  :root[data-style='slim'] {
306
- --ev-bg: #1c1b19;
307
- --ev-surface-1: #242320;
308
- --ev-surface-2: #2b2a27;
309
- --ev-surface-3: #33312e;
310
- --ev-surface-input: #1f1e1c;
311
- --ev-surface-overlay: rgba(10, 9, 8, 0.6);
312
- --ev-surface-tooltip: #3a3834;
306
+ --ev-bg: #181817;
307
+ --ev-surface-1: #20201e;
308
+ --ev-surface-2: #272725;
309
+ --ev-surface-3: #2f2e2c;
310
+ --ev-surface-input: #1b1b1a;
311
+ --ev-surface-overlay: rgba(8, 8, 7, 0.62);
312
+ --ev-surface-tooltip: #363533;
313
313
  --ev-surface-nested: var(--ev-surface-2);
314
314
 
315
- --ev-tint-1: rgba(255, 250, 240, 0.025);
316
- --ev-tint-2: rgba(255, 250, 240, 0.045);
317
- --ev-tint-3: rgba(255, 250, 240, 0.07);
318
- --ev-tint-4: rgba(255, 250, 240, 0.1);
315
+ --ev-tint-1: rgba(255, 253, 248, 0.025);
316
+ --ev-tint-2: rgba(255, 253, 248, 0.045);
317
+ --ev-tint-3: rgba(255, 253, 248, 0.07);
318
+ --ev-tint-4: rgba(255, 253, 248, 0.1);
319
319
 
320
- --ev-border-soft: rgba(255, 250, 240, 0.05);
321
- --ev-border: #33312d;
322
- --ev-border-strong: #403e39;
323
- --ev-border-control: #77736b;
324
- --ev-border-hover: #8d897f;
320
+ --ev-border-soft: rgba(255, 253, 248, 0.05);
321
+ --ev-border: #2f2f2c;
322
+ --ev-border-strong: #3c3b38;
323
+ --ev-border-control: #74726c;
324
+ --ev-border-hover: #8a8881;
325
325
 
326
- --ev-text: #ecebe6;
327
- --ev-text-secondary: #c2bfb7;
328
- --ev-text-muted: #9e9a91;
329
- --ev-text-disabled: #6a675f;
330
- --ev-text-on-solid: #1c1b19;
326
+ --ev-text: #ecebe8;
327
+ --ev-text-secondary: #c0beb8;
328
+ --ev-text-muted: #9c9a93;
329
+ --ev-text-disabled: #686660;
330
+ --ev-text-on-solid: #181817;
331
331
 
332
332
  --ev-success-rgb: 127, 191, 149;
333
333
  --ev-success-strong: #3f7d57;
@@ -339,9 +339,9 @@
339
339
  --ev-info-rgb: 134, 174, 214;
340
340
  --ev-violet-rgb: 182, 166, 224;
341
341
 
342
- --ev-chart-grid: #33312d;
343
- --ev-chart-axis: #403e39;
344
- --ev-chart-crosshair: #77736b;
342
+ --ev-chart-grid: #2f2f2c;
343
+ --ev-chart-axis: #3c3b38;
344
+ --ev-chart-crosshair: #74726c;
345
345
 
346
346
  --ev-radius-xs: 4px;
347
347
  --ev-radius-sm: 6px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "endfield-vision",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Endfield Vision - design system for React / Next.js: tokens, dark and light themes, accent presets, accessible components without third-party UI dependencies",
5
5
  "keywords": [
6
6
  "react",
@@ -3,10 +3,12 @@
3
3
  import { Search, X } from 'lucide-react'
4
4
  import { useEffect, useId, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'
5
5
  import { cx, normalizeSearch } from '../lib/cx'
6
- import { useEventCallback, useIsoLayoutEffect } from '../lib/hooks'
6
+ import { useControllable, useEventCallback, useIsoLayoutEffect } from '../lib/hooks'
7
7
  import { useMessages } from '../lib/i18n'
8
8
  import { Portal, useEscapeLayer, useFocusTrap, useScrollLock } from '../lib/overlay'
9
9
  import { Kbd } from './Display'
10
+ import { Highlight } from './Highlight'
11
+ import { Spinner } from './Spinner'
10
12
 
11
13
  /*
12
14
  * Палитра команд: окно поиска по командам и разделам (Ctrl/Cmd+K).
@@ -14,6 +16,10 @@ import { Kbd } from './Display'
14
16
  * aria-activedescendant, фокус всё время остаётся в поле. Окно - в портале
15
17
  * на слое модалок, прижато к верху экрана; Escape закрывает, фокус
16
18
  * возвращается на прежний элемент.
19
+ *
20
+ * Поиск с сервера: запрос уходит наружу через onQueryChange, пункты из
21
+ * ответа помечаются alwaysShow (или вся палитра filter={false}) - тогда
22
+ * локальное сопоставление их не отсекает; loading - индикатор ожидания.
17
23
  */
18
24
 
19
25
  export interface CommandItem {
@@ -28,6 +34,11 @@ export interface CommandItem {
28
34
  /** Дополнительные слова для поиска (синонимы, коды, английские названия). */
29
35
  keywords?: string[]
30
36
  disabled?: boolean
37
+ /**
38
+ * Показывать при любом запросе: пункт уже отобран снаружи (результат поиска
39
+ * с сервера, совпадение по ИНН или телефону, которого нет в подписи).
40
+ */
41
+ alwaysShow?: boolean
31
42
  /** Выполнить команду. Палитра закрывается перед вызовом. */
32
43
  onSelect: () => void
33
44
  }
@@ -42,6 +53,21 @@ export interface CommandPaletteProps {
42
53
  open: boolean
43
54
  onOpenChange: (open: boolean) => void
44
55
  items: CommandItem[]
56
+ /** Текст поиска (управляемо). Без него палитра хранит запрос сама и сбрасывает при открытии. */
57
+ query?: string
58
+ /** Начальный запрос при открытии (неуправляемый режим). */
59
+ defaultQuery?: string
60
+ /** Запрос изменился: поиск с сервера. Задержку (debounce) выдерживает приложение. */
61
+ onQueryChange?: (query: string) => void
62
+ /**
63
+ * Отбирать пункты по запросу (по умолчанию да). false - список показывается
64
+ * как есть: приложение фильтрует само. Для отдельных пунктов - alwaysShow.
65
+ */
66
+ filter?: boolean
67
+ /** Ждём результатов: индикатор в поле, при пустом списке - «Поиск...». */
68
+ loading?: boolean
69
+ /** Подсветить совпадения с запросом в названии и строковой подсказке. */
70
+ highlight?: boolean
45
71
  /** Плейсхолдер поля. По умолчанию - из словаря. */
46
72
  placeholder?: string
47
73
  /** Текст при пустом результате. По умолчанию - «Ничего не найдено». */
@@ -66,9 +92,17 @@ export function commandMatches(item: CommandItem, query: string): boolean {
66
92
  return words.every((w) => hay.includes(w))
67
93
  }
68
94
 
69
- /** Отбор команд по запросу (без учёта регистра и «ё/е»), порядок сохраняется. */
95
+ /**
96
+ * Отбор команд по запросу (без учёта регистра и «ё/е»), порядок сохраняется.
97
+ * Пункты с alwaysShow остаются при любом запросе.
98
+ */
70
99
  export function filterCommands(items: CommandItem[], query: string): CommandItem[] {
71
- return items.filter((it) => commandMatches(it, query))
100
+ return items.filter((it) => it.alwaysShow || commandMatches(it, query))
101
+ }
102
+
103
+ /** Слова запроса для подсветки совпадений. */
104
+ function queryWords(query: string): string[] {
105
+ return query.split(/\s+/).filter(Boolean)
72
106
  }
73
107
 
74
108
  /**
@@ -155,6 +189,12 @@ function optionDomId(listId: string, index: number) {
155
189
  function PalettePanel({
156
190
  onOpenChange,
157
191
  items,
192
+ query: queryProp,
193
+ defaultQuery = '',
194
+ onQueryChange,
195
+ filter = true,
196
+ loading = false,
197
+ highlight = false,
158
198
  placeholder,
159
199
  emptyText,
160
200
  footer,
@@ -164,8 +204,9 @@ function PalettePanel({
164
204
  const t = useMessages()
165
205
  const listId = useId()
166
206
  const ref = useRef<HTMLDivElement | null>(null)
167
- const [query, setQuery] = useState('')
168
- const groups = useMemo(() => groupCommands(filterCommands(items, query)), [items, query])
207
+ const [query, setQuery] = useControllable(queryProp, defaultQuery, onQueryChange)
208
+ const groups = useMemo(() => groupCommands(filter ? filterCommands(items, query) : items), [items, query, filter])
209
+ const marks = highlight ? queryWords(query) : []
169
210
  const flat = useMemo(() => groups.flatMap((g) => g.items), [groups])
170
211
  const [activeId, setActiveId] = useState<string | null>(null)
171
212
 
@@ -240,7 +281,14 @@ function PalettePanel({
240
281
  className={cx('ev-cmdk', className)}
241
282
  >
242
283
  <div className="ev-cmdk-head">
243
- <Search size={17} className="ev-cmdk-search-icon" aria-hidden="true" />
284
+ {loading ? (
285
+ // Ожидание объявляет строка состояния под списком; индикатор - только для глаз.
286
+ <span className="ev-cmdk-search-icon ev-cmdk-spinner" aria-hidden="true">
287
+ <Spinner size={16} />
288
+ </span>
289
+ ) : (
290
+ <Search size={17} className="ev-cmdk-search-icon" aria-hidden="true" />
291
+ )}
244
292
  <input
245
293
  className="ev-cmdk-input"
246
294
  type="text"
@@ -266,7 +314,13 @@ function PalettePanel({
266
314
  <X size={16} />
267
315
  </button>
268
316
  </div>
269
- <div id={listId} role="listbox" aria-label={ariaLabel ?? t.commandPalette.label} className="ev-cmdk-list">
317
+ <div
318
+ id={listId}
319
+ role="listbox"
320
+ aria-label={ariaLabel ?? t.commandPalette.label}
321
+ aria-busy={loading || undefined}
322
+ className="ev-cmdk-list"
323
+ >
270
324
  {groups.map((g, gi) => {
271
325
  const headId = `${listId}-g${gi}`
272
326
  const options = g.items.map((it) => (
@@ -287,8 +341,12 @@ function PalettePanel({
287
341
  {it.icon}
288
342
  </span>
289
343
  ) : null}
290
- <span className="ev-cmdk-item-label">{it.label}</span>
291
- {it.hint ? <span className="ev-cmdk-item-hint">{it.hint}</span> : null}
344
+ <span className="ev-cmdk-item-label">{marks.length > 0 ? <Highlight text={it.label} query={marks} /> : it.label}</span>
345
+ {it.hint ? (
346
+ <span className="ev-cmdk-item-hint">
347
+ {marks.length > 0 && typeof it.hint === 'string' ? <Highlight text={it.hint} query={marks} /> : it.hint}
348
+ </span>
349
+ ) : null}
292
350
  </div>
293
351
  ))
294
352
  if (g.group === undefined) {
@@ -311,7 +369,7 @@ function PalettePanel({
311
369
  })}
312
370
  </div>
313
371
  <div className="ev-cmdk-empty" role="status">
314
- {flat.length === 0 ? (emptyText ?? t.common.nothingFound) : null}
372
+ {flat.length === 0 ? (loading ? t.commandPalette.searching : (emptyText ?? t.common.nothingFound)) : null}
315
373
  </div>
316
374
  <div className="ev-cmdk-foot">
317
375
  <div className="ev-cmdk-foot-left">{footer}</div>