Перейти к содержимому
ФреймворкJavaScript

Страница: ссылки на подстраницы

Однострочная ссылка на подстраницу, как подстраница в Notion. Показывает значок и название страницы. Каждая страница это отдельный документ, и хранит его ваше приложение. Блок сохраняет только id страницы и копию названия и значка для показа.

Blok никогда не загружает и не сохраняет страницу. Ваше приложение подключает страницы через четыре параметра конфигурации ниже.

У блока три особых состояния:

  • «Новая страница»: у страницы нет названия, и вместо него видно «Новая страница». Ссылка при этом работает.
  • «Страница не найдена»: resolve вернул null.
  • «Нет доступа»: resolve вернул { access: 'none' }. Сохранённое название скрыто.

Ненайденная или недоступная страница никогда не удаляется автоматически. Блок остаётся, но перестаёт работать как ссылка.

Обычный клик вызывает open. Enter тоже, когда блок выбран с клавиатуры. Без open ссылка ведёт на href.

Cmd/Ctrl-клик, Shift-клик и клик средней кнопкой работают как обычно в браузере, например открывают новую вкладку. Открытие в новой вкладке требует href. Ссылка не получает фокус, поэтому после клика отмена и Escape работают.

page не входит в defaultBlockTools. Зарегистрируйте его сами, вместе с конфигурацией.

Для HTML только для чтения вызовите blocksToHtml(data, { pageHref }) из @bloklabs/core/view. Он отрисует блок как значок и название, а ссылкой блок станет, только если передать pageHref. Представление читает только сохранённую копию, поэтому не может показать состояния «не найдена» и «нет доступа». Тело страницы оно никогда не отрисовывает.

Импорт

TypeScript
import { Page } from '@bloklabs/core/tools';

Конфигурация

ОпцияТипПо умолчаниюОписание
href(pageId: string) => stringundefined

Строит URL страницы по её id. Blok ставит его на ссылку, поэтому Cmd/Ctrl-клик или клик средней кнопкой открывает страницу в новой вкладке. Без него у ссылки нет URL. Небезопасные схемы вроде javascript: отбрасываются.

open(pageId: string, ctx: { event?: MouseEvent | KeyboardEvent }) => voidundefined

Открывает страницу. Blok вызывает его при обычном клике левой кнопкой. Без него клик ведёт на href.

Enter открывает страницу и тогда, когда блок выбран с клавиатуры (Escape, затем стрелки). В этом случае ctx.event это нажатие клавиши.

Ещё Blok вызывает его один раз сразу после того, как пользователь вставил новую страницу из тулбокса. В этом случае ctx.event пуст. Он не вызывается для страницы, вставленной через API, и когда create выбросил ошибку.

resolve(pageId: string) => PageInfo | null | undefined | Promise<…>undefined

Возвращает текущие название и значок страницы. Blok спрашивает один раз, когда блок отрисовывается. Можно вернуть значение или промис:

  • { title, icon }: сохранённая копия обновится, если изменилась.
  • null: страницы нет. Блок покажет «Страница не найдена».
  • { access: 'none' }: этому пользователю страница недоступна. Блок покажет «Нет доступа».
  • undefined: ничего не известно. Сохранённая копия остаётся.

Возвращайте полную картину. Отсутствие title или icon значит, что у страницы их нет. В режиме только для чтения свежая копия показывается, но не сохраняется. Она сохранится, когда включится редактирование. Если resolve выбросит ошибку, сохранённая копия остаётся.

create(init: { pageId: string }) => void | Promise<void>undefined

Создаёт страницу в вашем приложении. Blok вызывает его один раз с id, который сгенерировал сам, когда блок страницы вставлен без pageId. Так бывает, когда пользователь выбирает «Страница» в тулбоксе или когда ваш код вставляет блок через API.

Он никогда не вызывается при загрузке, вставке из буфера обмена, отмене, повторе, изменении от соавтора и в режиме только для чтения. Если он выбросит ошибку, блок остаётся со своим id и показывает «Страница не найдена». Blok всё равно спрашивает resolve, так что страница, которую ваше приложение всё-таки создало, появится.

Формат данных

TypeScript
interface PageData {
  pageId: string; // Id of the page. Blok mints one for a new page.
  cache?: {       // Display copy, refreshed from resolve(). It can be stale.
    title?: string;
    icon?: { type: 'emoji'; value: string } | { type: 'image'; url: string };
  };
}
// The page's body is NOT here. It is a separate document your app stores.
JSON
{
  "id": "pg001",
  "type": "page",
  "data": {
    "pageId": "p1",
    "cache": {
      "title": "Roadmap",
      "icon": { "type": "emoji", "value": "🗺" }
    }
  }
}

Пример использования

TypeScript
import { Blok } from '@bloklabs/core';
import { Page } from '@bloklabs/core/tools';
import { blocksToHtml } from '@bloklabs/core/view';

const pageUrl = (pageId) => `/pages/${pageId}`;

const editor = new Blok({
  holder: 'editor',
  tools: {
    // Not in defaultBlockTools: register it with your own config.
    page: {
      class: Page,
      config: {
        href: pageUrl,
        open: (pageId) => router.push(pageUrl(pageId)),
        resolve: (pageId) => myApi.getPageInfo(pageId), // { title, icon } | null
        create: ({ pageId }) => myApi.createPage(pageId),
      },
    },
  },
});

// Read-only HTML: pass the same URL builder.
const html = blocksToHtml(savedData, { pageHref: pageUrl });