fix(layer-editor): use stable _key for drag-and-drop sorting
Add a unique `_key` to each layer config to maintain stable sortable IDs across reorder operations. Update LayerRow to use the sortableId prop and the `use:sortable` directive. Refactor layer-crud utilities to generate and propagate `_key` values on creation and initialization.
This commit is contained in:
@@ -463,8 +463,12 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
|
||||
setState({
|
||||
cards: data,
|
||||
activeTab: 0,
|
||||
frontLayerConfigs: initLayerConfigsForSide(data, layersStr),
|
||||
backLayerConfigs: initLayerConfigsForSide(data, backLayersStr),
|
||||
frontLayerConfigs: layerCrud.withKeys(
|
||||
initLayerConfigsForSide(data, layersStr),
|
||||
),
|
||||
backLayerConfigs: layerCrud.withKeys(
|
||||
initLayerConfigsForSide(data, backLayersStr),
|
||||
),
|
||||
isLoading: false,
|
||||
});
|
||||
updateDimensions();
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import type { CardSide, LayerConfig } from "../types";
|
||||
import type { LayerConfig } from "../types";
|
||||
|
||||
/** Creates a new LayerConfig with default placement. */
|
||||
let nextKey = 1;
|
||||
|
||||
export function resetKeyCounter(value = 1) {
|
||||
nextKey = value;
|
||||
}
|
||||
|
||||
/** Creates a new LayerConfig with default placement and a stable _key. */
|
||||
export function createDefaultLayer(prop: string): LayerConfig {
|
||||
return { prop, visible: true, x1: 1, y1: 1, x2: 2, y2: 2 };
|
||||
return { prop, visible: true, x1: 1, y1: 1, x2: 2, y2: 2, _key: nextKey++ };
|
||||
}
|
||||
|
||||
/** Assign stable _key to layers that don't have one (e.g. parsed from string). */
|
||||
export function withKeys(layers: LayerConfig[]): LayerConfig[] {
|
||||
return layers.map((l) => (l._key ? l : { ...l, _key: nextKey++ }));
|
||||
}
|
||||
|
||||
/** Insert a layer and return the new array. */
|
||||
@@ -11,18 +22,33 @@ export function addLayer(configs: LayerConfig[], prop: string): LayerConfig[] {
|
||||
}
|
||||
|
||||
/** Remove a layer by index and return the new array. */
|
||||
export function removeLayer(configs: LayerConfig[], index: number): LayerConfig[] {
|
||||
export function removeLayer(
|
||||
configs: LayerConfig[],
|
||||
index: number,
|
||||
): LayerConfig[] {
|
||||
return configs.filter((_, i) => i !== index);
|
||||
}
|
||||
|
||||
/** Reorder a layer within the array. Returns new array. */
|
||||
export function reorderLayers(configs: LayerConfig[], from: number, to: number): LayerConfig[] {
|
||||
export function reorderLayers(
|
||||
configs: LayerConfig[],
|
||||
from: number,
|
||||
to: number,
|
||||
): LayerConfig[] {
|
||||
const next = [...configs];
|
||||
const [item] = next.splice(from, 1);
|
||||
next.splice(to, 0, item);
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert layer _key to flake ID (unique string) for SortableProvider.
|
||||
* Allows multiple instances of the same field name.
|
||||
*/
|
||||
export function toSortableId(layer: LayerConfig, index: number): string {
|
||||
return layer._key ? `layer-${layer._key}` : `layer-idx-${index}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate selectedLayer after a structural change (add/remove/reorder).
|
||||
* Returns the new selectedLayer index, or null.
|
||||
@@ -42,11 +68,12 @@ export function adjustSelectedLayer(
|
||||
|
||||
if (action === "reorder" && to !== undefined) {
|
||||
if (currentSelected === from) return to;
|
||||
if (from < to && currentSelected > from && currentSelected <= to) return currentSelected - 1;
|
||||
if (from > to && currentSelected >= to && currentSelected < from) return currentSelected + 1;
|
||||
if (from < to && currentSelected > from && currentSelected <= to)
|
||||
return currentSelected - 1;
|
||||
if (from > to && currentSelected >= to && currentSelected < from)
|
||||
return currentSelected + 1;
|
||||
return currentSelected;
|
||||
}
|
||||
|
||||
// "add" — doesn't affect existing indices
|
||||
return currentSelected;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user