Skip to content

Implement item layers#78

Merged
zmarinov-astea merged 3 commits into
mainfrom
feature/layers
Jul 17, 2026
Merged

Implement item layers#78
zmarinov-astea merged 3 commits into
mainfrom
feature/layers

Conversation

@zmarinov-astea

Copy link
Copy Markdown
Collaborator
  1. Added a layer column for the Items.
  2. Every item strives to have a unique non-negative layer. Items with equal layers should be avoided, since their order is not determined, but they don't break the layering logic.
  3. Items in pixi use zIndex now, since changing the snapshot resolution redraws the item on top otherwise.
  4. Layers are normalized on the client every time the user clicks "Bring to top" or "Bring to back".
  5. Refactored export schemas, so the V7 export schema can now handle the layers.

tapestryId,
} as MediaItemCreateDto & { type: T }
layer: DEFAULT_LAYER,
} satisfies MediaItemCreateDto as MediaItemCreateDto & { type: T }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the satisfies operator?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To enforce type constraint on the object. Otherwise a missing layer prop would not result a compile time error.

)

sortBy(frontItems, ['layer'])
sortBy(backItems, ['layer'])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't sortBy return a new array and not sort it in-place? Moreover it might be a bit cleaner to sort the array returned by idMapToArray(store.get('items'))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, sortBy really is not used properly. But I won't sort the initial array, since it is not guaranteed that lodash' partition will preserve the ordering.

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getMaxLayer(store: Store<EditableTapestryViewModel, any>) {
const elements = idMapToArray(store.get('items')).map((element) => element.dto.layer)
return elements.length === 0 ? DEFAULT_LAYER : Math.max(...elements)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could probably also use maxBy(idMapToArray(store.get('items')), 'dto.layer')?.dto.layer ?? DEFAULT_LAYER

}),
),
)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this (and isDraggingItems) no longer needed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is being handled in the core-client item controller, and this function here was not being called at all. Also in the editor-item-controller the drag handler is set so that it is impossible to start a drag operation without dragging items.

Comment thread core-client/src/stage/renderer/index.ts Outdated
this.getGroups().forEach((group) => this.renderViewModel(group, selection, interactiveElement))
this.getRels().forEach((rel) => this.renderViewModel(rel, selection, interactiveElement))
this.getItems().forEach((item) => this.renderViewModel(item, selection, interactiveElement))
sortBy(this.getItems(), 'dto.layer').forEach((item, zIndex) =>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how likely is this to cause performance issue, but isn't there an easy way to make sure we keep the items sorted and not sort them on every render?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorting is only managed by the zIndex, I put this additional sortBy to normalize the values, but we probably don't need it

sortBy(frontItems, ['layer'])
sortBy(backItems, ['layer'])
const allItems = [...backItems, ...frontItems]
store.dispatch(...allItems.map((item, layer) => updateItem(item.dto.id, { dto: { layer } })))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will make a single batch-mutation request instead of N updates, right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this updateItem command is also used when manipulating a selection - deleting, arranging, grouping....

@zmarinov-astea
zmarinov-astea merged commit c81a08d into main Jul 17, 2026
1 check passed
@zmarinov-astea
zmarinov-astea deleted the feature/layers branch July 17, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants