Grid locks and interaction events
Toggle global editable and per-widget locked state while observing move, resize, and commit events.
Actions to try
Section titled “Actions to try”- Drag A by its title. The log shows
beforeMove:a,commitfor changed geometry, and finallyafterMove:a. - B starts with
locked: true; dragging it does not change its layout. - Lock all also blocks A from moving or resizing. Unlock all restores A’s interaction.
- Unlock B enables interaction for B.
- Drag A’s bottom-right resize handle to observe
beforeResize:aandafterResize:a. Geometry changes also appear in JSON. - Clear events clears only the log; Reset restores initial geometry and lock settings.
Two levels of locking
Section titled “Two levels of locking”| Setting | Scope |
|---|---|
editable={false} |
Blocks pointer movement and resizing in this Grid. |
widget.locked |
Applies constraints to that individual widget. |
commands.updateWidget |
Updates the consumer-owned React widget definition. |
Locks constrain user interaction; they are not authorization rules blocking application state commands. Both global editable and widget constraints must permit interaction.
Events and state
Section titled “Events and state”onBeforeMove, onAfterMove, onBeforeResize, and onAfterResize receive { id, layout } notifications. They return void; returning false from a before callback is not a cancellation API. Set constraints before the interaction to block it.
onLayoutCommit passes the snapshot to commands.applyLayoutSnapshot to update React state. Identical commits are suppressed; do not assume exactly one commit for each pointer input. Initial engine synchronization can appear in the log, so clear it before a comparison. The log retains the most recent 16 entries.
This example displays start/end and commit notifications. Frame-level onMove/onResize, pixel sizes from onWidgetResizeFrame, and command transitions from onLayoutMutation serve different purposes.
Related documentation
Section titled “Related documentation”Widget interaction guide · Events guide · State commands · Grid ref
Try it live
Section titled “Try it live”import { useState } from 'react';import { DashboardGrid, useDashboardGrid } from 'comins-grid-layout';import 'gridstack/dist/gridstack.min.css';import 'comins-grid-layout/styles.css';const engineOptions = { cellHeight: 60, margin: 6, float: true, animate: false, alwaysShowResizeHandle: true };export default function GridLockEventsDemo({ lang = 'ko' }: { lang?: string }) { const ko = lang === 'ko'; const [editable, setEditable] = useState(true); const [events, setEvents] = useState<string[]>([]); const grid = useDashboardGrid({ initialColumns: 12, initialWidgets: [ { id: 'a', title: 'A', layout: { id: 'a', x: 0, y: 0, w: 4, h: 2 } }, { id: 'b', title: 'B', locked: true, layout: { id: 'b', x: 7, y: 0, w: 4, h: 2 } }, ] }); const log = (name: string, id = '') => setEvents(current => [...current, `${name}${id ? ':' + id : ''}`].slice(-16)); const locked = grid.widgets.find(widget => widget.id === 'b')?.locked === true; return <div className="demo grid-demo" data-demo="grid-lock-events"> <div className="demo-toolbar"> <button onClick={() => setEditable(value => !value)}>{editable ? (ko ? '전체 잠금' : 'Lock all') : (ko ? '전체 잠금 해제' : 'Unlock all')}</button> <button onClick={() => grid.commands.updateWidget('b', { locked: !locked })}>{locked ? (ko ? 'B 잠금 해제' : 'Unlock B') : (ko ? 'B 잠금' : 'Lock B')}</button> <button onClick={() => setEvents([])}>{ko ? '이벤트 지우기' : 'Clear events'}</button> <button onClick={() => { grid.commands.resetLayout(); setEditable(true); setEvents([]); }}>{ko ? '초기화' : 'Reset'}</button> </div> <DashboardGrid widgets={grid.widgets} columns={grid.columns} editable={editable} showControls={false} engineOptions={engineOptions} onLayoutCommit={snapshot => { grid.commands.applyLayoutSnapshot(snapshot); log('commit'); }} onBeforeMove={event => log('beforeMove', event.id)} onAfterMove={event => log('afterMove', event.id)} onBeforeResize={event => log('beforeResize', event.id)} onAfterResize={event => log('afterResize', event.id)} renderWidget={widget => <div className="widget-content">{widget.id === 'b' && locked ? (ko ? '잠금 상태' : 'Locked') : (ko ? '이동·크기 변경 가능' : 'Move and resize')}</div>} /> <p role="status">editable: {String(editable)} · B.locked: {String(locked)}</p> <pre data-lock-events>{JSON.stringify(events, null, 2)}</pre> <details><summary>{ko ? '현재 배치' : 'Current layout'}</summary><pre data-lock-layout>{JSON.stringify(grid.commands.serializeLayout(), null, 2)}</pre></details> </div>;}