@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 +358 -105
- package/dist/index.js +926 -640
- package/dist/style.css +1 -1
- package/package.json +1 -1
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`
|
|
69
|
-
|
|
68
|
+
Меню `DropdownButtonWithAction` и `HamburgerMenu`, список `SelectSingle` и
|
|
69
|
+
календари `SelectDateInterval` стоят на Popover API браузера: Safari 17+,
|
|
70
|
+
Chrome 114+, Firefox 125+. В браузерах старше меню, список и календари
|
|
71
|
+
видны в потоке всегда.
|
|
70
72
|
|
|
71
73
|
## Компоненты
|
|
72
74
|
|
|
73
|
-
Пакет экспортирует
|
|
74
|
-
`
|
|
75
|
-
`
|
|
76
|
-
`
|
|
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
|
-
«Почему у
|
|
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
|
-
|
|
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
|
-
соответствующем
|
|
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
|
-
Кнопка
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
<dropdown-button-with-action
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
| `
|
|
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
|
-
Неделя начинается с понедельника.
|
|
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
|
-
- `
|
|
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
|
-
возможность (`
|
|
747
|
+
возможность (`withPulse` у `Dot`).
|
|
664
748
|
- Проп-перечисление — ограниченный набор строковых значений, как `color` у
|
|
665
|
-
`Dot` или `
|
|
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
|
-
## Почему у
|
|
780
|
+
## Почему у Dot нет события changed
|
|
694
781
|
|
|
695
782
|
`changed` означает не «значение изменилось», а «пользователь закончил
|
|
696
783
|
выбор, можно обновлять выдачу» — фильтры дашборда эмитят его как сигнал к
|
|
697
784
|
перезапросу данных.
|
|
698
785
|
|
|
699
|
-
У `
|
|
700
|
-
|
|
701
|
-
данные. Момент, когда выбор действительно завершён, отслеживает
|
|
702
|
-
компонент, который использует `Popup` — сам или через внутренние
|
|
703
|
-
компоненты пакета, — `changed` эмитит он.
|
|
704
|
-
|
|
705
|
-
У `Dot` события нет по другой причине: у него нет значения вообще, поэтому
|
|
706
|
-
эмитить не о чем.
|
|
786
|
+
У `Dot` такого момента нет: у него нет значения вообще, поэтому эмитить
|
|
787
|
+
не о чем.
|
|
707
788
|
|
|
708
|
-
Отсутствие `changed` у `
|
|
709
|
-
пропуск.
|
|
789
|
+
Отсутствие `changed` у `Dot` — сознательное решение, а не пропуск.
|
|
710
790
|
|
|
711
791
|
## Почему у ConfirmationModal нет v-model
|
|
712
792
|
|
|
713
793
|
Правило пакета — значение через `v-model` — относится к состоянию, которое
|
|
714
|
-
компонент меняет сам: `
|
|
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
|
-
внутри `
|
|
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.
|