diff --git a/config/catalog-groups.json b/config/catalog-groups.json index 7401a319a..f0db037ce 100644 --- a/config/catalog-groups.json +++ b/config/catalog-groups.json @@ -116,7 +116,8 @@ "segmented", "close", "download-file", - "copy-button" + "copy-button", + "confirm" ] }, "forms": { @@ -249,6 +250,7 @@ "editor": { "title": "Редактор страниц", "items": [ + "editor", "content-editor", "blocks" ] diff --git a/config/reference-pages.json b/config/reference-pages.json index 7736f16d9..7e2e8c12b 100644 --- a/config/reference-pages.json +++ b/config/reference-pages.json @@ -1935,6 +1935,10 @@ "kind": "smart", "standard": 3 }, + "smart/actions/confirm.md": { + "kind": "smart", + "standard": 3 + }, "pro/admin/change-confirm.md": { "kind": "smart", "standard": 2 diff --git a/config/smart-reference.json b/config/smart-reference.json index 3f743c6bc..f247e82c8 100644 --- a/config/smart-reference.json +++ b/config/smart-reference.json @@ -7,7 +7,7 @@ "button-copy": "smart/actions/copy-button.md", "button-icon": "smart/actions/icon-buttons.md", "chat-threads": "pro/chat/thread-list.md", - "confirm": "pro/admin/change-confirm.md", + "confirm": "smart/actions/confirm.md", "confirm-change": "pro/admin/change-confirm.md", "editor-content": "pro/editor/content-editor.md", "editor-inline": "smart/editor/inline-editor.md", @@ -28,11 +28,10 @@ "slider-range": "smart/forms/range-slider.md", "view": "pro/admin/table.md", "view-properties": "pro/admin/property-view.md", - "view-table": "pro/admin/table.md" - }, - "skip": { - "editor": "sf-editor (the block editor, simai/ui-pro components/src/smart/editor) has no page yet; its page comes with the editor documentation, not with a repin." + "view-table": "pro/admin/table.md", + "editor": "pro/editor/editor.md" }, + "skip": {}, "suppress": { "popover": { "sections": [ diff --git a/config/smart-reference.ru.json b/config/smart-reference.ru.json index 83714d410..7976c20e7 100644 --- a/config/smart-reference.ru.json +++ b/config/smart-reference.ru.json @@ -2414,6 +2414,33 @@ "The question, said in place: what will happen, as a sentence («Удалить файл «Отчёт.pdf»?»). It names the group; without one the group is named «Подтверждение».": "Вопрос, сказанный на месте: что произойдёт, одним предложением («Удалить файл «Отчёт.pdf»?»). Он же называет группу; без него группа называется «Подтверждение».", "The runtime loader: loads what a page uses, components and utilities, on demand, and the theme. ensure(tags, {timeout}) asks for elements and settles with {ready, missing}, never rejecting; watch(root) marks a box an element renders into as a portal the Loader reads as the page (unwatch unmarks it). Once a theme is set (setTheme, changeTheme), SF.Loader.theme and SF.Loader.themePreference read it.": "Загрузчик: по требованию загружает то, что использует страница, — компоненты и утилиты — и тему. `ensure(tags, {timeout})` запрашивает элементы и завершается значением `{ready, missing}`, никогда не отклоняясь; `watch(root)` отмечает блок, в который элемент рисует содержимое, как портал, который Loader читает как страницу (`unwatch` снимает отметку). Когда тема задана (`setTheme`, `changeTheme`), её читают `SF.Loader.theme` и `SF.Loader.themePreference`.", "Whether this toggle button is on: aria-pressed on the native button and the selected look (the class sheet's active state); pressed=\"false\" says off; absent, the button is not a toggle and nothing is said": "Включена ли эта кнопка-переключатель: `aria-pressed` на нативной кнопке и вид выбранной (активное состояние таблицы стилей классов); `pressed=\"false\"` сообщает «выключено»; без атрибута кнопка не переключатель и ничего не сообщает", - "inline: the question and its answers in the flow of a row, no frame of their own. card: in a framed card with its own inset.": "inline: вопрос и ответы в потоке строки, без своей рамки. card: в карточке с рамкой и собственным отступом." + "inline: the question and its answers in the flow of a row, no frame of their own. card: in a framed card with its own inset.": "inline: вопрос и ответы в потоке строки, без своей рамки. card: в карточке с рамкой и собственным отступом.", + "A short line under the name: where the page lives or its state (the host's words).": "Короткая строка под названием: где живёт страница или её состояние (слова хоста).", + "A site block was saved: {manifest, words, changes}. Keep the manifest in site-blocks.": "Сохранён блок сайта: {manifest, words, changes}. Сохраните манифест в site-blocks.", + "Add a block of a type (and variant) after a block, or where it first fits; the value is the new block's id.": "Добавляет блок типа (и варианта) после блока или туда, где он первым подходит; значение — id нового блока.", + "Choose a block as a press does; with no id the choice is let go.": "Выбирает блок так же, как нажатие; без id выбор снимается.", + "Composition editor manifests of the host's types, or ones that replace the Framework's: the parameter panel is built from them.": "Манифесты редактора композиции для типов хоста или заменяющие манифесты SIMAI Framework: по ним строится панель параметров.", + "One step back, as ⌘Z: typing and structure are one history.": "Шаг назад, как ⌘Z: набор текста и структура — одна история.", + "One step forward, as ⇧⌘Z.": "Шаг вперёд, как ⇧⌘Z.", + "Open the block builder: a new site block, one made from a block, or a site block to change everywhere.": "Открывает конструктор блоков: новый блок сайта, блок из выбранного блока или блок сайта, который нужно изменить везде.", + "Put the caret at the end of a text block.": "Ставит курсор в конец текстового блока.", + "Set a parameter of a block, for a screen when it differs by screen.": "Задаёт параметр блока, для экрана — если значение различается по экранам.", + "Start (or end, on false) editing the site template: the header and footer of every page.": "Начинает (или, при false, заканчивает) правку шаблона сайта: шапки и подвала всех страниц.", + "The accessible name of the page.": "Доступное имя страницы.", + "The host's own block types beside the Framework's; their renderers come in renderers.": "Свои типы блоков хоста рядом с типами SIMAI Framework; их отрисовщики приходят в renderers.", + "The name of the page being edited, shown at the start of the top bar (the host's page name).": "Название редактируемой страницы в начале верхней строки (имя страницы у хоста).", + "The one block editor of the SIMAI page: layout and text in one Composition Document, edited by depth. A click on text types, a click beside it chooses the block, Esc goes up and Enter down. One catalogue (Text and Blocks, groups, variants with pictures, search, favourites, recent) serves the + between blocks, / in text and the library; blocks move by the grip with drop lines or Alt+arrows, and a drop beside a block splits it into columns. The parameter panel says where every value comes from (type, theme, variant, block, screen) and sets values per screen; the Data tab binds a block to a host source through the source port. The block builder makes and changes site blocks everywhere; the site template is a separate, explicit edit. Rights and theme come from the host. One undo history; output as JSON, HTML and Markdown.": "Единый редактор блоков страницы SIMAI: раскладка и текст в одном Composition Document, правка по глубине. Щелчок по тексту — печать, щелчок рядом — выбор блока, Esc — вверх, Enter — вниз. Один каталог («Текст» и «Блоки», группы, варианты с картинками, поиск, избранное, недавние) служит «+» между блоками, «/» в тексте и библиотеке; блоки двигаются за ручку с линиями места или Alt+стрелками, а сброс рядом с блоком делит место на колонки. Панель параметров говорит, откуда каждое значение (тип, тема, вариант, блок, экран), и задаёт значения по экранам; вкладка «Данные» связывает блок с источником хоста через порт источников. Конструктор блоков создаёт и меняет блоки сайта везде; шаблон сайта — отдельная явная правка. Права и тема приходят от хоста. Одна история отмены; результат — JSON, HTML и Markdown.", + "The page as JSON, HTML (utility classes) or Markdown of its text areas; the value is the text.": "Страница как JSON, HTML (классы утилит) или Markdown её текстовых областей; значение — текст.", + "The page changed: {document, reason, scope}. The host saves the document; scope template means the header or footer of every page.": "Страница изменилась: {document, reason, scope}. Хост сохраняет документ; scope template означает шапку или подвал всех страниц.", + "The page is one tab stop; Enter and Esc move by depth, the arrows by siblings; the panels are Framework controls in tab order; changes are said in a status line.": "Страница — одна остановка Tab; Enter и Esc двигают по глубине, стрелки — по соседям; панели — элементы управления SIMAI Framework в порядке Tab; изменения сообщаются в строке состояния.", + "The page: a Composition Document of the profile ui-layout (layout.page, or layout.regions with a banner, main and contentinfo region for a page with a site template). A new one starts editing it; the one the editor sent in change is recognised and kept.": "Страница: Composition Document профиля ui-layout (layout.page или layout.regions с областями banner, main и contentinfo для страницы с шаблоном сайта). Новый документ начинает новую правку; документ, который редактор сам отправил в change, узнаётся и сохраняется.", + "The renderers of the host's types: the page is drawn by them as the site draws it.": "Отрисовщики типов хоста: страница рисуется ими так, как её рисует сайт.", + "The screen the page is drawn and its values are set for: desktop (lg), tablet (md), phone (xs).": "Экран, для которого рисуется страница и задаются значения: desktop (lg), tablet (md), phone (xs).", + "The site's own blocks made in the block builder; the newest version of each is offered in the catalogue.": "Собственные блоки сайта из конструктора блоков; в каталоге предлагается новейшая версия каждого.", + "The site's theme: its defaults are a level of where a value comes from, its attributes are put on the drawn page. The editor never changes it; settings_url links to where it is changed.": "Тема сайта: её значения по умолчанию — один из уровней происхождения значения, её атрибуты ставятся на нарисованную страницу. Редактор её никогда не меняет; settings_url ведёт туда, где её меняют.", + "The words of the host's blocks, by locale.": "Слова блоков хоста по локалям.", + "This block is chosen: {ids}; empty when none is.": "Выбран блок: {ids}; пусто, если не выбран ни один.", + "What the person may change, from the host: layout false lets the author write only inside text areas; template false keeps the site template closed; builder false hides the block builder. There is no role switch in the editor.": "Что человеку можно менять, по данным хоста: layout false позволяет автору писать только внутри текстовых областей; template false держит шаблон сайта закрытым; builder false скрывает конструктор блоков. Переключателя ролей в редакторе нет.", + "Where favourites and recent blocks are kept through SF.Preferences (sf-editor/); empty keeps them for the session only.": "Где хранятся избранные и недавние блоки через SF.Preferences (sf-editor/); пусто — только на время сеанса." } } diff --git a/content/en/index.md b/content/en/index.md index b385a8cad..ca8e1eb1c 100644 --- a/content/en/index.md +++ b/content/en/index.md @@ -16,16 +16,16 @@ an English page. ## What you can build The admin, the chat and the editor are live: click and type in them. A tab -loads when you open it. The demo data is in Russian. +loads when you open it. :::internal_preview {size=tall title="Admin, chat and editor"} [Open the demo](/demos/home/showcase/) ::: More: [the admin](/demos/home/admin/) on its own page, -[the chat](/ru/pro/chat/) and [the editor](/ru/pro/editor/content-editor/) (in -Russian). All three are [SIMAI Pro](/ru/pro/) components under their own -licence; the rest of SIMAI Framework is open under Apache-2.0. +[the chat](/ru/pro/chat/) and [the editor](/ru/pro/editor/editor/) (in +Russian). They are the [component sets](/ru/guide/sets/) `admin`, `chat` and +`editor` under the Pro licence; SIMAI Framework is open under Apache-2.0. ## Three steps @@ -55,7 +55,7 @@ licence; the rest of SIMAI Framework is open under Apache-2.0. :::features - **[Themes](/ru/guide/colour/themes/)** Light, dark and your own theme from one colour. - **[Updating](/ru/guide/connection/updating/)** A pinned release and the change journal. -- **[Background effects](https://effects.simai.io/)** Live backgrounds for first screens. +- **[Component sets](/ru/guide/sets/)** Admin, tables, chat, mail, editor, effects. - **[Working with AI](/ru/guide/standards/ai/)** The `ai.json` index and a text version of every page. ::: diff --git a/content/ru/components/navigation/context-menu.md b/content/ru/components/navigation/context-menu.md index 8511b294c..a86cd92e8 100644 --- a/content/ru/components/navigation/context-menu.md +++ b/content/ru/components/navigation/context-menu.md @@ -32,7 +32,7 @@ profile: reference Обычно берут вид `sf-button--link` со схемой `sf-button--on-surface`. - Компонент — только оформление. Открыть меню у объекта, поставить его рядом, закрыть по Escape и вернуть фокус — задача вызывающего кода или - смарт-компонента ``. + смарт-компонента ``. ## Варианты @@ -50,7 +50,7 @@ profile: reference - Статическая разметка `sf-context-menu` не задаёт ролей. Не ставьте ей `role="menu"`, если не реализуете клавиатурную модель меню целиком. -- Для плоского списка действий берите смарт-компонент `` с +- Для плоского списка действий берите смарт-компонент `` с `aria-label`: он даёт прямым кнопкам и ссылкам роль `menuitem`, оставляет в порядке Tab один пункт и переводит фокус клавишами со стрелками вверх и вниз, Home и End. diff --git a/content/ru/components/overlays-media/tooltip.md b/content/ru/components/overlays-media/tooltip.md index bddfe175f..6939a0bf4 100644 --- a/content/ru/components/overlays-media/tooltip.md +++ b/content/ru/components/overlays-media/tooltip.md @@ -27,7 +27,7 @@ profile: reference `sf-tooltip-supporting-text`, затем хвост `sf-tooltip-tail`. - Триггер — нативный фокусируемый элемент: кнопка, ссылка, поле. На теге - смарт-компонента, например ``, описание достаётся самому + смарт-компонента, например ``, описание достаётся самому тегу, а не кнопке внутри него; для подсказки берите классовую кнопку `sf-icon-button`. Атрибут `data-tooltip` на нём хранит `id` поверхности; пустой `data-tooltip` берёт diff --git a/content/ru/guide/connection/loader.md b/content/ru/guide/connection/loader.md index 49032673e..bd576595c 100644 --- a/content/ru/guide/connection/loader.md +++ b/content/ru/guide/connection/loader.md @@ -69,7 +69,7 @@ SF.RuleLoader['cl-datepicker'] = { Имя правила смарт-компонента начинается с `cl-`, папка в поставке — без него: `cl-datepicker` → `smart/datepicker/`. Это внутреннее имя: в разметке и коде -проекта используется только тег ``. +проекта используется только тег ``. Полный список правил выпуска — `distr/rule/rule.json`: имя, теги, вид и связи каждого правила. Ядро несёт те же правила в `distr/core/js/core-rules.js`. @@ -105,7 +105,7 @@ SF.RuleLoader['cl-datepicker'] = { ## Зависимости Поле `relation` перечисляет модули, без которых компонент не работает. Для -`` это обычный компонент календаря, смарт-поле ввода, кнопки, +`` это обычный компонент календаря, смарт-поле ввода, кнопки, кнопки-иконки и иконки. Loader загружает зависимости до самого компонента и ждёт их готовности. Поэтому автор страницы пишет один тег, а не пять. diff --git a/content/ru/guide/introduction/levels.md b/content/ru/guide/introduction/levels.md index 4b2431288..3b4162f08 100644 --- a/content/ru/guide/introduction/levels.md +++ b/content/ru/guide/introduction/levels.md @@ -18,7 +18,7 @@ Framework устроен как лестница уровней. Каждый с | Утилиты | Одно свойство элемента в одном классе | `p-4`, `gap-2`, `bg-surface-container` | Раскладка и точечное оформление | | Компоненты | Готовая структура и оформление повторяемого элемента | `.sf-button` | Элемент без собственного поведения | | Смарт-компоненты | Собственный тег с поведением, состоянием и событиями | ``, `` | Нужны ввод, состояние, события, данные | -| Составные смарт-компоненты | Несколько смарт-компонентов с общей логикой | ``; таблица данных с поиском и фильтрами | Повторяется целый сценарий | +| Составные смарт-компоненты | Несколько смарт-компонентов с общей логикой | ``; таблица данных с поиском и фильтрами | Повторяется целый сценарий | | Макеты | Страница целиком как проверяемые данные | JSON-документ страницы | Структурой страницы нужно управлять | ### Дизайн-токены и Core @@ -140,7 +140,7 @@ Framework устроен как лестница уровней. Каждый с их работу в одной пользовательской задаче. Таблица данных со строкой поиска, фильтрами, сортировкой и постраничным выводом — один такой компонент, а не четыре отдельных, которые проект связывает вручную. Выбор даты -(``) — тоже составной компонент: поле, кнопки и календарь в нём +(``) — тоже составной компонент: поле, кнопки и календарь в нём работают как одно целое. Составной выбор товара может связать варианты, цену, доступность и кнопку diff --git a/content/ru/guide/layouts/editor/inline-text.md b/content/ru/guide/layouts/editor/inline-text.md index 0e4330af3..6a0bfa566 100644 --- a/content/ru/guide/layouts/editor/inline-text.md +++ b/content/ru/guide/layouts/editor/inline-text.md @@ -23,7 +23,7 @@ document.addEventListener('sf-inline-editor-change', (event) => { Правила подключения: -- на странице редактора обязательно должен стоять ``: по +- на странице редактора обязательно должен стоять ``: по этому тегу Loader подключает код правки; - до загрузки `startInlineEdit` выбрасывает ошибку `inline_editor_not_loaded`. diff --git a/content/ru/guide/layouts/editor/overview.md b/content/ru/guide/layouts/editor/overview.md index d3e5af403..0f0c3a63d 100644 --- a/content/ru/guide/layouts/editor/overview.md +++ b/content/ru/guide/layouts/editor/overview.md @@ -27,8 +27,8 @@ HTML**. Страница описана Document. Редактор показы человек видит понятное сообщение. Редактор собирается из смарт-компонентов: оверлея холста -`` ([справочник](/ru/smart/editor/composition-overlay/)), перетаскивания `` -([справочник](/ru/smart/editor/sortable/)), редактора текста `` ([справочник](/ru/smart/editor/inline-editor/)) и закреплённых +`` ([справочник](/ru/smart/editor/composition-overlay/)), перетаскивания `` +([справочник](/ru/smart/editor/sortable/)), редактора текста `` ([справочник](/ru/smart/editor/inline-editor/)) и закреплённых панелей `` ([справочник](/ru/smart/overlays/drawer/)). Их код загружается только на странице редактора, поэтому обычные страницы сайта не получают лишний JavaScript, а опубликованный HTML одинаков с открытым и @@ -41,7 +41,7 @@ HTML**. Страница описана Document. Редактор показы ## Холст и оверлей -`` рисует поверх отрисованного документа +`` рисует поверх отрисованного документа рамки наведения и выбора, панель действий над выбранным блоком и одно место вставки — у стыка блоков под указателем. Блок переносят за ручку на панели или с клавиатуры, а после изменения Document блоки плавно встают на новые diff --git a/content/ru/guide/layouts/editor/panels.md b/content/ru/guide/layouts/editor/panels.md index dca2226ab..f4e53c9ab 100644 --- a/content/ru/guide/layouts/editor/panels.md +++ b/content/ru/guide/layouts/editor/panels.md @@ -18,7 +18,7 @@ description: "Библиотеку блоков и свойства выбран ```html
- +
diff --git a/content/ru/guide/sets/index.md b/content/ru/guide/sets/index.md index 8882151b7..1d94945eb 100644 --- a/content/ru/guide/sets/index.md +++ b/content/ru/guide/sets/index.md @@ -31,15 +31,15 @@ description: "Набор компонентов — готовые смарт-к ## Какие бывают наборы -| Набор | Для чего | Лицензия | Сайт | -|:---|:---|:---|:---| -| `admin` | Админка: меню, рабочие экраны, настройки | Pro | [admin.simai.io](https://admin.simai.io/) | -| `view` | Представления данных: умная таблица с сортировкой, фильтрами и страницами, канбан, календарь, диаграмма Ганта | Pro | view.simai.io, появится | -| `chat` | Чат: лента сообщений, поле ввода, список диалогов | Pro | свой поддомен, появится | -| `mail` | Письма: конструктор и просмотр писем | Pro | свой поддомен, появится | -| `editor` | Редактор страниц и текста | Pro | свой поддомен, появится | -| `chart` | Графики и диаграммы | Pro | свой поддомен, появится | -| `effects` | Фоновые эффекты и анимации | Бесплатно | [effects.simai.io](https://effects.simai.io/) | +| Набор | Для чего | Лицензия | Сайт набора | Документация сейчас | +|:---|:---|:---|:---|:---| +| `admin` | Админка: меню, рабочие экраны, настройки | Pro | [admin.simai.io](https://admin.simai.io/) | [Pro → Админка](/ru/pro/admin/) | +| `view` | Представления данных: умная таблица с сортировкой, фильтрами и страницами, канбан, календарь, диаграмма Ганта | Pro | view.simai.io, появится | [Pro → Таблица](/ru/pro/admin/table/), [Свойства записи](/ru/pro/admin/property-view/) | +| `chat` | Чат: лента сообщений, поле ввода, список диалогов | Pro | свой поддомен, появится | [Pro → Чат](/ru/pro/chat/) | +| `mail` | Письма: конструктор и просмотр писем | Pro | свой поддомен, появится | [Pro → Письма](/ru/pro/mail/) | +| `editor` | Редактор страниц и текста | Pro | свой поддомен, появится | [Pro → Редактор](/ru/pro/editor/editor/) | +| `chart` | Графики и диаграммы | Pro | свой поддомен, появится | появится | +| `effects` | Фоновые эффекты и анимации | Бесплатно | [effects.simai.io](https://effects.simai.io/) | на сайте набора | Простой список и простая таблица остаются в бесплатном SIMAI Framework. Набор `view` нужен, когда данным требуются сортировка, фильтры, страницы или diff --git a/content/ru/guide/sets/names.md b/content/ru/guide/sets/names.md index fa1942b04..0e86e88fb 100644 --- a/content/ru/guide/sets/names.md +++ b/content/ru/guide/sets/names.md @@ -19,7 +19,7 @@ description: "У всех наборов одна приставка sf-, имя |:---|:---|:---| | `sf-effect` | `effect` | набор `effects` | | `sf-chat-composer` | `chat` | набор `chat` | -| `sf-message-log` | `message` | набор `chat` | +| `sf-chat-window` | `chat` | набор `chat` | | `sf-view-table` | `view` | набор `view` | | `sf-button` | `button` | SIMAI Framework | @@ -45,4 +45,5 @@ description: "У всех наборов одна приставка sf-, имя Когда компонент переименовывают, старое имя ещё какое-то время работает и пишет в консоль подсказку с новым именем, как и у компонентов SIMAI Framework (см. [Жизненный цикл изменений](/ru/guide/connection/change-lifecycle/)). Срок, -до которого действует старое имя, набор указывает сам. +до которого действует старое имя, набор указывает сам. Таблица переименований +ноября 2026 — на странице [Обновление](/ru/guide/connection/updating/). diff --git a/content/ru/guide/size-and-type/language.md b/content/ru/guide/size-and-type/language.md index 27230b962..348d61838 100644 --- a/content/ru/guide/size-and-type/language.md +++ b/content/ru/guide/size-and-type/language.md @@ -35,7 +35,7 @@ description: "Компоненты Framework сами подставляют ч ```html
- +
``` diff --git a/content/ru/guide/smart-components/assemblies.md b/content/ru/guide/smart-components/assemblies.md index ddc46e5c9..814b7a06e 100644 --- a/content/ru/guide/smart-components/assemblies.md +++ b/content/ru/guide/smart-components/assemblies.md @@ -39,7 +39,7 @@ description: "Сборка — компонент из компонентов: "search": {"part": "sf-search-field", "bind": {"values.query": "query"}}, "source": {"part": "sf-source", "bind": {"settings.collection": "collection", "settings.query": "query", "values.items": "items", "values.total": "total", "values.state": "state"}}, - "list": {"part": "sf-table", "bind": {"settings.items": "items", "settings.state": "state"}} + "list": {"part": "sf-view-table", "bind": {"settings.items": "items", "settings.state": "state"}} }, "layouts": {"default": {"file": "layout.default.html"}}, "rules": [ @@ -205,7 +205,7 @@ flowchart LR ```json "roles": { "inn": {"part": "sf-input", "if": "is-company", "settings": {"name": "inn", "required": true}}, - "note": {"part": "sf-textarea", "hidden": "!has-note"} + "note": {"part": "sf-input-textarea", "hidden": "!has-note"} } ``` diff --git a/content/ru/guide/smart-components/comparison.md b/content/ru/guide/smart-components/comparison.md index 70c274381..452b030d6 100644 --- a/content/ru/guide/smart-components/comparison.md +++ b/content/ru/guide/smart-components/comparison.md @@ -119,11 +119,11 @@ SIMAI Framework; все 59 смарт-компонентов переведен - `sf-tree` рисует записи плоско, строками на классах по ключу, и при более чем 300 видимых записях — только те, что в окне (simai/ui-source#43, `sf-tree` 2.0, выпуск `ui-363de49bdfa8`); -- `sf-property-view` держит одну версию на запись и читает разделы через +- `sf-view-properties` держит одну версию на запись и читает разделы через `group.load` (пункты 25 и 35 переноса, порт 1.15.0 в выпуске `ui-dde3ef154c34`, ui-pro `c6f3cfc`); - деталь под ложным `if`, в закрытом контейнере или с `start` `opened` - или `visible` не загружает код, а редакторы полей `sf-property-view` + или `visible` не загружает код, а редакторы полей `sf-view-properties` загружаются лениво (simai/ui-source#47, выпуск `ui-6d7ee4232223`; ui-pro `41956dc`); - базовое действие `connect-port` есть у каждого смарт-элемента, а @@ -149,7 +149,7 @@ SIMAI Framework; все 59 смарт-компонентов переведен | `sf-modal` копирует содержимое слота через `outerHTML`, обработчики страницы теряются | simai/ui-source#42 | | Идентификаторы из счётчика совпадают у классового и смарт-поля | simai/ui-source#41 | | Три списка слоёв и две основы z-index | simai/ui-source#36, #37 | -| `sf-table` рисует около 108 узлов на строку и не имеет окна | simai/ui-pro#2 | +| `sf-view-table` рисует около 108 узлов на строку и не имеет окна | simai/ui-pro#2 | | Описание `sf-switch` попадает в имя; `Escape` у `sf-menu` объявлен для всех раскладок | simai/ui-source#44 | | `SF.language.use` побеждает `[lang]`; нет множественного числа; пара не поставляет файлы слов | simai/ui-source#45 | | `sf-modal` в `body` теряет язык, тему и переменные хоста; цвет включённого `sf-switch` — только токен роли | simai/ui-source#46 | @@ -166,7 +166,7 @@ SIMAI Framework; все 59 смарт-компонентов переведен | Все теги определяются в одном общем реестре элементов, поэтому две старшие версии тега не уживаются на странице | пункт 34 переноса | | ui-admin передаёт порт каждому компоненту вручную (`setHostPort`) и сам отвечает на `sf-host-port-request`; рантайм сборок доводит порт, повторяет панели, откладывает чтения и говорит слова по коду | пункт 37 переноса | | Порт представления данных 1.12 не пускает `confirm` в массовое `bulk.apply_selected`, хотя хосты просят подтверждения; порт 1.13.0 добавляет поле | simai/ui-source#48, пункт 38 переноса | -| `sf-property-view` не сообщает `saved` и не бывает закрытым, поэтому страница сама создаёт и убирает панель | simai/ui-pro#4, пункт 39 переноса | +| `sf-view-properties` не сообщает `saved` и не бывает закрытым, поэтому страница сама создаёт и убирает панель | simai/ui-pro#4, пункт 39 переноса | | Полигон ui-admin дважды обновляет список при запуске; эталонный хост пишет причины через дефис, а Larena — через подчёркивание; эталонный хост не отвечает на `record.delete` | simai/ui-admin#1, пункт 40 переноса | | Ключ `view` занимает зарезервированное имя, порядок записан как `{field, dir}` | пункт 41 переноса | | `sf-tabs` берёт пункты методом `setItems` и объявляет `items` строкой; `sf-modal` и `sf-toast` не берут `code` и `count` | пункт 42 переноса | diff --git a/content/ru/guide/smart-components/data.md b/content/ru/guide/smart-components/data.md index 3c73062fe..cb76a6d10 100644 --- a/content/ru/guide/smart-components/data.md +++ b/content/ru/guide/smart-components/data.md @@ -63,7 +63,7 @@ orders.connectPort({ port: window.app.dataViewPort }); объявляет порт прямо в элементе — JSON-островом внутри него: ```html - + …начальный вид элемента… - + ``` | Поле | Что задаёт | @@ -135,7 +135,7 @@ simai/ui-source. ### Первый ответ и тихая перепроверка Страница может напечатать первый ответ списка вместе с HTML — остров -`first-answer` у `sf-data-view` в форме ответа порта, — и список работает +`first-answer` у `sf-view` в форме ответа порта, — и список работает ещё до порта. Тогда: - **возможности порта известны до его ответа** — из объявленного порта или @@ -296,7 +296,7 @@ flowchart LR отвечает версией всей записи, `group.save` сравнивается с ней, а своей версии у раздела нет. Сохранение по более старой версии отвечает `conflict` с текущей. Так работает порт `simai.property-view-port` 1.15.0 - (выпуск `ui-dde3ef154c34`) и `sf-property-view` из ui-pro на нём; хост без + (выпуск `ui-dde3ef154c34`) и `sf-view-properties` из ui-pro на нём; хост без возможности `group-load` обслуживается через `record.load` один раз на версию, см. [Один раздел записи](/ru/guide/smart-components/first-paint/#один-раздел-записи). diff --git a/content/ru/guide/smart-components/field-renderers.md b/content/ru/guide/smart-components/field-renderers.md index a8d41b7b2..bfc02fc0d 100644 --- a/content/ru/guide/smart-components/field-renderers.md +++ b/content/ru/guide/smart-components/field-renderers.md @@ -26,8 +26,8 @@ description: "Набор SF.fieldRenderers рисует значение сво - **ячейка** — значение в строке таблицы; - **фильтр** — элемент окна фильтра по этому свойству. -Таблица `` берёт из набора ячейки и фильтры, карточка записи -`` — просмотр и правку. Оба компонента входят в пакет +Таблица `` берёт из набора ячейки и фильтры, карточка записи +`` — просмотр и правку. Оба компонента входят в пакет `pro` ([Представление данных](/ru/pro/admin/table/), [Представление свойств](/ru/pro/admin/property-view/)). Страница вызывает те же функции сама, когда рисует поле без этих компонентов. @@ -47,7 +47,7 @@ description: "Набор SF.fieldRenderers рисует значение сво ## Когда набор доступен У набора нет тега, поэтому разметка страницы его не загружает. Набор -публикуют файлы ``, `` и `` пакета +публикуют файлы ``, `` и `` пакета `pro`, которые несут свою копию, и отдельный файл выпуска `smart/field-renderers/js/field-renderers.js` в базе смарт-компонентов `SF.Loader.smartPath`. @@ -154,7 +154,7 @@ const set = await SF.whenFieldRenderers(); выпадающий список. Дата со временем (`datetime`) правится в одной строке двумя полями: день — -`` (прежнее имя ``), время — `` с маской `чч:мм`. Оба поля того же +`` (прежнее имя ``), время — `` с маской `чч:мм`. Оба поля того же размера, что и остальные, внутри группы с именем поля; у каждого своё доступное имя — «Начало: дата» и «Начало: время». Время можно ввести и без маски: `930`, `9:30` и `9.30` читаются как `09:30`. diff --git a/content/ru/guide/smart-components/first-paint.md b/content/ru/guide/smart-components/first-paint.md index ef66a148e..6d29a975f 100644 --- a/content/ru/guide/smart-components/first-paint.md +++ b/content/ru/guide/smart-components/first-paint.md @@ -49,7 +49,7 @@ description: "Начальный вид у каждого смарт-компо начальный вид не входит. - **Снимок не больше 8 КБ.** Снимок крупнее допускается, только если пример объявляет `"first-paint": {"max-bytes": N, "why": "…"}`; без этого проверка - снимков его не пропускает. Так снимок `sf-country-code` сократился со 107 + снимков его не пропускает. Так снимок `sf-input-country` сократился со 107 до 1,7 КБ. - **Снимок одинаков от прогона к прогону:** два прогона записи дают одни и те же байты (с выпуска `ui-0edd49be08f8`). Для этого: @@ -119,7 +119,7 @@ node scripts/snapshot-first-paint.mjs <папка сборки> --check или карточки с полосками внутри и повторяется столько раз, сколько задаёт настройка, ограничивающая данные: размер страницы, предел строк или объявленное ожидаемое число — например `expected-columns` у -`sf-data-view` и `sf-admin-menu`. Поэтому те же стили, вместе с медиа- и +`sf-view` и `sf-admin-menu`. Поэтому те же стили, вместе с медиа- и контейнерными запросами, дают скелетону и данным одну рамку на любой ширине. - Где размер зависит от данных, которых не знает ни одна настройка @@ -134,7 +134,7 @@ node scripts/snapshot-first-paint.mjs <папка сборки> --check место под компонент, он публикует размеры открытыми CSS-переменными, посчитанными из токенов размера, с формулой в документации. Пример — `--sf-table-row-block-size-<размер>`, `--sf-table-head-block-size-<размер>` -и `--sf-table-toolbar-block-size-<размер>` у `sf-table`. +и `--sf-table-toolbar-block-size-<размер>` у `sf-view-table`. В выпуске `ui-133dc02b833d` данные приходят позже у трёх компонентов из 59: `sf-tree`, `sf-menu` и `sf-profile-picker`. Вручную написан только скелетон @@ -202,8 +202,8 @@ node scripts/snapshot-first-paint.mjs <папка сборки> --check | `none` | заглушки нет: так объявлены элементы в строке текста и то, что открывается поверх страницы, — заглушка сдвинула бы строку, в которой элемент стоит | ```html - - + + ``` Заглушка видна, только пока элемент не определён (`:not(:defined)`) и в нём @@ -280,11 +280,11 @@ flowchart LR рядом с напечатанной разметкой). Сдвиг после прихода кода — не больше 0,0006. -На стенде экрана админки (сборка из выпущенных `sf-data-view`, -`sf-property-view`, `sf-button`, `sf-page-header` и `sf-toast`) первый кадр +На стенде экрана админки (сборка из выпущенных `sf-view`, +`sf-view-properties`, `sf-button`, `sf-header-page` и `sf-toast`) первый кадр стоит неподвижно до кода и после данных на обеих ширинах. Небольшой сдвиг при приходе кода давали две ошибки SIMAI Framework — напечатанная -`sf-button` рисовалась заново, а строка `sf-page-header` меняла высоту со +`sf-button` рисовалась заново, а строка `sf-header-page` меняла высоту со звёздочкой; обе исправлены в выпуске `ui-6d7ee4232223`. ## Когда запускается деталь @@ -395,7 +395,7 @@ stateDiagram-v2 Это уже в поставке: `group.load` появился в порте `simai.property-view-port` 1.15.0 (выпуск `ui-dde3ef154c34`), и -`sf-property-view` из ui-pro читает разделы через него, а у хоста без +`sf-view-properties` из ui-pro читает разделы через него, а у хоста без `group-load` — через `record.load`. На стенде экрана админки открытие записи просит два `group.load` (заголовок с первым разделом и второй раздел «Общее») и ни одного `record.load`; закрытая вкладка ничего не просит, пока @@ -413,10 +413,10 @@ stateDiagram-v2 `visible` не загружает код. На стенде экрана админки первое посещение загружает 24 модуля вместо 41, а когда код подхватывает разметку, сдвиг равен 0 на ширине 1280 и 390 px. -- **Редакторы полей `sf-property-view` загружаются лениво** (ui-pro +- **Редакторы полей `sf-view-properties` загружаются лениво** (ui-pro `41956dc`, 14 связей): пока запись не открыта, их код не приходит. - **У семи компонентов ui-pro свой начальный вид**: проверка 24 — 92 из 92, - сдвиг 0. Скелетон `sf-property-view` — вкладки разделов и карточки групп + сдвиг 0. Скелетон `sf-view-properties` — вкладки разделов и карточки групп полей вместо прежней сетки три на три. Открытых задач по первому кадру и ленивой загрузке сейчас нет. diff --git a/content/ru/guide/smart-components/folder.md b/content/ru/guide/smart-components/folder.md index 13658c930..30a612355 100644 --- a/content/ru/guide/smart-components/folder.md +++ b/content/ru/guide/smart-components/folder.md @@ -122,7 +122,7 @@ flowchart LR **Зачем пять файлов.** Когда справочник, Студия и проба повторяют договор своими файлами, они расходятся с `component.json`: в реестре вида 1.3 у `sf-button` не было события `press`, у `sf-tree` — шести событий версии -2.0, у `sf-table` — тринадцати настроек списка, и эти ошибки видели +2.0, у `sf-view-table` — тринадцати настроек списка, и эти ошибки видели справочник на сайте, каталог Студии и подсказки редактора. По стандарту 1.4 всё это выводится из одного файла. Человек держит меньше: у `sf-button` 5 файлов договора вместо 12 (1189 строк вместо 1728), у `sf-tree` — 5 вместо diff --git a/content/ru/guide/smart-components/forms.md b/content/ru/guide/smart-components/forms.md index e7af84673..c58ef6ad7 100644 --- a/content/ru/guide/smart-components/forms.md +++ b/content/ru/guide/smart-components/forms.md @@ -130,7 +130,7 @@ amount.form.reportValidity(); // false: форма не отправится Поле может показывать значение, унаследованное от уровня выше: от раздела, от шаблона, от общих настроек. Черта используется в `sf-input`, -`sf-textarea`, `sf-dropdown`, `sf-color`, `sf-switch` и `sf-quantity` и +`sf-input-textarea`, `sf-dropdown`, `sf-input-color`, `sf-switch` и `sf-input-quantity` и приносит: | Часть | Имена | diff --git a/content/ru/guide/smart-components/language.md b/content/ru/guide/smart-components/language.md index b0abe9d28..c57ecb665 100644 --- a/content/ru/guide/smart-components/language.md +++ b/content/ru/guide/smart-components/language.md @@ -103,7 +103,7 @@ description: "Слова смарт-компонента в файлах lang/< ```json "roles": { "status": {"part": "sf-dropdown", "settings": {"options": [{"value": "open"}, {"value": "won"}, {"value": "lost"}]}, "bind": {"values.value": "status"}}, - "table": {"part": "sf-table", "settings": {"columns": [{"key": "title"}, {"key": "amount"}]}, "bind": {"settings.items": "deals"}} + "table": {"part": "sf-view-table", "settings": {"columns": [{"key": "title"}, {"key": "amount"}]}, "bind": {"settings.items": "deals"}} } ``` @@ -144,9 +144,9 @@ description: "Слова смарт-компонента в файлах lang/< ```html - +
-
diff --git a/content/ru/guide/smart-components/names.md b/content/ru/guide/smart-components/names.md index 7b8d5a07c..6147ddae2 100644 --- a/content/ru/guide/smart-components/names.md +++ b/content/ru/guide/smart-components/names.md @@ -12,15 +12,21 @@ description: "Как называются смарт-компоненты, их ## Имя тега говорит, что это -- Тег — английское словосочетание в kebab-case после приставки: - `sf-<уточнение>-<главное слово>`, **главное слово в конце**: - `sf-context-menu`, `sf-admin-menu`, `sf-range-slider`, `sf-page-header`, - `sf-thread-list`, `sf-message-log`. -- Компонент того же рода сохраняет главное слово и получает уточнение: - `sf-menu`, `sf-context-menu`, `sf-admin-menu`, `sf-site-menu`. В семье - может быть сколько угодно членов. -- Сборка из сборок называется так же — по тому, что она есть: - `sf-record-screen`, а не по своим частям. +- Тег идёт от общего к частному: `sf-[набор]-<семья>[-<вариант>]`. + **Семья — в начале**, вариант — за ней: `sf-button-icon`, + `sf-input-date`, `sf-menu-context`, `sf-header-page`, `sf-view-table`. +- **Семья** — что это за деталь: `button`, `input`, `menu`, `header`, + `view`, `slider`. Главный член семьи носит одно её имя: `sf-button`, + `sf-input`, `sf-menu`, `sf-view`. Остальные добавляют вариант: + `sf-button`, `sf-button-icon`, `sf-button-copy`, `sf-button-close`. +- **Набор** ставится впереди, только если компонент живёт лишь внутри + своего набора: `sf-admin-menu`, `sf-chat-threads`, `sf-chat-composer`. + Общая деталь имени набора не носит. +- Деталь без родни — одна семья из одного слова: `sf-modal`, `sf-tabs`. + Детали, похожие только ролью (хлебные крошки, шаги, страницы), — разные + семьи, а не одна. +- Вид одной детали — её настройки, а не новое имя: таблица, которая + выглядит иначе, остаётся `sf-view-table`. - После приставки — не больше трёх слов. ## Чего в имени нет @@ -37,16 +43,18 @@ description: "Как называются смарт-компоненты, их - Манифест называет семью компонента: `"family": "menu"`. Каталог, документация и Студия группируют по семье, поэтому все меню видны вместе, как бы они ни назывались. -- **Одна приставка `sf-`** — для всех компонентов SIMAI Framework и ui-pro. -- **Набор `sf-` растёт только через ui-pro.** Новое имя `sf-` сначала - выходит в ui-pro; базовый компонент потом переезжает в SIMAI Framework и - сохраняет имя. -- **Один реестр имён** на обоих. Имя занято, когда компонент выпущен; - второй компонент с занятым именем проверка не пропускает. -- Продукты SIMAI компонентов не делают. Компонент, который продукт всё же - выпустил, переезжает в ui-pro с именем `sf-` и сохраняет старое как - псевдоним с датой. Все поставляемые теги — `sf-`; `sf-chat-composer` - сделан в UI Chat и переехал в ui-pro. +- **Одна приставка `sf-`** — для SIMAI Framework и всех + [наборов компонентов](/ru/guide/sets/) SIMAI. +- **Семьёй владеет кто-то один.** Паспорт набора перечисляет семьи, которые + ему принадлежат (`admin`, `chat`, `mail`, `view`…); общий реестр не + пропускает семью, которую заявили два набора, и имя, которое уже занято. + Подробнее — [Имена в наборах](/ru/guide/sets/names/). +- **Общее — в SIMAI Framework.** Деталь, нужная больше чем одному набору, + уходит из набора в SIMAI Framework и становится общим смарт-компонентом; + набор собирает свои экраны из неё. +- Компонент, который сделал продукт, переезжает в набор или в + SIMAI Framework с именем `sf-` и сохраняет старое как псевдоним с датой: + `sf-chat-composer` сделан в UI Chat и переехал в набор `chat`. - **Пакет партнёра или проекта** берёт свою приставку, никогда не `sf-` — см. [Пакеты партнёров и проектов](/ru/guide/smart-components/packages/). - Слова продукта, места и вида, которых нет в именах, проверка берёт из @@ -59,18 +67,18 @@ description: "Как называются смарт-компоненты, их Экран записей — раздел админки Larena, окно чата, почтовый ящик — собирается из этих ролей; продукт берёт нужные. -| Роль | Что делает | В ui-pro | +| Роль | Что делает | Где сейчас | |:---|:---|:---| | `source` | спрашивает данные у хоста через порт по `collection`, `query`, `filters`, `sort` и `page`; применяет только новейший ответ (`concurrency: latest`); вычисляет состояние данных; сообщает о присланном без запроса и о потоках фактами | порты представления данных и свойств, порт чата | | `search` | текстовый запрос | поиск таблицы и админ-меню | | `filter` | условия на записи | фильтры таблицы | -| `list` | показывает записи; открывает одну, выбирает несколько | `sf-table`, `sf-data-view`, `sf-thread-list` | +| `list` | показывает записи; открывает одну, выбирает несколько | `sf-view-table`, `sf-view`, `sf-chat-threads` | | `pagination` | страница, размер страницы, полоса массовых действий | `sf-pagination` | -| `feed` | лента, новое внизу, раннее по запросу | `sf-message-log` | -| `record` | показывает и правит одну запись | `sf-property-view` | +| `feed` | лента, новое внизу, раннее по запросу | `sf-message-log` (станет общим `sf-view-feed`) | +| `record` | показывает и правит одну запись | `sf-view-properties` | | `actions` | команды для выбранного или записи | панель инструментов, действия строки | | `composer` | пишет новую запись | поле ввода UI Chat | -| `confirm` | спрашивает перед изменением, отвечает отпечатком | `sf-change-confirm` | +| `confirm` | спрашивает перед изменением, отвечает отпечатком | `sf-confirm-change` | | `navigation` | где человек; переход между разделами | `sf-admin-menu` | ## Ключи экрана @@ -106,8 +114,10 @@ description: "Как называются смарт-компоненты, их ## Имена событий - Просьба: `sf-<тег>-<глагол>`; факт: `sf-<тег>-<глагол>ed`. В имени — - **тег компонента**: `sf-property-view-close`, `sf-table-apply`, - `sf-thread-list-open`, `sf-property-view-saved`. Компонент пакета партнёра + **тег компонента**: `sf-view-properties-close`, `sf-view-table-apply`, + `sf-chat-threads-open`, `sf-view-properties-saved`. Когда тег + переименован, его события переименованы вместе с ним, а старые имена + событий приходят следом до той же даты, что и старый тег. Компонент пакета партнёра подставляет свой тег: `acme-map-pick`. - Манифест объявляет событие глаголом (`close`, `saved`), а DOM-имя выводит построение. @@ -226,6 +236,15 @@ id?}` значит «перестать ждать», а `wait` принимае ## Псевдонимы старых имён +**Теги.** Волна переименований тегов ноября 2026 (`sf-table` → +`sf-view-table`, `sf-datepicker` → `sf-input-date` и ещё 24 имени) — в +таблице на странице [Обновление](/ru/guide/connection/updating/). Старый тег +работает до **2027-01-01** и пишет в консоль, каким тегом его заменить. +Подсказки `sf-tooltip` и `sf-help-mark` станут одним `sf-popover` (с +`mark="help|info"` для знака «?» и «i»), когда этот выпуск выйдет. + +**События и ключи** первой волны (октябрь 2026): + Когда семья компонентов переходит на эти имена, каждое старое имя работает как псевдоним с датой. Прослойка отправляет сначала новое имя, затем старые, с одним `detail` и одной отменой, и записывает у каждого `since` и diff --git a/content/ru/guide/smart-components/packages.md b/content/ru/guide/smart-components/packages.md index df470577d..8193f92a5 100644 --- a/content/ru/guide/smart-components/packages.md +++ b/content/ru/guide/smart-components/packages.md @@ -5,7 +5,8 @@ description: "Как партнёр или проект заказчика вы # Пакеты партнёров и проектов -Компоненты SIMAI делают в SIMAI Framework и ui-pro, а партнёр или проект +Компоненты SIMAI делают в SIMAI Framework и в +[наборах компонентов](/ru/guide/sets/) SIMAI, а партнёр или проект заказчика может выпустить **свой пакет** компонентов. Страница описывает, кто может выпускать пакет, как называются его компоненты, как они входят в сборки SIMAI и что проверяется. @@ -14,12 +15,12 @@ description: "Как партнёр или проект заказчика вы | Кто | Может ли выпустить свои компоненты | |:---|:---| -| SIMAI Framework | да: базовые компоненты, приставка `sf-` | -| ui-pro | да: продвинутые и общие компоненты, приставка `sf-` | -| Продукт SIMAI: Larena, решения Bitrix, почта, чат | нет: нужный компонент делают в ui-pro | +| SIMAI Framework | да: общие компоненты, приставка `sf-` | +| Набор компонентов SIMAI (`view`, `admin`, `chat`, `mail`, `editor`, `effects`…) | да: компоненты своих семей, приставка `sf-` | +| Продукт SIMAI: Larena, решения Bitrix | нет: нужный компонент делают в наборе или в SIMAI Framework | | Партнёр, проект заказчика | да: пакет под своей приставкой | -Пакет зависит от SIMAI Framework или ui-pro, а не наоборот: компоненты SIMAI +Пакет зависит от SIMAI Framework и наборов, а не наоборот: компоненты SIMAI о пакетах партнёров ничего не знают. Пакет — это компоненты, которые выпускаются и версионируются вместе под одной приставкой. @@ -29,7 +30,7 @@ description: "Как партнёр или проект заказчика вы `acme-route-list`. - Приставка никогда не `sf-` и не `simai-` и не слово из списка `naming-words.json`. -- Остальные правила имён те же: главное слово в конце, не больше трёх слов +- Остальные правила имён те же: семья в начале, вариант за ней, не больше трёх слов после приставки, семья в поле `family` — см. [Имена и общие слова](/ru/guide/smart-components/names/). - События компонента пакета называются по его тегу: `acme-map-pick`. diff --git a/content/ru/guide/smart-components/page.md b/content/ru/guide/smart-components/page.md index 1224cc5c7..5f4a20a9c 100644 --- a/content/ru/guide/smart-components/page.md +++ b/content/ru/guide/smart-components/page.md @@ -39,7 +39,7 @@ description: "Как интерфейс смарт-компонента выгл которая объявила его пакет в `SF_BOOT_CONFIG.packages` до подключения Core. Если пакет не объявлен, загрузчик компонент не загружает и пишет в консоли, какой тег и почему. Так подключаются компоненты пакета `pro`: - ``, ``, ``, `` и + ``, ``, ``, `` и другие — см. [Готовая сборка пакета `pro`](/ru/guide/introduction/structure/#готовая-сборка-пакета-pro) и [Пакеты партнёров и проектов](/ru/guide/smart-components/packages/). diff --git a/content/ru/guide/smart-components/view-preferences.md b/content/ru/guide/smart-components/view-preferences.md index 0cf3cef97..f7bfdc036 100644 --- a/content/ru/guide/smart-components/view-preferences.md +++ b/content/ru/guide/smart-components/view-preferences.md @@ -153,7 +153,7 @@ SF.Preferences.useHost({ ## Раскладка таблицы: предпочтение и слой порта -У `` раскладку колонок помнят два места, и у каждого своя роль: +У `` раскладку колонок помнят два места, и у каждого своя роль: - **Предпочтение** `sf-table/` — значение из таблицы выше. Оно читается до первого запроса: запрос несёт сортировку и размер @@ -193,7 +193,7 @@ SF.Preferences.useHost({ Компонент, который хранит вид, предлагает «Сбросить вид» там, где лежат его настройки. Кнопка «Сбросить» в настройках таблицы убирает из значения раскладку колонок (`columnSettings` и `widths`), а сортировку, размер -страницы и `view` оставляет; в `` она ещё записывает пустую +страницы и `view` оставляет; в `` она ещё записывает пустую раскладку `{}` в личный слой порта против его ревизии. Ширину панели сбрасывает двойной щелчок по ручке. diff --git a/content/ru/index.md b/content/ru/index.md index 679e8e30a..a61070537 100644 --- a/content/ru/index.md +++ b/content/ru/index.md @@ -18,9 +18,9 @@ ::: Подробнее: [админка](/demos/home/admin/) на отдельной странице, -[чат](/ru/pro/chat/), [редактор](/ru/pro/editor/content-editor/). Все три — -компоненты [SIMAI Pro](/ru/pro/) с отдельной лицензией; остальное в SIMAI -Framework открыто под Apache-2.0. +[чат](/ru/pro/chat/), [редактор](/ru/pro/editor/editor/). Это +[наборы компонентов](/ru/guide/sets/) `admin`, `chat` и `editor` с лицензией +Pro; SIMAI Framework открыт под Apache-2.0. ## Три шага @@ -50,7 +50,7 @@ Framework открыто под Apache-2.0. :::features - **[Темы](/ru/guide/colour/themes/)** Светлая, тёмная и своя тема из одного цвета. - **[Обновление](/ru/guide/connection/updating/)** Закреплённый выпуск и журнал изменений. -- **[Фоновые эффекты](https://effects.simai.io/)** Живые фоны для первых экранов. +- **[Наборы компонентов](/ru/guide/sets/)** Админка, таблицы, чат, письма, редактор, эффекты. - **[Работа с ИИ](/ru/guide/standards/ai/)** Индекс `ai.json` и текстовая версия каждой страницы. ::: diff --git a/content/ru/pro/admin/change-confirm.md b/content/ru/pro/admin/change-confirm.md index 0f823753c..24c19cebc 100644 --- a/content/ru/pro/admin/change-confirm.md +++ b/content/ru/pro/admin/change-confirm.md @@ -13,7 +13,7 @@ description: "Подготовленное изменение ждёт отве **Теперь в SIMAI Framework.** С выпуска `ui-c3015f76bcc2` карточка входит в выпуск SIMAI Framework под тегом ``, а пакет `pro` её -больше не поставляет. Прежний тег `` работает до +больше не поставляет. Прежний тег `` работает до 2027-01-01. Рядом с ней в Framework — ``: тот же вопрос без «было и станет», например удаление, о котором спрашивают в строке; оба описаны в справочнике ниже. @@ -105,71 +105,10 @@ card.addEventListener('sf-confirm-change-answer', async (event) => { ## Справочник API -Раздел собран из манифеста компонентов выпуска `ui-e22fefb3c1f8` и обновляется вместе с выпуском. Чтобы исправить описание, исправьте манифест. +Раздел собран из манифеста компонента выпуска `ui-e22fefb3c1f8` и обновляется вместе с выпуском. Чтобы исправить описание, исправьте манифест. Каждому атрибуту соответствует свойство элемента с тем же именем в camelCase: `search-placeholder` — `searchPlaceholder`. Свойство атрибута `template` называется `templateName`. -### `` - -| | | -|:---|:---| -| Тег | `` | -| Назначение | Вопрос человеку на месте — удаление, о котором спрашивают в его строке, исключение участника, сброс вида: что произойдёт, два ответа, состояние ожидания, пока ответ применяется, итог и необязательный срок, после которого вопрос истекает. Сам ничего не применяет: нажатие — это событие. | -| Контракт | `smart.confirm`, версия `1.0.0` | -| Поставка | выпуск Framework `ui-e22fefb3c1f8` | -| [Жизненный цикл](/ru/guide/smart-components/quality/#готовность-компонента-к-использованию) | экспериментальный: API может меняться между выпусками | -| [Готовность](/ru/guide/smart-components/quality/#готовность-компонента-к-использованию) | `host-required` | -| Заготовка до прихода кода | нет: атрибут `data-sf-skeleton` не пишется; своего скелетона при загрузке данных нет ([Коробка до кода](/ru/guide/smart-components/first-paint/#коробка-до-кода)) | - -#### Атрибуты - -- `template` — `string`, по умолчанию `default`. Имя варианта шаблона для внешних шаблонов SfBaseElement. -- `root-class` — `string`, по умолчанию пусто. Классы, которые добавляются корневому элементу, нарисованному компонентом. -- `root-style` — `string`, по умолчанию пусто. Объявления стиля, которые ставятся корневому элементу, нарисованному компонентом, в том числе пользовательские свойства. -- `question` — `string`, по умолчанию пусто. Вопрос, сказанный на месте: что произойдёт, одним предложением («Удалить файл «Отчёт.pdf»?»). Он же называет группу; без него группа называется «Подтверждение». -- `state` — `string`, по умолчанию `open`, значения: `open`, `pending`, `accepted`, `declined`, `expired`. open: ответы предложены. pending: ответ отправляется (ставится нажатием). accepted, declined, expired: виден только итог. -- `expires-at` — `string`, по умолчанию пусто. ISO 8601. После этого срока открытый вопрос считается истёкшим и один раз сообщает об этом событием `sf-confirm-expired`. -- `confirm-id` — `string`, по умолчанию пусто. Имя вопроса у хоста (строка, запись); возвращается в каждом событии. -- `disabled` — `boolean`, по умолчанию `false`. Ответы видны, но их нельзя нажать (где-то уже идёт отправка). -- `destructive` — `boolean`, по умолчанию `false`. Согласие что-то уничтожает (удаление): у принимающего ответа статус error. -- `appearance` — `string`, по умолчанию `inline`, значения: `inline`, `card`. inline: вопрос и ответы в потоке строки, без своей рамки. card: в карточке с рамкой и собственным отступом. -- `accept-text` — `string`. Слова ответа «да»; пусто — «Подтвердить» на языке страницы. -- `decline-text` — `string`. Отказывающий ответ; пусто — «Отмена» на языке страницы. - -#### Методы - -- `open()` — возвращает `object`. Задаёт вопрос снова; если его попросила сборка, отвечает `{accepted, reason, value}`, когда человек ответил или время вышло. -- `answer(accepted: boolean)` — возвращает `object`. Отвечает, как ответил бы человек: `accepted` true подтверждает, false отказывается. Отказ: `unknown`, `closed`, `disabled` или `prevented`. -- `confirm()` — возвращает `object`. Согласие; `data-sf-action=".confirm"`. -- `cancel()` — возвращает `object`. Отказывающий ответ; `data-sf-action=".cancel"`. Escape внутри открытого вопроса — тот же ответ. - -#### События - -Все события приходят на сам элемент и всплывают. Что означает глагол в имени события, говорит общий словарь: [Имена и общие слова](/ru/guide/smart-components/names/). Общие события базового элемента перечислены в главе [Модель компонента → Базовый интерфейс](/ru/guide/smart-components/model/#базовый-интерфейс). - -- `sf-confirm-answer` — `detail`: `{ id: any, accepted: boolean }`. Человек ответил (слово словаря answer). Отмените событие, чтобы вопрос остался открытым. -- `sf-confirm-expired` — `detail`: `{ id: any }`. Открытый вопрос дождался `expires-at`; сообщается один раз на каждый срок. - -#### Доступность - -- Роль: группа (`group`). -- Доступное имя: внутренний. -- Клавиши: `Tab`, `Enter`, `Space`, `Escape`. -- Фокус: Группа, названная своим вопросом. Строка состояния — вежливая живая область, которая есть на странице до того, как заговорит; когда нажатие убирает ответы, фокус переходит на неё, а не падает на страницу, и возвращается к первому ответу, если хост отменил. Escape внутри открытого вопроса — отказ. - -#### Минимальная разметка - -```html - -``` - -#### Источник - -- `simai/ui@e22fefb3c1f8bed4c7ad8d8745a408ecedc5029f:distr/smart/confirm` -- `simai/ui@e22fefb3c1f8bed4c7ad8d8745a408ecedc5029f:distr/rule/rule.json#name=cl-confirm` - -### `` - | | | |:---|:---| | Тег | `` | @@ -180,7 +119,7 @@ card.addEventListener('sf-confirm-change-answer', async (event) => { | [Готовность](/ru/guide/smart-components/quality/#готовность-компонента-к-использованию) | `host-required` | | Заготовка до прихода кода | нет: атрибут `data-sf-skeleton` не пишется; своего скелетона при загрузке данных нет ([Коробка до кода](/ru/guide/smart-components/first-paint/#коробка-до-кода)) | -#### Атрибуты +### Атрибуты - `template` — `string`, по умолчанию `default`, значения: `default`. Имя варианта шаблона для внешних шаблонов SfBaseElement. - `root-class` — `string`, по умолчанию пусто. Классы, которые добавляются корневому элементу, нарисованному компонентом. @@ -195,14 +134,14 @@ card.addEventListener('sf-confirm-change-answer', async (event) => { - `decline-text` — `string`. Слова ответа «нет»; пусто — «Отклонить» на языке страницы. - `changes` — `array` из `object`, по умолчанию `[]`. Значения в JSON для разметки; свойство, заданное из скрипта, важнее. -#### Методы +### Методы - `open()` — возвращает `object`. Задаёт вопрос снова; если его попросила сборка, отвечает `{accepted, reason, value}`, когда человек ответил или время вышло. - `answer(accepted: boolean)` — возвращает `object`. Ответить, как ответил бы человек: `accepted` true подтверждает, false отклоняет. Отказ, если карточка закрыта, выключена или ответ отменён. `answer('accept')` и `answer('decline')` читаются до 2026-12-01. - `confirm()` — возвращает `object`. Согласие; `data-sf-action=".confirm"`. - `cancel()` — возвращает `object`. Отказ; `data-sf-action=".cancel"`. -#### События +### События Все события приходят на сам элемент и всплывают. Что означает глагол в имени события, говорит общий словарь: [Имена и общие слова](/ru/guide/smart-components/names/). Общие события базового элемента перечислены в главе [Модель компонента → Базовый интерфейс](/ru/guide/smart-components/model/#базовый-интерфейс). @@ -214,20 +153,20 @@ card.addEventListener('sf-confirm-change-answer', async (event) => { - `sf-change-confirm-answer` — устаревший псевдоним события `sf-confirm-change-answer`: приходит с тем же `detail`, подписывайтесь на `sf-confirm-change-answer`. - `sf-change-confirm-expired` — устаревший псевдоним события `sf-confirm-change-expired`: приходит с тем же `detail`, подписывайтесь на `sf-confirm-change-expired`. -#### Доступность +### Доступность - Роль: область (`region`). - Доступное имя: внутренний. - Клавиши: `Tab`, `Enter`, `Space`. - Фокус: Раздел, названный заголовком (role=heading уровня heading-level). Значения — список описаний. Строка состояния — вежливая живая область, которая есть на странице до того, как заговорит; когда нажатие убирает ответы, фокус переходит на неё, а не падает на страницу, и возвращается на первый ответ, если хост отменил событие. -#### Минимальная разметка +### Минимальная разметка ```html ``` -#### Источник +### Источник - `simai/ui@e22fefb3c1f8bed4c7ad8d8745a408ecedc5029f:distr/smart/confirm-change` - `simai/ui@e22fefb3c1f8bed4c7ad8d8745a408ecedc5029f:distr/rule/rule.json#name=cl-confirm-change` diff --git a/content/ru/pro/admin/property-view.md b/content/ru/pro/admin/property-view.md index 51176eb7c..6ea8d2e31 100644 --- a/content/ru/pro/admin/property-view.md +++ b/content/ru/pro/admin/property-view.md @@ -5,7 +5,7 @@ description: "Показывает и правит поля одного объ # Представление свойств -`` — карточка одного объекта: поля сгруппированы в области, +`` — карточка одного объекта: поля сгруппированы в области, области — в разделы, а каждое поле рисуется отрисовщиком своего типа. Берите его для записи, настроек сайта или профиля; множество записей показывает [представление данных](/ru/pro/admin/table/). @@ -355,7 +355,7 @@ view: { ### Дата и время Поле `datetime` правится в одной строке двумя элементами: день — в -`sf-datepicker`, время — в `sf-input` с маской чч:мм. Оба размера +`sf-input-date`, время — в `sf-input` с маской чч:мм. Оба размера представления, и у каждого своё доступное имя («Начало: дата», «Начало: время»). Значение — текст хоста в часовом поясе сайта в формате ISO 8601 (день, или день и время после `T` или пробела, с секундами, долями и поясом diff --git a/content/ru/pro/admin/table.md b/content/ru/pro/admin/table.md index c5d24a9da..ddf7cfa74 100644 --- a/content/ru/pro/admin/table.md +++ b/content/ru/pro/admin/table.md @@ -5,11 +5,11 @@ description: "Показывает серверную коллекцию таб # Представление данных -`` показывает серверную коллекцию записей: поиск, фильтры, таблицу, +`` показывает серверную коллекцию записей: поиск, фильтры, таблицу, выбор строк, действия, настройки колонок и пагинацию. Данные, права и выполнение действий остаются за приложением: компонент показывает то, что ему передали, и сообщает о намерениях пользователя событиями. -`` оборачивает таблицу и связывает её с приложением через порт +`` оборачивает таблицу и связывает её с приложением через порт хоста. Оба элемента входят в пакет компонентов `pro`. **Компонент Pro.** Входит в [SIMAI Pro](/ru/pro/), а не в выпуск SIMAI @@ -25,12 +25,12 @@ Framework: поставляется пакетом `pro` из репозитор ### Подключение пакета -Элементы `` и `` поставляет Larena, а не выпуск +Элементы `` и `` поставляет Larena, а не выпуск Framework: пакет компонентов Loader с именем `pro` из репозитория `simai/ui-pro` (лицензия PolyForm Small Business 1.0.0 или PolyForm Noncommercial 1.0.0, по запросу — коммерческая лицензия SIMAI; в составе Larena — лицензия Larena). В пакет входят ещё -[``](/ru/pro/admin/property-view/) и +[``](/ru/pro/admin/property-view/) и [``](/ru/pro/admin/admin-menu/). Объявите пакет до подключения Core: `window.SF_BOOT_CONFIG = {packages: {pro: {base: '/assets/pro/<коммит пакета>/smart'}}}`. @@ -85,15 +85,15 @@ Larena — лицензия Larena). В пакет входят ещё `vertical-scroll="page"` отдаёт прокрутку странице вместо самой таблицы, `context-menu-columns` (от 1 до 4) делит меню настроек на колонки. -У `` свои части перечисляет атрибут `chrome`: `toolbar`, +У `` свои части перечисляет атрибут `chrome`: `toolbar`, `pagination`, `select`, `settings`; без атрибута включены все. Часть `toolbar` — запасная строка поиска самого представления. Если в слот -`content` вставлена своя `` со своей панелью инструментов, +`content` вставлена своя `` со своей панелью инструментов, запасная строка не показывается. Без `toolbar` в `chrome` представление ставит таблице `layout="table"`; список на узком экране сохраняет `layout="auto"`, записанный на самой таблице. -Область представления называет `aria-label` самого ``; без +Область представления называет `aria-label` самого ``; без него — общее «Представление данных». Строка страниц с «Всего» появляется после первого ответа приложения и скрыта, пока список не загрузился, при ошибке и при отказе в доступе. @@ -141,7 +141,7 @@ record_ids}` приходит следом до 2026-12-01. Встроенные `sf-table-toolbar-action` с `{action, href}`, и без `preventDefault()` компонент переходит по `href` того же сайта. -В `` кнопку «Создать» показывает возможность `create-record` +В `` кнопку «Создать» показывает возможность `create-record` адаптера. Отдельного права на создание у человека порт не передаёт: хост объявляет `create-record` в адаптере, который строит для этого человека, а на `view.create_record` без права отвечает `refused`. @@ -152,9 +152,9 @@ record_ids}` приходит следом до 2026-12-01. Встроенные `list` — компактным списком, `auto` — таблицей, пока контейнер таблицы не уже порога `list-below`, и списком, когда уже. Решает ширина контейнера, а не окна: таблица рядом с открытой панелью тоже становится списком. -`` с панелью инструментов задаёт своей таблице `auto`; +`` с панелью инструментов задаёт своей таблице `auto`; встроенный список без панели остаётся таблицей, а `layout`, который -приложение написало на `` само, сохраняется. +приложение написало на `` само, сохраняется. В компактном списке каждая запись — блок: флажок выбора (при `selectable`), заголовок, под ним несколько полей с подписями и меню строки. Заголовок — колонка с @@ -175,7 +175,7 @@ record_ids}` приходит следом до 2026-12-01. Встроенные ### Дерево -Адаптер с возможностью `tree` отвечает деревом, и `` +Адаптер с возможностью `tree` отвечает деревом, и `` показывает записи по уровням. Ответ на запрос несёт `tree: {mode: 'tree', parent: null}` и записи одного уровня — корни; каждая запись сообщает `has_children`. Открытие узла поднимает намерение `tree.expand` с `id` узла, и адаптер @@ -202,8 +202,8 @@ record_ids}` приходит следом до 2026-12-01. Встроенные `view.create_record` уходит с `parent`. Оба действия появляются только у записей, которые перечисляют их в `actions`. -`` дерево только рисует: строки с `__tree` пишет -``. Нажатие на кнопку узла отправляет `sf-table-expand` +`` дерево только рисует: строки с `__tree` пишет +``. Нажатие на кнопку узла отправляет `sf-table-expand` с `{id, expanded}`, строка «Показать ещё» — `sf-table-load-more` с `{direction, parent, cursor}`; на оба отвечает представление. Намерения дерева, которые оно поднимает, приходят событием `sf-data-view-tree` с @@ -247,9 +247,9 @@ record_ids}` приходит следом до 2026-12-01. Встроенные перемещённые идентификаторы, строка, перед которой они встали (`null` — в конце), и весь новый порядок. Если порядок не изменился, события нет. Прежнее имя `sf-table-rows-reorder` приходит следом до 2026-12-01. - Порядок сохраняет приложение; в `` — через порт хоста + Порядок сохраняет приложение; в `` — через порт хоста ([Перестановка строк и запись в ячейке](#перестановка-строк-и-запись-в-ячейке)). -- `accept-files` на `` — на представление можно перетащить файлы +- `accept-files` на `` — на представление можно перетащить файлы с компьютера: пока они над ним, видна зона сброса, после отпускания приходит `sf-data-view-files-drop` с `{files}`. Загружает файлы приложение. @@ -274,7 +274,7 @@ table.setRows([ Нажатие флажка отправляет `sf-table-cell-set` с `{id, key, value, revision}`; пока приложение отвечает, флажок выключен. Таблица сама значение не сохраняет: строку перерисовывает то, что ответило приложение. В -`` запись в ячейке идёт через порт хоста +`` запись в ячейке идёт через порт хоста ([Перестановка строк и запись в ячейке](#перестановка-строк-и-запись-в-ячейке)). Кнопки действий строки по умолчанию видны в строке под указателем, в фокусе @@ -295,7 +295,7 @@ table.setRows([ Уже при первой отрисовке таблица вынимает элемент слота из документа, и поиск по `id` после этого вернёт `null` — в том числе после -`customElements.whenDefined('sf-data-view')`. Поэтому ссылку на блок берите на +`customElements.whenDefined('sf-view')`. Поэтому ссылку на блок берите на верхнем уровне скрипта в конце `body`, до того как придёт код пакета, и меняйте блок по событию `sf-data-view-state`. Таблица записывает причину в свойство `emptyContext` до того, как представление отправляет событие: @@ -311,11 +311,11 @@ table.setRows([ или очищает то, что назвал `reset`. Второй запрос из кода страницы не нужен. ```html - - + + - - + + ``` -Отдельная таблица без `` показывает только «нет записей» и +Отдельная таблица без `` показывает только «нет записей» и ошибку. Подробнее — в [Пустое состояние](/ru/guide/smart-components/data/#пустое-состояние). ### Прокрутка на странице списка -`vertical-scroll` у `` выбирает, что прокручивается вверх и вниз: +`vertical-scroll` у `` выбирает, что прокручивается вверх и вниз: `self` — таблица внутри своей рамки, `page` — сама страница. Представление данных по умолчанию ставит своей таблице `page`, если приложение не выбрало иное. Тогда таблица растёт на высоту своих строк, и на странице списка @@ -355,7 +355,7 @@ table.setRows([ ### Персональный вид -С атрибутом `preferences-key` на `` (обычно это имя +С атрибутом `preferences-key` на `` (обычно это имя коллекции) раскладка таблицы, сортировка и размер страницы хранятся как [персональный вид](/ru/guide/smart-components/view-preferences/) под ключом `sf-table/` и применяются до первого запроса: первый @@ -364,7 +364,7 @@ table.setRows([ данных не показывает. Без ключа ничего не запоминается. Поля значения — `columnSettings`, `widths`, `view`, `sort` и `pageSize` — таблица объявляет в манифесте (`persistence.view_preference`); все пять сервер может положить в -страницу для первой отрисовки. Их описание — в справочнике `` ниже +страницу для первой отрисовки. Их описание — в справочнике `` ниже и в [Персональный вид → Поля значения](/ru/guide/smart-components/view-preferences/#поля-значения). ### Шаблоны и настройки колонок @@ -375,7 +375,7 @@ table.setRows([ `sf-table-column-settings-change` без `kind` приходит следом до 2026-12-01. Отдельная таблица отдаёт их приложению, и оно при следующей загрузке применяет их через -`setTableSettings(settings)`. В `` то же изменение всегда +`setTableSettings(settings)`. В `` то же изменение всегда уходит в личный слой человека — намерением `settings.save_personal` с `base_revision` — ревизией слоя `data.column_settings.layers.personal`, а если хост этого слоя ещё не @@ -406,11 +406,11 @@ table.setRows([ `sf-table-template-select` (`{key}`) и `sf-table-template-delete` (`{key}`, по одному на шаблон). `sf-table-template-save` несёт один шаблон из формы или массив шаблонов, если «Применить» на вкладке «Шаблоны» изменило -несколько (порядок, закрепление, выбор по умолчанию); `` +несколько (порядок, закрепление, выбор по умолчанию); `` сохраняет каждый шаблон с `key` отдельным намерением `view.save`. Общее сохранённое представление записывает код страницы: он отправляет на -`` событие `sf-data-view-saved-view-intent` с `{key, label, +`` событие `sf-data-view-saved-view-intent` с `{key, label, revision, roles}` (`revision` и `roles` необязательны), и представление отправляет хосту `view.save_shared` с текущим запросом, колонками и настройками. Нужна возможность `shared-saved-views` и порт версии 1.1.0 или @@ -425,14 +425,14 @@ view.dispatchEvent(new CustomEvent('sf-data-view-saved-view-intent', { ### Порт хоста -`` получает данные методами и сообщает о намерениях событиями; +`` получает данные методами и сообщает о намерениях событиями; метода `setHostPort()` у него нет. Адаптер приложения подключается к -``: `setHostPort(host)` принимает объект с полями `version` и +``: `setHostPort(host)` принимает объект с полями `version` и `capabilities` и методом `raise(intent, payload, {signal})`, который отвечает `{answer: 'applied' | 'conflict' | 'refused' | 'unavailable', …}`. -`` говорит на порте `simai.dataview-port` версии 1.12.0 и -принимает адаптеры версий от 1.0.0 до 1.12.0; манифест `` записывает -версию порта 1.0.0. Если адаптер не передан, `` один раз +`` говорит на порте `simai.dataview-port` версии 1.12.0 и +принимает адаптеры версий от 1.0.0 до 1.12.0; манифест `` записывает +версию порта 1.0.0. Если адаптер не передан, `` один раз запрашивает его событием `sf-host-port-request`. Возможности хост объявляет в `capabilities`: представление предлагает @@ -476,7 +476,7 @@ const host = { return response.json(); // {answer, reason?, message?, sequence?, data?} }, }; -document.querySelector('sf-data-view').setHostPort(host); +document.querySelector('sf-view').setHostPort(host); ``` Транспорт, сессия и защита от подделки запроса (CSRF) — дело приложения: @@ -562,7 +562,7 @@ document.querySelector('sf-data-view').setHostPort(host); 30 и 40; действующий размер страницы представление добавляет в этот список, поэтому 25 тоже есть в выборе. Свои размеры задаёт своя пагинация в слоте: ``; `page-sizes` на -`` внутри представления не действует. +`` внутри представления не действует. Поиск, фильтр и сортировка возвращают `page` к 1. `reason` называет причину запроса: `search`, `filter`, `sort`, `page-change`, @@ -598,7 +598,7 @@ document.querySelector('sf-data-view').setHostPort(host); Поля фильтра порт не передаёт. Кнопку «Фильтр» таблица показывает, только когда есть по чему фильтровать: страница передала поля фильтра, есть сохранённые шаблоны или фильтр уже применён. Поля задаёт страница: она вставляет свою -`` в слот `content` представления и вызывает у неё +`` в слот `content` представления и вызывает у неё `setFilterFields(fields)` (см. [Поиск и фильтр](#поиск-и-фильтр)); применённый фильтр представление отправляет хосту в `query.filters`. @@ -614,7 +614,7 @@ document.querySelector('sf-data-view').setHostPort(host); называет свой `sequence` и тогда, когда он `refused` или `unavailable`; ответ без него представление считает ответом на текущий запрос. -У порта карточки записи (``) свой формат: запись приходит +У порта карточки записи (``) свой формат: запись приходит в самом ответе, без `data`. Порт описывает стык отдельно от компонента: @@ -656,7 +656,7 @@ document.querySelector('sf-data-view').setHostPort(host); ### Пример: связь с приложением -Пример подключает `` к приложению через порт хоста. Приложение +Пример подключает `` к приложению через порт хоста. Приложение здесь — несколько строк JavaScript с массивом записей и задержкой ответа; на своей странице `raise()` отправляет те же намерения на сервер и возвращает его ответ в том же конверте. Переключатель над таблицей меняет ответ приложения: @@ -755,7 +755,7 @@ document.querySelector('sf-data-view').setHostPort(host); проверяет их на сервере. ```js -const table = document.querySelector('sf-table'); +const table = document.querySelector('sf-view-table'); table.setDataState('loading'); const page = await api.listProjects({sort: table.getSort()}); table.setTableData({columns: page.columns, rows: page.rows}); @@ -770,7 +770,7 @@ table.addEventListener('sf-table-apply', async (event) => { ## Доступность - Таблица — именованная область (`role="region"`): задайте `aria-label`, - который называет коллекцию. У `` своё обязательное имя. + который называет коллекцию. У `` своё обязательное имя. - Все действия только с иконкой и все меню имеют доступные имена. Меню, фильтры и окна настроек закрываются по `Escape` и возвращают фокус на кнопку, которая их открыла. diff --git a/content/ru/pro/chat/chat-window.md b/content/ru/pro/chat/chat-window.md index 7bfbe558e..00a3cb655 100644 --- a/content/ru/pro/chat/chat-window.md +++ b/content/ru/pro/chat/chat-window.md @@ -184,7 +184,7 @@ false`), поэтому ответ уходит своим намерением С возможностью `x-conversation-start` в голове списка две команды: «Новый диалог» и «Новая группа». Форма встаёт на место разговора, на телефоне — на весь экран. Людей -ищет `sf-lookup`; окно спрашивает хоста намерением `recipients.search {query}` +ищет `sf-input-lookup`; окно спрашивает хоста намерением `recipients.search {query}` (событие `query`, `kind: recipients`), ответ — `data: {items: [{id, name}]}`. Себя выбрать нельзя. diff --git a/content/ru/pro/chat/thread-list.md b/content/ru/pro/chat/thread-list.md index 0cbbea7d7..0b8837249 100644 --- a/content/ru/pro/chat/thread-list.md +++ b/content/ru/pro/chat/thread-list.md @@ -5,7 +5,7 @@ description: "Разговоры человека, новые сверху: гр # Список разговоров -`` — разговоры человека, новые сверху: с ассистентом или +`` — разговоры человека, новые сверху: с ассистентом или между людьми. Он группирует их по дню последнего изменения, отмечает открытый, даёт переименовать разговор на месте, отправить его в архив и просит следующую страницу, когда показался конец списка. Сам список ничего не @@ -79,7 +79,7 @@ Framework: поставляется пакетом `pro` из репозитор - `sf-thread-list-view` — `{archived}`, переход в архив и обратно. ```js -const list = document.querySelector('sf-thread-list'); +const list = document.querySelector('sf-chat-threads'); list.setThreads(await api.threads(), {hasMore: true}); list.addEventListener('sf-thread-list-select', (event) => { openConversation(event.detail.id); diff --git a/content/ru/pro/editor/blocks.md b/content/ru/pro/editor/blocks.md index b33551e42..53f68e9d8 100644 --- a/content/ru/pro/editor/blocks.md +++ b/content/ru/pro/editor/blocks.md @@ -296,7 +296,7 @@ HTML рендерера — это и предпросмотр блока в р - `validate` сообщает `type_unknown` в этом узле, документ недействителен, и `render` не рисует ничего: страница никогда не рисуется наполовину; -- редактор содержимого `sf-content-editor` ([Редактор содержимого](/ru/pro/editor/content-editor/)) +- редактор содержимого `sf-editor-content` ([Редактор содержимого](/ru/pro/editor/content-editor/)) показывает неизвестный узел заглушкой с именем типа, сохраняет его данные и слоты байт в байт и только перемещает или удаляет его целиком; редактор текста так же поступает с незнакомым встроенным элементом; diff --git a/content/ru/pro/editor/content-editor.md b/content/ru/pro/editor/content-editor.md index d6e4f56e1..e92942d48 100644 --- a/content/ru/pro/editor/content-editor.md +++ b/content/ru/pro/editor/content-editor.md @@ -5,7 +5,7 @@ description: "Страница набирается одним потоком: # Редактор содержимого -`` — режим «Содержимое» редактора страниц SIMAI: страница +`` — режим «Содержимое» редактора страниц SIMAI: страница набирается одним потоком, как в обычном текстовом редакторе. Элемент ничего не хранит: он правит свою копию документа и после каждого изменения отправляет событие `change` с новым документом, а сохраняет приложение. @@ -85,7 +85,7 @@ editor.addEventListener('change', (event) => save(event.detail.document)); (английское имя вида находит его на любом языке: `/div`, `/image`), стрелки выбирают, Enter применяет, Esc закрывает. Строка с «/» сама становится выбранным блоком. -- Выделение текста показывает панель [`sf-inline-editor`](/ru/smart/editor/inline-editor/): +- Выделение текста показывает панель [`sf-editor-inline`](/ru/smart/editor/inline-editor/): жирный, курсив, код, ссылка (⌘B, ⌘I, ⌘E, ⌘K). Пока курсор просто стоит, панели нет. - ↑ и ↓ на первой и последней строке переходят в соседнее поле; ⌥↑ ⌥↓ @@ -156,7 +156,7 @@ Esc в поле выбирает его блок. Стрелки выбираю Блок «Файл» (`content.file`, с 1.2.0) — такая же зона для файла любого типа. Любой файл, кроме картинки, брошенный или вставленный на страницу, становится -блоком файла. Он рисуется строкой `sf-download-file`: значок по типу файла, +блоком файла. Он рисуется строкой `sf-file-download`: значок по типу файла, название и размер. Название набирается прямо в блоке, адрес — в параметрах. Пустой блок файла, как и пустая картинка, не проходит проверку документа: приложение показывает его заглушкой, пока файл не придёт. diff --git a/content/ru/pro/editor/editor.md b/content/ru/pro/editor/editor.md new file mode 100644 index 000000000..4c6a613fb --- /dev/null +++ b/content/ru/pro/editor/editor.md @@ -0,0 +1,214 @@ +--- +title: "Редактор страниц" +description: "Единый редактор блоков страницы: раскладка и текст — один документ, глубина вместо режимов." +--- + +# Редактор страниц + +`` — единый редактор блоков страницы SIMAI: раскладка и текст — +один документ и один редактор. Режимов нет, есть глубина: щелчок по тексту — +печать, щелчок рядом — выбор блока, `Esc` — уровень вверх, `Enter` — вниз. + +Элемент ничего не хранит: после каждого изменения он отправляет `change` с +новым документом, а сохраняет хост. Режим одного потока текста — отдельный +компонент [Редактор содержимого](/ru/pro/editor/content-editor/); свои блоки +для каталога редактора описывает страница [Свои блоки](/ru/pro/editor/blocks/). + +## Данные + +```js +editor.document = doc; // Composition Document, профиль ui-layout +editor.types = [cardsType]; // свои типы блоков хоста +editor.renderers = {'site.cards': renderCards}; +editor.editorManifests = [cardsEditor]; +editor.siteBlocks = savedBlocks; // блоки сайта из конструктора +editor.words = {ru: {...}, en: {...}}; +editor.rights = {layout: true, template: false, builder: true}; +editor.theme = {name: 'brand', label: 'Фирменная', settings_url: '/admin/theme', defaults: {}}; +editor.addEventListener('change', (event) => save(event.detail.document, event.detail.scope)); +editor.addEventListener('sf-editor-block-save', (event) => keepBlock(event.detail.manifest, event.detail.words)); +editor.connectPort({sources, fields, preview, saveBlock}); +``` + +Страница с шаблоном сайта — `layout.regions` с областями `banner`, `main` и +`contentinfo`. Шапка и подвал — шаблон: они приглушены с замком и меняются +только явно, кнопкой «Изменить шаблон» (`edit-template`). `change` с +`scope: 'template'` — изменение шапки или подвала всех страниц. + +## Панель и места хоста + +Сверху одна строка: отмена и повтор, переключатель экранов по центру, +справка, результат и тема — кнопками со значком. Свои элементы хоста +(переключатель списка страниц, языка) ставятся в ту же строку через места +(slots) — второй строки у хоста нет: + +```html + + + + +``` + +- `start` — в начале строки, перед отменой и повтором; +- `actions` — в конце строки, перед справкой, результатом и темой. + +Элементы мест забираются при первом подключении элемента (как у +`sf-header-page`); добавленные позже ставятся через `setSlot(name, element)`. +Размер элементов в строке — `1/2`. + +Название страницы и подпись под ним хост передаёт настройками `heading` и +`caption` — они стоят в начале строки, как в прототипе: + +```html + +``` + +«Изменить шаблон» — тихая текстовая ссылка рядом, без значка. + +Библиотека слева — разделы SIMAI Framework со значком и числом блоков +(`CATALOGUE_GROUPS`, `CATALOGUE_GROUP_TABLE`), избранное (пустое — с короткой подсказкой) и недавние сверху. +Раздел открывается на месте, с «Назад»; найденное поиском подписано +разделом. Варианты блока — карточки с картинкой из дерева варианта. + +Страница — лист с тенью на спокойном фоне. Выбранный блок обведён, над ним — +плавающая панель: путь, ручка, вверх, вниз, копия, «Ещё». Панель справа — +значок, имя и раздел блока, вкладки «Параметры», «JSON», «HTML», поля в +группах «Содержимое», «Расположение», «Дополнительно»; у каждого поля — +точка происхождения значения, «Сбросить» и привязка SIMAI Framework мелким +моноширинным шрифтом. + +## Права и тема + +Права приходят от хоста, переключателя ролей в редакторе нет. +`layout: false` — автор пишет только внутри текстовых областей +(`layout.area`), остальное приглушено с замком и объяснением. +`reasons` заменяют объяснения замка словами хоста. + +Тема только читается: её значения по умолчанию — один из уровней +происхождения значения, её атрибуты ставятся на нарисованную страницу. +`settings_url` ведёт туда, где тему меняют. + +## Вставка, перемещение, колонки + +Один каталог (вкладки «Текст» и «Блоки», группы, варианты с картинками, +поиск, избранное, недавние) открывается тремя путями: «+» между блоками, «/» +в пустой строке и библиотека слева (перетаскиванием или нажатием). Вкладка +выбирается по месту; вкладка, где ничего не подходит, скрыта. + +Блок двигают за ручку с линией места, Alt+↑↓ у выбранного блока и +Alt+Shift+↑↓ во время набора. Сброс к боковому краю соседнего блока делит +место на колонки; «Разделить на колонки» есть в меню ручки и в панели. + +## Параметры и данные + +Панель справа показывает у каждого значения, откуда оно: тип, тема, вариант, +блок, экран. Переключатель экранов (компьютер, планшет, телефон) задаёт, для +какого экрана пишется значение, и показывает страницу на этом экране. +Вкладка «Данные» связывает блок с источником хоста через порт источников: +источник, сопоставление полей, сколько, текст на пустой случай, состояние +предпросмотра (данные, пусто, ошибка, загрузка) и связь с блоком той же +секции. Блок сам ничего не загружает. + +## Конструктор блоков + +«Создать свой блок» или «Сделать блоком сайта» открывает конструктор: блок +собирается из тех же частей, для автора открываются выбранные параметры, +блок сохраняется как блок сайта (`sf-editor-block-save`). Изменение блока +сайта сохраняется новой версией и переносит все его экземпляры на странице +(`upgradeComposed`), сохраняя тексты и значения автора. + +## Результат + +«Результат» показывает страницу как JSON, как HTML с классами утилит и как +Markdown текстовых областей; то же даёт действие `output`. + +## Клавиши + +| Где | Клавиша | Что делает | +|:---|:---|:---| +| страница | Enter / Esc | вниз / вверх по глубине | +| страница | ↑ ↓ | соседний блок | +| страница | Alt+↑↓ | переместить блок | +| страница | ⌫, ⌘D | удалить, дублировать | +| текст | Enter, ⌫ в начале | разделить, соединить | +| текст | / | каталог | +| текст | Alt+Shift+↑↓ | переместить блок, не прерывая набор | +| везде | ⌘Z, ⇧⌘Z | одна история для текста и структуры | + +## Справочник API + +Раздел собран из манифеста компонента выпуска `ui-e22fefb3c1f8` и обновляется вместе с выпуском. Чтобы исправить описание, исправьте манифест. + +Каждому атрибуту соответствует свойство элемента с тем же именем в camelCase: `search-placeholder` — `searchPlaceholder`. Свойство атрибута `template` называется `templateName`. + +| | | +|:---|:---| +| Тег | `` | +| Назначение | Единый редактор блоков страницы SIMAI: раскладка и текст в одном Composition Document, правка по глубине. Щелчок по тексту — печать, щелчок рядом — выбор блока, Esc — вверх, Enter — вниз. Один каталог («Текст» и «Блоки», группы, варианты с картинками, поиск, избранное, недавние) служит «+» между блоками, «/» в тексте и библиотеке; блоки двигаются за ручку с линиями места или Alt+стрелками, а сброс рядом с блоком делит место на колонки. Панель параметров говорит, откуда каждое значение (тип, тема, вариант, блок, экран), и задаёт значения по экранам; вкладка «Данные» связывает блок с источником хоста через порт источников. Конструктор блоков создаёт и меняет блоки сайта везде; шаблон сайта — отдельная явная правка. Права и тема приходят от хоста. Одна история отмены; результат — JSON, HTML и Markdown. | +| Контракт | `smart.editor`, версия `1.1.0` | +| Поставка | пакет `pro` (`SF_BOOT_CONFIG.packages.pro`) | +| [Жизненный цикл](/ru/guide/smart-components/quality/#готовность-компонента-к-использованию) | экспериментальный: API может меняться между выпусками | +| [Готовность](/ru/guide/smart-components/quality/#готовность-компонента-к-использованию) | `host-required` | + +### Атрибуты + +- `template` — `string`, по умолчанию `default`. Имя варианта шаблона для внешних шаблонов SfBaseElement. +- `root-class` — `string`, по умолчанию пусто. Классы, которые добавляются корневому элементу, нарисованному компонентом. +- `root-style` — `string`, по умолчанию пусто. Объявления стиля, которые ставятся корневому элементу, нарисованному компонентом, в том числе пользовательские свойства. +- `screen` — `string`, по умолчанию `desktop`, значения: `desktop`, `tablet`, `phone`. Экран, для которого рисуется страница и задаются значения: desktop (lg), tablet (md), phone (xs). +- `preferences-key` — `string`, по умолчанию пусто. Где хранятся избранные и недавние блоки через SF.Preferences (sf-editor/<key>); пусто — только на время сеанса. +- `heading` — `string`, по умолчанию пусто. Название редактируемой страницы в начале верхней строки (имя страницы у хоста). +- `caption` — `string`, по умолчанию пусто. Короткая строка под названием: где живёт страница или её состояние (слова хоста). +- `label` — `string`. Доступное имя страницы. +- `document` — `object`, по умолчанию `null`. Страница: Composition Document профиля ui-layout (layout.page или layout.regions с областями banner, main и contentinfo для страницы с шаблоном сайта). Новый документ начинает новую правку; документ, который редактор сам отправил в change, узнаётся и сохраняется. +- `types` — `array`, по умолчанию `[]`. Свои типы блоков хоста рядом с типами SIMAI Framework; их отрисовщики приходят в renderers. +- `renderers` — `object`, по умолчанию `null`. Отрисовщики типов хоста: страница рисуется ими так, как её рисует сайт. +- `editor-manifests` — `array` из `object`, по умолчанию `[]`. Манифесты редактора композиции для типов хоста или заменяющие манифесты SIMAI Framework: по ним строится панель параметров. +- `site-blocks` — `array`, по умолчанию `[]`. Собственные блоки сайта из конструктора блоков; в каталоге предлагается новейшая версия каждого. +- `words` — `object`, по умолчанию `null`. Слова блоков хоста по локалям. +- `rights` — `object`, по умолчанию `null`. Что человеку можно менять, по данным хоста: layout false позволяет автору писать только внутри текстовых областей; template false держит шаблон сайта закрытым; builder false скрывает конструктор блоков. Переключателя ролей в редакторе нет. +- `theme` — `object`, по умолчанию `null`. Тема сайта: её значения по умолчанию — один из уровней происхождения значения, её атрибуты ставятся на нарисованную страницу. Редактор её никогда не меняет; settings_url ведёт туда, где её меняют. + +### Методы + +- `select(id: any)` — возвращает `object`. Выбирает блок так же, как нажатие; без id выбор снимается. +- `edit(id: any)` — возвращает `object`. Ставит курсор в конец текстового блока. +- `insert(type: string, variant: any, after: any)` — возвращает `object`. Добавляет блок типа (и варианта) после блока или туда, где он первым подходит; значение — id нового блока. +- `undo()` — возвращает `object`. Шаг назад, как ⌘Z: набор текста и структура — одна история. +- `redo()` — возвращает `object`. Шаг вперёд, как ⇧⌘Z. +- `setParameter(id: any, key: string, value: string, screen: any)` — возвращает `object`. Задаёт параметр блока, для экрана — если значение различается по экранам. +- `editTemplate(on: boolean)` — возвращает `object`. Начинает (или, при false, заканчивает) правку шаблона сайта: шапки и подвала всех страниц. +- `buildBlock(id: any, type: string)` — возвращает `object`. Открывает конструктор блоков: новый блок сайта, блок из выбранного блока или блок сайта, который нужно изменить везде. +- `output(format: any)` — возвращает `object`. Страница как JSON, HTML (классы утилит) или Markdown её текстовых областей; значение — текст. + +### События + +Все события приходят на сам элемент и всплывают. Что означает глагол в имени события, говорит общий словарь: [Имена и общие слова](/ru/guide/smart-components/names/). Общие события базового элемента перечислены в главе [Модель компонента → Базовый интерфейс](/ru/guide/smart-components/model/#базовый-интерфейс). + +- `change` — `detail`: `{ document: object, reason: string, scope: any }`. Страница изменилась: {document, reason, scope}. Хост сохраняет документ; scope template означает шапку или подвал всех страниц. +- `sf-editor-select` — `detail`: `{ ids: array }`. Выбран блок: {ids}; пусто, если не выбран ни один. +- `sf-editor-block-save` — `detail`: `{ manifest: object, words: object, changes: array }`. Сохранён блок сайта: {manifest, words, changes}. Сохраните манифест в site-blocks. + +### Доступность + +- Роль: группа (`group`). +- Доступное имя: внутренний. +- Клавиши: `Tab`, `Enter`, `Escape`, `ArrowUp`, `ArrowDown`, `Alt+ArrowUp`, `Alt+ArrowDown`, `Meta+Z`. +- Фокус: Страница — одна остановка Tab; Enter и Esc двигают по глубине, стрелки — по соседям; панели — элементы управления SIMAI Framework в порядке Tab; изменения сообщаются в строке состояния. + +### Минимальная разметка + +```html + +``` + +### Источник + +- `simai/ui-pro@0516a01732c81983bc88a0db311c876c21666679:components/src/smart/editor` +- `simai/ui-pro@0516a01732c81983bc88a0db311c876c21666679:components/src/smart/editor/rule.js#name=cl-editor` + +## См. также + +- [Редактор содержимого](/ru/pro/editor/content-editor/) — страница одним потоком текста. +- [Свои блоки](/ru/pro/editor/blocks/) — как сделать свой блок для каталога редактора. +- [Редактор раскладки](/ru/guide/layouts/editor/overview/) — части, из которых собран редактор. diff --git a/content/ru/pro/editor/editor.page.json b/content/ru/pro/editor/editor.page.json new file mode 100644 index 000000000..c005d68a2 --- /dev/null +++ b/content/ru/pro/editor/editor.page.json @@ -0,0 +1,6 @@ +{ + "schema": "docara.page.v1", + "navigation": { + "order": 5 + } +} diff --git a/content/ru/pro/editor/index.md b/content/ru/pro/editor/index.md index fcdf6fa96..3152214a4 100644 --- a/content/ru/pro/editor/index.md +++ b/content/ru/pro/editor/index.md @@ -1,10 +1,11 @@ --- title: "Редактор страниц" -description: "Редактор страниц — 1 компонент SIMAI Pro." +description: "Редактор страниц — 2 компонента SIMAI Pro." profile: reference --- # Редактор страниц +- [Редактор страниц](/ru/pro/editor/editor/) — Единый редактор блоков страницы: раскладка и текст — один документ, глубина вместо режимов. - [Редактор содержимого](/ru/pro/editor/content-editor/) — Страница набирается одним потоком: блоки, меню «/» и «+», параметры блока; документ сохраняет приложение. - [Свои блоки](/ru/pro/editor/blocks/) — как сделать свой блок для каталога редактора: шаблон, генератор и проверка. diff --git a/content/ru/pro/index.md b/content/ru/pro/index.md index d1eac4983..4c8a5f819 100644 --- a/content/ru/pro/index.md +++ b/content/ru/pro/index.md @@ -5,6 +5,11 @@ description: "Компоненты SIMAI Pro: админка, чат, письм # Pro +> Компоненты Pro переходят в [наборы компонентов](/ru/guide/sets/): `admin`, +> `view`, `chat`, `mail`, `editor`. У каждого набора будет свой сайт с +> документацией и демонстрациями. Пока сайта набора нет, его компоненты +> описаны здесь, а пакет `pro` подключает их все сразу. + SIMAI Pro — большие смарт-компоненты для продуктов SIMAI: админка, чат, конструктор писем и редактор страниц. Они построены на SIMAI Framework и устроены так же, как его смарт-компоненты, но поставляются отдельно и под @@ -70,5 +75,6 @@ SIMAI Framework распространяется по лицензии Apache-2. Редактор содержимого страниц блоками: меню «/», параметры блока, картинки и файлы. +- [Редактор страниц](/ru/pro/editor/editor/) - [Редактор содержимого](/ru/pro/editor/content-editor/) - [Свои блоки](/ru/pro/editor/blocks/) — как сделать свой блок для каталога редактора: шаблон, генератор и проверка. diff --git a/content/ru/pro/mail/mail-canvas.md b/content/ru/pro/mail/mail-canvas.md index b9eb0cece..e193e5525 100644 --- a/content/ru/pro/mail/mail-canvas.md +++ b/content/ru/pro/mail/mail-canvas.md @@ -7,7 +7,7 @@ description: "Готовое письмо в рамке, которая ниче `` — холст редактора писем: готовое письмо в рамке, которая ничего не запускает, переключатель ширины над ним, две заметки о письме («устарел» и -«тяжёлое») и `sf-composition-overlay` сверху — рамки, панель действий и дерево для +«тяжёлое») и `sf-editor-overlay` сверху — рамки, панель действий и дерево для клавиатуры. Элемент не знает письма и рендерера: приложение передаёт отрисованный HTML и данные о нём, а элемент сообщает, что сделал человек. @@ -74,7 +74,7 @@ canvas.html = renderedLetter; | `stale` | заметка «предпросмотр устарел»; картинка остаётся прежней | | `weight` | `{bytes, max}` в килобайтах — предупреждение о весе; пусто — заметки нет | | `items` | что есть в письме для оверлея: `{id, label, kind, parent, editable, rect}` | -| `actions` | панель действий выбранного блока: `{id, label, icon, shortcut, disabled, tone}`, до 16 (см. документацию `sf-composition-overlay`) | +| `actions` | панель действий выбранного блока: `{id, label, icon, shortcut, disabled, tone}`, до 16 (см. документацию `sf-editor-overlay`) | | `accepts` | группы `sf-sortable`, которые можно бросать на письмо, через пробел | | `overlay-target` | `id` оверлея, поставленного хостом в другом месте страницы | @@ -146,7 +146,7 @@ canvas.html = renderedLetter; Свой оверлей элемента лежит внутри прокручиваемой области, перед рамкой: рамки и панель едут вместе с письмом. Над письмом в области есть отступ для панели, поэтому она стоит над блоком и у первого блока. `contain: layout` у предка ему не мешает -(simai/ui-source#58 закрыт в `sf-composition-overlay` 1.2.5). +(simai/ui-source#58 закрыт в `sf-editor-overlay` 1.2.5). Оверлей можно поставить и в другом месте страницы, но вне области он следует за прокруткой скриптом, с задержкой в кадр. Поэтому `overlay-target` нужен, только если @@ -157,7 +157,7 @@ canvas.html = renderedLetter;
- +
``` @@ -169,7 +169,7 @@ canvas.html = renderedLetter; ## Доступность Сначала переключатель ширины, затем дерево оверлея; рамка и ссылки письма в порядок -Tab не входят. Клавиши дерева и панели — у `sf-composition-overlay` (см. его +Tab не входят. Клавиши дерева и панели — у `sf-editor-overlay` (см. его документацию): стрелки, Home и End, Enter к панели или правке, Escape. После щелчка по письму те же клавиши работают и внутри рамки. Заметка о весе — строка состояния. У рамки и слоя есть имена на языке страницы. diff --git a/content/ru/pro/mail/mail-inspector.md b/content/ru/pro/mail/mail-inspector.md index 19c22b41c..33c5ef75b 100644 --- a/content/ru/pro/mail/mail-inspector.md +++ b/content/ru/pro/mail/mail-inspector.md @@ -69,13 +69,13 @@ panel.sections = [ | `kind` | Что это | Своё | |:---|:---|:---| | `text` | `sf-input` | `inputmode`, `inheritedValue`, `ariaLabel` | -| `multiline` | `sf-textarea`, растёт от двух строк до двенадцати | `inheritedValue`, `ariaLabel` | +| `multiline` | `sf-input-textarea`, растёт от двух строк до двенадцати | `inheritedValue`, `ariaLabel` | | `link` | `sf-input` для адреса | как у `text` | | `number` | `sf-input` с цифровой клавиатурой | как у `text`; `inputmode: 'decimal'` для цены | | `select` | `sf-dropdown` | `options: [{value, text}]`; `value: ''` — выбор «ничего» | | `switch` | `sf-switch` | `value` — логическое | | `group` | поля под одним именем, цена например | `rows: [[поле, поле], [поле]]`; ряд из двух стоит парой | -| `image` | `sf-image-field` | `value: {value, label, mime, size, href}` или `null`, `accept`, `emptyText`, `ariaLabel`, `choices: [{value, text}]`, `chooseLabel`, `choosePlaceholder` | +| `image` | `sf-input-image` | `value: {value, label, mime, size, href}` или `null`, `accept`, `emptyText`, `ariaLabel`, `choices: [{value, text}]`, `chooseLabel`, `choosePlaceholder` | | `items` | список на `sf-sortable` | `items: [{label, problem, fields}]`, `open`, `min`, `max` | `hint` — настоящий текст под полем: так значение из бренда слышит и читатель diff --git a/content/ru/pro/mail/mail-letters.md b/content/ru/pro/mail/mail-letters.md index b9b8c63e8..9000f2678 100644 --- a/content/ru/pro/mail/mail-letters.md +++ b/content/ru/pro/mail/mail-letters.md @@ -98,7 +98,7 @@ list.addEventListener('sf-mail-letters-delete', async (event) => { - Порядок `Tab`: сначала панель — поиск, порядок, «Новое письмо», — затем у каждой строки «Открыть» и ⋯. -- Меню строки — `sf-context-menu` со своей клавиатурой. +- Меню строки — `sf-menu-context` со своей клавиатурой. - Имя кнопки ⋯ называет письмо: «Действия с письмом: <название>», поэтому две строки не звучат одинаково. diff --git a/content/ru/section.json b/content/ru/section.json index 7815c4ec2..1191de682 100644 --- a/content/ru/section.json +++ b/content/ru/section.json @@ -33,6 +33,11 @@ "label": "Смарт-компоненты", "href": "/ru/smart/" }, + { + "id": "sets", + "label": "Наборы", + "href": "/ru/guide/sets/" + }, { "id": "pro", "label": "Pro", diff --git a/content/ru/smart/actions/close.md b/content/ru/smart/actions/close.md index 2c98f5f56..8f2eabdd9 100644 --- a/content/ru/smart/actions/close.md +++ b/content/ru/smart/actions/close.md @@ -5,13 +5,13 @@ description: "Рисует знак закрытия внутри кнопки, # Закрытие -`` рисует только знак закрытия — крестик нужного размера. Это не +`` рисует только знак закрытия — крестик нужного размера. Это не кнопка: её даёт нативный ` ``` diff --git a/content/ru/smart/actions/confirm.md b/content/ru/smart/actions/confirm.md new file mode 100644 index 000000000..888ef51f1 --- /dev/null +++ b/content/ru/smart/actions/confirm.md @@ -0,0 +1,134 @@ +--- +title: "Подтверждение" +description: "Вопрос человеку прямо на месте: что случится, два ответа, ожидание, итог и срок." +--- + +# Подтверждение + +`` задаёт человеку вопрос там, где он работает: удалить файл в его +строке, убрать участника, вернуть вид раздела. Компонент показывает, что +случится, два ответа, ожидание, пока ответ применяется, и итог. Сам он ничего +не применяет: нажатие — это событие, а действие выполняет страница. + +Окно поверх страницы для такого вопроса не нужно: вопрос стоит в потоке, рядом +с тем, о чём спрашивает. Если нужно показать, каким станет каждое значение, +берите [Подтверждение изменения](/ru/pro/admin/change-confirm/). + +## Пример + +:::example {id="smart-components/confirm/overview" label="Удаление в строке"} +::: + +## Разметка + +```html + +``` + +`question` — вопрос целым предложением: он же имя группы ответов. Без него +группа называется «Подтверждение». `confirm-id` — имя вопроса у страницы +(строка, запись); оно приходит в каждом событии. + +```js +confirm.addEventListener('sf-confirm-answer', (event) => { + if (!event.detail.accepted) return; + removeFile(event.detail.id) + .then(() => { confirm.state = 'accepted'; }) + .catch(() => { event.target.state = 'open'; }); +}); +``` + +Нажатый ответ переводит вопрос в `pending`. Страница ставит итог — +`accepted` или `declined`, а если применить не удалось, возвращает `open`. +Отменённое событие `sf-confirm-answer` оставляет вопрос открытым. + +## Варианты + +### В карточке + +`appearance="card"` ставит вопрос в рамку со своим отступом. Пояснение — +в месте `description`. + +:::example {id="smart-components/confirm/card" label="В карточке, с пояснением"} +::: + +### Ожидание, итог и срок + +`state` показывает, где вопрос: `open` — ответы предложены, `pending` — ответ +отправлен, `accepted`, `declined`, `expired` — виден только итог. +`expires-at` (ISO 8601) задаёт срок: после него открытый вопрос читается как +истёкший и один раз сообщает об этом событием `sf-confirm-expired`. + +:::example {id="smart-components/confirm/states" label="Ожидание, итог, время вышло"} +::: + +## Доступность + +- Вопрос — группа, названная самим вопросом. Строка состояния — вежливая + живая область: она есть на странице до того, как заговорит. +- Когда нажатие убирает ответы, фокус переходит на строку состояния, а не + падает на страницу; если страница вернула вопрос в `open`, фокус + возвращается к первому ответу. +- `Tab` доходит до ответов, `Enter` и `Space` отвечают, `Escape` внутри + открытого вопроса — тот же ответ, что «Отмена». +- `destructive` красит принимающий ответ цветом ошибки: так видно, что + действие что-то уничтожает. + +## Справочник + +Собран из манифеста компонента выпуска `ui-e22fefb3c1f8` и обновляется вместе с выпуском. Чтобы исправить описание, исправьте манифест. + +### Атрибуты + +| Атрибут | Тип | По умолчанию | Значения | Что делает | +|:---|:---|:---|:---|:---| +| `template` | строка | `default` | — | Имя варианта шаблона для внешних шаблонов SfBaseElement. | +| `root-class` | строка | пусто | — | Классы, которые добавляются корневому элементу, нарисованному компонентом. | +| `root-style` | строка | пусто | — | Объявления стиля, которые ставятся корневому элементу, нарисованному компонентом, в том числе пользовательские свойства. | +| `question` | строка | пусто | — | Вопрос, сказанный на месте: что произойдёт, одним предложением («Удалить файл «Отчёт.pdf»?»). Он же называет группу; без него группа называется «Подтверждение». | +| `state` | строка | `open` | `open`, `pending`, `accepted`, `declined`, `expired` | open: ответы предложены. pending: ответ отправляется (ставится нажатием). accepted, declined, expired: виден только итог. | +| `expires-at` | строка | пусто | — | ISO 8601. После этого срока открытый вопрос считается истёкшим и один раз сообщает об этом событием `sf-confirm-expired`. | +| `confirm-id` | строка | пусто | — | Имя вопроса у хоста (строка, запись); возвращается в каждом событии. | +| `disabled` | да/нет | нет | — | Ответы видны, но их нельзя нажать (где-то уже идёт отправка). | +| `destructive` | да/нет | нет | — | Согласие что-то уничтожает (удаление): у принимающего ответа статус error. | +| `appearance` | строка | `inline` | `inline`, `card` | inline: вопрос и ответы в потоке строки, без своей рамки. card: в карточке с рамкой и собственным отступом. | +| `accept-text` | строка | — | — | Слова ответа «да»; пусто — «Подтвердить» на языке страницы. | +| `decline-text` | строка | — | — | Отказывающий ответ; пусто — «Отмена» на языке страницы. | + +Каждому атрибуту соответствует свойство элемента с тем же именем в camelCase: `icon-start` — `iconStart`. Свойство атрибута `template` называется `templateName`. + +### Методы + +| Метод | Возвращает | Что делает | +|:---|:---|:---| +| `open()` | объект | Задаёт вопрос снова; если его попросила сборка, отвечает `{accepted, reason, value}`, когда человек ответил или время вышло. | +| `answer(accepted: boolean)` | объект | Отвечает, как ответил бы человек: `accepted` true подтверждает, false отказывается. Отказ: `unknown`, `closed`, `disabled` или `prevented`. | +| `confirm()` | объект | Согласие; `data-sf-action=".confirm"`. | +| `cancel()` | объект | Отказывающий ответ; `data-sf-action=".cancel"`. Escape внутри открытого вопроса — тот же ответ. | + +### События + +События приходят на сам элемент и всплывают; глаголы в именах — из [общего словаря](/ru/guide/smart-components/names/). Общие события базового элемента — в главе [Модель компонента](/ru/guide/smart-components/model/#базовый-интерфейс). + +| Событие | `detail` | Когда приходит | +|:---|:---|:---| +| `sf-confirm-answer` | `{ id: any, accepted: boolean }` | Человек ответил (слово словаря answer). Отмените событие, чтобы вопрос остался открытым. | +| `sf-confirm-expired` | `{ id: any }` | Открытый вопрос дождался `expires-at`; сообщается один раз на каждый срок. | + +### О компоненте + +| | | +|:---|:---| +| Тег | ``, контракт `smart.confirm` версии `1.0.0` | +| Поставка | выпуск Framework `ui-e22fefb3c1f8` | +| [Готовность](/ru/guide/smart-components/quality/#готовность-компонента-к-использованию) | экспериментальный: API может меняться между выпусками; `host-required` | +| До прихода кода | нет: атрибут `data-sf-skeleton` не пишется; своего скелетона при загрузке данных нет ([Коробка до кода](/ru/guide/smart-components/first-paint/#коробка-до-кода)) | +| Доступность | роль группа (`group`); доступное имя внутренний; клавиши `Tab`, `Enter`, `Space`, `Escape` | +| Источник | `simai/ui@e22fefb3c1f8bed4c7ad8d8745a408ecedc5029f:distr/smart/confirm` | +| Правило классов | `simai/ui@e22fefb3c1f8bed4c7ad8d8745a408ecedc5029f:distr/rule/rule.json#name=cl-confirm` | + +## См. также + +- [Подтверждение изменения](/ru/pro/admin/change-confirm/) — как было и как станет для каждого значения. +- [Модальное окно](/ru/smart/overlays/modal/) — когда вопрос должен остановить всю страницу. diff --git a/content/ru/smart/actions/confirm.page.json b/content/ru/smart/actions/confirm.page.json new file mode 100644 index 000000000..656a0614d --- /dev/null +++ b/content/ru/smart/actions/confirm.page.json @@ -0,0 +1,6 @@ +{ + "schema": "docara.page.v1", + "navigation": { + "order": 35 + } +} diff --git a/content/ru/smart/actions/copy-button.md b/content/ru/smart/actions/copy-button.md index e7825fd4f..b8fffa80f 100644 --- a/content/ru/smart/actions/copy-button.md +++ b/content/ru/smart/actions/copy-button.md @@ -5,7 +5,7 @@ description: "Копирует значение или текст элемент # Кнопка копирования -`` копирует значение и сообщает о результате: значок на миг +`` копирует значение и сообщает о результате: значок на миг становится галочкой, а «Скопировано» вежливо объявляется для чтения с экрана. Берите его, когда копируется содержимое другого элемента (`target`), нужно сообщение о результате и запасной путь там, где буфер обмена недоступен. @@ -24,8 +24,8 @@ description: "Копирует значение или текст элемент ```html sk_test_51H… - - + + ``` Источник задаёт один из двух атрибутов: diff --git a/content/ru/smart/actions/download-file.md b/content/ru/smart/actions/download-file.md index fa6439892..d7c7c5131 100644 --- a/content/ru/smart/actions/download-file.md +++ b/content/ru/smart/actions/download-file.md @@ -5,7 +5,7 @@ description: "Показывает файл для скачивания с им # Скачивание файла -`` показывает файл, который можно скачать: иконку, имя и +`` показывает файл, который можно скачать: иконку, имя и размер. С адресом он становится нативной ссылкой, без адреса — кнопкой, по которой приложение само готовит файл. @@ -22,8 +22,8 @@ description: "Показывает файл для скачивания с им Готовый файл — ссылка с адресом, именем и размером: ```html - + ``` `href` с адресом файла рисует ссылку. С `download` браузер скачивает файл под diff --git a/content/ru/smart/actions/icon-buttons.md b/content/ru/smart/actions/icon-buttons.md index 03e5852f9..09a3ef695 100644 --- a/content/ru/smart/actions/icon-buttons.md +++ b/content/ru/smart/actions/icon-buttons.md @@ -5,7 +5,7 @@ description: "Запускает действие компактной кноп # Кнопки с иконкой -`` — кнопка с одной иконкой вместо подписи: изменить, +`` — кнопка с одной иконкой вместо подписи: изменить, поделиться, закрыть, открыть меню. Берите её в панелях инструментов и строках таблиц, где подпись не помещается, а смысл иконки понятен; если сомневаетесь, берите [кнопку с подписью](/ru/smart/actions/buttons/). @@ -23,7 +23,7 @@ description: "Запускает действие компактной кноп Кнопке нужны иконка и доступное имя: ```html - + ``` `icon` — имя иконки Material Symbols, по умолчанию `add`. Свою разметку @@ -165,15 +165,15 @@ remove.onClick(() => deleteRow(row)); ### Атрибуты `data-*` и фокус Как у [Кнопки](/ru/smart/actions/buttons/#атрибуты-data-и-фокус), -атрибут `data-*` с `` переносится на внутреннюю нативную +атрибут `data-*` с `` переносится на внутреннюю нативную кнопку: у неё есть размеры, она получает фокус и нажатие, поэтому селектор `[data-…]` находит ровно один элемент — её. Хост своей области не имеет -(`display: contents`), к нему ведёт `closest('sf-icon-button')`. +(`display: contents`), к нему ведёт `closest('sf-button-icon')`. ```js const toggle = menu.querySelector('[data-search-toggle]'); // нативная кнопка toggle.focus(); -toggle.closest('sf-icon-button'); // хост +toggle.closest('sf-button-icon'); // хост ``` Записанное на хосте читает свойство `dataAttributes` diff --git a/content/ru/smart/actions/index.md b/content/ru/smart/actions/index.md index 6efb55f22..78f71af44 100644 --- a/content/ru/smart/actions/index.md +++ b/content/ru/smart/actions/index.md @@ -1,6 +1,6 @@ --- title: "Действия" -description: "Действия — 7 смарт-компонентов SIMAI Framework." +description: "Действия — 8 смарт-компонентов SIMAI Framework." profile: reference --- @@ -13,3 +13,4 @@ profile: reference - [Закрытие](/ru/smart/actions/close/) — Рисует знак закрытия внутри кнопки, которую задаёт страница. - [Скачивание файла](/ru/smart/actions/download-file/) — Показывает файл для скачивания с именем и размером и сообщает о нажатии. - [Кнопка копирования](/ru/smart/actions/copy-button/) — Копирует значение или текст элемента, показывает результат галочкой и сообщает о нём экранному диктору. +- [Подтверждение](/ru/smart/actions/confirm/) — Вопрос человеку прямо на месте: что случится, два ответа, ожидание, итог и срок. diff --git a/content/ru/smart/composite/page-header.md b/content/ru/smart/composite/page-header.md index ae0be663d..a9f04c99e 100644 --- a/content/ru/smart/composite/page-header.md +++ b/content/ru/smart/composite/page-header.md @@ -5,7 +5,7 @@ description: "Показывает в начале страницы админи # Шапка страницы -`` — шапка страницы административного приложения: путь к +`` — шапка страницы административного приложения: путь к странице, заголовок, число записей, звезда избранного и главные действия в одной строке. Ставьте её в начало каждой страницы администрирования рядом с [Меню администратора](/ru/pro/admin/admin-menu/): меню показывает, где @@ -21,10 +21,10 @@ description: "Показывает в начале страницы админи Шапке нужен заголовок; путь и действия передаются в слоты: ```html - + - + ``` `heading` — название страницы. Компонент выводит его как `h1` четвёртой @@ -80,7 +80,7 @@ description: "Показывает в начале страницы админи Кнопкам в слоте `actions` берите ступень остальных элементов страницы: рядом с полями `sf-input--size-1` — кнопки размера 1 (`size="1"` у смарт-кнопки, `sf-button--size-1` у классовой). Кнопка только со значком называется -своим атрибутом: `label` у ``, `aria-label` у классовой +своим атрибутом: `label` у ``, `aria-label` у классовой кнопки. Своих отступов по краям у шапки нет: она встаёт в контейнер страницы с @@ -89,10 +89,10 @@ description: "Показывает в начале страницы админи ```html
- + … - - + +
…
``` @@ -107,7 +107,7 @@ description: "Показывает в начале страницы админи состояние, которое не сохранилось. ```js -const header = document.querySelector('sf-page-header'); +const header = document.querySelector('sf-header-page'); header.addEventListener('sf-page-header-toggle', async (event) => { event.preventDefault(); const saved = await saveFavourite(event.detail.on); diff --git a/content/ru/smart/composite/search.md b/content/ru/smart/composite/search.md index 77378a185..67a74db6b 100644 --- a/content/ru/smart/composite/search.md +++ b/content/ru/smart/composite/search.md @@ -39,7 +39,7 @@ description: "Окно поиска по сайту: крупное поле, ф Кнопка-вызов в другом месте страницы: ```html - + ``` diff --git a/content/ru/smart/composite/site-header.md b/content/ru/smart/composite/site-header.md index cc6f670f7..8f136ed3a 100644 --- a/content/ru/smart/composite/site-header.md +++ b/content/ru/smart/composite/site-header.md @@ -5,7 +5,7 @@ description: "Верхняя полоса сайта: бренд, разделы # Шапка сайта -`` — верхняя полоса сайта: бренд, разделы, ссылки и +`` — верхняя полоса сайта: бренд, разделы, ссылки и инструменты в одну строку по ширине контейнера. На узком экране разделы и ссылки уходят в боковую панель, которую открывает кнопка меню. Одна шапка для всех сайтов SIMAI: каталога эффектов, сайтов документации и портала. @@ -26,7 +26,7 @@ description: "Верхняя полоса сайта: бренд, разделы и состояние сохраняются: ```html - + SIMAI GitHub - + ``` | Слот | В широкой строке | На узком экране | @@ -107,7 +107,7 @@ description: "Верхняя полоса сайта: бренд, разделы `outside`, `cancelled` или `api`. ```js -const header = document.querySelector('sf-site-header'); +const header = document.querySelector('sf-header-site'); header.addEventListener('sf-site-header-opened', () => { header.querySelector('[slot="sheet"]')?.replaceChildren(buildDocumentationTree()); }); diff --git a/content/ru/smart/content/avatars.md b/content/ru/smart/content/avatars.md index 9f7d0108e..3fae1adf5 100644 --- a/content/ru/smart/content/avatars.md +++ b/content/ru/smart/content/avatars.md @@ -5,7 +5,7 @@ description: "Выводит готовые сочетания аватаров: # Аватары -`` собирает из аватаров готовые сочетания: человек с источником, +`` собирает из аватаров готовые сочетания: человек с источником, группа с остатком, аватар с двумя строками подписи, аватар профиля. Для одного аватара, аватара с подписью и карточки берите [Аватар](/ru/smart/content/avatar/) — у него есть имя для @@ -24,7 +24,7 @@ description: "Выводит готовые сочетания аватаров: Сочетание выбирает `template`, остальное — атрибуты этого шаблона: ```html - + ``` - `user` — изображение из `image-url`, имя из `name` и источник из `source` @@ -55,9 +55,9 @@ description: "Выводит готовые сочетания аватаров: `tiny`, `small`, `big`, `extra_big`. ```html - + avatars-more="5"> ``` ## Доступность diff --git a/content/ru/smart/content/gallery.md b/content/ru/smart/content/gallery.md index 89beb03c3..10a2391eb 100644 --- a/content/ru/smart/content/gallery.md +++ b/content/ru/smart/content/gallery.md @@ -86,7 +86,7 @@ description: "Показывает набор материалов слайде Смена материала отправляет `change`, открытие и закрытие окна — `sf-gallery-opened` и `sf-gallery-closed` (прежние имена — `sf-change`, -`sf-open` и `sf-close` — приходят с тем же `detail`). Все три всплывают; в +`sf-open` и `sf-button-close` — приходят с тем же `detail`). Все три всплывают; в `event.detail` приходят `current` и `total`, а у `change` ещё `previous` и `from` — откуда пришла смена: `inline`, `lightbox` или `api`. diff --git a/content/ru/smart/editor/composition-overlay.md b/content/ru/smart/editor/composition-overlay.md index 5f72e4718..2dd10a12e 100644 --- a/content/ru/smart/editor/composition-overlay.md +++ b/content/ru/smart/editor/composition-overlay.md @@ -5,7 +5,7 @@ description: "Рисует поверх отрисованной страниц # Оверлей редактора -`` — часть редактора страниц: рисует поверх страницы, +`` — часть редактора страниц: рисует поверх страницы, собранной из Composition Document, рамку при наведении и выборе, подпись и значки узла, панель действий, место вставки у стыка блоков и дерево структуры для клавиатуры. HTML холста оверлей не меняет, поэтому опубликованная страница @@ -22,7 +22,7 @@ description: "Рисует поверх отрисованной страниц Оверлей ставят перед холстом и называют холст атрибутом `for`: ```html - +

О компании

@@ -34,8 +34,8 @@ description: "Рисует поверх отрисованной страниц отмечен в нём атрибутом `data-sf-composition-id`. Атрибут `for` называет `id` холста, метод `attach(canvas)` передаёт его явно. Код оверлея загрузчик SIMAI Framework подключает по требованию — только на странице с тегом -`sf-composition-overlay`, поэтому перед вызовом методов дождитесь -`customElements.whenDefined('sf-composition-overlay')`. +`sf-editor-overlay`, поэтому перед вызовом методов дождитесь +`customElements.whenDefined('sf-editor-overlay')`. Оверлей ставят перед холстом, внутри той же прокручиваемой области: дерево идёт первым в порядке `Tab`, а рамки и панель лежат в прокручиваемом @@ -160,7 +160,7 @@ Document оверлею. или английском). `setMessages(messages)` заменяет их своими. ```js -await customElements.whenDefined('sf-composition-overlay'); +await customElements.whenDefined('sf-editor-overlay'); overlay.attach(canvas); overlay.setDocument(document, { describe: (node) => ({label: titleOf(node), badges: node.shared ? ['Общий'] : []}), diff --git a/content/ru/smart/editor/inline-editor.md b/content/ru/smart/editor/inline-editor.md index 1e1c50191..374541ab0 100644 --- a/content/ru/smart/editor/inline-editor.md +++ b/content/ru/smart/editor/inline-editor.md @@ -5,7 +5,7 @@ description: "Даёт править текст заголовков и абз # Inline-редактор -`` — часть редактора страниц: правка текста +`` — часть редактора страниц: правка текста `content.heading` и `content.paragraph` прямо на холсте. Доступно то, что есть в модели content: полужирный, курсив, код, ссылки и встроенные компоненты (`badge`, `kbd`, `icon`, `button`); списков нет, потому что их нет в модели. События несут нормализованную модель, а не HTML. @@ -18,11 +18,11 @@ description: "Даёт править текст заголовков и абз ## Разметка Элемент ничего не показывает: достаточно поставить на странице -``, и загрузчик SIMAI Framework подключит код редактора по +``, и загрузчик SIMAI Framework подключит код редактора по требованию. ```html - +

Доставка по городу — бесплатно.

``` @@ -30,7 +30,7 @@ description: "Даёт править текст заголовков и абз кода. Правку запускает `SF.Composition.startInlineEdit(element, options)` или метод `edit(element, options)` элемента. До загрузки вызов завершается ошибкой `inline_editor_not_loaded`, поэтому сначала дождитесь -`customElements.whenDefined('sf-inline-editor')`. Повторный запуск на элементе, +`customElements.whenDefined('sf-editor-inline')`. Повторный запуск на элементе, который уже правится, завершается ошибкой `inline_edit_active`. ## Поведение @@ -65,7 +65,7 @@ description: "Даёт править текст заголовков и абз и перерисовывайте страницу. ```js -await customElements.whenDefined('sf-inline-editor'); +await customElements.whenDefined('sf-editor-inline'); SF.Composition.startInlineEdit(element, { content: node.data.content, nodeId: node.id, diff --git a/content/ru/smart/forms/code-input.md b/content/ru/smart/forms/code-input.md index 894454b27..3fbaf3ba9 100644 --- a/content/ru/smart/forms/code-input.md +++ b/content/ru/smart/forms/code-input.md @@ -5,14 +5,14 @@ description: "Поле одноразового кода из сообщения # Код подтверждения -`` — поле одноразового кода из сообщения, приложения-генератора +`` — поле одноразового кода из сообщения, приложения-генератора или письма: код хранит один обычный input, а ячейки его показывают. Берите его для кода, который человек получил и вводит или вставляет. PIN-код или пароль, который человек придумывает сам, вводят в [текстовое поле](/ru/smart/forms/inputs/) с `input-type="password"`. То же на классах — [Код подтверждения](/ru/components/forms/verification/): -`` собирает его разметку из атрибутов. +`` собирает его разметку из атрибутов. ## Пример @@ -24,7 +24,7 @@ description: "Поле одноразового кода из сообщения Минимальная разметка — тег с `label` и `name`: ```html - + ``` - Значение формы — один нативный input с именем `name` и diff --git a/content/ru/smart/forms/color.md b/content/ru/smart/forms/color.md index 74e104226..ebdd253bc 100644 --- a/content/ru/smart/forms/color.md +++ b/content/ru/smart/forms/color.md @@ -5,7 +5,7 @@ description: "Выбирает цвет системной палитрой ил # Цвет -`` — контрол выбора цвета: образец открывает системную палитру, +`` — контрол выбора цвета: образец открывает системную палитру, рядом стоят цвета, которые у продукта уже есть, а кнопка возврата снимает выбор, и снова действует унаследованный цвет. Берите его для цвета темы, внешнего вида сайта, метки или серии графика. @@ -21,7 +21,7 @@ description: "Выбирает цвет системной палитрой ил ```html Основной цвет - + ``` `label` задаёт доступное имя, но видимой подписи не выводит: видимую подпись diff --git a/content/ru/smart/forms/country-code.md b/content/ru/smart/forms/country-code.md index d7a192f2e..235fc8307 100644 --- a/content/ru/smart/forms/country-code.md +++ b/content/ru/smart/forms/country-code.md @@ -5,13 +5,13 @@ description: "Телефонное поле с выбором страны, фл # Код страны -`` — телефонное поле с кнопкой выбора страны: флаг и код +`` — телефонное поле с кнопкой выбора страны: флаг и код страны стоят перед номером, а маска подстраивается под выбранную страну. Для телефона без выбора страны хватит [поля ввода](/ru/smart/forms/inputs/) с маской. То же на классах — [Код страны](/ru/components/forms/country-code/): -`` собирает его разметку из атрибутов. +`` собирает его разметку из атрибутов. ## Пример @@ -23,7 +23,7 @@ description: "Телефонное поле с выбором страны, фл Полю нужна подпись, имя для формы и начальная страна: ```html - + ``` `label` — видимая подпись поля. Без `label` строки подписи нет вовсе, и @@ -71,7 +71,7 @@ description: "Телефонное поле с выбором страны, фл `dialCode`, `label` и необязательным `maskPattern`. Для известных кодов ISO пропущенные поля дополняются встроенным справочником. `max-items` ограничивает число стран в списке. Присваивайте свойство после регистрации элемента — -`await customElements.whenDefined('sf-country-code')`. +`await customElements.whenDefined('sf-input-country')`. :::example {id="components/country-code/smart-countries" label="Список из двух стран"} ::: @@ -124,7 +124,7 @@ description: "Телефонное поле с выбором страны, фл ### Состояние из кода и событие `change` Когда номер или страна зафиксированы, нативное событие `change` внутреннего -поля всплывает через ``; `detail` у него нет. Номер +поля всплывает через ``; `detail` у него нет. Номер читайте из `event.target.value`, а страну и код — из свойства `state`: `value`, `iso2`, `dialCode`, `open` и `disabled`. diff --git a/content/ru/smart/forms/datepicker.md b/content/ru/smart/forms/datepicker.md index e2888ffcd..8e99694e2 100644 --- a/content/ru/smart/forms/datepicker.md +++ b/content/ru/smart/forms/datepicker.md @@ -5,13 +5,13 @@ description: "Выбирает дату или период в календар # Календарь -`` — выбор одной даты или периода в календаре с переходом по +`` — выбор одной даты или периода в календаре с переходом по месяцам и годам. Берите его, когда дату удобнее найти в календаре, чем напечатать; если основной сценарий — ввод с клавиатуры, хватит [поля ввода](/ru/smart/forms/inputs/). То же на классах — [Календарь](/ru/components/forms/datepicker/): оформление -календаря `` берёт из этого компонента. +календаря `` берёт из этого компонента. ## Пример @@ -24,7 +24,7 @@ description: "Выбирает дату или период в календар открывается по щелчку или с клавиатуры: ```html - + ``` `label` выводит подпись над полем. Если подпись выводит сама страница — @@ -202,7 +202,7 @@ ISO принимается тоже: `14.10.2026` и `2026-10-14` дают од календарём: ```html - + - + appearance="bordered"
- + - + appearance="filled"
diff --git a/examples/smart-components/lookup/overview/index.html b/examples/smart-components/lookup/overview/index.html index 2ba189366..a310e5120 100644 --- a/examples/smart-components/lookup/overview/index.html +++ b/examples/smart-components/lookup/overview/index.html @@ -1,4 +1,4 @@ - - + diff --git a/examples/smart-components/lookup/single/index.html b/examples/smart-components/lookup/single/index.html index 5a7a611d5..0c64ab973 100644 --- a/examples/smart-components/lookup/single/index.html +++ b/examples/smart-components/lookup/single/index.html @@ -1,3 +1,3 @@ - + - + diff --git a/examples/smart-components/lookup/sizes/index.html b/examples/smart-components/lookup/sizes/index.html index e22f5f94c..0e9a1d8a9 100644 --- a/examples/smart-components/lookup/sizes/index.html +++ b/examples/smart-components/lookup/sizes/index.html @@ -1,20 +1,20 @@
- + - + size="1/2"
- + - + size="1"
- + - + size="2"
diff --git a/examples/smart-components/lookup/state-disabled/index.html b/examples/smart-components/lookup/state-disabled/index.html index 6df93784c..dfb4a5929 100644 --- a/examples/smart-components/lookup/state-disabled/index.html +++ b/examples/smart-components/lookup/state-disabled/index.html @@ -1,3 +1,3 @@ - + - + diff --git a/examples/smart-components/lookup/state-error/index.html b/examples/smart-components/lookup/state-error/index.html index 3e0726d56..72d66726b 100644 --- a/examples/smart-components/lookup/state-error/index.html +++ b/examples/smart-components/lookup/state-error/index.html @@ -1,3 +1,3 @@ - + - + diff --git a/examples/smart-components/lookup/state-readonly/index.html b/examples/smart-components/lookup/state-readonly/index.html index 62ed1e173..0045eee39 100644 --- a/examples/smart-components/lookup/state-readonly/index.html +++ b/examples/smart-components/lookup/state-readonly/index.html @@ -1,3 +1,3 @@ - + - + diff --git a/examples/smart-components/page-header/install/index.html b/examples/smart-components/page-header/install/index.html index 293b7acdb..16f4b61d3 100644 --- a/examples/smart-components/page-header/install/index.html +++ b/examples/smart-components/page-header/install/index.html @@ -1,4 +1,4 @@ - + - + diff --git a/examples/smart-components/page-header/overview/index.html b/examples/smart-components/page-header/overview/index.html index 8bb0967f7..5c2e998dc 100644 --- a/examples/smart-components/page-header/overview/index.html +++ b/examples/smart-components/page-header/overview/index.html @@ -1,5 +1,5 @@ - + - + diff --git a/examples/smart-components/page-header/state-star/index.html b/examples/smart-components/page-header/state-star/index.html index a1c8f0f89..e99e58af0 100644 --- a/examples/smart-components/page-header/state-star/index.html +++ b/examples/smart-components/page-header/state-star/index.html @@ -1,10 +1,10 @@
- + star="off"
- + star="on"
diff --git a/examples/smart-components/property-view/overview/index.html b/examples/smart-components/property-view/overview/index.html index 0b008e162..7ea6afcc7 100644 --- a/examples/smart-components/property-view/overview/index.html +++ b/examples/smart-components/property-view/overview/index.html @@ -1,5 +1,5 @@ - - +
diff --git a/examples/smart-components/property-view/overview/index.js b/examples/smart-components/property-view/overview/index.js index b6c1a83ed..f73629b14 100644 --- a/examples/smart-components/property-view/overview/index.js +++ b/examples/smart-components/property-view/overview/index.js @@ -74,7 +74,7 @@ const answer = () => { }; const initializeSolutionPropertyView = async () => { - await customElements.whenDefined('sf-property-view'); + await customElements.whenDefined('sf-view-properties'); const panel = document.querySelector('#solution-property-view'); if (!panel || panel.dataset.exampleReady === 'true') return; diff --git a/examples/smart-components/quantity/bounds/index.html b/examples/smart-components/quantity/bounds/index.html index 46519f06d..c04593e40 100644 --- a/examples/smart-components/quantity/bounds/index.html +++ b/examples/smart-components/quantity/bounds/index.html @@ -1,4 +1,4 @@
- - + +
diff --git a/examples/smart-components/quantity/decimals/index.html b/examples/smart-components/quantity/decimals/index.html index 55e6580f3..eff7be3c7 100644 --- a/examples/smart-components/quantity/decimals/index.html +++ b/examples/smart-components/quantity/decimals/index.html @@ -1,4 +1,4 @@
- - + +
diff --git a/examples/smart-components/quantity/inherited/index.html b/examples/smart-components/quantity/inherited/index.html index 107df15ab..de0bb176a 100644 --- a/examples/smart-components/quantity/inherited/index.html +++ b/examples/smart-components/quantity/inherited/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/quantity/overview/index.html b/examples/smart-components/quantity/overview/index.html index cbe99cb94..d3d590a29 100644 --- a/examples/smart-components/quantity/overview/index.html +++ b/examples/smart-components/quantity/overview/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/quantity/sizes/index.html b/examples/smart-components/quantity/sizes/index.html index bbd81347b..82cd87616 100644 --- a/examples/smart-components/quantity/sizes/index.html +++ b/examples/smart-components/quantity/sizes/index.html @@ -1,5 +1,5 @@
- - - + + +
diff --git a/examples/smart-components/quantity/state-bound/index.html b/examples/smart-components/quantity/state-bound/index.html index 7b7632f69..2698a54db 100644 --- a/examples/smart-components/quantity/state-bound/index.html +++ b/examples/smart-components/quantity/state-bound/index.html @@ -1,10 +1,10 @@
- + value="1" min="1"
- + value="8" max="8"
diff --git a/examples/smart-components/quantity/state-disabled/index.html b/examples/smart-components/quantity/state-disabled/index.html index 2bbed5129..6cd463949 100644 --- a/examples/smart-components/quantity/state-disabled/index.html +++ b/examples/smart-components/quantity/state-disabled/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/quantity/state-error/index.html b/examples/smart-components/quantity/state-error/index.html index c645c9a23..82af54ad1 100644 --- a/examples/smart-components/quantity/state-error/index.html +++ b/examples/smart-components/quantity/state-error/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/quantity/state-readonly/index.html b/examples/smart-components/quantity/state-readonly/index.html index 28101f49c..91d5d8f4a 100644 --- a/examples/smart-components/quantity/state-readonly/index.html +++ b/examples/smart-components/quantity/state-readonly/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/range-slider/connect/index.html b/examples/smart-components/range-slider/connect/index.html index 650682540..c5ec4f80f 100644 --- a/examples/smart-components/range-slider/connect/index.html +++ b/examples/smart-components/range-slider/connect/index.html @@ -1,14 +1,14 @@
- + connect="lower"
- + connect="upper"
- + connect="false"
diff --git a/examples/smart-components/range-slider/handles/index.html b/examples/smart-components/range-slider/handles/index.html index 91988edb4..cc2587625 100644 --- a/examples/smart-components/range-slider/handles/index.html +++ b/examples/smart-components/range-slider/handles/index.html @@ -1,10 +1,10 @@
- + value="20"
- + value="10,30"
diff --git a/examples/smart-components/range-slider/label-position/index.html b/examples/smart-components/range-slider/label-position/index.html index 8a34cbd74..c91356411 100644 --- a/examples/smart-components/range-slider/label-position/index.html +++ b/examples/smart-components/range-slider/label-position/index.html @@ -1,10 +1,10 @@
- + label-position="top"
- + label-position="bottom"
diff --git a/examples/smart-components/range-slider/labels/index.html b/examples/smart-components/range-slider/labels/index.html index 245123d8c..4799d4372 100644 --- a/examples/smart-components/range-slider/labels/index.html +++ b/examples/smart-components/range-slider/labels/index.html @@ -1,14 +1,14 @@
- + label="none"
- + label="text"
- + label="tooltip"
diff --git a/examples/smart-components/range-slider/overview/index.html b/examples/smart-components/range-slider/overview/index.html index d9e2b7534..1bd5bda87 100644 --- a/examples/smart-components/range-slider/overview/index.html +++ b/examples/smart-components/range-slider/overview/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/range-slider/range/index.html b/examples/smart-components/range-slider/range/index.html index 7b4d51831..e54e72101 100644 --- a/examples/smart-components/range-slider/range/index.html +++ b/examples/smart-components/range-slider/range/index.html @@ -1,5 +1,5 @@
- - + Цена: 5000–20000 ₽
diff --git a/examples/smart-components/range-slider/range/index.js b/examples/smart-components/range-slider/range/index.js index b1c9edc5a..187da08ef 100644 --- a/examples/smart-components/range-slider/range/index.js +++ b/examples/smart-components/range-slider/range/index.js @@ -6,7 +6,7 @@ const initializePriceRange = async () => { if (!slider || !output || slider.dataset.exampleReady === 'true') return; slider.dataset.exampleReady = 'true'; - await customElements.whenDefined('sf-range-slider'); + await customElements.whenDefined('sf-slider-range'); slider.addEventListener('change', (event) => { const [from, to] = event.detail.values; output.value = `Цена: ${from}–${to} ₽`; diff --git a/examples/smart-components/range-slider/state-disabled/index.html b/examples/smart-components/range-slider/state-disabled/index.html index 561c5f712..cbc5bec8c 100644 --- a/examples/smart-components/range-slider/state-disabled/index.html +++ b/examples/smart-components/range-slider/state-disabled/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/range-slider/step/index.html b/examples/smart-components/range-slider/step/index.html index f89d3fd2f..3d650d330 100644 --- a/examples/smart-components/range-slider/step/index.html +++ b/examples/smart-components/range-slider/step/index.html @@ -1,14 +1,14 @@
- + step="5"
- + step="0.5"
- + step="0.5" decimals="2"
diff --git a/examples/smart-components/rich-text/appearance/index.html b/examples/smart-components/rich-text/appearance/index.html index c8dc6a65b..614970d22 100644 --- a/examples/smart-components/rich-text/appearance/index.html +++ b/examples/smart-components/rich-text/appearance/index.html @@ -1,10 +1,10 @@
- + appearance="bordered"
- + appearance="filled"
diff --git a/examples/smart-components/rich-text/cleaning/index.html b/examples/smart-components/rich-text/cleaning/index.html index 0caa6dd3d..c25f10e78 100644 --- a/examples/smart-components/rich-text/cleaning/index.html +++ b/examples/smart-components/rich-text/cleaning/index.html @@ -1,7 +1,7 @@ - - + diff --git a/examples/smart-components/rich-text/overview/index.html b/examples/smart-components/rich-text/overview/index.html index acc812879..fbfa780ad 100644 --- a/examples/smart-components/rich-text/overview/index.html +++ b/examples/smart-components/rich-text/overview/index.html @@ -1,6 +1,6 @@ - - + diff --git a/examples/smart-components/rich-text/rows/index.html b/examples/smart-components/rich-text/rows/index.html index 01f1683c7..d833f83c7 100644 --- a/examples/smart-components/rich-text/rows/index.html +++ b/examples/smart-components/rich-text/rows/index.html @@ -1,10 +1,10 @@
- + rows="2"
- + rows="4" max-rows="8"
diff --git a/examples/smart-components/rich-text/sizes/index.html b/examples/smart-components/rich-text/sizes/index.html index 35fef582e..51183e2b8 100644 --- a/examples/smart-components/rich-text/sizes/index.html +++ b/examples/smart-components/rich-text/sizes/index.html @@ -1,14 +1,14 @@
- + size="1/2"
- + size="1"
- + size="2"
diff --git a/examples/smart-components/rich-text/state-disabled/index.html b/examples/smart-components/rich-text/state-disabled/index.html index 291808cc3..5f90858a2 100644 --- a/examples/smart-components/rich-text/state-disabled/index.html +++ b/examples/smart-components/rich-text/state-disabled/index.html @@ -1,7 +1,7 @@ - - + diff --git a/examples/smart-components/rich-text/state-error/index.html b/examples/smart-components/rich-text/state-error/index.html index f5b33db6f..febb9fb6a 100644 --- a/examples/smart-components/rich-text/state-error/index.html +++ b/examples/smart-components/rich-text/state-error/index.html @@ -1,4 +1,4 @@ - - + diff --git a/examples/smart-components/rich-text/state-readonly/index.html b/examples/smart-components/rich-text/state-readonly/index.html index 32826cec2..f735a3644 100644 --- a/examples/smart-components/rich-text/state-readonly/index.html +++ b/examples/smart-components/rich-text/state-readonly/index.html @@ -1,8 +1,8 @@ - - + diff --git a/examples/smart-components/search/icon/index.html b/examples/smart-components/search/icon/index.html index cc66d3d18..1d90e888a 100644 --- a/examples/smart-components/search/icon/index.html +++ b/examples/smart-components/search/icon/index.html @@ -1,7 +1,7 @@
- +

Нажмите кнопку и наберите «таб» или «тема».

diff --git a/examples/smart-components/site-header/center/index.html b/examples/smart-components/site-header/center/index.html index 6f570c201..f75034a7c 100644 --- a/examples/smart-components/site-header/center/index.html +++ b/examples/smart-components/site-header/center/index.html @@ -1,4 +1,4 @@ - + Эффекты - + diff --git a/examples/smart-components/site-header/collapse/index.html b/examples/smart-components/site-header/collapse/index.html index 0de5972f8..4e6293639 100644 --- a/examples/smart-components/site-header/collapse/index.html +++ b/examples/smart-components/site-header/collapse/index.html @@ -1,4 +1,4 @@ - + SIMAI - + diff --git a/examples/smart-components/site-header/compact/index.html b/examples/smart-components/site-header/compact/index.html index 3ec2fc97b..4ff635383 100644 --- a/examples/smart-components/site-header/compact/index.html +++ b/examples/smart-components/site-header/compact/index.html @@ -1,10 +1,10 @@ - + Эффекты - + - +

Полоса остаётся вверху окна, когда страница прокручивается.

diff --git a/examples/smart-components/site-header/overview/index.html b/examples/smart-components/site-header/overview/index.html index 00f60d5b8..26ba4c711 100644 --- a/examples/smart-components/site-header/overview/index.html +++ b/examples/smart-components/site-header/overview/index.html @@ -1,4 +1,4 @@ - + SIMAI Поставка - + diff --git a/examples/smart-components/spinner/page-look/index.html b/examples/smart-components/spinner/page-look/index.html index a9ddf88f3..025e8f855 100644 --- a/examples/smart-components/spinner/page-look/index.html +++ b/examples/smart-components/spinner/page-look/index.html @@ -9,7 +9,7 @@ - +
diff --git a/examples/smart-components/table/application/index.html b/examples/smart-components/table/application/index.html index f667b4973..9e112ba99 100644 --- a/examples/smart-components/table/application/index.html +++ b/examples/smart-components/table/application/index.html @@ -4,12 +4,12 @@
- - + - +

diff --git a/examples/smart-components/table/application/index.js b/examples/smart-components/table/application/index.js index ccb7a4678..9e8bb88b5 100644 --- a/examples/smart-components/table/application/index.js +++ b/examples/smart-components/table/application/index.js @@ -32,10 +32,10 @@ const dateFormat = new Intl.DateTimeFormat('ru-RU', {dateStyle: 'medium', timeSt const initializeApplicationView = async () => { const root = document.querySelector('[data-application-demo]'); if (!root || root.dataset.exampleReady === 'true') return; - await customElements.whenDefined('sf-data-view'); + await customElements.whenDefined('sf-view'); root.dataset.exampleReady = 'true'; - const view = root.querySelector('sf-data-view'); + const view = root.querySelector('sf-view'); const log = root.querySelector('[data-application-log]'); let scenario = 'normal'; const records = PROJECTS.map((project) => ({...project, revision: '1'})); diff --git a/examples/smart-components/table/list/index.html b/examples/smart-components/table/list/index.html index d6ef8ff61..9fce42831 100644 --- a/examples/smart-components/table/list/index.html +++ b/examples/smart-components/table/list/index.html @@ -1,8 +1,8 @@ - - +
diff --git a/examples/smart-components/table/list/index.js b/examples/smart-components/table/list/index.js index 2bae7dd65..bda089140 100644 --- a/examples/smart-components/table/list/index.js +++ b/examples/smart-components/table/list/index.js @@ -1,5 +1,5 @@ const initializeProjectsList = async () => { - await customElements.whenDefined('sf-table'); + await customElements.whenDefined('sf-view-table'); const table = document.querySelector('#projects-list-view'); if (!table || table.dataset.exampleReady === 'true') return; diff --git a/examples/smart-components/table/overview/index.html b/examples/smart-components/table/overview/index.html index ac18176e1..84dc9ccf5 100644 --- a/examples/smart-components/table/overview/index.html +++ b/examples/smart-components/table/overview/index.html @@ -1,6 +1,6 @@ - - +
diff --git a/examples/smart-components/table/overview/index.js b/examples/smart-components/table/overview/index.js index fe0331748..45641d754 100644 --- a/examples/smart-components/table/overview/index.js +++ b/examples/smart-components/table/overview/index.js @@ -1,5 +1,5 @@ const initializeProjectsDataView = async () => { - await customElements.whenDefined('sf-table'); + await customElements.whenDefined('sf-view-table'); const table = document.querySelector('#projects-data-view'); if (!table || table.dataset.exampleReady === 'true') return; diff --git a/examples/smart-components/textarea/appearance/index.html b/examples/smart-components/textarea/appearance/index.html index 5f51e324f..bae193c17 100644 --- a/examples/smart-components/textarea/appearance/index.html +++ b/examples/smart-components/textarea/appearance/index.html @@ -1,10 +1,10 @@
- + appearance="bordered"
- + appearance="filled"
diff --git a/examples/smart-components/textarea/auto-grow/index.html b/examples/smart-components/textarea/auto-grow/index.html index 1aead3722..5725d18fe 100644 --- a/examples/smart-components/textarea/auto-grow/index.html +++ b/examples/smart-components/textarea/auto-grow/index.html @@ -1,14 +1,14 @@
- - + - - +
diff --git a/examples/smart-components/textarea/flush/index.html b/examples/smart-components/textarea/flush/index.html index c48095bad..fc71e2550 100644 --- a/examples/smart-components/textarea/flush/index.html +++ b/examples/smart-components/textarea/flush/index.html @@ -4,13 +4,13 @@ .composer > sf-textarea { flex: 1 1 auto; min-inline-size: 0; }
- - - + +
diff --git a/examples/smart-components/textarea/overview/index.html b/examples/smart-components/textarea/overview/index.html index d0b4a067c..3a0e98f96 100644 --- a/examples/smart-components/textarea/overview/index.html +++ b/examples/smart-components/textarea/overview/index.html @@ -1,8 +1,8 @@ - - + diff --git a/examples/smart-components/textarea/rows/index.html b/examples/smart-components/textarea/rows/index.html index 07e0bb31e..f16cfba8d 100644 --- a/examples/smart-components/textarea/rows/index.html +++ b/examples/smart-components/textarea/rows/index.html @@ -1,10 +1,10 @@
- + rows="2"
- + rows="6"
diff --git a/examples/smart-components/textarea/sizes/index.html b/examples/smart-components/textarea/sizes/index.html index f7387ecac..76c66736a 100644 --- a/examples/smart-components/textarea/sizes/index.html +++ b/examples/smart-components/textarea/sizes/index.html @@ -1,14 +1,14 @@
- + size="1/2"
- + size="1"
- + size="2"
diff --git a/examples/smart-components/textarea/state-disabled/index.html b/examples/smart-components/textarea/state-disabled/index.html index 0a16952eb..e80cd1d5f 100644 --- a/examples/smart-components/textarea/state-disabled/index.html +++ b/examples/smart-components/textarea/state-disabled/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/textarea/state-error/index.html b/examples/smart-components/textarea/state-error/index.html index 9f554a5d5..1d03a2e28 100644 --- a/examples/smart-components/textarea/state-error/index.html +++ b/examples/smart-components/textarea/state-error/index.html @@ -1,8 +1,8 @@ - - + diff --git a/examples/smart-components/textarea/state-readonly/index.html b/examples/smart-components/textarea/state-readonly/index.html index 90ba48dfa..36790cb52 100644 --- a/examples/smart-components/textarea/state-readonly/index.html +++ b/examples/smart-components/textarea/state-readonly/index.html @@ -1 +1 @@ - + diff --git a/examples/smart-components/thread-list/overview/index.html b/examples/smart-components/thread-list/overview/index.html index 932585a4a..bab06beb9 100644 --- a/examples/smart-components/thread-list/overview/index.html +++ b/examples/smart-components/thread-list/overview/index.html @@ -1 +1 @@ -
+
diff --git a/examples/smart-components/thread-list/overview/index.js b/examples/smart-components/thread-list/overview/index.js index 9bc6e3338..f02183946 100644 --- a/examples/smart-components/thread-list/overview/index.js +++ b/examples/smart-components/thread-list/overview/index.js @@ -10,7 +10,7 @@ const threads = [ ]; const initialize = async () => { - await customElements.whenDefined('sf-thread-list'); + await customElements.whenDefined('sf-chat-threads'); const list = document.querySelector('#thread-list-overview'); if (!list || list.dataset.exampleReady === 'true') return; list.dataset.exampleReady = 'true'; diff --git a/examples/smart-components/thread-list/state-empty/index.html b/examples/smart-components/thread-list/state-empty/index.html index 17a0936ef..ccd64681f 100644 --- a/examples/smart-components/thread-list/state-empty/index.html +++ b/examples/smart-components/thread-list/state-empty/index.html @@ -1 +1 @@ -
+
diff --git a/examples/smart-components/thread-list/state-empty/index.js b/examples/smart-components/thread-list/state-empty/index.js index a9bffe2a5..a238e8d0c 100644 --- a/examples/smart-components/thread-list/state-empty/index.js +++ b/examples/smart-components/thread-list/state-empty/index.js @@ -1,6 +1,6 @@ // No conversations yet: the list says so and offers a new one. const initialize = async () => { - await customElements.whenDefined('sf-thread-list'); + await customElements.whenDefined('sf-chat-threads'); const list = document.querySelector('#thread-list-empty'); if (!list || list.dataset.exampleReady === 'true') return; list.dataset.exampleReady = 'true'; diff --git a/examples/smart-components/thread-list/state-loading/index.html b/examples/smart-components/thread-list/state-loading/index.html index e46cf0065..61be6cf06 100644 --- a/examples/smart-components/thread-list/state-loading/index.html +++ b/examples/smart-components/thread-list/state-loading/index.html @@ -1 +1 @@ -
+
diff --git a/examples/smart-components/thread-list/state-loading/index.js b/examples/smart-components/thread-list/state-loading/index.js index 4cac72bd4..8e51ea427 100644 --- a/examples/smart-components/thread-list/state-loading/index.js +++ b/examples/smart-components/thread-list/state-loading/index.js @@ -1,6 +1,6 @@ // While the first page loads the list shows its shape and says it is busy. const initialize = async () => { - await customElements.whenDefined('sf-thread-list'); + await customElements.whenDefined('sf-chat-threads'); const list = document.querySelector('#thread-list-loading'); if (!list || list.dataset.exampleReady === 'true') return; list.dataset.exampleReady = 'true'; diff --git a/examples/smart-components/thread-list/ungrouped/index.html b/examples/smart-components/thread-list/ungrouped/index.html index c90ff07f6..52cfc07e7 100644 --- a/examples/smart-components/thread-list/ungrouped/index.html +++ b/examples/smart-components/thread-list/ungrouped/index.html @@ -1 +1 @@ -
+
diff --git a/examples/smart-components/thread-list/ungrouped/index.js b/examples/smart-components/thread-list/ungrouped/index.js index cc4a210b8..46113b809 100644 --- a/examples/smart-components/thread-list/ungrouped/index.js +++ b/examples/smart-components/thread-list/ungrouped/index.js @@ -10,7 +10,7 @@ const threads = [ ]; const initialize = async () => { - await customElements.whenDefined('sf-thread-list'); + await customElements.whenDefined('sf-chat-threads'); const list = document.querySelector('#thread-list-ungrouped'); if (!list || list.dataset.exampleReady === 'true') return; list.dataset.exampleReady = 'true'; diff --git a/scripts/audit-guide-information-architecture.php b/scripts/audit-guide-information-architecture.php index c195c5419..ca5315583 100755 --- a/scripts/audit-guide-information-architecture.php +++ b/scripts/audit-guide-information-architecture.php @@ -51,6 +51,7 @@ ['Утилиты', '/ru/utilities/'], ['Компоненты', '/ru/components/'], ['Смарт-компоненты', '/ru/smart/'], + ['Наборы', '/ru/guide/sets/'], ['Pro', '/ru/pro/'], ]; $check($headerActual === $headerExpected, 'header_navigation_mismatch', ['actual' => $headerActual]); diff --git a/scripts/audit-reference-separation.php b/scripts/audit-reference-separation.php index 49c64b0eb..60e6b62d9 100644 --- a/scripts/audit-reference-separation.php +++ b/scripts/audit-reference-separation.php @@ -18,6 +18,8 @@ ['Утилиты', '/ru/utilities/'], ['Компоненты', '/ru/components/'], ['Смарт-компоненты', '/ru/smart/'], + // Component sets are taught in the guide; Pro stays until each set has its own site. + ['Наборы', '/ru/guide/sets/'], ['Pro', '/ru/pro/'], ], 'header_is_not_guide_plus_references', ['actual' => $actualHeader]); diff --git a/scripts/export-live-demonstrations.php b/scripts/export-live-demonstrations.php index b7e6c24de..33a7471a8 100644 --- a/scripts/export-live-demonstrations.php +++ b/scripts/export-live-demonstrations.php @@ -46,57 +46,120 @@ // components come from the product package the site pins (the lock's // package_projections), so the page declares that package to the Loader the // way the documentation pages do. Links inside it never navigate (index.js). -$adminSource=$root.'/examples/home/admin'; -$adminTarget=$build.'/demos/home/admin'; +// The English home page shows the same demonstrations in English: the export +// writes a second copy under demos/home/en/, its text taken from +// examples/home/i18n/en.json. A string is matched whole -- a JSON value, a part +// of a JavaScript literal between ${...}, an HTML text or attribute part -- and a +// Russian string the dictionary does not hold stops the export. +$homeTranslation=$root.'/examples/home/i18n/en.json'; +$inputs[substr($homeTranslation,strlen($root)+1)]=hash_file('sha256',$homeTranslation); +$dictionary=json_decode(file_get_contents($homeTranslation),true,512,JSON_THROW_ON_ERROR); +$untranslated=[]; +$translate=static function(string $text) use ($dictionary,&$untranslated): string { + $core=trim($text); + if ($core==='' || preg_match('/[\p{Cyrillic}№]/u',$core)!==1 || in_array($core,$dictionary['keep'],true)) return $text; + $lead=substr($text,0,strlen($text)-strlen(ltrim($text)));$trail=substr($text,strlen(rtrim($text))); + if (array_key_exists($core,$dictionary['strings'])) return $lead.$dictionary['strings'][$core].$trail; + foreach ($dictionary['patterns'] as $pattern) { + if (preg_match('/'.$pattern['match'].'/u',$core,$m)!==1 || !isset($pattern['words'][$m[1]])) continue; + $rest=substr($core,strlen($m[0])); + if ($rest!=='' && !array_key_exists($rest,$dictionary['strings'])) break; + return $lead.str_replace(['{1}','{2}'],[$pattern['words'][$m[1]],$m[2]],$pattern['replace']).($rest===''?'':$dictionary['strings'][$rest]).$trail; + } + $untranslated[$core]=true; + return $text; +}; +$translateParts=static fn(string $text): string=>implode('',array_map( + static fn(string $part): string=>str_starts_with($part,'${')?$part:$translate($part), + preg_split('/(\$\{[^}]*\})/',$text,-1,PREG_SPLIT_DELIM_CAPTURE))); +$translateFile=static function(string $path,string $content) use ($translate,$translateParts,$dictionary): string { + $extension=pathinfo($path,PATHINFO_EXTENSION); + if ($extension==='json') { + $walk=static function($value) use (&$walk,$translate){ + if (is_array($value)) return array_map($walk,$value); + return is_string($value)?$translate($value):$value; + }; + $data=$walk(json_decode($content,true,512,JSON_THROW_ON_ERROR)); + // Values other than text, such as the person's language that the admin + // menu gives the page. + foreach ($dictionary['values'][$path] ?? [] as $key=>$value) { + $node=&$data; + foreach (explode('.',$key) as $step) { + if (!is_array($node) || !array_key_exists($step,$node)) throw new RuntimeException('Home demonstrations: '.$path.' has no '.$key); + $node=&$node[$step]; + } + $node=$value; + unset($node); + } + return json_encode($data,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); + } + if ($extension==='js') return preg_replace_callback('/`((?:[^`\\\\]|\\\\.)*)`|\'((?:[^\'\\\\\n]|\\\\.)*)\'|"((?:[^"\\\\\n]|\\\\.)*)"/', + static function(array $m) use ($translateParts): string { $quote=$m[0][0]; return $quote.$translateParts(substr($m[0],1,-1)).$quote; },$content); + if ($extension==='html') { + $parts=static fn(string $value): string=>implode('|',array_map(static function(string $part) use ($translate): string { + return preg_match('/\A([a-z-]+:)(.*)\z/s',$part,$m)===1?$m[1].$translate($m[2]):$translate($part); + },explode('|',$value))); + $content=preg_replace('//s','',$content); + return preg_replace_callback('/>([^<>]+)<|="([^"]*)"/',static fn(array $m): string=>isset($m[2])?'="'.$parts($m[2]).'"':'>'.$parts($m[1]).'<',$content); + } + return $content; +}; + +// The administrative interface on the home page (examples/home/admin): a +// standalone page rather than a sandboxed example frame, because it runs its +// own data host, reads its data files and opens records in place. Its +// components come from the product package the site pins (the lock's +// package_projections), so the page declares that package to the Loader the +// way the documentation pages do. Links inside it never navigate (index.js). +// The home page showcase (examples/home/showcase): tabs of the admin, the chat +// and the editor. The admin is the page above in a frame; the chat and the +// editor come from the same product package and are made when their tab opens. $proMount=$lock['package_projections']['pro']['mount'] ?? null; if (!is_string($proMount) || preg_match('~\A_docara/framework-packages/pro/[0-9a-f]{40}\z~', $proMount) !== 1) throw new RuntimeException('Admin demonstration: package mount missing from the lock'); -if (!is_dir($adminTarget)) mkdir($adminTarget,0775,true); -foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($adminSource, FilesystemIterator::SKIP_DOTS)) as $input) { - if (!$input->isFile()) continue; - $relative=substr($input->getPathname(),strlen($adminSource)+1); - $inputs[substr($input->getPathname(),strlen($root)+1)]=hash_file('sha256',$input->getPathname()); - if ($relative==='index.html') continue; - $dest=$adminTarget.'/'.$relative; - if(!is_dir(dirname($dest)))mkdir(dirname($dest),0775,true); - copy($input->getPathname(),$dest); -} // Icons from the runtime's own icon font: the menu and the list use many more // glyphs than the documentation's subset carries. $adminBoot=['theme'=>false,'icons'=>['enabled'=>true,'accumulate'=>false],'smart'=>['base'=>true],'minified'=>false,'packages'=>['pro'=>['base'=>$base.$proMount.'/smart']]]; -$adminHtml='Административный интерфейс' - .'' - .'' - .'' - .'' - .''.file_get_contents($adminSource.'/index.html') - .'' - .''; -file_put_contents($adminTarget.'/index.html',$adminHtml); -foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($adminTarget, FilesystemIterator::SKIP_DOTS)) as $f) if($f->isFile()) $files[]=['path'=>substr($f->getPathname(),strlen($build)+1),'sha256'=>hash_file('sha256',$f->getPathname())]; - -// The home page showcase (examples/home/showcase): tabs of the admin, the chat -// and the editor. The admin is the page above in a frame; the chat and the -// editor come from the same product package and are made when their tab opens. -$showcaseSource=$root.'/examples/home/showcase'; -$showcaseTarget=$build.'/demos/home/showcase'; -if (!is_dir($showcaseTarget)) mkdir($showcaseTarget,0775,true); -foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($showcaseSource, FilesystemIterator::SKIP_DOTS)) as $input) { - if (!$input->isFile()) continue; - $relative=substr($input->getPathname(),strlen($showcaseSource)+1); - $inputs[substr($input->getPathname(),strlen($root)+1)]=hash_file('sha256',$input->getPathname()); - if ($relative==='index.html') continue; - copy($input->getPathname(),$showcaseTarget.'/'.$relative); +$themeFirst=''; +$themeSync=''; +$boot=''; +// The English home links the same addresses as the Russian one, so a Russian +// page moves to its English copy when the page around it is English, or when +// it was opened from an English page. +$toEnglish=''; +$homeDemos=[ + 'admin'=>['html'=>'class="max-h-screen h-full" data-sf-density="compact"','title'=>'Административный интерфейс','modules'=>['index.js','demo.js'],'body'=>'class="flex h-full"'], + 'showcase'=>['html'=>'','title'=>'Что можно собрать','modules'=>['index.js'],'body'=>''], +]; +foreach (['ru'=>'','en'=>'en/'] as $language=>$prefix) { + $tr=$language==='ru'?static fn(string $path,string $content): string=>$content:$translateFile; + foreach ($homeDemos as $name=>$demo) { + $source=$root.'/examples/home/'.$name; + $target=$build.'/demos/home/'.$prefix.$name; + if (!is_dir($target)) mkdir($target,0775,true); + foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source, FilesystemIterator::SKIP_DOTS)) as $input) { + if (!$input->isFile()) continue; + $relative=substr($input->getPathname(),strlen($source)+1); + $inputs[substr($input->getPathname(),strlen($root)+1)]=hash_file('sha256',$input->getPathname()); + if ($relative==='index.html') continue; + $dest=$target.'/'.$relative; + if(!is_dir(dirname($dest)))mkdir(dirname($dest),0775,true); + file_put_contents($dest,$tr($name.'/'.$relative,file_get_contents($input->getPathname()))); + } + $title=$language==='ru'?$demo['title']:$translate($demo['title']); + $html=''.$title.'' + .($language==='ru'?$toEnglish:'') + .'' + .$themeFirst.$boot + .'' + .implode('',array_map(static fn(string $module): string=>'',$demo['modules'])).'' + .''.$tr('index.html',file_get_contents($source.'/index.html')) + .$themeSync + .''; + file_put_contents($target.'/index.html',$html); + foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($target, FilesystemIterator::SKIP_DOTS)) as $f) if($f->isFile()) $files[]=['path'=>substr($f->getPathname(),strlen($build)+1),'sha256'=>hash_file('sha256',$f->getPathname())]; + } } -$showcaseHtml='Что можно собрать' - .'' - .'' - .'' - .'' - .''.file_get_contents($showcaseSource.'/index.html') - .'' - .''; -file_put_contents($showcaseTarget.'/index.html',$showcaseHtml); -foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($showcaseTarget, FilesystemIterator::SKIP_DOTS)) as $f) if($f->isFile()) $files[]=['path'=>substr($f->getPathname(),strlen($build)+1),'sha256'=>hash_file('sha256',$f->getPathname())]; +if ($untranslated!==[]) throw new RuntimeException('Home demonstrations: no English text for '.implode(' | ',array_keys($untranslated))); $recipeFixture=$root.'/assets/examples/composition/header-switch.json'; $recipeTarget=$build.'/demos/guide/composition-recipe/header-switch.json'; diff --git a/translations.lock.json b/translations.lock.json index 835b44070..fd5b8979b 100644 --- a/translations.lock.json +++ b/translations.lock.json @@ -30,11 +30,11 @@ "locale": "en", "review": "ai_verified", "source_path": "content/ru/index.md", - "source_sha256": "a0ec35d67e1ad940d736713c212ba946e12e308631b4c844e68df57b860ae8d4", - "source_structure_sha256": "b1a193f4f03f149994ca921fe18e034a397b6a551b39570b3f71b3d69627c8e8", + "source_sha256": "a4b61800b80a90d810b9146b6b2e34b843d7962288ff0961c759fff5c2fe8aa2", + "source_structure_sha256": "e4c323242ea6efa7e2dd9b72e06c73c1773bba5d382af94f82507de236e1902b", "target_path": "content/en/index.md", - "target_sha256": "19b890af88ccd08273452976df864c930ff1fec53645cd4e7cf06ed04c014afa", - "target_structure_sha256": "b1a193f4f03f149994ca921fe18e034a397b6a551b39570b3f71b3d69627c8e8" + "target_sha256": "761d66228f0328fdc636c4974865835bfa1dc165def9472be9f8b44b17080310", + "target_structure_sha256": "e4c323242ea6efa7e2dd9b72e06c73c1773bba5d382af94f82507de236e1902b" } ], "exclusions": [],