Librerías / DashKit

DashKit

Un componente de cuadrícula para crear dashboards interactivos.

@gravity-ui/dashkit · npm package CI storybook

DashKit

Una librería para renderizar cuadrículas de dashboards.

Instalación

npm i @gravity-ui/dashkit @gravity-ui/uikit

Descripción

La librería se utiliza para alinear widgets en una cuadrícula, redimensionarlos, añadir nuevos y eliminarlos. Un widget es un componente de React. Por ejemplo, texto, gráficos e imágenes.

Los nuevos widgets se añaden a través de un sistema de plugins.

Plugins

Los plugins son necesarios para crear widgets personalizados.

Props

type ItemManipulationCallback = (eventData: {
  layout: Layout[];
  oldItem: Layout;
  newItem: Layout;
  placeholder: Layout;
  e: MouseEvent;
  element: HTMLElement;
}) => void;

interface DashKitProps {
  config: Config;
  editMode: boolean;
  onItemEdit: ({id}: {id: string}) => void;
  onChange: (data: {config: Config; itemsStateAndParams: ItemsStateAndParams}) => void;
  onDrop: (dropProps: ItemDropProps) => void;
  onItemMountChange: (item: ConfigItem, state: {isAsync: boolead; isMounted: boolean}) => void;
  onItemRender: (item: ConfigItem) => void;

  onDragStart?: ItemManipulationCallback;
  onDrag?: ItemManipulationCallback;
  onDragStop?: ItemManipulationCallback;
  onResizeStart?: ItemManipulationCallback;
  onResize?: ItemManipulationCallback;
  onResizeStop?: ItemManipulationCallback;

  defaultGlobalParams: GlobalParams;
  globalParams: GlobalParams;
  itemsStateAndParams: ItemsStateAndParams;
  settings: SettingsProps;
  context: ContextProps;
  overlayControls?: Record<string, OverlayControlItem[]> | null;
  overlayMenuItems?: MenuItems[] | null;
  noOverlay?: boolean;

  focusable?: boolean;
  onItemFocus: (item: ConfigItem) => void;
  onItemBlur: (item: ConfigItem) => void;

  draggableHandleClassName?: string;
  getPreparedCopyItemOptions?: (options: PreparedCopyItemOptions) => PreparedCopyItemOptions;
  onCopyFulfill?: (error: null | Error, data?: PreparedCopyItemOptions) => void;
}
  • config: Config.
  • editMode: Indica si el modo de edición está activado.
  • onItemEdit: Se llama al hacer clic para editar un widget.
  • onChange: Se llama cuando cambian la configuración o itemsStateAndParams.
  • onDrop: Se llama cuando un elemento se suelta desde ActionPanel usando (#DashKitDnDWrapper).
  • onItemMountChange: Se llama cuando cambia el estado de montaje de un elemento.
  • onItemRender: Se llama cuando finaliza el renderizado de un elemento.
  • defaultGlobalParams, globalParams: Parámetros que afectan a todos los widgets. En DataLens, defaultGlobalParams son los parámetros globales establecidos en la configuración del dashboard. globalParams son parámetros globales que se pueden establecer en la URL.
  • itemsStateAndParams: itemsStateAndParams.
  • settings: Configuración de DashKit.
  • context: Objeto que se pasará a todos los widgets.
  • overlayControls: Objeto que reemplaza los controles del widget durante la edición. Si no se proporciona, se mostrarán los controles básicos. Si se pasa null, solo se mostrará el botón de cierre o un menú personalizado.
  • overlayMenuItems: Elementos del menú desplegable personalizados.
  • noOverlay: Si es true, la superposición y los controles no se muestran durante la edición.
  • focusable: Si es true, los elementos de la cuadrícula serán enfocables.
  • onItemFocus: Se llama cuando focusable es true y un elemento recibe el foco.
  • onItemBlur: Se llama cuando focusable es true y un elemento pierde el foco.
  • draggableHandleClassName: Nombre de la clase CSS del elemento que hace que el widget sea arrastrable.
  • onDragStart: Llamado por ReactGridLayout cuando se inicia el arrastre de un elemento.
  • onDrag: Llamado por ReactGridLayout mientras se arrastra un elemento.
  • onDragStop: Llamado por ReactGridLayout cuando se detiene el arrastre de un elemento.
  • onResizeStart: Llamado por ReactGridLayout cuando se inicia el redimensionamiento de un elemento.
  • onResize: Llamado por ReactGridLayout mientras se redimensiona un elemento.
  • onResizeStop: Llamado por ReactGridLayout cuando se detiene el redimensionamiento de un elemento.
  • getPreparedCopyItemOptions: Se llama para convertir un elemento copiado en un objeto serializable antes de guardarlo en el almacenamiento local. Debe usarse en lugar de la prop obsoleta context.getPreparedCopyItemOptions.
  • onCopyFulfill: Se llama cuando la copia de un elemento finaliza con error=null y data definido en caso de éxito, y con error: Error sin data en caso contrario.

Uso

Configuración de DashKit

Antes de usar DashKit como un componente de React, debe configurarse.

  • Establecer el idioma

    import {configure, Lang} from '@gravity-ui/uikit';
    
    configure({lang: Lang.En});
    
  • DashKit.setSettings

    Se utiliza para la configuración global de DashKit (como márgenes entre widgets, tamaños de widget predeterminados y menú de superposición de widgets).

    import {DashKit} from '@gravity-ui/dashkit';
    
    DashKit.setSettings({
      gridLayout: {margin: [8, 8]},
      isMobile: true,
      // menu: [] as Array<MenuItem>,
    });
    
  • DashKit.registerPlugins

    Registro y configuración de plugins.

    import {DashKit} from '@gravity-ui/dashkit';
    import {pluginTitle, pluginText} from '@gravity-ui/dashkit';
    
    DashKit.registerPlugins(
      pluginTitle,
      pluginText.setSettings({
        apiHandler({text}) {
          return api.getMarkdown(text);
        },
      }),
    );
    
    DashKit.registerPlugins({
      type: 'custom',
      defaultLayout: {
        w: 10,
        h: 8,
      },
      renderer: function CustomPlugin() {
        return <div>Widget personalizado con controles personalizados</div>;
      },
    });
    

Config

export interface Config {
  salt: string; // para formar un ID único
  counter: number; // para formar un ID único, solo aumenta
  items: ConfigItem[]; // estados iniciales de los widgets
  layout: ConfigLayout[]; // posición del widget en la cuadrícula https://github.com/react-grid-layout
  aliases: ConfigAliases; // alias para parámetros ver #Params
  connections: ConfigConnection[]; // enlaces entre widgets ver #Params
}

Ejemplo de configuración:

import {DashKitProps} from '@gravity-ui/dashkit';
const config: DashKitProps['config'] = {
  salt: '0.46703554571365613',
  counter: 4,
  items: [
    {
      id: 'tT',
      data: {
        size: 'm',
        text: 'Título',
        showInTOC: true,
      },
      type: 'title',
      namespace: 'default',
      orderId: 1,
    },
    {
      id: 'Ea',
      data: {
        text: 'modo _editActive',
        _editActive: true,
      },
      type: 'text',
      namespace: 'default',
    },
    {
      id: 'zR',
      data: {
        text: '### Texto',
      },
      type: 'text',
      namespace: 'default',
      orderId: 0,
    },
    {
      id: 'Dk',
      data: {
        foo: 'bar',
      },
      type: 'custom',
      namespace: 'default',
      orderId: 5,
    },
  ],
  layout: [
    {
      h: 2,
      i: 'tT',
      w: 36,
      x: 0,
      y: 0,
    },
    {
      h: 6,
      i: 'Ea',
      w: 12,
      x: 0,
      y: 2,
    },
    {
      h: 6,
      i: 'zR',
      w: 12,
      x: 12,
      y: 2,
    },
    {
      h: 4,
      i: 'Dk',
      w: 8,
      x: 0,
      y: 8,
    },
  ],
  aliases: {},
  connections: [],
};

Añadir un nuevo elemento a la configuración:

const newLayout = updateLayout: [
  {
    h: 6,
    i: 'Ea',
    w: 12,
    x: 0,
    y: 6,
  },
  {
    h: 4,
    i: 'Dk',
    w: 8,
    x: 0,
    y: 12,
  },
];

const newConfig = DashKit.setItem({
  item: {
    data: {
      text: `Algún texto`,
    },
    namespace: 'default',
    type: 'text',
    // Opcional. Si se necesita insertar un nuevo elemento en el diseño actual con dimensiones predefinidas
    layout: { // El elemento actual se inserta antes de 'Ea'
      h: 6,
      w: 12,
      x: 0,
      y: 2,
    },,
  },
  config: config,
  options: {
    // Opcional. Nuevos valores de diseño para elementos existentes cuando se suelta un nuevo elemento desde ActionPanel
    updateLayout: newLayout,
  },
});

Cambiar un elemento existente en la configuración:

const newConfig = DashKit.setItem({
  item: {
    id: 'tT', // item.id
    data: {
      size: 'm',
      text: `Nuevo título`,
    },
    namespace: 'default',
    type: 'title',
  },
  config: config,
});

Eliminar un elemento de la configuración:

import {DashKitProps} from '@gravity-ui/dashkit';

const oldItemsStateAndParams: DashKitProps['itemsStateAndParams'] = {};

const {config: newConfig, itemsStateAndParams} = DashKit.removeItem({
  id: 'tT', // item.id
  config: config,
  itemsStateAndParams: this.state.itemsStateAndParams,
});

Parámetros

type Params = Record<string, string | string[]>;

DashKit genera parámetros según los parámetros predeterminados para widgets, enlaces y alias. Estos parámetros son necesarios para la biblioteca ChartKit.

Orden de generación:

  1. defaultGlobalParams
  2. Parámetros predeterminados del widget item.default
  3. globalParams
  4. Parámetros de itemsStateAndParams según la cola.

itemsStateAndParams

Objeto que almacena los parámetros y estados de los widgets, así como una cola de cambios de parámetros. Tiene un campo __meta__ para almacenar la cola e información meta.

interface StateAndParamsMeta = {
    __meta__: {
        queue: {id: string}[]; // cola
        version: number; // versión actual de itemsStateAndParams
    };
}

Y también estados y parámetros de los widgets:

interface ItemsStateAndParamsBase {
  [itemId: string]: {
    state?: Record<string, any>;
    params?: Params;
  };
}
type ItemsStateAndParams = StateAndParamsMeta & ItemsStateAndParamsBase;

Eventos experimentales de DashKit

Experimental: esta API puede cambiar en versiones menores.

DashKit expone una API de eventos de instancia experimental. Utiliza una referencia de componente y suscríbete con dashkitRef.current?.on(eventName, handler). El método devuelve una función de cancelación de suscripción.

El primer evento admitido es change. Se emite cuando cambia el diseño, antes de que se llame a onChange. El manejador puede leer los diseños completos anterior y siguiente, leer los parches del diseño o llamar a preventDefault() para detener la llamada predeterminada a onChange.

import React from 'react';
import {DashKit} from '@gravity-ui/dashkit';
import type {DashKitChangeEvent} from '@gravity-ui/dashkit';

function Dashboard() {
  const dashkitRef = React.useRef<DashKit>(null);

  React.useEffect(() => {
    const unsubscribe = dashkitRef.current?.on('change', (event: DashKitChangeEvent) => {
      console.log(event.patches);

      if (event.patches.length > 0) {
        event.preventDefault();
      }
    });

    return () => unsubscribe?.();
  }, []);

  return <DashKit ref={dashkitRef} config={config} editMode={true} onChange={onChange} />;
}
type DashKitLayoutPatch = Pick<ConfigLayout, 'i'> &
  Partial<Pick<ConfigLayout, 'x' | 'y' | 'w' | 'h' | 'parent'>>;

type DashKitChangeEvent = {
  patches: DashKitLayoutPatch[];
  layout: ConfigLayout[];
  previousLayout: ConfigLayout[];
  preventDefault: () => void;
  readonly defaultPrevented: boolean;
};

Actualizaciones de diseño basadas en eventos

Si utilizas preventDefault() en el manejador del evento change, ahora puedes gestionar las actualizaciones de diseño sin necesidad de volver a inicializar la prop config. DashKit mantiene una línea base interna y calcula los parches de forma incremental:

function Dashboard() {
  const [config, setConfig] = useState(initialConfig);
  const dashkitRef = useRef<DashKit>(null);

  useEffect(() => {
    const unsubscribe = dashkitRef.current?.on('change', (event) => {
      event.preventDefault(); // No llamar a onChange

      // Envía solo los parches incrementales a tu backend
      sendPatches(event.patches);

      // No es necesario llamar a setConfig({ ...config, layout: event.layout })
      // DashKit mantiene el estado visual internamente
    });

    return unsubscribe;
  }, []);

  return <DashKit ref={dashkitRef} config={config} editMode onChange={() => {}} />;
}

Importante: Si más tarde actualizas config.layout desde las props (por ejemplo, desde una sincronización del servidor), DashKit restablecerá su línea base interna para que coincida con la nueva prop. Esto garantiza la compatibilidad tanto con flujos de trabajo basados en eventos como controlados.

Menú

Puedes especificar un menú superpuesto de widgets DashKit personalizado en modo de edición.

type MenuItem = {
  id: string; // id único
  title?: string; // título de texto
  icon?: ReactNode; // nodo de icono
  iconSize?: number | string; // tamaño del icono en px como número o como cadena con unidades
  handler?: (item: ConfigItem) => void; // manejador de acción de elemento personalizado
  visible?: (item: ConfigItem) => boolean; // manejador de visibilidad opcional para filtrar elementos del menú
  className?: string; // propiedad de clase personalizada
};

// usa un array de elementos de menú en la configuración
<Dashkit overlayMenuItems={[] as Array<MenuItem> | null} />

[obsoleto]
// la propiedad overlayMenuItems tiene mayor prioridad que el menú setSettings
DashKit.setSettings({menu: [] as Array<MenuItem>});

Arrastrar y soltar elementos desde ActionPanel

DashKitDnDWrapper

type DraggedOverItem = {
  h: number;
  w: number;
  type: string;
  parent: string;
  i?: number;
};

interface DashKitDnDWrapperProps {
  dragImageSrc?: string;
  onDragStart?: (dragProps: ItemDragProps) => void;
  onDragEnd?: () => void;
  onDropDragOver?: (
    draggedItem: DraggedOverItem,
    sharedItem: DraggedOverItem | null,
  ) => void | boolean;
}
  • dragImageSrc: Imagen de arrastre personalizada opcional. Por defecto, se muestra el elemento de ActionPanel arrastrado como vista previa.
  • onDragStart: Callback que se llama cuando un elemento se arrastra desde ActionPanel.
  • onDragEnd: Callback que se llama cuando se suelta un elemento o se cancela el arrastre.
type ItemDragProps = {
  type: string; // Tipo de plugin
  layout?: {
    // Opcional. Tamaño del elemento de diseño para vista previa e inicialización
    w?: number;
    h?: number;
  };
  extra?: any; // Contexto de usuario personalizado
};
type ItemDropProps = {
  commit: () => void; // Callback que debe llamarse después de que se realicen todas las operaciones de configuración
  dragProps: ItemDragProps; // Props de arrastre del elemento
  itemLayout: ConfigLayout; // Dimensiones del diseño del elemento calculadas
  newLayout: ConfigLayout[]; // Nuevo diseño después de soltar el elemento
};

Ejemplo:

const overlayMenuItems = [
  {
    id: 'chart',
    icon: <Icon data={ChartColumn} />,
    title: 'Chart',
    qa: 'chart',
    dragProps: { // ItemDragProps
        type: 'custom', // Tipo de plugin registrado
    },
  }
]

const onDrop = (dropProps: ItemDropProps) => {
  // ... añade el elemento a tu configuración
  dropProps.commit();
}

<DashKitDnDWrapper>
  <DashKit editMode={true} config={config} onChange={onChange} onDrop={onDrop} />
  <ActionPanel items={overlayMenuItems} />
</DashKitDnDWrapper>

API CSS

NombreDescripción
Variables de Action panel
--dashkit-action-panel-colorColor de fondo
--dashkit-action-panel-border-colorColor del borde
--dashkit-action-panel-border-radiusRadio del borde
Variables de elementos de Action panel
--dashkit-action-panel-item-colorColor de fondo
--dashkit-action-panel-item-text-colorColor del texto
--dashkit-action-panel-item-color-hoverColor de fondo al pasar el ratón
--dashkit-action-panel-item-color-draggingColor de fondo al arrastrar
--dashkit-action-panel-item-text-color-hoverColor del texto al pasar el ratón
--dashkit-action-panel-item-text-color-draggingColor del texto al arrastrar
Variables de Overlay
--dashkit-overlay-border-colorColor del borde
--dashkit-overlay-colorColor de fondo
--dashkit-overlay-opacityOpacidad
Variables de elementos de Grid
--dashkit-grid-item-edit-opacityOpacidad
--dashkit-grid-item-border-radiusRadio del borde
Variables de Placeholder
--dashkit-placeholder-colorColor de fondo
--dashkit-placeholder-opacityOpacidad

Ejemplo de uso

.custom-theme-wrapper {
  --dashkit-grid-item-edit-opacit: 1;
  --dashkit-overlay-color: var(--g-color-base-float);
  --dashkit-overlay-border-color: var(--g-color-base-float);
  --dashkit-overlay-opacity: 0.5;

  --dashkit-action-panel-border-color: var(--g-color-line-info);
  --dashkit-action-panel-color: var(--g-color-base-float-accent);
  --dashkit-action-panel-border-radius: var(--g-border-radius-xxl);
}
// ....

const CustomThemeWrapper = (props: {
  dashkitProps: DashkitProps;
  actionPanelProps: ActionPanelProps;
}) => {
  return (
    <div className="custom-theme-wrapper">
      <Dashkit {...props.dashkitProps} />
      <ActionPanel {...props.actionPanelProps} />
    </div>
  );
};

Desarrollo

Compilación y observación

  • Compilar dependencias npm ci
  • Compilar proyecto npm run build
  • Compilar storybook npm run start

Por defecto, storybook se ejecuta en http://localhost:7120/. Los cambios nuevos en un proyecto no siempre se detectan cuando storybook está en ejecución, por lo que es mejor recompilar un proyecto manualmente y reiniciar storybook.

Ejemplo de configuración de nginx para desarrollo en una máquina de desarrollo

server {
    server_name dashkit.username.ru;

    include common/ssl;

    access_log /home/username/logs/common.access.log;
    error_log /home/username/logs/common.error.log;

    root /home/username/projects/dashkit;

    location / {
        try_files $uri @node;
    }

    location @node {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:7120;
        proxy_redirect off;
    }
}

Licencia

Distribuido bajo la Licencia MIT. Ver LICENSE para más detalles.

Para agentes de IA

Un compositor de cuadrículas de paneles que organiza widgets redimensionables y arrastrables en una cuadrícula responsiva a través de un sistema de plugins: recurre a él cuando crees un panel editable por el usuario (agregar/mover/redimensionar/eliminar widgets) en lugar de colocar gráficos o paneles individuales manualmente.

Cuándo usar

  • Renderizar un panel configurable donde los widgets se posicionan, redimensionan y reorganizan en una cuadrícula (basado en react-grid-layout).
  • Diseños editables por el usuario: agregar/eliminar widgets de un panel de acciones, arrastrar y soltar, modo de edición con controles superpuestos.
  • Widgets basados en plugins donde cada tipo de widget (título, texto, gráfico, personalizado) se registra una vez y se controla mediante una config.

Cuándo no usar

  • Para un único gráfico o panel fijo, usa @gravity-ui/charts o @gravity-ui/chartkit directamente: la maquinaria de cuadrícula/plugins es una sobrecarga para un solo widget.
  • Para una cuadrícula responsiva de propósito general que no sea un panel de widgets, usa react-grid-layout directamente.
  • Para incrustar widgets de gráficos basados en ChartKit dentro de un panel de DashKit, DashKit es el "contenedor"; aún depende de @gravity-ui/chartkit para renderizar los gráficos reales.

Errores comunes

  • Componente <Dashboard> inexistente — la exportación es <DashKit> (el contenedor de arrastrar y soltar es <DashKitDnDWrapper> que envuelve <DashKit> + <ActionPanel>).
  • Mutar config en lugar de usar helpers — usa los helpers estáticos DashKit.setItem({...}) / DashKit.removeItem({...}) para agregar/cambiar/eliminar elementos, de modo que el diseño y los IDs se mantengan consistentes.
  • Olvidar DashKit.setSettings / DashKit.registerPlugins — el componente debe configurarse (idioma, configuración de la cuadrícula, registro de plugins) antes de renderizarse, o los widgets no mostrarán nada.
  • Confundir las dos props de parámetros — defaultGlobalParams (valores predeterminados a nivel de panel) vs globalParams (globales que se pueden anular por URL); ambos fluyen a la cola de generación de parámetros consumida por ChartKit.
  • Llamar a onChange manualmente con el evento change — cuando usas event.preventDefault() en el manejador experimental change, DashKit mantiene el estado visual internamente; restablecer config.layout desde las props restablece esa línea base.

Documentación para agentes de IA

La documentación legible por agentes para la versión instalada se encuentra en node_modules/@gravity-ui/dashkit/build/docs/INDEX.md.

Acerca de la librería
Apoya la librería con una estrella
Versión
10.5.2
Última actualización
06.10.2026
Repositorio
github.com/gravity-ui/dashkit
Licencia
MIT License
Mantenedores
Colaboradores