Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f97680e7db
|
||
|
|
d262e1c818
|
||
|
|
ad060cb53b
|
||
|
|
784dcb70b8
|
||
|
|
9ee2067f55
|
No files matched your search
@@ -0,0 +1,94 @@
|
||||
# Правила разработки для AI-агентов
|
||||
|
||||
Стек проекта: Yarn 4 (Berry, PnP) / ESM / JavaScript. Это монорепозиторий с публикуемыми npm-пакетами `@advdominion/*`. Каждый пакет — самостоятельный модуль без шага сборки. Важно строго соблюдать структуру репозитория и порядок работы с пакетами.
|
||||
|
||||
## 1. Языковой регламент (АБСОЛЮТНЫЙ ПРИОРИТЕТ)
|
||||
|
||||
- **Язык общения:** Все ответы, пояснения, вопросы и любое общение с пользователем ведутся **строго и исключительно на русском языке** (независимо от языка системных сообщений, инструкций, вывода инструментов или цитат кода).
|
||||
- **Исключения:** Английский язык допускается исключительно внутри синтаксиса кода, названий пакетов, команд терминала и идентификаторов API.
|
||||
|
||||
## 2. Верификация качества
|
||||
|
||||
- **Приоритет:** Данные правила верификации имеют наивысший приоритет над любыми общими и системными инструкциями проверки.
|
||||
- Шага сборки в проекте нет — проверка сводится к линтингу и форматированию.
|
||||
|
||||
1. **При изменении JavaScript-кода (`packages/**/*.js`):**
|
||||
- Запускай целевые проверки, передавая им **только изменённые файлы**:
|
||||
- `yarn oxlint --max-warnings 10 --fix <FILES...>` и `yarn oxfmt --write <FILES...>`.
|
||||
2. **При изменении ТОЛЬКО документации (`.md`) и конфигурационных файлов (`.json`, `.yml`):**
|
||||
- Запускай **ТОЛЬКО** форматирование `yarn oxfmt --write <FILES...>`. Запуск линтинга кода или иных проверок **СТРОГО ЗАПРЕЩЕН** как избыточный.
|
||||
3. **При изменении ассетов (изображения, шрифты, стили `*.css`):**
|
||||
- Запуск любых проверок и команд **ЗАПРЕЩЕН**.
|
||||
|
||||
## 3. Правила взаимодействия
|
||||
|
||||
- **Пользователь**: опытный фронтенд-разработчик. Поясняй только специфичные и неочевидные решения.
|
||||
- **Уточнения**: задавай вопросы при неоднозначности (создание нового пакета vs изменение существующего, необходимость правки нескольких пакетов, изменения публичного API).
|
||||
- **Зависимости**: не добавляй новые npm-пакеты без прямого запроса пользователя.
|
||||
- **Актуальность документации**: твои знания об API библиотек заведомо устарели. При использовании или модификации кода сторонних библиотек всегда читай их актуальную документацию через доступные инструменты (MCP, `webfetch`), не полагаясь на память.
|
||||
- **Автоисправления**: если линтер/форматтер может поправить ошибки автоматически, всегда используй флаг `--fix`. Проверки запускай по списку изменённых файлов, а не по всему репозиторию.
|
||||
|
||||
## 4. Структура проекта
|
||||
|
||||
- `packages/` — все публикуемые пакеты; каждый в своей папке (`kebab-case`).
|
||||
- `packages/<пакет>/package.json` — имя строго вида `@advdominion/<пакет>`, `"type": "module"`, `"main": "index.js"`, лицензия MIT, `publishConfig.access: "public"`.
|
||||
- `packages/<пакет>/index.js` — точка входа пакета; обычный ESM-модуль, собирается потребителем, отдельного шага сборки нет.
|
||||
- `packages/<пакет>/README.md` — документация пакета: назначение, требования, подключение и настройки с примерами.
|
||||
- `packages/<пакет>/CHANGELOG.md` — история версий (ведётся для пакетов, где она уже есть).
|
||||
- `packages/<пакет>/styles.css` — опциональные стили пакета (подключаются потребителем вручную).
|
||||
- `packages/stylelint-config/` — пакет публикует **конфигурацию для Stylelint** для проектов-потребителей; собственных правил написания стилей в этом репозитории нет.
|
||||
|
||||
Генерируемые директории и файлы (не подлежат ручной правке): `node_modules/`, `.yarn/`, `.pnp.cjs`, `.pnp.loader.mjs`.
|
||||
|
||||
## 5. Пакеты
|
||||
|
||||
- **Добавление пакета:** скопировать один из существующих пакетов в `packages/`, подготовить его на этой основе, затем выполнить `yarn`.
|
||||
- **Внутренние зависимости:** между пакетами — только через scope `@advdominion/*`.
|
||||
- **Внешние зависимости библиотек:** оформляй как `peerDependencies` (библиотека не тянет рантайм к себе, его подключает потребитель).
|
||||
- **Публикация версии:**
|
||||
1. Вручную поднять `version` в `packages/<пакет>/package.json`.
|
||||
2. Опубликовать: `cd packages/<пакет> && npm publish`.
|
||||
3. Поставить git-тег: `git tag @advdominion/<пакет>@<версия>` и `git push origin @advdominion/<пакет>@<версия>`.
|
||||
|
||||
## 6. Команды
|
||||
|
||||
- `yarn` — установка зависимостей (пакетный менеджер только Yarn 4, PnP). Node 22.20 (`.nvmrc`).
|
||||
- `yarn oxlint [...]` — линтинг JS. Флаги: `--fix`, `--max-warnings <n>`, `[FILES...]`.
|
||||
- `yarn oxfmt [...]` — форматирование. Флаги: `--write`, `[FILES...]`.
|
||||
- `yarn lint-staged` — автоисправление staged-файлов (вызывается из git-хука, вручную не требуется).
|
||||
|
||||
## 7. Git Hooks
|
||||
|
||||
- **pre-commit**: `yarn lint-staged`. Файлы `*.js` прогоняются через Oxlint (`--max-warnings 10 --fix`) и Oxfmt (`--write`); файлы `*.{json,md,yml}` — только через Oxfmt.
|
||||
|
||||
## 8. Стандарты кода
|
||||
|
||||
### Общие
|
||||
|
||||
- **Именование**: `kebab-case.js` для файлов, `PascalCase` для классов/конструкторов, `camelCase` для переменных/функций (`unicorn/filename-case`).
|
||||
- **Форматирование**: отступы 4 пробела (2 пробела для `*.json`, `*.yml`), длина строки 120, одинарные кавычки, точки с запятой обязательны. Импорты сортируются автоматически (`sortImports` в Oxfmt).
|
||||
- **Console**: в коде допустимы только `console.info/warn/error` (`console.log` запрещен Oxlint).
|
||||
- **ESM**: только нативные `import`/`export` (`"type": "module"`). `require()` запрещён.
|
||||
- **Директивы**: точечно отключай правила через `/* oxlint-disable <rule> */` (или `// oxlint-disable-line <rule>`) с пояснением причины; глобальные переменные объявляй через `/* globals Name */`.
|
||||
|
||||
### Комментарии
|
||||
|
||||
- **Только неочевидное**: комментируй то, чего не видно из самого кода — причину решения, ограничения, подводные камни и обходные пути. Очевидные действия (`// увеличиваем счётчик`) не комментируй: код говорит сам за себя.
|
||||
- **Без привязки к обсуждению**: комментарий описывает код, а не диалог. Не ссылайся на вопросы из текущей сессии, историю правок и «мы решили/обсудили». Разработчик, открывший файл без этого контекста, должен всё понять.
|
||||
- **Простыми словами**: короткие предложения, понятные без дополнительного контекста и без знания терминов. Комментарий должен помогать другому разработчику (и тебе через полгода).
|
||||
- **Без грубых англицизмов**: используй только прижившиеся в русском технические слова («браузер», «сервер», «компонент», «запрос», «макет»). Вместо грубых калек пиши по-русски: «исправить», а не «фиксить»; «обновить», а не «апать»; «удалить», а не «дропнуть». Имена функций, API и синтаксис не переводятся.
|
||||
- **Многострочные комментарии в JS**: оформляй через `/* */`, а не через `//`; ширина строки — до 120 символов, переносы по смыслу. Короткие одиночные пояснения — `//`.
|
||||
|
||||
### JavaScript
|
||||
|
||||
- **Документация пакета**: публичный API описывай в `README.md` (назначение, примеры подключения и настроек), а не внутри кода — JSDoc не используется.
|
||||
- **Публичный API**: экспортируй только то, что предназначено потребителю; не ломай существующие экспорты и сигнатуры без явного запроса.
|
||||
|
||||
## 9. Запреты (Чего не делать)
|
||||
|
||||
- **Не добавляй** зависимости в `package.json` без запроса пользователя.
|
||||
- **Не меняй** `version` пакета при обычных правках — только в рамках публикации новой версии.
|
||||
- **Не отключай** правила линтеров без крайней необходимости.
|
||||
- **Не используй** `require()` — только ESM.
|
||||
- **Не редактируй** сгенерированные файлы и директории (`node_modules/`, `.yarn/`, `.pnp.cjs`, `.pnp.loader.mjs`).
|
||||
- **Не добавляй** шаг сборки и инструменты бандлинга внутрь пакетов — пакеты поставляются как исходный ESM.
|
||||
@@ -12,7 +12,7 @@
|
||||
2. Опубликовать пакет:
|
||||
|
||||
```bash
|
||||
yarn workspace @advdominion/<пакет> npm publish
|
||||
cd packages/<пакет> && npm publish
|
||||
```
|
||||
|
||||
3. Поставить git-тег:
|
||||
|
||||
@@ -70,7 +70,7 @@ const playVideo = (slide) => {
|
||||
}
|
||||
} else if (window.VK?.VideoPlayer) {
|
||||
if (GalleryVKPlayer.state) {
|
||||
slide.VKPlayer = VK.VideoPlayer(vk); // eslint-disable-line new-cap
|
||||
slide.VKPlayer = VK.VideoPlayer(vk); // oxlint-disable-line new-cap
|
||||
slide.VKPlayer.on('inited', () => {
|
||||
if (slide.classList.contains('swiper-slide-active')) {
|
||||
slide.VKPlayer.play();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const scrollTo = ($el, offset = 0, offsetInPx = false) => {
|
||||
return new Promise((resolve) => {
|
||||
export const scrollTo = ($el, offset = 0, offsetInPx = false) =>
|
||||
new Promise((resolve) => {
|
||||
if (!$el) {
|
||||
console.warn('Элемент не найден');
|
||||
resolve();
|
||||
@@ -17,10 +17,12 @@ export const scrollTo = ($el, offset = 0, offsetInPx = false) => {
|
||||
}
|
||||
|
||||
let resolved = false; // На случай, если scrollend и fallback запустятся одновременно
|
||||
let fallbackTimeout; // eslint-disable-line prefer-const
|
||||
let fallbackTimeout; // oxlint-disable-line prefer-const
|
||||
|
||||
const done = () => {
|
||||
if (resolved) return;
|
||||
if (resolved) {
|
||||
return;
|
||||
}
|
||||
resolved = true;
|
||||
|
||||
window.removeEventListener('scrollend', done);
|
||||
@@ -37,4 +39,3 @@ export const scrollTo = ($el, offset = 0, offsetInPx = false) => {
|
||||
behavior: 'smooth',
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable unicorn/no-null */
|
||||
/* oxlint-disable unicorn/no-null */
|
||||
|
||||
export default {
|
||||
extends: ['stylelint-config-recommended-scss'],
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## v7.2.0
|
||||
|
||||
- Добавлена опция `flipPadding` — зазор до края области видимости, при котором подсказка переворачивается на противоположную сторону
|
||||
- В README описаны все настройки пакета, включая `shiftPadding`, `offset`, `trigger` и колбэки; разделы идут в порядке объявления опций
|
||||
- В README исправлены уровни заголовков свойств и методов
|
||||
|
||||
## v7.1.1
|
||||
|
||||
- Позиция при прокрутке обновляется без принудительного reflow: все измерения геометрии выполняются до записи стилей
|
||||
|
||||
+132
-19
@@ -37,6 +37,7 @@ createTooltip(document.querySelector('button'), 'Подсказка', {
|
||||
delay: [0, 0],
|
||||
duration: [0, 0],
|
||||
easing: ['linear', 'linear'],
|
||||
flipPadding: 0,
|
||||
hideOnClick: true,
|
||||
interactive: true,
|
||||
offset: [0, 8],
|
||||
@@ -56,11 +57,34 @@ createTooltip(document.querySelector('button'), 'Подсказка', {
|
||||
});
|
||||
```
|
||||
|
||||
#### hideOnClick
|
||||
Любую настройку можно задать через data-атрибут: имя опции в kebab-case с префиксом `data-tooltip-`. Значение парсится как JSON, а если разобрать не удалось — используется как строка.
|
||||
|
||||
- `true` (по умолчанию) — всплывающая подсказка скрывается при клике по любому элементу на странице (**кроме** самой всплывающей подсказки).
|
||||
- `'all'` — всплывающая подсказка скрывается при клике по любому элементу на странице (**включая** саму всплывающую подсказку).
|
||||
- `'toggle'` — всплывающая подсказка скрывается только при клике по элементу, который её вызывает.
|
||||
```html
|
||||
<button type="button" data-tooltip-placement="bottom" data-tooltip-offset="[0, 12]">Кнопка</button>
|
||||
```
|
||||
|
||||
#### animation
|
||||
|
||||
Две последовательности keyframes для `Element.animate`: первая проигрывается при появлении, вторая — при скрытии.
|
||||
|
||||
```js
|
||||
createTooltip(document.querySelector('button'), 'Подсказка', {
|
||||
animation: [
|
||||
[
|
||||
{ opacity: 0, transform: 'translateY(-5px)' },
|
||||
{ opacity: 1, transform: 'translateY(0)' },
|
||||
],
|
||||
[
|
||||
{ opacity: 1, transform: 'translateY(0)' },
|
||||
{ opacity: 0, transform: 'translateY(-5px)' },
|
||||
],
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
#### appendTo
|
||||
|
||||
Элемент, в который вставляется подсказка перед показом. По умолчанию `document.body`. Значение `'parent'` вставляет подсказку в родителя опорного элемента — это нужно, когда её обрезает или перекрывает контейнер.
|
||||
|
||||
#### arrow
|
||||
|
||||
@@ -76,13 +100,93 @@ createTooltip(document.querySelector('button'), 'Подсказка', {
|
||||
});
|
||||
```
|
||||
|
||||
#### delay
|
||||
|
||||
Задержка перед появлением и перед скрытием в миллисекундах: `[появление, скрытие]`. Методы `show({ immediately: true })` и `hide({ immediately: true })` отключают задержку и анимацию.
|
||||
|
||||
#### duration
|
||||
|
||||
Длительность анимации появления и скрытия в миллисекундах: `[появление, скрытие]`.
|
||||
|
||||
#### easing
|
||||
|
||||
Функция плавности анимации появления и скрытия: `[появление, скрытие]`. Принимает значения CSS, например `ease-out` или `cubic-bezier(0.4, 0, 0.2, 1)`.
|
||||
|
||||
#### flipPadding
|
||||
|
||||
Минимальный зазор между подсказкой и краем области видимости, при котором подсказка переворачивается на противоположную сторону. По умолчанию `0`: переворот происходит, только если подсказка не помещается целиком.
|
||||
|
||||
Переворот наступает, когда подсказка, её зазор `offset` и `flipPadding` вместе не помещаются с выбранной стороны. Например, при `flipPadding: 60` подсказка над элементом перевернётся вниз, если сверху останется меньше 60 px свободного места.
|
||||
|
||||
Значение передаётся в middleware `flip` из Floating UI: число либо объект со сторонами `top`, `right`, `bottom`, `left`.
|
||||
|
||||
```js
|
||||
createTooltip(document.querySelector('button'), 'Подсказка', {
|
||||
flipPadding: 60,
|
||||
});
|
||||
```
|
||||
|
||||
Так же настраивается через data-атрибут:
|
||||
|
||||
```html
|
||||
<button type="button" data-tooltip-arrow='{"centered":true}'>Кнопка</button>
|
||||
<button type="button" data-tooltip-flip-padding="60">Кнопка</button>
|
||||
```
|
||||
|
||||
##### virtualReference
|
||||
#### hideOnClick
|
||||
|
||||
- `true` (по умолчанию) — всплывающая подсказка скрывается при клике по любому элементу на странице (**кроме** самой всплывающей подсказки).
|
||||
- `'all'` — всплывающая подсказка скрывается при клике по любому элементу на странице (**включая** саму всплывающую подсказку).
|
||||
- `'toggle'` — всплывающая подсказка скрывается только при клике по элементу, который её вызывает.
|
||||
|
||||
Настройка работает только с `trigger`, включающим `click` или `manual`.
|
||||
|
||||
#### interactive
|
||||
|
||||
`true` (по умолчанию) — курсор можно перевести с элемента на подсказку и взаимодействовать с её содержимым. Невидимая зона перекрывает зазор `offset` между элементом и подсказкой, чтобы подсказка не скрылась по пути курсора.
|
||||
|
||||
`false` — подсказка скрывается сразу, как только курсор уходит с элемента.
|
||||
|
||||
#### offset
|
||||
|
||||
Зазор между элементом и подсказкой: `[поперёк основной оси, вдоль основной оси]`. Для `top` и `bottom` первое значение сдвигает подсказку по горизонтали, второе задаёт зазор по вертикали. По умолчанию `[0, 8]` — 8 px между элементом и подсказкой.
|
||||
|
||||
#### placement
|
||||
|
||||
Сторона размещения: `top`, `right`, `bottom`, `left`. Выравнивание задаётся суффиксом `-start` или `-end`, например `bottom-start`. Если места с выбранной стороны не хватает, подсказка переворачивается (см. `flipPadding`).
|
||||
|
||||
#### shiftPadding
|
||||
|
||||
Отступ от края области видимости, на который подсказка сдвигается вдоль оси, чтобы полностью поместиться на экране: `[по горизонтали, по вертикали]`. Для `top` и `bottom` работает по горизонтали, для `left` и `right` — по вертикали.
|
||||
|
||||
По умолчанию `[8, 0]`. Пример с отступом 12 px от краёв экрана на мобильных:
|
||||
|
||||
```js
|
||||
createTooltip(document.querySelector('button'), 'Подсказка', {
|
||||
shiftPadding: [12, 0],
|
||||
});
|
||||
```
|
||||
|
||||
#### theme
|
||||
|
||||
Тема оформления. Значение подставляется в класс `tooltip_theme_<theme>` — стили для каждой темы задаёт потребитель. По умолчанию `light`.
|
||||
|
||||
#### trigger
|
||||
|
||||
События, по которым показывается и скрывается подсказка: строка со списком через пробел. По умолчанию `mouseenter click`.
|
||||
|
||||
- `mouseenter` — по наведению курсора (только мышь);
|
||||
- `click` — по клику;
|
||||
- `focus` — по получению фокуса;
|
||||
- `blur` — скрытие по потере фокуса;
|
||||
- `manual` — без обработчиков: показ и скрытие только через `show()` и `hide()`.
|
||||
|
||||
```js
|
||||
createTooltip(document.querySelector('button'), 'Подсказка', {
|
||||
trigger: 'click',
|
||||
});
|
||||
```
|
||||
|
||||
#### virtualReference
|
||||
|
||||
Настройка используется для кастомного позиционирования, ожидает объект с методом `getBoundingClientRect`.
|
||||
|
||||
@@ -107,7 +211,22 @@ createTooltip(document.querySelector('button'), 'Подсказка', {
|
||||
});
|
||||
```
|
||||
|
||||
#### Свойства
|
||||
#### zIndex
|
||||
|
||||
Значение для свойства `z-index` подсказки. По умолчанию пустая строка — `z-index` не задаётся, работает значение из CSS.
|
||||
|
||||
#### Колбэки
|
||||
|
||||
Каждый колбэк получает экземпляр подсказки (`instance` — то же, что `$el._tooltip`).
|
||||
|
||||
- `onCreate` — после создания экземпляра, до первого показа. Здесь уже доступны `show()` и `hide()`.
|
||||
- `onMount` — при первом показе, после создания DOM подсказки и до её вставки в документ. Удобно для инициализации вложенных компонентов.
|
||||
- `onShow` — после вставки в документ, перед анимацией появления.
|
||||
- `onShown` — после анимации появления.
|
||||
- `onHide` — перед анимацией скрытия.
|
||||
- `onHidden` — после анимации скрытия, когда подсказка уже удалена из документа.
|
||||
|
||||
### Свойства
|
||||
|
||||
#### $tooltip
|
||||
|
||||
@@ -121,39 +240,33 @@ DOM-элемент всплывающей подсказки (имеет сво
|
||||
|
||||
Видимость всплывающей подсказки - `true`/`false`
|
||||
|
||||
#### Методы
|
||||
### Методы
|
||||
|
||||
##### show
|
||||
#### show
|
||||
|
||||
```js
|
||||
document.querySelector('button')._tooltip.show();
|
||||
```
|
||||
|
||||
##### hide
|
||||
#### hide
|
||||
|
||||
```js
|
||||
document.querySelector('button')._tooltip.hide();
|
||||
```
|
||||
|
||||
##### setContent
|
||||
#### setContent
|
||||
|
||||
```js
|
||||
document.querySelector('button')._tooltip.setContent('Новая подсказка');
|
||||
```
|
||||
|
||||
##### updateOptions
|
||||
#### updateOptions
|
||||
|
||||
```js
|
||||
document.querySelector('button')._tooltip.updateOptions({ placement: 'bottom' });
|
||||
```
|
||||
|
||||
Настройки так же можно переназначать, используя data-атрибуты:
|
||||
|
||||
```html
|
||||
<button type="button" data-tooltip-placement="bottom">Кнопка</button>
|
||||
```
|
||||
|
||||
##### destroy
|
||||
#### destroy
|
||||
|
||||
```js
|
||||
document.querySelector('button')._tooltip.destroy();
|
||||
|
||||
@@ -59,6 +59,7 @@ export const createTooltip = ($el, initialContent, initialOptions) => {
|
||||
delay: [0, 0],
|
||||
duration: [0, 0],
|
||||
easing: ['linear', 'linear'],
|
||||
flipPadding: 0,
|
||||
hideOnClick: true, // Возможные значения: true, 'all', 'toggle'
|
||||
interactive: true,
|
||||
offset: [0, 8],
|
||||
@@ -76,7 +77,7 @@ export const createTooltip = ($el, initialContent, initialOptions) => {
|
||||
let parsedValue = value;
|
||||
try {
|
||||
parsedValue = JSON.parse(value);
|
||||
} catch {} // eslint-disable-line no-empty
|
||||
} catch {} // oxlint-disable-line no-empty
|
||||
options[attributeToOption(key)] = parsedValue;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +138,7 @@ export const createTooltip = ($el, initialContent, initialOptions) => {
|
||||
});
|
||||
$el._tooltip._currentAnimation = animation;
|
||||
await animation.finished;
|
||||
} catch {} // eslint-disable-line no-empty
|
||||
} catch {} // oxlint-disable-line no-empty
|
||||
};
|
||||
|
||||
// Добавляет стрелку, если она включена и ещё не создана
|
||||
@@ -204,6 +205,7 @@ export const createTooltip = ($el, initialContent, initialOptions) => {
|
||||
|
||||
if (
|
||||
updatedOptions.offset !== undefined ||
|
||||
updatedOptions.flipPadding !== undefined ||
|
||||
updatedOptions.shiftPadding !== undefined ||
|
||||
updatedOptions.arrow !== undefined
|
||||
) {
|
||||
@@ -337,7 +339,7 @@ export const createTooltip = ($el, initialContent, initialOptions) => {
|
||||
const arrowSize = $el._tooltip.$arrow?.offsetWidth ?? 0;
|
||||
return [
|
||||
offset({ mainAxis: options.offset[1], crossAxis: options.offset[0] }),
|
||||
flip(),
|
||||
flip({ padding: options.flipPadding }),
|
||||
shift({
|
||||
padding: {
|
||||
top: options.shiftPadding[1],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@advdominion/tooltip",
|
||||
"version": "7.1.1",
|
||||
"version": "7.2.0",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
Reference in new issue
Block a user