@boobooking/dashboard-ui-components 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,42 +65,22 @@ Vue не ниже 3.5, ESM, сборщик, умеющий импорт CSS из
65
65
  Tailwind, `@tailwindcss/forms`, Inertia и какие-либо токены в CSS приложения
66
66
  пакету не нужны.
67
67
 
68
- Меню `DropdownButtonWithAction` стоит на Popover API браузера: Safari 17+,
69
- Chrome 114+, Firefox 125+. В браузерах старше меню видно в потоке всегда.
68
+ Меню `DropdownButtonWithAction` и `HamburgerMenu`, список `SelectSingle` и
69
+ календари `SelectDateInterval` стоят на Popover API браузера: Safari 17+,
70
+ Chrome 114+, Firefox 125+. В браузерах старше меню, список и календари
71
+ видны в потоке всегда.
70
72
 
71
73
  ## Компоненты
72
74
 
73
- Пакет экспортирует семнадцать компонентов: `Popup`, `Dot`, `PickDay`,
74
- `RussianMobileFilter`, `Search`, `SelectDateInterval`, `SelectSingle`,
75
- `SmallBadge`, `ErrorMessages`, `Closer`, `DownloadLink`,
76
- `ConfirmationModal`, `DropdownButtonWithAction`, `DataTable`,
75
+ Пакет экспортирует пятнадцать компонентов: `Dot`, `RussianMobileFilter`,
76
+ `Search`, `SelectDateInterval`, `SelectSingle`, `SmallBadge`,
77
+ `ErrorMessages`, `DownloadLink`, `ConfirmationModal`,
78
+ `DropdownButtonWithAction`, `HamburgerMenu`, `DataTable`,
77
79
  `NavigationMenuElement`, `PageCard`, `NotificationMessage` и плагин
78
80
  `dashboardUi`. В архив пакета (`files: ["dist"]`) исходники не попадают,
79
81
  поэтому контракт каждого компонента — здесь и в playground, а не в исходном
80
82
  коде.
81
83
 
82
- ### Popup
83
-
84
- Всплывающее меню с оверлеем, закрывающим по клику вне, и переходом на
85
- открытие/закрытие. Другие компоненты пакета используют его как обёртку над
86
- своим содержимым — некоторые напрямую, некоторые через внутренние
87
- компоненты.
88
-
89
- <popup v-model="isOpen" align="right">
90
- …содержимое меню…
91
- </popup>
92
-
93
- | Проп | Тип | По умолчанию | Описание |
94
- | --- | --- | --- | --- |
95
- | `modelValue` | `Boolean` | `false` | Открыто ли меню |
96
- | `align` | `String` | `"left"` | `"left"` или `"right"` — с какой стороны прижать меню |
97
-
98
- Событие: `update:modelValue` — при каждом открытии и закрытии, включая клик по
99
- оверлею.
100
-
101
- У `Popup` нет события `changed`, и это не пропуск — см. раздел
102
- «Почему у Popup и Dot нет события changed».
103
-
104
84
  ### Dot
105
85
 
106
86
  Индикатор-точка: красная или зелёная, с пульсацией или без.
@@ -113,26 +93,7 @@ Chrome 114+, Firefox 125+. В браузерах старше меню видн
113
93
  | `withPulse` | `Boolean` | `false` | Добавляет анимацию пульсации |
114
94
 
115
95
  Событий нет. Значения у `Dot` тоже нет — см. раздел
116
- «Почему у Popup и Dot нет события changed».
117
-
118
- ### PickDay
119
-
120
- Поле выбора одной даты на Pikaday, с ластиком очистки. Pikaday загружается
121
- в браузере при монтировании компонента, а не при загрузке пакета, поэтому
122
- календарь появляется, когда загрузка закончится.
123
-
124
- <pick-day v-model="date" placeholder-text="от" />
125
-
126
- | Проп | Тип | По умолчанию | Описание |
127
- | --- | --- | --- | --- |
128
- | `modelValue` | `String` | `""` | Текущая дата |
129
- | `withEraser` | `Boolean` | `true` | Показывать ли ластик очистки |
130
- | `placeholderText` | `String` | `""` | Текст подсказки в пустом поле |
131
- | `lang` | `String` | язык плагина | `"ru"` или `"en"`; календарь берёт язык при монтировании |
132
-
133
- События: `update:modelValue` — при выборе даты в календаре и при очистке
134
- (пустой строкой); `changed` — в тех же двух случаях, для использования наравне
135
- с остальными фильтрами.
96
+ «Почему у Dot нет события changed».
136
97
 
137
98
  ### RussianMobileFilter
138
99
 
@@ -179,7 +140,10 @@ Chrome 114+, Firefox 125+. В браузерах старше меню видн
179
140
 
180
141
  ### SelectDateInterval
181
142
 
182
- Пара `PickDay` под одним заголовком — интервал дат «от» и «до».
143
+ Два поля даты под одним заголовком — интервал дат «от» и «до». Дата
144
+ выбирается в календаре Pikaday, у каждого поля — ластик очистки. Pikaday
145
+ загружается в браузере при монтировании компонента, а не при загрузке
146
+ пакета, поэтому календари появляются, когда загрузка закончится.
183
147
 
184
148
  <select-date-interval
185
149
  header="Период"
@@ -199,9 +163,20 @@ Chrome 114+, Firefox 125+. В браузерах старше меню видн
199
163
  | `isLoading` | `Boolean` | `false` | Индикатор загрузки в заголовке |
200
164
  | `lang` | `String` | язык плагина | `"ru"` или `"en"`; действует и на подсказки полей, и на оба календаря |
201
165
 
202
- События: `update:dateFrom`, `update:dateTo` — на каждый выбор в
203
- соответствующем `PickDay`; `changed` — когда новое значение отличается от
204
- переданного пропса (отдельно для каждой из двух дат).
166
+ События: `update:dateFrom`, `update:dateTo` — на каждый выбор даты
167
+ в соответствующем поле и на его очистку ластиком (пустой строкой);
168
+ `changed` — когда новое значение отличается от переданного пропса
169
+ (отдельно для каждой из двух дат).
170
+
171
+ Клик по любому месту поля открывает его календарь. Календарь открывается
172
+ в верхнем слое браузера (Popover API): его не обрезает обёртка
173
+ с прокруткой. Он открывается под полем, левым краем по левому краю поля.
174
+ Не поместившись снизу, он открывается вверх, если сверху места больше;
175
+ не поместившись справа — правым краем по правому краю поля, если слева
176
+ места больше. Выбор даты, клик вне календаря, Escape, прокрутка страницы
177
+ и изменение размера окна закрывают календарь. Клик вне доходит до элемента
178
+ под курсором, поэтому календарь «до» открывается тем же кликом, что
179
+ закрывает «от».
205
180
 
206
181
  ### SelectSingle
207
182
 
@@ -226,6 +201,29 @@ Chrome 114+, Firefox 125+. В браузерах старше меню видн
226
201
  События: `update:modelValue` и `changed` — при выборе элемента (повторный
227
202
  клик по уже выбранному снимает выбор) и при очистке через ластик.
228
203
 
204
+ С клавиатуры: пока список открыт, стрелки вверх и вниз переводят фокус по
205
+ пунктам и страницу не прокручивают. Отсчёт идёт от выбранного пункта: вниз —
206
+ следующий за ним, вверх — предыдущий; без выбранного вниз — первый пункт,
207
+ вверх — последний; с краёв — по кругу. Enter или пробел выбирают пункт под
208
+ фокусом так же, как клик, Escape закрывает список. После закрытия фокус
209
+ возвращается на кнопку фильтра. У закрытого списка стрелки прокручивают
210
+ страницу.
211
+
212
+ Подсветка пункта — это фокус, и мышь ведёт тот же фокус: движение по пункту
213
+ ставит фокус на него, стрелки продолжают от пункта под курсором. Подсвечен
214
+ всегда один пункт — тот, на который последними указали клавиатура или мышь.
215
+
216
+ Список открывается в верхнем слое браузера (Popover API): его не обрезает
217
+ обёртка с прокруткой. Он открывается под полем, левым краем по левому
218
+ краю поля. Не поместившись снизу, он открывается вверх, если сверху места
219
+ больше; не поместившись справа — правым краем по правому краю поля, если
220
+ слева места больше. Длинный список ограничен высотой до края окна
221
+ и прокручивается внутри. Клик вне списка закрывает его и доходит до
222
+ элемента под курсором: соседний фильтр открывается тем же кликом.
223
+ Прокрутка страницы и изменение размера окна закрывают список, прокрутка
224
+ внутри списка — нет. Открытие другого списка, календаря или меню
225
+ закрывает предыдущее.
226
+
229
227
  ### SmallBadge
230
228
 
231
229
  Бейдж-пилюля с коротким текстом. Текст не переносится.
@@ -253,16 +251,6 @@ Chrome 114+, Firefox 125+. В браузерах старше меню видн
253
251
 
254
252
  Событий нет.
255
253
 
256
- ### Closer
257
-
258
- Кнопка-крестик закрытия. Размер и положение задаёт приложение классами на
259
- корне: без классов размера крестик получает размер SVG по умолчанию
260
- (300×300).
261
-
262
- <closer class="absolute top-0 right-0 w-8 h-8" @clicked="close" />
263
-
264
- Пропов нет. Событие: `clicked` — по клику.
265
-
266
254
  ### DownloadLink
267
255
 
268
256
  Ссылка на файл в виде бейджа с иконкой скачивания. Кликабелен весь бейдж.
@@ -315,32 +303,127 @@ ConfirmationModal нет v-model».
315
303
 
316
304
  ### DropdownButtonWithAction
317
305
 
318
- Кнопка с меню дополнительных действий: основная часть — слот `button`, стрелка
319
- справа открывает меню из слота `actions`. Без слота `actions` стрелки нет, и
320
- кнопка скругляется с обеих сторон.
321
-
322
- <dropdown-button-with-action v-model="menuIsOpen">
323
- <template #button>…основное действие…</template>
324
- <template #actions>…пункты меню…</template>
325
- </dropdown-button-with-action>
306
+ Кнопка из списка действий `actions`. Первое действие — основная кнопка,
307
+ остальные — меню за стрелкой справа. Одно действие — кнопка без стрелки;
308
+ без действий компонент ничего не рисует.
309
+
310
+ <dropdown-button-with-action
311
+ v-model="menuIsOpen"
312
+ :actions="[
313
+ { label: 'Редактировать', href: route('user.edit', { user: row.getId() }) },
314
+ { label: 'Поменять пароль', href: route('user.password.edit', { user: row.getId() }) },
315
+ { label: 'Удалить', color: 'red', onSelect: () => prepareUserDelete(row) },
316
+ ]"
317
+ />
326
318
 
327
319
  | Проп | Тип | По умолчанию | Описание |
328
320
  | --- | --- | --- | --- |
321
+ | `actions` | `Array` | `[]` | Действия, см. ниже |
329
322
  | `modelValue` | `Boolean` | `false` | Открыто ли меню |
330
323
  | `lang` | `String` | язык плагина | `"ru"` или `"en"` |
331
324
 
325
+ Действие — объект одной из двух форм: переход (`href` без `onSelect`) или
326
+ действие (`onSelect` без `href`). Поле со значением `undefined` считается
327
+ отсутствующим.
328
+
329
+ | Поле | Тип | Описание |
330
+ | --- | --- | --- |
331
+ | `label` | `String`, обязательно | Текст кнопки или пункта меню |
332
+ | `href` | `String` | Переход: обычный клик уходит в `navigate` плагина `dashboardUi`, клик с Cmd/Ctrl/Shift и средней кнопкой — браузеру; без `navigate` — обычная ссылка |
333
+ | `onSelect` | `Function` | Действие: вызывается без аргументов |
334
+ | `color` | `"yellow"` или `"red"` | Цвет; без поля — обычный |
335
+
336
+ Основная кнопка работает как пункт меню: переход — ссылка, действие —
337
+ кнопка, и `onSelect` вызывает сам компонент — по клику, Enter и пробелу.
338
+ Своего события клика у кнопки нет: странице не нужно ловить клик и
339
+ разбираться в разметке компонента.
340
+
341
+ Цвет первого действия красит основную кнопку вместе со стрелкой, цвет
342
+ пункта меню — только этот пункт. Обычная кнопка — белая с серой рамкой,
343
+ жёлтая и красная — светлый фон, рамка и текст своего цвета. Обычный пункт
344
+ меню — серый текст, при подсветке серый фон; жёлтый и красный — текст
345
+ своего цвета, при подсветке светлый фон того же цвета.
346
+
347
+ Неверный список Vue отмечает предупреждением о пропе в режиме разработки,
348
+ а компонент не падает: элементы-необъекты пропускает, кнопка без функции
349
+ `onSelect` по клику ничего не вызывает, пункт без неё только закрывает
350
+ меню. `null` — пустой список.
351
+
352
+ Вид кнопки и пунктов задаёт компонент: страница передаёт данные, а не
353
+ разметку, и классов не даёт. Слота `button` и поля `danger` нет с версии
354
+ 0.15.0 — см. «Обновление с 0.14»; слота `actions` — с 0.14.0, см.
355
+ «Обновление с 0.13».
356
+
332
357
  Событие: `update:modelValue` — только когда меню открыл или закрыл сам
333
358
  компонент или браузер: клик по стрелке, клик вне меню, Escape, клик
334
- по пункту меню, прокрутка, изменение размера окна. Значение, пришедшее
359
+ по пункту меню, прокрутка, изменение размера окна, а ещё пропажа открытого
360
+ меню, когда действий осталось меньше двух. Значение, пришедшее
335
361
  от родителя, компонент принимает молча: ответное событие вернуло бы
336
362
  родителю его же решение. Без `v-model` меню работает само по себе.
337
363
 
338
364
  Меню открывается в верхнем слое браузера (Popover API): его не обрезает
339
365
  таблица с горизонтальной прокруткой, и оно стоит поверх страницы. Правый
340
- край меню — по правому краю стрелки; меню открывается вниз или вверх —
341
- где больше места, сужается у левого края окна и закрывается кликом
342
- по любому пункту, прокруткой страницы или таблицы и изменением размера
343
- окна. Открытие другого меню закрывает предыдущее.
366
+ край меню — по правому краю стрелки; меню открывается под стрелкой,
367
+ а если там не помещается — туда, где больше места, сужается у левого края
368
+ окна и закрывается кликом по любому пункту, прокруткой страницы или
369
+ таблицы и изменением размера окна. Открытие другого меню закрывает
370
+ предыдущее.
371
+
372
+ Пока меню открыто, стрелки вверх и вниз переводят фокус по пунктам и
373
+ страницу не прокручивают: вниз — на первый пункт, вверх — на последний,
374
+ дальше — по соседним, с краёв — по кругу. Enter на пункте под фокусом
375
+ выбирает его. Подсветка пункта — это фокус, и мышь ведёт тот же фокус:
376
+ движение по пункту ставит фокус на него, стрелки продолжают от пункта под
377
+ курсором. Подсвечен всегда один пункт — тот, на который последними указали
378
+ клавиатура или мышь.
379
+
380
+ ### HamburgerMenu
381
+
382
+ Кнопка ☰ с меню из пунктов пропа `actions` — меню профиля в шапке
383
+ дашборда. Кнопку, панель и пункты рисует компонент, страница передаёт
384
+ только пункты. Без пунктов кнопки нет.
385
+
386
+ <hamburger-menu :actions="menuActions" />
387
+
388
+ import { router } from '@inertiajs/vue3';
389
+
390
+ computed: {
391
+ menuActions() {
392
+ return [
393
+ { label: 'Администраторы', href: this.route('users') },
394
+ { label: 'Поменять пароль', href: this.route('my.password.edit') },
395
+ { label: 'Выйти', onSelect: () => router.delete(this.route('logout')) },
396
+ ];
397
+ },
398
+ },
399
+
400
+ | Проп | Тип | По умолчанию | Описание |
401
+ | --- | --- | --- | --- |
402
+ | `actions` | `Array` | `[]` | Пункты меню — в том же формате, что у `DropdownButtonWithAction` |
403
+ | `lang` | `String` | язык плагина | `"ru"` или `"en"` |
404
+
405
+ Пункты, их проверка и вид — как у `DropdownButtonWithAction`: переход —
406
+ `{ label, href }`, действие — `{ label, onSelect }`, цвет —
407
+ `color: 'yellow'` или `color: 'red'`. Выход запросом DELETE — действие:
408
+ Inertia пакет не импортирует, `router.delete` вызывает страница.
409
+
410
+ Событий и `v-model` нет: меню открывают и закрывают кнопка, клик вне меню,
411
+ Escape, клик по пункту, прокрутка и изменение размера окна. Меню
412
+ открывается в верхнем слое браузера (Popover API), правым краем по правому
413
+ краю кнопки, под ней, а если там не помещается — туда, где больше места.
414
+ Открытие другого меню закрывает предыдущее.
415
+
416
+ Это исключение из правила «состояние, которое компонент меняет сам, — через
417
+ `v-model`»: шапке не нужно ни открывать меню извне, ни знать, открыто ли
418
+ оно. Меню строк таблицы на `DropdownButtonWithAction` страница держит под
419
+ `v-model`, чтобы знать, какая строка выбрана; у меню профиля такой нужды нет.
420
+ `v-model`, переданный на `<hamburger-menu>`, в контракт не входит. Атрибуты
421
+ с `<hamburger-menu>` — `class`, `style`, `data-*` — ложатся на обёртку
422
+ кнопки.
423
+
424
+ Клавиатура и мышь — как в меню `DropdownButtonWithAction`: пока меню
425
+ открыто, стрелки вверх и вниз переводят фокус по пунктам, Enter выбирает
426
+ пункт под фокусом; подсветка пункта — это фокус, и мышь ведёт тот же фокус.
344
427
 
345
428
  ### DataTable
346
429
 
@@ -621,15 +704,16 @@ sm:rounded-lg`, `box-sizing: border-box`. Отступов у неё нет: ф
621
704
  | `ConfirmationModal` — кнопка отмены | Отмена | Cancel |
622
705
  | `DownloadLink` — подпись | Скачать | Download |
623
706
  | `DropdownButtonWithAction` — подпись стрелки для скринридера | Открыть меню | Open menu |
624
- | `PickDay` — месяцы, дни недели, кнопки календаря | по-русски | in English |
707
+ | `HamburgerMenu` — подпись кнопки для скринридера | Открыть меню | Open menu |
708
+ | `SelectDateInterval` — месяцы, дни недели, кнопки календарей | по-русски | in English |
625
709
  | `SelectDateInterval` — подсказки полей | от / до | from / to |
626
710
  | `DataTable` — пагинация | Предыдущая / Следующая / Показаны результаты X - Y из Z | Previous / Next / Showing X - Y of Z results |
627
711
  | `PageCard` — доступное имя крестика | Назад | Back |
628
712
  | `NotificationMessage` — подпись крестика для скринридера | Закрыть | Close |
629
713
 
630
714
  Формат даты — `дд.мм.гггг` в обоих языках: это формат значения, а не текст.
631
- Неделя начинается с понедельника. Календарь `PickDay` берёт язык при
632
- монтировании: смена языка после монтирования в нём не отражается.
715
+ Неделя начинается с понедельника. Календари `SelectDateInterval` берут язык
716
+ при монтировании: смена языка после монтирования в них не отражается.
633
717
 
634
718
  ## SSR
635
719
 
@@ -640,8 +724,8 @@ Inertia SSR), а серверная разметка гидратируется
640
724
  - `PageCard` на сервере рисует крестик, только если задан `fallback-url`:
641
725
  адрес возврата известен только в браузере, и крестик, ведущий на него,
642
726
  появляется после монтирования;
643
- - `PickDay` загружает Pikaday в браузере при монтировании: календарь
644
- появляется, когда загрузка закончится;
727
+ - `SelectDateInterval` загружает Pikaday в браузере при монтировании:
728
+ календари появляются, когда загрузка закончится;
645
729
  - остальные компоненты рендерятся на сервере так же, как в браузере.
646
730
 
647
731
  ## Правила API пакета
@@ -660,9 +744,9 @@ Inertia SSR), а серверная разметка гидратируется
660
744
  для него это и есть обычный `v-model`.
661
745
  - Булев проп называется `is*`, если описывает состояние компонента
662
746
  (`isRequired`, `isLoading`), и `with*`, если описывает необязательную
663
- возможность (`withEraser` у `PickDay`, `withPulse` у `Dot`).
747
+ возможность (`withPulse` у `Dot`).
664
748
  - Проп-перечисление — ограниченный набор строковых значений, как `color` у
665
- `Dot` или `align` у `Popup`, — обязательно имеет `validator`,
749
+ `Dot` или `variant` у `DataTable`, — обязательно имеет `validator`,
666
750
  отклоняющий значения вне списка.
667
751
  - Компонент не падает ни на каком значении пропа. `null` и `undefined` там,
668
752
  где ждут строку, объект или массив, он считает пустыми: пустой строкой,
@@ -681,8 +765,11 @@ Inertia SSR), а серверная разметка гидратируется
681
765
  `followLink($event, href)` миксина `withNavigation`, который отдаёт его
682
766
  `navigate` плагина. Ни Inertia, ни роутер пакет не импортирует. Исключение —
683
767
  `DownloadLink`: это обычная ссылка браузера, скачивающая файл, и через
684
- `navigate` она не идёт. Через `navigate` ходят `DataTable` (его пагинация) и
685
- `NavigationMenuElement`.
768
+ `navigate` она не идёт. Через `navigate` ходят `DataTable` (его пагинация),
769
+ `NavigationMenuElement`, основная кнопка-переход и пункты-переходы
770
+ `DropdownButtonWithAction` и пункты-переходы `HamburgerMenu`. Крестик
771
+ `PageCard` — кнопка, а не ссылка: адрес возврата он тоже отдаёт
772
+ `navigate`.
686
773
  - Компонент обращается к `window`, `document` и другим API браузера только
687
774
  в `mounted()`, `beforeUnmount()` и обработчиках событий; загрузка модуля,
688
775
  `data()`, `computed` и рендер работают без браузера. Так пакет
@@ -690,31 +777,25 @@ Inertia SSR), а серверная разметка гидратируется
690
777
  `tests/ssr.test.js` и `tests/hydration.test.js`: каждый экспортируемый
691
778
  компонент обязан иметь фикстуру в `tests/ssrFixtures.js`.
692
779
 
693
- ## Почему у Popup и Dot нет события changed
780
+ ## Почему у Dot нет события changed
694
781
 
695
782
  `changed` означает не «значение изменилось», а «пользователь закончил
696
783
  выбор, можно обновлять выдачу» — фильтры дашборда эмитят его как сигнал к
697
784
  перезапросу данных.
698
785
 
699
- У `Popup` такого момента нет: он не хранит значение фильтра, а лишь
700
- управляет видимостью меню, и открытие меню — не повод перезагружать
701
- данные. Момент, когда выбор действительно завершён, отслеживает
702
- компонент, который использует `Popup` — сам или через внутренние
703
- компоненты пакета, — `changed` эмитит он.
704
-
705
- У `Dot` события нет по другой причине: у него нет значения вообще, поэтому
706
- эмитить не о чем.
786
+ У `Dot` такого момента нет: у него нет значения вообще, поэтому эмитить
787
+ не о чем.
707
788
 
708
- Отсутствие `changed` у `Popup` и `Dot` — сознательное решение, а не
709
- пропуск.
789
+ Отсутствие `changed` у `Dot` — сознательное решение, а не пропуск.
710
790
 
711
791
  ## Почему у ConfirmationModal нет v-model
712
792
 
713
793
  Правило пакета — значение через `v-model` — относится к состоянию, которое
714
- компонент меняет сам: `Popup` закрывается по клику вне, `SelectSingle` меняет
715
- выбор. `ConfirmationModal` свою видимость не меняет никогда. Обе кнопки только
716
- сообщают родителю решение пользователя, а закрыть модалку, оставить её открытой
717
- на время запроса или открыть следующую — решает родитель.
794
+ компонент меняет сам: меню `DropdownButtonWithAction` закрывается по клику
795
+ вне, `SelectSingle` меняет выбор. `ConfirmationModal` свою видимость не меняет
796
+ никогда. Обе кнопки только сообщают родителю решение пользователя, а закрыть
797
+ модалку, оставить её открытой на время запроса или открыть следующую — решает
798
+ родитель.
718
799
 
719
800
  `v-model` обещал бы двустороннюю связь, которой нет, поэтому видимость
720
801
  передаётся пропом `isOpen`.
@@ -723,7 +804,7 @@ Inertia SSR), а серверная разметка гидратируется
723
804
 
724
805
  npm run playground
725
806
 
726
- Поднимает демо-страницы со всеми семнадцатью компонентами в двух окружениях:
807
+ Поднимает демо-страницы со всеми пятнадцатью компонентами в двух окружениях:
727
808
  голом (без Tailwind и стилей приложения) и в окружении приложения (с
728
809
  Tailwind, `@tailwindcss/forms` и типографикой, как у потребителя). Это
729
810
  основной способ посмотреть, как компоненты выглядят и ведут себя, а не
@@ -739,6 +820,182 @@ Tailwind, `@tailwindcss/forms` и типографикой, как у потре
739
820
  Правила номера телефона в `RussianMobileFilter` — российские; язык текстов
740
821
  задаётся, см. «Языки».
741
822
 
823
+ ## Обновление с 0.14
824
+
825
+ - Меню профиля в шапке переходит с `Popup` на `HamburgerMenu`: кнопку ☰,
826
+ панель и пункты рисует компонент, страница передаёт только пункты. Было:
827
+
828
+ <div class="relative">
829
+ <div>
830
+ <button
831
+ @click="menuDropdownOpen = !menuDropdownOpen"
832
+ :class="{ 'z-20': menuDropdownOpen }"
833
+ class="relative inline-flex items-center justify-center p-2 rounded-md text-gray-400 …"
834
+ aria-label="Main menu"
835
+ aria-expanded="false"
836
+ >
837
+ <svg class="h-6 w-6" …>…</svg>
838
+ </button>
839
+ </div>
840
+ <popup class="origin-top-right right-0" v-model="menuDropdownOpen" align="right">
841
+ <div class="mt-1 w-56 rounded-md shadow-lg bg-white ring-1 ring-black/5" role="menu" …>
842
+ <Link :href="route('users')" as="button" class="…" role="menuitem">Администраторы</Link>
843
+ <Link :href="route('logout')" method="delete" as="button" class="…" role="menuitem">Выйти</Link>
844
+ </div>
845
+ </popup>
846
+ </div>
847
+
848
+ Стало:
849
+
850
+ <hamburger-menu :actions="menuActions" />
851
+
852
+ import { router } from '@inertiajs/vue3';
853
+
854
+ computed: {
855
+ menuActions() {
856
+ return [
857
+ { label: 'Администраторы', href: this.route('users') },
858
+ { label: 'Выйти', onSelect: () => router.delete(this.route('logout')) },
859
+ ];
860
+ },
861
+ },
862
+
863
+ Пункты собираются в `computed`: в шаблоне импорт `router` недоступен.
864
+ Выход запросом DELETE — действие с `router.delete`, тот же вызов, что
865
+ делал `<Link method="delete">`. `menuDropdownOpen` и импорт `Popup`
866
+ больше не нужны.
867
+ - Пункты-переходы `HamburgerMenu` — ссылки `<a href>`, а не `<Link>`:
868
+ обычный клик уходит в `navigate` плагина `dashboardUi` (для Inertia —
869
+ `router.visit`), Cmd-клик и средняя кнопка открывают новую вкладку. Без
870
+ `navigate` переход идёт полной загрузкой страницы — подключите его
871
+ к плагину (см. «Подключение к приложению»).
872
+ - `Popup`, `PickDay` и `Closer` больше не экспортируются: это внутренние
873
+ части `SelectSingle`, `SelectDateInterval` и `PageCard`. Импорт любого
874
+ из них ломает сборку: «… is not exported by …».
875
+ - Список `SelectSingle` и календари `SelectDateInterval` открываются
876
+ в верхнем слое браузера, как меню. Клик вне закрывает их и доходит до
877
+ элемента под курсором, прокрутка страницы закрывает, длинный список
878
+ прокручивается внутри. Без Popover API (Safari до 17, Chrome до 114,
879
+ Firefox до 125) они видны всегда.
880
+ - Меню `DropdownButtonWithAction` и `HamburgerMenu` открывается под
881
+ кнопкой, а вверх — только если снизу не помещается и сверху места
882
+ больше. В 0.14 меню в нижней половине окна открывалось вверх всегда.
883
+ - `DropdownButtonWithAction` строится из списка действий: первое действие —
884
+ основная кнопка, остальные — меню. Слота `button` нет: переданный слот
885
+ не рисуется, а в режиме разработки в консоль уходит предупреждение.
886
+ Отступы и шрифт основной кнопки задаёт компонент (`px-4 py-2 text-sm
887
+ font-medium`). Было:
888
+
889
+ <dropdown-button-with-action
890
+ :actions="[
891
+ { label: 'Поменять пароль', href: route('user.password.edit', { user: row.getId() }) },
892
+ { label: 'Удалить', danger: true, onSelect: () => prepareUserDelete(row) },
893
+ ]"
894
+ >
895
+ <template v-slot:button>
896
+ <Link class="block px-4 py-2 text-sm font-medium text-gray-700" :href="route('user.edit', { user: row.getId() })">
897
+ Редактировать
898
+ </Link>
899
+ </template>
900
+ </dropdown-button-with-action>
901
+
902
+ Стало:
903
+
904
+ <dropdown-button-with-action
905
+ :actions="[
906
+ { label: 'Редактировать', href: route('user.edit', { user: row.getId() }) },
907
+ { label: 'Поменять пароль', href: route('user.password.edit', { user: row.getId() }) },
908
+ { label: 'Удалить', color: 'red', onSelect: () => prepareUserDelete(row) },
909
+ ]"
910
+ />
911
+
912
+ - Кнопка без меню — одно действие. Было:
913
+
914
+ <dropdown-button-with-action>
915
+ <template v-slot:button>
916
+ <Link class="block px-4 py-2 text-sm font-medium text-gray-700" :href="showGroupLink(row)">
917
+ Посмотреть
918
+ </Link>
919
+ </template>
920
+ </dropdown-button-with-action>
921
+
922
+ Стало:
923
+
924
+ <dropdown-button-with-action :actions="[{ label: 'Посмотреть', href: showGroupLink(row) }]" />
925
+
926
+ - Основное действие-функция больше не ловится на корне компонента:
927
+ `onSelect` первого действия вызывает сам компонент — по клику, Enter и
928
+ пробелу. Было:
929
+
930
+ <dropdown-button-with-action
931
+ :actions="[{ label: 'Зафиксировать как неотправленное', onSelect: () => askUnsent(row) }]"
932
+ v-on:click="retryFromDropdown($event, row)"
933
+ >
934
+ <template v-slot:button>
935
+ <span class="block px-2 py-1 text-sm text-gray-700">Отправить заново</span>
936
+ </template>
937
+ </dropdown-button-with-action>
938
+
939
+ retryFromDropdown(event, sms) {
940
+ if (event.currentTarget.firstElementChild.contains(event.target)) {
941
+ this.askRetry(sms);
942
+ }
943
+ },
944
+
945
+ Стало:
946
+
947
+ <dropdown-button-with-action
948
+ :actions="[
949
+ { label: 'Отправить заново', onSelect: () => askRetry(row) },
950
+ { label: 'Зафиксировать как неотправленное', onSelect: () => askUnsent(row) },
951
+ ]"
952
+ />
953
+
954
+ `retryFromDropdown` удаляется: он полагался на разметку пакета.
955
+ - Поле `danger` заменено цветом: `danger: true` → `color: 'red'`. Пункт
956
+ с `danger` рисуется обычным, а в режиме разработки в консоль уходит
957
+ предупреждение с заменой. Новый цвет — `color: 'yellow'`. Цвета есть
958
+ и у пунктов `HamburgerMenu`.
959
+ - Красный пункт меню — мягкий: красный текст, при подсветке светлый
960
+ красный фон, а не заливка `red-400` с белым текстом.
961
+ - `0.15.0` не подтянется по `^0.14.0`: для версий `0.x` знак `^` пропускает
962
+ только патчи. Обновление — `npm install @boobooking/dashboard-ui-components@^0.15.0`.
963
+
964
+ ## Обновление с 0.13
965
+
966
+ - `DropdownButtonWithAction`: пункты меню передаются пропом `actions`,
967
+ слот `actions` убран, и страница больше не оформляет пункты — вид задаёт
968
+ компонент. Было:
969
+
970
+ <template v-slot:actions>
971
+ <Link
972
+ class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
973
+ :href="route('user.password.edit', {'user': row.getId()})"
974
+ >
975
+ Поменять пароль
976
+ </Link>
977
+ <a
978
+ href="#"
979
+ @click.prevent="prepareUserDelete(row)"
980
+ class="block px-4 py-2 text-sm bg-red-400 hover:bg-red-500 text-white"
981
+ role="menuitem"
982
+ >
983
+ Удалить
984
+ </a>
985
+ </template>
986
+
987
+ Стало — проп на самом `dropdown-button-with-action`:
988
+
989
+ :actions="[
990
+ { label: 'Поменять пароль', href: route('user.password.edit', { user: row.getId() }) },
991
+ { label: 'Удалить', danger: true, onSelect: () => prepareUserDelete(row) },
992
+ ]"
993
+
994
+ Меню со старым слотом остаётся без пунктов и без стрелки; в режиме
995
+ разработки компонент пишет об этом в консоль.
996
+ - `0.14.0` не подтянется по `^0.13.0`: для версий `0.x` знак `^` пропускает
997
+ только патчи. Обновление — `npm install @boobooking/dashboard-ui-components@^0.14.0`.
998
+
742
999
  ## Обновление с 0.12
743
1000
 
744
1001
  - Крестик `PageCard` больше не ходит по истории: подключите `currentUrl`
@@ -787,7 +1044,7 @@ Tailwind, `@tailwindcss/forms` и типографикой, как у потре
787
1044
  `ConfirmationModal`.
788
1045
 
789
1046
  Ресет пакета распространяется и на содержимое слотов — разметку приложения
790
- внутри `Popup` и `DropdownButtonWithAction`. Ресет лежит в слое
1047
+ внутри `DropdownButtonWithAction`. Ресет лежит в слое
791
1048
  `components`, поэтому внутри слота он сильнее всего, что приложение объявило
792
1049
  в слое `base`: preflight, `@tailwindcss/forms` и собственных умолчаний
793
1050
  приложения. В слоте он задаёт:
@@ -811,10 +1068,6 @@ Tailwind, `@tailwindcss/forms` и типографикой, как у потре
811
1068
  checked:bg-current checked:border-transparent focus:ring-2
812
1069
  focus:ring-offset-2`.
813
1070
 
814
- У `Closer` нет доступного имени, и он выключен из порядка табуляции
815
- (`tabindex="-1"`): скринридер его не назовёт, а с клавиатуры до него не
816
- добраться.
817
-
818
1071
  `ConfirmationModal` не переносит фокус внутрь при открытии, не удерживает его
819
1072
  внутри, не возвращает на прежнее место после закрытия и не закрывается по
820
1073
  Escape.