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 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 transmite, 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 de menú desplegable personalizados.
  • noOverlay: Si es true, la superposición y los controles no se mostrarán 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: Se llama desde ReactGridLayout cuando comienza a arrastrar un elemento.
  • onDrag: Se llama desde ReactGridLayout mientras se arrastra un elemento.
  • onDragStop: Se llama desde ReactGridLayout cuando se detiene el arrastre de un elemento.
  • onResizeStart: Se llama desde ReactGridLayout cuando comienza a redimensionar un elemento.
  • onResize: Se llama desde ReactGridLayout mientras se redimensiona un elemento.
  • onResizeStop: Se llama desde 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 localStorage. 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>Custom widget with custom controls</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 layout 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 layout 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 el layout cambia, antes de que se llame a onChange. El manejador puede leer los layouts completos siguiente y anterior, leer los parches del layout 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 layout basadas en eventos

Si usas preventDefault() en el manejador del evento change, ahora puedes gestionar las actualizaciones del layout sin necesidad de reinicializar 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 personalizado para DashKit 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: Vista previa de la imagen de arrastre, por defecto se usa un png transparente de 1px en base64
  • onDragStart: Callback que se llama cuando un elemento se arrastra desde ActionPanel
  • onDragEnd: Callback que se llama cuando se suelta el elemento o se cancela el arrastre
type ItemDragProps = {
  type: string; // Tipo de plugin
  layout?: {
    // Opcional. Tamaño del elemento de layout 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 layout del elemento calculadas
  newLayout: ConfigLayout[]; // Nuevo layout 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 del panel de acciones
--dashkit-action-panel-colorColor de fondo
--dashkit-action-panel-border-colorColor del borde
--dashkit-action-panel-border-radiusRadio del borde
Variables del elemento del panel de acciones
--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-text-color-hoverColor del texto al pasar el ratón
Variables de superposición
--dashkit-overlay-border-colorColor del borde
--dashkit-overlay-colorColor de fondo
--dashkit-overlay-opacityOpacidad
Variables del elemento de la cuadrícula
--dashkit-grid-item-edit-opacityOpacidad
--dashkit-grid-item-border-radiusRadio del borde
Variables del marcador de posición
--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 DashKit, DashKit es el "shell"; aún depende de @gravity-ui/chartkit para renderizar los gráficos reales.

Errores comunes

  • Componente <Dashboard> inexistente — la exportación es <DashKit> (el shell de arrastrar y soltar es <DashKitDnDWrapper> que envuelve <DashKit> + <ActionPanel>).
  • Mutar config en lugar de usar ayudantes — usa los ayudantes 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ámetrosdefaultGlobalParams (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; restable
Acerca de la librería
Apoya la librería con una estrella
Versión
10.4.0
Última actualización
27.07.2026
Repositorio
github.com/gravity-ui/dashkit
Licencia
MIT License
Mantenedores
Colaboradores