r3ui-lib 1.3.16 → 1.3.18
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/CHANGELOG.md +11 -0
- package/README.md +67 -0
- package/dist/r3ui-lib.css +1 -1
- package/dist/r3ui-lib.es.js +5371 -5257
- package/dist/r3ui-lib.umd.js +30 -30
- package/dist/types/components/form/CustomInput.vue.d.ts +4 -0
- package/dist/types/components/form/CustomInput.vue.d.ts.map +1 -1
- package/dist/types/composables/forms/useUnsavedChanges.d.ts +20 -0
- package/dist/types/composables/forms/useUnsavedChanges.d.ts.map +1 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/main.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,17 @@
|
|
|
11
11
|
Формат основан на [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
12
12
|
и этот проект придерживается [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
13
13
|
|
|
14
|
+
## [1.3.18] - 2026-10-27
|
|
15
|
+
|
|
16
|
+
- `CustomInput` передаёт атрибуты и нативные обработчики внутреннему полю, сохраняя `class` и `style` оболочки. Встроенная подпись связана со стабильным ID.
|
|
17
|
+
- Очистка доступна с клавиатуры, возвращает фокус полю и сохраняет прежние события. Добавлены `readonly`, `clearLabel` и общее реактивное название очистки через `provide`.
|
|
18
|
+
|
|
19
|
+
## [1.3.17] - 2026-10-26
|
|
20
|
+
|
|
21
|
+
- Добавлен `useUnsavedChanges`: независимый исходный снимок в памяти, обнаружение изменений и подтверждение последнего запроса ухода без сброса черновика.
|
|
22
|
+
- Незавершённая запись блокирует уход; её начало, отключение защиты и освобождение владельца отменяют ожидающее подтверждение.
|
|
23
|
+
- `beforeunload` подключается только при необходимости и снимается при деактивации или уничтожении владельца. API не выполняет запросов и не сохраняет данные в хранилища.
|
|
24
|
+
|
|
14
25
|
## [1.3.13-runtime.0] - 2026-10-22
|
|
15
26
|
|
|
16
27
|
- Общая граница совместимости проверяет DataLoader, RowManager и модули запроса до установки перехватов; повторная установка не дублирует владельца.
|
package/README.md
CHANGED
|
@@ -328,3 +328,70 @@ MIT License
|
|
|
328
328
|
- ✅ Добавлена полная поддержка TypeScript
|
|
329
329
|
- ✅ Все 57+ компонентов теперь доступны
|
|
330
330
|
- ✅ Улучшена производительность сборки
|
|
331
|
+
|
|
332
|
+
## Защита несохранённых форм (1.3.17)
|
|
333
|
+
|
|
334
|
+
`useUnsavedChanges` хранит независимый исходный снимок только в памяти Vue scope.
|
|
335
|
+
Он не изменяет поля, не выполняет навигацию, HTTP-запросы или запись в хранилища.
|
|
336
|
+
Вызывать внутри `setup` или `effectScope`; значения формы и флаги должны быть реактивными.
|
|
337
|
+
|
|
338
|
+
```vue
|
|
339
|
+
<script setup lang="ts">
|
|
340
|
+
import { reactive, ref } from 'vue';
|
|
341
|
+
import { ConfirmationModal, useUnsavedChanges } from 'r3ui-lib';
|
|
342
|
+
|
|
343
|
+
const form = reactive({ name: '' });
|
|
344
|
+
const editorOpen = ref(true);
|
|
345
|
+
const isPersisting = ref(false);
|
|
346
|
+
const enabled = ref(true);
|
|
347
|
+
const guard = useUnsavedChanges({ getSnapshot: () => ({ name: form.name }), isPersisting, enabled });
|
|
348
|
+
const { confirmationOpen } = guard;
|
|
349
|
+
// В настоящем редакторе снимок фиксируется после начального заполнения, до ввода.
|
|
350
|
+
guard.captureBaseline();
|
|
351
|
+
|
|
352
|
+
async function requestClose() {
|
|
353
|
+
if (await guard.requestLeave()) {
|
|
354
|
+
editorOpen.value = false;
|
|
355
|
+
// Закрытие уже принято владельцем; отменённая навигация снимок не очищает.
|
|
356
|
+
guard.clearBaseline();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
</script>
|
|
360
|
+
|
|
361
|
+
<template>
|
|
362
|
+
<div v-if="editorOpen">
|
|
363
|
+
<input v-model="form.name" aria-label="Название" :disabled="isPersisting">
|
|
364
|
+
<button type="button" @click="requestClose">Закрыть редактор</button>
|
|
365
|
+
</div>
|
|
366
|
+
<ConfirmationModal
|
|
367
|
+
:modal-visibility="confirmationOpen"
|
|
368
|
+
title="Несохранённые изменения"
|
|
369
|
+
message="Выйти без сохранения введённых данных?"
|
|
370
|
+
confirm-text="Выйти без сохранения"
|
|
371
|
+
cancel-text="Остаться"
|
|
372
|
+
@confirm="guard.confirmLeave"
|
|
373
|
+
@cancel="guard.cancelLeave"
|
|
374
|
+
@update:modal-visibility="open => { if (!open) guard.cancelLeave(); }"
|
|
375
|
+
/>
|
|
376
|
+
</template>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
- `getSnapshot` возвращает бизнес-значения без локализованных подписей и индикаторов загрузки. Порядок наборов идентификаторов нормализует приложение.
|
|
380
|
+
- `captureBaseline(snapshot?)` клонирует переданный снимок либо текущее значение `getSnapshot`. Позволяет отдельно нормализовать исходную запись без подмены её ранним пользовательским вводом. Ошибка клонирования сохраняет прежний снимок.
|
|
381
|
+
- `isDirty` становится `true` при отличии от снимка; возвращение исходных значений снимает защиту. До первого снимка изменений нет, но незавершённая запись уже блокирует уход.
|
|
382
|
+
- `requestLeave()` возвращает разрешение, не сбрасывая поля или снимок. Повторный вызов завершает прежний запрос отказом; подтверждается только последний. При отменённом другим guard переходе форма остаётся защищённой.
|
|
383
|
+
- `isPersisting` означает только неподтверждённую запись: навигация и последующий GET в него не входят. Её начало отменяет открытое подтверждение; окончание не запускает отложенный уход.
|
|
384
|
+
- `enabled=false` отменяет ожидающее подтверждение и разрешает новые попытки ухода, в том числе обязательный выход при потере сессии. При подтверждении флаги проверяются заново.
|
|
385
|
+
- `clearBaseline()` отменяет ожидание и удаляет снимок, не меняя поля. Вызывать при фактическом закрытии или смене редактора. После уничтожения scope старый guard отклоняет уход и запрещает новый снимок.
|
|
386
|
+
- `beforeunload` предупреждает только при включённой активной защите и изменениях либо записи. Браузер определяет показ и текст предупреждения; после перезагрузки черновик не восстанавливается.
|
|
387
|
+
- KeepAlive отменяет ожидающее подтверждение и снимает предупреждение при деактивации; при возврате исходный снимок и обнаружение изменений сохраняются. Уничтожение освобождает снимок и обработчики. На сервере обращения к `window` отсутствуют.
|
|
388
|
+
|
|
389
|
+
Для маршрутного guard возвращать `guard.requestLeave()` напрямую. Смену идентификатора в том же маршруте также проверяет приложение; новая форма получает собственный scope. Обычные формы и модальные окна без подключения composable сохраняют прежнее поведение.
|
|
390
|
+
|
|
391
|
+
## Семантика CustomInput (1.3.18)
|
|
392
|
+
|
|
393
|
+
- `id`, `aria-*`, `required`, `autocomplete`, `inputmode`, `data-*` и нативные обработчики передаются настоящему `input`. `class` и `style` остаются на внешней оболочке; `inputClass` задаёт класс поля.
|
|
394
|
+
- Встроенный `title` отображается как `label`, связанный с явно переданным или автоматически созданным стабильным ID. Внешние подписи связывает приложение.
|
|
395
|
+
- Очистка доступна через Tab, Enter и Space, не отправляет форму и возвращает фокус полю. Сохраняется контракт `update:modelValue(null)` и `change(null)`. Для пустого, `disabled` или `readonly` поля кнопка скрыта; число `0` можно очистить.
|
|
396
|
+
- Название очистки задаётся пропом `clearLabel`. Общее реактивное значение для вложенных полей можно передать из приложения: `provide('r3ui-custom-input-clear-label', computed(() => t('custom_input.clear')))`. Переводы находятся в `r3ui-lang-front`; без настройки используется «Очистить поле».
|
|
397
|
+
- Существующая семантика `max`, `maxNumber`, `modelValue`, `blur` и `change` сохранена. Внешний нативный `input` получает событие после синхронного обновления модели.
|