@boobooking/dashboard-ui-components 0.17.1 → 0.19.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 +182 -24
- package/dist/index.js +894 -528
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,10 +65,10 @@ Vue не ниже 3.5, ESM, сборщик, умеющий импорт CSS из
|
|
|
65
65
|
Tailwind, `@tailwindcss/forms`, Inertia и какие-либо токены в CSS приложения
|
|
66
66
|
пакету не нужны.
|
|
67
67
|
|
|
68
|
-
Меню `DropdownButtonWithAction` и `HamburgerMenu`,
|
|
68
|
+
Меню `DropdownButtonWithAction` и `HamburgerMenu`, списки `SelectSingle` и `SelectMultiple`,
|
|
69
69
|
календари `SelectDateInterval` и подсказка `TextPopover` стоят на Popover API
|
|
70
70
|
браузера: Safari 17+, Chrome 114+, Firefox 125+. В браузерах старше меню,
|
|
71
|
-
|
|
71
|
+
списки, календари и подсказка видны в потоке всегда.
|
|
72
72
|
|
|
73
73
|
## Цвета
|
|
74
74
|
|
|
@@ -90,22 +90,24 @@ Tailwind, `@tailwindcss/forms`, Inertia и какие-либо токены в C
|
|
|
90
90
|
|
|
91
91
|
## Компоненты
|
|
92
92
|
|
|
93
|
-
Пакет экспортирует
|
|
94
|
-
`Search`, `SelectDateInterval`, `SelectSingle`, `InfoPill`,
|
|
95
|
-
`ErrorMessages`, `ActionPill`, `ConfirmationModal`,
|
|
93
|
+
Пакет экспортирует девятнадцать компонентов: `Dot`, `RussianMobileFilter`,
|
|
94
|
+
`Search`, `SelectDateInterval`, `SelectSingle`, `SelectMultiple`, `InfoPill`,
|
|
95
|
+
`ErrorMessages`, `FormInput`, `ActionPill`, `ConfirmationModal`,
|
|
96
96
|
`DropdownButtonWithAction`, `HamburgerMenu`, `DataTable`,
|
|
97
97
|
`NavigationMenuElement`, `PageTabs`, `PageCard`, `NotificationMessage`,
|
|
98
98
|
`TextPopover`, функцию `downloadFile` и плагин `dashboardUi`. В архив пакета
|
|
99
99
|
(`files: ["dist"]`) исходники не попадают, поэтому контракт каждого
|
|
100
100
|
компонента — здесь и в playground, а не в исходном коде.
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
Ластики очистки фильтров идут в порядке Tab сразу после своего поля или
|
|
103
|
+
кнопки, в фокусе с клавиатуры вокруг крестика кольцо; нажимаются Enter и
|
|
104
|
+
пробелом. Крестики — закрытие `PageCard` и ластики очистки фильтров —
|
|
105
|
+
при нажатии коротко «вдавливаются», и действие выполняется после этого,
|
|
106
|
+
через 150 мс: иначе нажатия не было бы видно — ластик пропадает сразу
|
|
107
|
+
после очистки, крестик уводит со страницы. Так при любом нажатии: мышью,
|
|
108
|
+
касанием, Enter, пробелом, расширением клавиатурной навигации. Нажатия
|
|
109
|
+
во время анимации не считаются: одно нажатие — одно действие. При
|
|
110
|
+
«уменьшении движения» в системе — без анимации и сразу.
|
|
109
111
|
|
|
110
112
|
### Dot
|
|
111
113
|
|
|
@@ -164,6 +166,65 @@ Enter, пробелом, расширением клавиатурной нав
|
|
|
164
166
|
изменённое родителем, поле показывает сразу и без событий: ответный
|
|
165
167
|
`update:modelValue` вернул бы родителю его же значение.
|
|
166
168
|
|
|
169
|
+
### FormInput
|
|
170
|
+
|
|
171
|
+
Поле формы с подписью над ним. Значение забирает форма: у поля нет
|
|
172
|
+
события `changed`.
|
|
173
|
+
|
|
174
|
+
<form-input v-model="name" label="Имя" :is-required="markNameRequired" />
|
|
175
|
+
<form-input v-model="email" label="Email" type="email" :is-required="markEmailRequired" />
|
|
176
|
+
<form-input v-model="password" label="Пароль" type="password" :is-required="markPasswordRequired" />
|
|
177
|
+
|
|
178
|
+
| Проп | Тип | По умолчанию | Описание |
|
|
179
|
+
| --- | --- | --- | --- |
|
|
180
|
+
| `modelValue` | `String` | `""` | Значение поля |
|
|
181
|
+
| `label` | `String` | обязателен | Подпись над полем |
|
|
182
|
+
| `type` | `String` | `"text"` | `"text"`, `"email"`, `"password"`, `"tel"`, `"url"` или `"search"` |
|
|
183
|
+
| `isRequired` | `Boolean` | `false` | Красная точка у подписи |
|
|
184
|
+
|
|
185
|
+
Событие `update:modelValue` уходит на каждый ввод со строкой из поля как
|
|
186
|
+
есть: пробелы не обрезаются, это решение формы. Исключение — `type="email"`
|
|
187
|
+
и `type="url"`: браузер сам убирает пробелы по краям значения, и Email из
|
|
188
|
+
одних пробелов приходит пустой строкой. Формы, которые проверяют
|
|
189
|
+
`length > 0`, оставляют у Email `type="text"`. Значение, изменённое
|
|
190
|
+
родителем, поле показывает сразу и без события. Набор через IME
|
|
191
|
+
(composition) отдаёт значение по его окончании.
|
|
192
|
+
|
|
193
|
+
Используйте `v-model` без модификаторов: модификаторы (`.trim`, `.number`,
|
|
194
|
+
`.lazy`) не входят в API `FormInput`; пробелы форма обрезает сама при
|
|
195
|
+
отправке.
|
|
196
|
+
|
|
197
|
+
Другой `type`, не из списка, отклоняется предупреждением Vue, а поле
|
|
198
|
+
рисуется с `type="text"`.
|
|
199
|
+
|
|
200
|
+
`class` и `style` ложатся на корень компонента, все остальные атрибуты
|
|
201
|
+
и слушатели (`autocomplete`, `placeholder`, `name`, `disabled`, `@blur`,
|
|
202
|
+
`@keydown.enter`) — на `<input>`. `id` поля создаётся сам, `for` подписи
|
|
203
|
+
указывает на него, поэтому две копии на странице не делят один `id`.
|
|
204
|
+
Атрибут `id` от формы заменяет созданный и в поле, и в `for`.
|
|
205
|
+
|
|
206
|
+
Красная точка значит «не заполнено»; когда её показывать, решает форма.
|
|
207
|
+
Основной способ: точки появляются после попытки отправить, ввод в поле
|
|
208
|
+
снимает свою, а стирание поля ставит точку снова (так делает watcher
|
|
209
|
+
из примера).
|
|
210
|
+
|
|
211
|
+
watch: {
|
|
212
|
+
name() { this.markNameRequired = this.name.length === 0 },
|
|
213
|
+
},
|
|
214
|
+
methods: {
|
|
215
|
+
create() {
|
|
216
|
+
this.markNameRequired = this.name.length === 0
|
|
217
|
+
// то же для остальных полей
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
Способ «сразу», точка видна, пока поле пусто:
|
|
222
|
+
|
|
223
|
+
<form-input v-model="number" label="Номер" :is-required="number.trim() === ''" />
|
|
224
|
+
|
|
225
|
+
`isRequired` только показывает точку: атрибут `required` и `aria-*` полю
|
|
226
|
+
он не ставит.
|
|
227
|
+
|
|
167
228
|
### SelectDateInterval
|
|
168
229
|
|
|
169
230
|
Два поля даты под одним заголовком — интервал дат «от» и «до». Дата
|
|
@@ -204,13 +265,14 @@ Enter, пробелом, расширением клавиатурной нав
|
|
|
204
265
|
под курсором, поэтому календарь «до» открывается тем же кликом, что
|
|
205
266
|
закрывает «от».
|
|
206
267
|
|
|
207
|
-
С клавиатуры:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
268
|
+
С клавиатуры: в поле даты стрелка вниз, Enter и пробел открывают календарь.
|
|
269
|
+
Кнопки календаря и списков в фокусе с клавиатуры обведены кольцом пакета. Пока
|
|
270
|
+
календарь открыт, стрелки переводят фокус по дням: влево и вправо — на день,
|
|
271
|
+
вверх и вниз — на неделю; с края месяца календарь листается, дата при этом не
|
|
272
|
+
выбирается. Первая стрелка ставит фокус на выбранный день, без выбранного — на
|
|
273
|
+
сегодня. Enter или пробел выбирают день под фокусом и стрелки месяцев — так
|
|
274
|
+
же, как клик; Escape закрывает календарь. После закрытия фокус возвращается на
|
|
275
|
+
поле. В поле можно перейти Tab и набрать дату.
|
|
214
276
|
|
|
215
277
|
### SelectSingle
|
|
216
278
|
|
|
@@ -239,7 +301,8 @@ Enter или пробел выбирают день под фокусом и с
|
|
|
239
301
|
пунктам и страницу не прокручивают. Отсчёт идёт от выбранного пункта: вниз —
|
|
240
302
|
следующий за ним, вверх — предыдущий; без выбранного вниз — первый пункт,
|
|
241
303
|
вверх — последний; с краёв — по кругу. Enter или пробел выбирают пункт под
|
|
242
|
-
фокусом так же, как клик, Escape закрывает список.
|
|
304
|
+
фокусом так же, как клик, Escape закрывает список. Tab и Shift+Tab ходят по
|
|
305
|
+
пунктам по кругу и наружу не выводят, пока список открыт. После закрытия фокус
|
|
243
306
|
возвращается на кнопку фильтра. У закрытого списка стрелки прокручивают
|
|
244
307
|
страницу.
|
|
245
308
|
|
|
@@ -258,6 +321,85 @@ Enter или пробел выбирают день под фокусом и с
|
|
|
258
321
|
внутри списка — нет. Открытие другого списка, календаря или меню
|
|
259
322
|
закрывает предыдущее.
|
|
260
323
|
|
|
324
|
+
### SelectMultiple
|
|
325
|
+
|
|
326
|
+
<select-multiple
|
|
327
|
+
header="Бренд"
|
|
328
|
+
:items="[{ id: 'rc', name: 'Royal Canin' }, { id: 'eukanuba', name: 'Eukanuba' }]"
|
|
329
|
+
v-model="brands"
|
|
330
|
+
@changed="reload"
|
|
331
|
+
/>
|
|
332
|
+
|
|
333
|
+
<select-multiple header="Мероприятие" :items="events" v-model="eventIds" with-search @changed="reload"/>
|
|
334
|
+
|
|
335
|
+
Тот же фильтр, что `SelectSingle`, но выбрать можно несколько пунктов.
|
|
336
|
+
`items` — массив объектов `{ id, name }`. `v-model` держит массив `id`
|
|
337
|
+
выбранных пунктов, пустой массив — ничего не выбрано. `id` — строка или число,
|
|
338
|
+
сравнение строгое.
|
|
339
|
+
|
|
340
|
+
| Проп | Тип | По умолчанию | Описание |
|
|
341
|
+
| --- | --- | --- | --- |
|
|
342
|
+
| `items` | `Array` | обязателен | Массив `{ id, name }` |
|
|
343
|
+
| `modelValue` | `Array` | `[]` | `id` выбранных пунктов |
|
|
344
|
+
| `header` | `String` | обязателен | Подпись поля |
|
|
345
|
+
| `isRequired` | `Boolean` | `false` | Пометка обязательности в заголовке |
|
|
346
|
+
| `isLoading` | `Boolean` | `false` | Индикатор загрузки в заголовке |
|
|
347
|
+
| `withSearch` | `Boolean` | `false` | Поле поиска вверху списка |
|
|
348
|
+
| `lang` | `String` | язык плагина | `"ru"` или `"en"` — надпись, поле поиска, «Ничего не найдено», подписи обоих ластиков |
|
|
349
|
+
|
|
350
|
+
События: `update:modelValue` — новый массив `id`; `changed` — после каждого
|
|
351
|
+
изменения выбора кликом по пункту или ластиком.
|
|
352
|
+
|
|
353
|
+
Клик по невыбранному пункту добавляет его `id` в конец массива, по выбранному —
|
|
354
|
+
убирает. Массив из пропа компонент не меняет: он отдаёт новый, порядок `id` не
|
|
355
|
+
сортирует. `id`, которых нет среди пунктов, при переключении других пунктов
|
|
356
|
+
остаются в массиве, в надписи не считаются и ластик сами не показывают;
|
|
357
|
+
ластик отдаёт `[]` — уходят и они. `null` и `undefined` в `items` — пустой
|
|
358
|
+
список, в `modelValue` — ничего не выбрано.
|
|
359
|
+
|
|
360
|
+
На кнопке: пока выбранных среди `items` нет — пусто; один — его название;
|
|
361
|
+
несколько — «Выбрано: 3». Считаются все пункты, а не только найденные поиском.
|
|
362
|
+
Ластик виден, когда выбран хотя бы один пункт и список закрыт.
|
|
363
|
+
|
|
364
|
+
Выбор пункта — клик, Enter или пробел — закрывает список, фокус возвращается
|
|
365
|
+
на кнопку фильтра, а запрос поиска сбрасывается. `changed` уходит после
|
|
366
|
+
каждого выбора, и страница сразу перезапрашивает выдачу.
|
|
367
|
+
|
|
368
|
+
Список открывается так же, как у `SelectSingle`: в верхнем слое браузера, место,
|
|
369
|
+
высота, прокрутка и способы закрытия те же, фокус после закрытия возвращается
|
|
370
|
+
на кнопку. Отличия: галочка стоит у каждого выбранного пункта; пункт
|
|
371
|
+
сообщает скринридеру, выбран ли он (`aria-pressed`).
|
|
372
|
+
Как и у `SelectSingle`, без пунктов кнопки нет, заголовок остаётся. Пункты
|
|
373
|
+
пропали при открытом списке — список закрывается, запрос поиска сбрасывается,
|
|
374
|
+
а вернувшиеся пункты появляются с закрытым списком.
|
|
375
|
+
|
|
376
|
+
С клавиатуры, пока список открыт, всё как у `SelectSingle`: стрелки вверх и вниз
|
|
377
|
+
ведут фокус по пунктам по кругу, Enter или пробел переключают пункт и закрывают
|
|
378
|
+
список, Escape закрывает его без выбора. Если выбрано несколько пунктов,
|
|
379
|
+
отсчёт идёт от первого выбранного среди видимых. Мышь ведёт тот же фокус, что
|
|
380
|
+
и стрелки: движение по пункту ставит фокус на него, подсвечен всегда один пункт.
|
|
381
|
+
|
|
382
|
+
С `with-search` над пунктами стоит поле поиска: при прокрутке длинного списка
|
|
383
|
+
оно остаётся на месте. Имя пункта должно содержать запрос, регистр не важен,
|
|
384
|
+
пробелы по краям запроса отбрасываются. Если совпадений нет, вместо пунктов
|
|
385
|
+
строка «Ничего не найдено». Выбранные пункты, скрытые поиском, остаются
|
|
386
|
+
выбранными. Запрос не входит в `v-model`; он сбрасывается при каждом закрытии
|
|
387
|
+
списка, в том числе при выборе пункта. Ластик в поле очищает запрос. При
|
|
388
|
+
открытии списка фокус встаёт в поле; при загрузке страницы фокус в поле
|
|
389
|
+
не уходит. Без Popover API список виден всегда, и фокус в поле сам
|
|
390
|
+
не ставится. Пробел в поле печатает пробел, Enter ничего не делает и форму
|
|
391
|
+
не отправляет. Поле — первая остановка стрелок: из него вниз — первый пункт,
|
|
392
|
+
вверх — последний, с краёв пунктов — обратно в поле. Без совпадений стрелки
|
|
393
|
+
в поле ничего не делают. Пока список открыт, Tab и Shift+Tab ходят по кругу
|
|
394
|
+
внутри него: поле, ластик поля, пока запрос не пуст, пункты; наружу, на
|
|
395
|
+
страницу, фокус уходит после закрытия списка. Ластик поля входит в Tab, но не
|
|
396
|
+
в стрелки. Ластик на кнопке фильтра идёт в Tab сразу после неё.
|
|
397
|
+
Печатный символ и Backspace на пункте уходят в поле, поэтому набор не
|
|
398
|
+
пропадает, когда мышь увела фокус на пункт. Пока список открыт, панель не
|
|
399
|
+
становится уже, чем при открытии, а поле не двигается, сколько бы пунктов ни
|
|
400
|
+
убрал поиск: открытая вниз панель становится ниже вместе с найденным, открытая
|
|
401
|
+
вверх не становится ниже, и пустое место остаётся под пунктами, у кнопки.
|
|
402
|
+
|
|
261
403
|
### InfoPill
|
|
262
404
|
|
|
263
405
|
Пилюля, которая только показывает текст: статус, счётчик, флаг, пометку.
|
|
@@ -866,7 +1008,10 @@ rounded-lg`, `box-sizing: border-box`. Отступов у неё нет: фор
|
|
|
866
1008
|
| `HamburgerMenu` — подпись кнопки для скринридера | Открыть меню | Open menu |
|
|
867
1009
|
| `SelectDateInterval` — месяцы, дни недели, кнопки календарей | по-русски | in English |
|
|
868
1010
|
| `SelectDateInterval` — подсказки полей | от / до | from / to |
|
|
869
|
-
| Ластик `Search`, `RussianMobileFilter`, `SelectSingle` (язык плагина) и `SelectDateInterval` — подпись для скринридера | Очистить | Clear |
|
|
1011
|
+
| Ластик `Search`, `RussianMobileFilter`, `SelectSingle` (язык плагина), `SelectMultiple` (проп `lang`) и `SelectDateInterval` — подпись для скринридера | Очистить | Clear |
|
|
1012
|
+
| `SelectMultiple` — надпись при нескольких выбранных | Выбрано: N | Selected: N |
|
|
1013
|
+
| `SelectMultiple` — подсказка и имя поля поиска | Поиск | Search |
|
|
1014
|
+
| `SelectMultiple` — нет совпадений | Ничего не найдено | Nothing found |
|
|
870
1015
|
| `DataTable` — пагинация | Предыдущая / Следующая / Показаны результаты X - Y из Z | Previous / Next / Showing X - Y of Z results |
|
|
871
1016
|
| `PageTabs` — имя навигации для скринридера | Закладки | Tabs |
|
|
872
1017
|
| `PageCard` — доступное имя крестика | Назад | Back |
|
|
@@ -881,7 +1026,20 @@ rounded-lg`, `box-sizing: border-box`. Отступов у неё нет: фор
|
|
|
881
1026
|
|
|
882
1027
|
Кнопки и ссылки компонентов нажимаются расширениями клавиатурной навигации —
|
|
883
1028
|
Vimium, Vimari, Vimlike — по их подсказкам, как мышью; подсказка у каждой
|
|
884
|
-
кнопки одна и стоит на всей кнопке.
|
|
1029
|
+
кнопки одна и стоит на всей кнопке. Ластики фильтров входят в порядок Tab
|
|
1030
|
+
и нажимаются Enter и пробелом; кольцо фокуса — вокруг крестика.
|
|
1031
|
+
После ластика, нажатого с клавиатуры, фокус переходит на поле или кнопку
|
|
1032
|
+
этого фильтра: ластик пропадает, и без этого фокус упал бы на страницу.
|
|
1033
|
+
При касании и клике мышью фокус остаётся как раньше.
|
|
1034
|
+
|
|
1035
|
+
Фокус у всех компонентов пакета выглядит одинаково, как у `FormInput`: линия
|
|
1036
|
+
1px `blue-300` по краю элемента и ореол 3px `blue-200` с прозрачностью 50%.
|
|
1037
|
+
Вид задан одной утилитой `focus-ring` в `src/styles/index.css`, компоненты
|
|
1038
|
+
выбирают только момент. Поля, крестик `NotificationMessage`, кнопки
|
|
1039
|
+
`ConfirmationModal` и `DropdownButtonWithAction` показывают фокус при любом
|
|
1040
|
+
его появлении, в том числе после клика мышью; остальные кнопки и ссылки —
|
|
1041
|
+
только с клавиатуры. Стрелки месяцев и подпись месяца в календаре `PickDay`
|
|
1042
|
+
берут тот же вид, когда фокус в них с клавиатуры.
|
|
885
1043
|
|
|
886
1044
|
Vimlike (Safari на iPhone, iPad и Mac) считает кнопкой любой элемент
|
|
887
1045
|
с `cursor: pointer` и из вложенных таких элементов оставляет самый глубокий.
|
|
@@ -895,7 +1053,7 @@ Vimlike (Safari на iPhone, iPad и Mac) считает кнопкой любо
|
|
|
895
1053
|
считал бы кнопкой вместо неё. В поле по-прежнему можно перейти Tab и набрать
|
|
896
1054
|
дату.
|
|
897
1055
|
|
|
898
|
-
Подсказки Vimlike в открытых меню,
|
|
1056
|
+
Подсказки Vimlike в открытых меню, списках `SelectSingle` и `SelectMultiple` и календарях
|
|
899
1057
|
`SelectDateInterval` не видны: Vimlike рисует их под верхним слоем браузера,
|
|
900
1058
|
в котором открываются эти панели. Vimium выводит свои подсказки в верхний
|
|
901
1059
|
слой, и у него такой проблемы нет. В Vimlike панель открывают подсказкой её
|
|
@@ -1006,7 +1164,7 @@ Inertia SSR), а серверная разметка гидратируется
|
|
|
1006
1164
|
|
|
1007
1165
|
npm run playground
|
|
1008
1166
|
|
|
1009
|
-
Поднимает демо-страницы со всеми
|
|
1167
|
+
Поднимает демо-страницы со всеми девятнадцатью компонентами в двух окружениях:
|
|
1010
1168
|
голом (без Tailwind и стилей приложения) и в окружении приложения (с
|
|
1011
1169
|
Tailwind, `@tailwindcss/forms` и типографикой, как у потребителя). Это
|
|
1012
1170
|
основной способ посмотреть, как компоненты выглядят и ведут себя, а не
|