Skip to content

Commit

Permalink
add iterator index state (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bloomca authored Jun 6, 2024
1 parent c919a89 commit 6da1a5c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 16 deletions.
68 changes: 68 additions & 0 deletions integration-tests/create-state/use-value-iterator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,72 @@ describe("createState", () => {
expect(listElement.childNodes.length).toBe(4);
expect(unmountSpy).toHaveBeenCalledTimes(1);
});

test("value iterator correctly updates indices after changing order", async () => {
const user = userEvent.setup();
type Item = { id: number; text: string };
const item1: Item = { id: 1, text: "first item" };
const item2: Item = { id: 2, text: "second item" };
const item3: Item = { id: 3, text: "third item" };
const item4: Item = { id: 4, text: "fourth item" };
const item5: Item = { id: 5, text: "fifth item" };
let items = [item1, item2, item3, item4, item5];
function App() {
const itemsState = createState(items);

return createElement("div", {
children: [
createElement("button", {
"data-testid": "button",
onClick: () => itemsState.setValue(items),
}),
createElement("div", {
"data-testid": "container",
children: [
itemsState.useValueIterator<Item>(
{ key: "id" },
({ elementState, indexState }) =>
createElement("div", {
children: [
createElement("div", {
children: indexState.useValue(),
}),
".",
createElement("div", {
children: elementState.useValueSelector(
(item) => item.text
),
}),
],
})
),
],
}),
],
});
}

cleanup = attachComponent({
htmlElement: document.body,
component: createElement(App),
});

const container = screen.getByTestId("container");
const children = container.childNodes;
expect(children.length).toBe(5);
expect(children[0].textContent).toBe("0.first item");
expect(children[1].textContent).toBe("1.second item");
expect(children[2].textContent).toBe("2.third item");
expect(children[3].textContent).toBe("3.fourth item");
expect(children[4].textContent).toBe("4.fifth item");

items = [item5, item3, item1, item4, item2];
await user.click(screen.getByTestId("button"));

expect(children[0].textContent).toBe("0.fifth item");
expect(children[1].textContent).toBe("1.third item");
expect(children[2].textContent).toBe("2.first item");
expect(children[3].textContent).toBe("3.fourth item");
expect(children[4].textContent).toBe("4.second item");
});
});
44 changes: 28 additions & 16 deletions src/hooks/create-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export type State<ValueType> = {
},
cb: (props: {
elementState: State<Element>;
index: number;
indexState: State<number>;
}) => VelesElement | VelesComponent
): VelesComponent | VelesElement | null;
getValue(): ValueType;
Expand All @@ -73,15 +73,16 @@ export type State<ValueType> = {
type TrackingParams = {
cb: (props: {
elementState: State<any>;
index: number;
indexState: State<number>;
}) => VelesElement | VelesComponent;
selector?: (value: unknown) => any[];
renderedElements: [VelesElement | VelesComponent, string, State<unknown>][];
key: string | ((options: { element: unknown; index: number }) => string);
elementsByKey: {
[key: string]: {
elementState: State<unknown>;
index: number;
indexState: State<number>;
indexValue: number;
node: VelesElement | VelesComponent;
};
};
Expand Down Expand Up @@ -360,7 +361,8 @@ function createState<T>(
const newElementsByKey: {
[key: string]: {
elementState: State<unknown>;
index: number;
indexState: State<number>;
indexValue: number;
node: VelesElement | VelesComponent;
};
} = {};
Expand Down Expand Up @@ -394,7 +396,11 @@ function createState<T>(
renderedExistingElements[calculatedKey] = true;
const currentValue = existingElement.elementState.getValue();
if (currentValue !== element) {
existingElement.elementState.setValue(() => element);
existingElement.elementState.setValue(element);
}
const currentIndex = existingElement.indexState.getValue();
if (currentIndex !== index) {
existingElement.indexState.setValue(index);
}

newRenderedElements.push([
Expand All @@ -404,17 +410,20 @@ function createState<T>(
]);
newElementsByKey[calculatedKey] = {
elementState: existingElement.elementState,
index,
indexState: existingElement.indexState,
indexValue: index,
node: existingElement.node,
};
} else {
const elementState = createState(element);
const node = cb({ elementState, index });
const indexState = createState(index);
const node = cb({ elementState, indexState });

newRenderedElements.push([node, calculatedKey, elementState]);
newElementsByKey[calculatedKey] = {
elementState,
index,
indexState,
indexValue: index,
node,
};
}
Expand Down Expand Up @@ -460,17 +469,17 @@ function createState<T>(
const { velesElementNode: existingElementNode } =
getComponentVelesNode(existingElement.node);
// the element is in the same relative position
if (existingElement.index + offset === index) {
if (existingElement.indexValue + offset === index) {
currentElement = existingElementNode.html;
return;
}

if (existingElement.index + offset > index) {
if (existingElement.indexValue + offset > index) {
if (currentElement) {
currentElement.after(existingElementNode.html);
// we adjust the offset of the item right after the one
// we repositioned
positioningOffset[existingElement.index + 1] = -1;
positioningOffset[existingElement.indexValue + 1] = -1;
} else {
// this means we at position 0
const firstRenderedElement = renderedElements[0]?.[0];
Expand All @@ -488,7 +497,7 @@ function createState<T>(
} else {
if (currentElement) {
currentElement.after(existingElementNode.html);
positioningOffset[existingElement.index + 1] = 1;
positioningOffset[existingElement.indexValue + 1] = 1;
} else {
// this means we at position 0
const firstRenderedElement = renderedElements[0]?.[0];
Expand Down Expand Up @@ -653,7 +662,7 @@ function createState<T>(
},
cb: (props: {
elementState: State<Element>;
index: number;
indexState: State<number>;
}) => VelesElement | VelesComponent
) {
const children: [
Expand All @@ -664,7 +673,8 @@ function createState<T>(
const elementsByKey: {
[key: string]: {
elementState: State<Element>;
index: number;
indexState: State<number>;
indexValue: number;
node: VelesElement | VelesComponent;
};
} = {};
Expand Down Expand Up @@ -694,16 +704,18 @@ function createState<T>(
}

const elementState = createState(element);
const indexState = createState(index);

if (!calculatedKey) {
return;
}

let node = cb({ elementState, index });
let node = cb({ elementState, indexState });

elementsByKey[calculatedKey] = {
node,
index,
indexState,
indexValue: index,
elementState,
};

Expand Down

0 comments on commit 6da1a5c

Please sign in to comment.