# FlexiWidget

> A component, such as a tile, that lives inside a target. You can move a widget within its target or to another target.

Source: https://www.flexiboards.dev/docs/components/widget

Framework: Svelte

## FlexiWidget (component)

**Props**

| Name | Type | Description |
| --- | --- | --- |
| `controller` (bindable) | `FlexiWidgetController \| undefined` | Optional. The controller managing this component's state and behaviour. Bind to it to access the component's imperative API. |
| `onfirstcreate` | `((instance: FlexiWidgetController) => void) \| undefined` | Optional. Fires when the component's controller is first created. |
| `component` | `(Component)` | Optional. The component that is rendered by this widget. |
| `draggability` | `'none' \| 'movable' \| 'full' \| undefined` | Optional. The draggability of the widget. Default: `full`. |
| `resizability` | `'none' \| 'horizontal' \| 'vertical' \| 'both' \| undefined` | Optional. The resizability of the widget. Default: `none`. |
| `componentProps` | `Record<string, any> \| undefined` | Optional. The props applied to the component rendered, if it has one. |
| `transition` | `FlexiWidgetTransitionConfiguration \| undefined` | Optional. The transition configuration for this widget. |
| `grabTrigger` | `FlexiWidgetTriggerConfiguration \| undefined` | Optional. The configuration for how pointer events should trigger a grab event on the widget. E.g. a long press. |
| `resizeTrigger` | `FlexiWidgetTriggerConfiguration \| undefined` | Optional. The configuration for how pointer events should trigger a resize event on the widget. E.g. a long press. |
| `minWidth` | `number \| undefined` | Optional. The minimum width of the widget in units. Defaults to 1, cannot be less than 1. |
| `minHeight` | `number \| undefined` | Optional. The minimum height of the widget in units. Defaults to 1, cannot be less than 1. |
| `maxWidth` | `number \| undefined` | Optional. The maximum width of the widget in units. Defaults to Infinity, cannot be less than 1. |
| `maxHeight` | `number \| undefined` | Optional. The maximum height of the widget in units. Defaults to Infinity, cannot be less than 1. |
| `id` | `string \| undefined` | Optional. A stable identifier for this widget, used for persistence and layout import/export. Read when the widget is created. |
| `type` | `string \| undefined` | Optional. The registry key used when creating this widget. Changing the prop does not recreate it. |
| `x` | `number \| undefined` | Optional. The starting column (x-coordinate) of the widget. After creation, use moveTo() to move it. |
| `y` | `number \| undefined` | Optional. The starting row (y-coordinate) of the widget. After creation, use moveTo() to move it. |
| `width` | `number \| undefined` | Optional. The initial width of the widget in grid units. Changing the prop does not resize an existing widget. |
| `height` | `number \| undefined` | Optional. The initial height of the widget in grid units. Changing the prop does not resize an existing widget. |
| `metadata` | `Record<string, any> \| undefined` | Optional. Arbitrary metadata associated with this widget, carried through layout export/import. |
| `class` (bindable) | `(ClassValue \| ((widget: FlexiWidgetController) => ClassValue))` | Optional. The class names to apply to this widget. Either a class value, or a function deriving one from the widget's state. |
| `children` (bindable) | `(Snippet<[{ widget: FlexiWidgetController }]>)` | Optional. The content rendered within the widget. |

Widget content is rendered either from `children`, which receives the widget's controller, or from the `component` prop (with `componentProps`), or both.

```svelte
<script lang="ts">
	import { FlexiWidget } from '@flexiboards/svelte';
</script>

<FlexiWidget
	draggability="full"
	resizability="both"
	width={2}
	height={1}
	class={(widget) => ['rounded-lg border p-4', widget.isGrabbed && 'opacity-50']}
>
	{#snippet children({ widget })}
		<span>{widget.width} × {widget.height}</span>
	{/snippet}
</FlexiWidget>
```

## Adding widgets later

You can mount new `FlexiWidget` declarations after the target has loaded. Each declaration uses the same placement rules as `target.createWidget()`: flow grids follow their placement strategy, and free-form grids check coordinates, dimensions, and collisions.

```svelte
<script lang="ts">
	import { FlexiSortable, FlexiWidget } from '@flexiboards/svelte';
	let notes = $state([1]);
</script>

<div class="w-full space-y-3">
	<button
		type="button"
		class="rounded border px-3 py-2"
		onclick={() => (notes = [...notes, notes.length + 1])}>Add note</button
	>
	<FlexiSortable class="gap-2">
		{#each notes as note (note)}
			<FlexiWidget id={`note-${note}`} class="rounded border p-3">Note {note}</FlexiWidget>
		{/each}
	</FlexiSortable>
</div>
```

Keep list keys stable. A declaration registers once per mount; rerendering it updates its props without adding another widget. The board owns the created widget, so removing its declaration does not delete it. Use `widget.delete()` or `target.clear()` to remove widgets.

An accepted addition fires `onfirstcreate` and reports the new layout through `onLayoutChange`. If placement fails, the widget is not created and a warning explains the failure. Freeing space later does not automatically retry a rejected declaration.

## FlexiWidgetController

You can access the controller by binding to the `controller` prop, from the `onfirstcreate` callback, or from the `children` snippet parameter. Inside a component rendered by the `component` prop, call `getFlexiwidgetCtx()`.

```svelte
<script lang="ts">
	import { getFlexiwidgetCtx } from '@flexiboards/svelte';

	const widget = getFlexiwidgetCtx();
</script>

<span>{widget.isGrabbed ? 'Moving' : 'Idle'}</span>
```

Use the `FlexiWidgetController` to read widget state directly.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `target` | `FlexiTargetController \| undefined` | The target this widget is under. Undefined until the widget is dropped in the board. |
| `ref` | `HTMLElement \| undefined` | The DOM element bound to this widget. |
| `isShadow` | `boolean` | Whether this widget is a shadow dropzone widget. |
| `isGrabbed` (readonly) | `boolean` | Whether this widget is grabbed. |
| `isResizing` (readonly) | `boolean` | Whether this widget is being resized. |
| `dropRejected` | `boolean` | Whether the widget is being grabbed or resized over a target that cannot accept it where it is: the drop would be rejected on release and the widget would return to where it came from. |
| `isInterpolating` (readonly) | `boolean` | Whether the widget is currently animating to a new position or size, e.g. mid drop flight. Useful for styling that should only apply at rest, such as hover effects that would otherwise fire as the widget lands under the pointer. |
| `currentAction` | `WidgetAction \| null` | When the widget is being grabbed, this contains information that includes its position, size and offset. When this is null, the widget is not being grabbed. |
| `draggable` (readonly) | `boolean` | Whether the widget can move at all: its `draggability` is not `'none'`. Read-only; set `draggability` to change it. |
| `draggability` | `'none' \| 'movable' \| 'full'` | The draggability of the widget. |
| `isGrabbable` (readonly) | `boolean` | Whether the widget can be grabbed. |
| `isMovable` (readonly) | `boolean` | Whether the widget can be moved. |
| `resizability` | `'none' \| 'horizontal' \| 'vertical' \| 'both'` | The resizability of the widget. |
| `resizable` (readonly) | `boolean` | Whether the widget is resizable. |
| `width` (readonly) | `number` | The width in units of the widget. |
| `height` (readonly) | `number` | The height in units of the widget. |
| `component` | `(Component) \| undefined` | The component that is rendered by this widget. |
| `componentProps` | `Record<string, any> \| undefined` | The props applied to the component rendered, if it has one. |
| `snippet` | `(Snippet<[{ widget: FlexiWidgetController }]>) \| undefined` | The render function used for this widget's content. |
| `className` | `unknown` | The class name that is applied to this widget. |
| `x` (readonly) | `number` | Gets the column (x-coordinate) of the widget. This value is readonly and is managed by the target. |
| `y` (readonly) | `number` | Gets the row (y-coordinate) of the widget. This value is readonly and is managed by the target. |
| `metadata` | `Record<string, any> \| undefined` | The metadata associated with this widget, if any. |
| `grabTrigger` (readonly) | `FlexiWidgetTriggerConfiguration` | Gets the configuration for how pointer events should trigger widget grabs (either on the widget directly or on a grabber). |
| `resizeTrigger` (readonly) | `FlexiWidgetTriggerConfiguration` | Gets the configuration for how pointer events should trigger widget resizing on a resizer. |
| `transitionConfig` (readonly) | `FlexiWidgetTransitionConfiguration` | Gets the transition configuration for this widget. |
| `hasGrabbers` (readonly) | `boolean` | Whether the widget has any grabbers attached. |
| `hasResizers` (readonly) | `boolean` | Whether the widget has any resizers attached |
| `isBeingDropped` | `boolean` | Whether the widget is currently being dropped after a drag operation. |
| `minWidth` (readonly) | `number` | The minimum width of the widget in units. |
| `minHeight` (readonly) | `number` | The minimum height of the widget in units. |
| `maxWidth` (readonly) | `number` | The maximum width of the widget in units. |
| `maxHeight` (readonly) | `number` | The maximum height of the widget in units. |
| `userProvidedId` (readonly) | `string \| undefined` | The user-provided stable identifier for this widget, if any. This is used for persistence and layout import/export. |
| `type` (readonly) | `string \| undefined` | The type of this widget (registry key for looking up configuration). |

**Methods**

| Name | Type | Description |
| --- | --- | --- |
| `delete` | `() => void` | Deletes this widget from its target and board. Fires the board's `onWidgetDelete` and `onLayoutChange`. |
| `moveTo` | `(options: { target?: FlexiTargetController; x?: number; y?: number }) => boolean` | Moves this widget through the controller API, with no user interaction: to a position in its own target, to another target (at a position, or wherever that target's grid puts it), or both. Runs the grid's placement rules but not `canDrop`, which is for user drops. Fires `onLayoutChange`. |

## FlexiWidgetConfiguration

`FlexiWidget` accepts configuration as props. Changes to rendering, metadata, interaction options, size limits, and transitions update the existing widget. `id`, `type`, `x`, `y`, `width`, and `height` initialize the widget; changing those props does not recreate or reposition it. Use `moveTo()` for movement, or import a layout to replace widget positions and sizes. See [Configuration reactivity](https://www.flexiboards.dev/docs/configuration#reactivity).

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `draggability` | `'none' \| 'movable' \| 'full' \| undefined` | Optional. The draggability of the widget. Default: `full`. |
| `resizability` | `'none' \| 'horizontal' \| 'vertical' \| 'both' \| undefined` | Optional. The resizability of the widget. Default: `none`. |
| `snippet` | `(Snippet<[{ widget: FlexiWidgetController }]>)` | Optional. The render function used for this widget's content. |
| `component` | `(Component)` | Optional. The component that is rendered by this widget. |
| `componentProps` | `Record<string, any> \| undefined` | Optional. The props applied to the component rendered, if it has one. |
| `className` | `(ClassValue \| ((widget: FlexiWidgetController) => ClassValue)) \| undefined` | Optional. The class names to apply to this widget. |
| `transition` | `FlexiWidgetTransitionConfiguration \| undefined` | Optional. The transition configuration for this widget. |
| `grabTrigger` | `FlexiWidgetTriggerConfiguration \| undefined` | Optional. The configuration for how pointer events should trigger a grab event on the widget. E.g. a long press. |
| `resizeTrigger` | `FlexiWidgetTriggerConfiguration \| undefined` | Optional. The configuration for how pointer events should trigger a resize event on the widget. E.g. a long press. |
| `minWidth` | `number \| undefined` | Optional. The minimum width of the widget in units. Defaults to 1, cannot be less than 1. |
| `minHeight` | `number \| undefined` | Optional. The minimum height of the widget in units. Defaults to 1, cannot be less than 1. |
| `maxWidth` | `number \| undefined` | Optional. The maximum width of the widget in units. Defaults to Infinity, cannot be less than 1. |
| `maxHeight` | `number \| undefined` | Optional. The maximum height of the widget in units. Defaults to Infinity, cannot be less than 1. |
| `id` | `string \| undefined` | Optional. A stable identifier for this widget, used for persistence and layout import/export. Read when the widget is created. |
| `type` | `string \| undefined` | Optional. The registry key used when creating this widget. Changing the prop does not recreate it. |
| `x` | `number \| undefined` | Optional. The starting column (x-coordinate) of the widget. After creation, use moveTo() to move it. |
| `y` | `number \| undefined` | Optional. The starting row (y-coordinate) of the widget. After creation, use moveTo() to move it. |
| `width` | `number \| undefined` | Optional. The initial width of the widget in grid units. Changing the prop does not resize an existing widget. |
| `height` | `number \| undefined` | Optional. The initial height of the widget in grid units. Changing the prop does not resize an existing widget. |
| `metadata` | `Record<string, any> \| undefined` | Optional. Arbitrary metadata associated with this widget, carried through layout export/import. |

## FlexiWidgetTransitionConfiguration

The `transition` property of a widget's configuration (or of `widgetDefaults`). See the [Transitions](https://www.flexiboards.dev/docs/transitions) guide for presets and the animation adapters.

**Properties**

| Name | Type | Description |
| --- | --- | --- |
| `move` | `({ duration?: number; easing?: string } \| AnimationAdapter)` | Optional. Plays when a widget moves between cells of a grid, including when it is pushed aside by another widget. Omit to play no animation. |
| `drop` | `({ duration?: number; easing?: string } \| AnimationAdapter)` | Optional. Plays when a grabbed widget is released and settles into its cell. Omit to play no animation. |
| `resize` | `({ duration?: number; easing?: string } \| AnimationAdapter)` | Optional. Plays when a widget is released from a resize and settles at its new size. Omit to play no animation. |

## Accessibility

Each placed widget renders as `role="gridcell"` with one-based `aria-colindex` and `aria-rowindex`, plus `aria-colspan` and `aria-rowspan`. The held widget temporarily uses `role="group"`; its preview is hidden and inert. The `data-flexi-widget` attribute remains present in every state. A grabbable widget is in the tab order unless it contains a [FlexiGrab](https://www.flexiboards.dev/docs/components/grab), in which case the handle is.

| Key               | Effect                                                                                                        |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| `Enter`  | Grabs the focused widget; while grabbed, drops it.                                                            |
| Arrow keys        | Moves the grabbed widget. `Shift` for larger steps, `Ctrl` / `Cmd` for finer ones. |
| `Escape` | Cancels a grab or resize.                                                                                     |

Grabs, resizes, releases, and rejected drops are announced through the board's live region. Styling the `isGrabbed`, `isShadow`, and `dropRejected` states is up to you; see [Widget Rendering](https://www.flexiboards.dev/docs/widget-rendering#styling-by-state). Full details in [Accessibility](https://www.flexiboards.dev/docs/accessibility).
