Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions src/packages/core/sorter/stories/sorter-controller.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { Canvas, Meta } from '@storybook/addon-docs';

import * as LocalizeStories from './sorter.stories';

<Meta title="API/Drag and Drop/Intro" />

# Drag and Drop

Drag and Drop can be done by using the `UmbSorterController`

To get started using drag and drop, finish the following steps:

- Preparing the model
- Setting the configuration
- Registering the controller

#### Preparing the model

The SorterController needs a model to know what item it is we are dealing with.

```typescript
type MySortEntryType = {
id: string;
value: string;
};

const awesomeModel: Array<MySortEntryType> = [
{
id: '0',
value: 'Entry 0',
},
{
id: '1',
value: 'Entry 1',
},
{
id: '2',
value: 'Entry 2',
},
];
```

#### Setting the configuration

When you know the model of which that is being sorted, you can set up the configuration.
The configuration has a lot of optional options, but the required ones are:

- compareElementToModel()
- querySelectModelToElement()
- identifier
- itemSelector
- containerSelector

It can be set up as following:

```typescript
import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';

type MySortEntryType = {...}
const awesomeModel: Array<MySortEntryType> = [...]

const MY_SORTER_CONFIG: UmbSorterConfig<MySortEntryType> = {
compareElementToModel: (element: HTMLElement, model: MySortEntryType) => {
return element.getAttribute('data-sort-entry-id') === model.id;
},
querySelectModelToElement: (container: HTMLElement, modelEntry: MySortEntryType) => {
return container.querySelector('data-sort-entry-id=[' + modelEntry.id + ']');
},
identifier: 'test-sorter',
itemSelector: 'li',
containerSelector: 'ul',
};

export class MyElement extends UmbElementMixin(LitElement) {

render() {
return html`
<ul>
${awesomeModel.map(
(entry) =>
html`<li data-sort-entry-id="${entry.id}">
<span>${entry.value}</span>
</li>`,
)}
</ul>
`;
}
}
```

#### Registering the controller

When the model and configuration are available we can register the controller and tell the controller what model we are using.

```typescript
import { UmbSorterController, UmbSorterController } from '@umbraco-cms/backoffice/sorter';

type MySortEntryType = {...}
const awesomeModel: Array<MySortEntryType> = [...]
const MY_SORTER_CONFIG: UmbSorterConfig<MySortEntryType> = {...}


export class MyElement extends UmbElementMixin(LitElement) {
#sorter = new UmbSorterController(this, MY_SORTER_CONFIG);

constructor() {
this.#sorter.setModel(awesomeModel);
}

render() {
return html`
<ul>
${awesomeModel.map(
(entry) =>
html`<li data-sort-entry-id="${entry.id}">
<span>${entry.value}</span>
</li>`,
)}
</ul>
`;
}
}
```

### Placeholder

While dragging an entry, the entry will get an additional class that can be styled.
The class is by default `--umb-sorter-placeholder` but can be changed via the configuration to a different value.

```typescript
const MY_SORTER_CONFIG: UmbSorterConfig<MySortEntryType> = {
...
placeholderClass: 'dragging-now',
};
```

```typescript
static styles = [
css`
li {
display:relative;
}

li.dragging-now span {
visibility: hidden;
}

li.dragging-now::after {
content: '';
position: absolute;
inset: 0px;
border: 1px dashed grey;
}
`,
];
```

### Horizontal sorting

By default, the sorter controller will sort vertically. You can sort your model horizontally by setting the `resolveVerticalDirection` to return false.

```typescript
const MY_SORTER_CONFIG: UmbSorterConfig<MySortEntryType> = {
...
resolveVerticalDirection: () => return false,
};
```

### Performing logic when using the controller (TODO: Better title)

Let's say your model has a property sortOrder that you would like to update when the entry is being sorted.
You can add your code logic in the configuration option `performItemInsert` and `performItemRemove`

```typescript
export class MyElement extends UmbElementMixin(LitElement) {
#sorter = new UmbSorterController(this, {
...SORTER_CONFIG,
performItemInsert: ({ item, newIndex }) => {
console.log(item, newIndex);
// Perform some logic here to calculate the new sortOrder & save it.
return true;
},
performItemRemove: () => true,
});
}
```
76 changes: 50 additions & 26 deletions src/packages/core/sorter/stories/test-sorter-controller.element.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import { UmbSorterConfig, UmbSorterController } from '../sorter.controller.js';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { css, customElement, html } from '@umbraco-cms/backoffice/external/lit';
import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit';

type SortEntryType = {
id: string;
value: string;
};

const sorterConfig: UmbSorterConfig<SortEntryType> = {
const SORTER_CONFIG: UmbSorterConfig<SortEntryType> = {
compareElementToModel: (element: HTMLElement, model: SortEntryType) => {
return element.getAttribute('data-sort-entry-id') === model.id;
},
querySelectModelToElement: (container: HTMLElement, modelEntry: SortEntryType) => {
return container.querySelector('data-sort-entry-id[' + modelEntry.id + ']');
return container.querySelector('data-sort-entry-id=[' + modelEntry.id + ']');
},
identifier: 'test-sorter',
itemSelector: 'li',
containerSelector: 'ul',
};

const model: Array<SortEntryType> = [
{
id: '0',
Expand All @@ -38,18 +37,35 @@ const model: Array<SortEntryType> = [
export default class UmbTestSorterControllerElement extends UmbLitElement {
public sorter;

@state()
private vertical = true;

constructor() {
super();

this.sorter = new UmbSorterController(this, sorterConfig);
this.sorter = new UmbSorterController(this, {
...SORTER_CONFIG,
resolveVerticalDirection: () => {
this.vertical ? true : false;
},
});
this.sorter.setModel(model);
}

#toggle() {
this.vertical = !this.vertical;
}

render() {
return html`
<ul>
<uui-button label="Change direction" look="outline" color="positive" @click=${this.#toggle}>
Horizontal/Vertical
</uui-button>
<ul class="${this.vertical ? 'vertical' : 'horizontal'}">
${model.map(
(entry) => html`<li id="${'sort' + entry.id}" data-sort-entry-id="${entry.id}">${entry.value}</li>`
(entry) =>
html`<li class="item" data-sort-entry-id="${entry.id}">
<span><uui-icon name="umb:wand"></uui-icon>${entry.value}</span>
</li>`,
)}
</ul>
`;
Expand All @@ -59,39 +75,47 @@ export default class UmbTestSorterControllerElement extends UmbLitElement {
css`
:host {
display: block;
box-sizing: border-box;
}

ul {
display: flex;
flex-direction: column;
gap: 5px;
list-style: none;
padding: 0;
margin: 0;
margin: 10px 0;
}

li {
padding: 10px;
margin: 5px;
background: #eee;
}

li:hover {
background: #ddd !important;
cursor: move;
ul.horizontal {
flex-direction: row;
}

li:active {
background: #ccc;
li {
cursor: grab;
position: relative;
flex: 1;
border-radius: var(--uui-border-radius);
}

#sort0 {
background: #f00;
li span {
display: flex;
align-items: center;
gap: 5px;
padding: 10px;
background-color: rgba(0, 255, 0, 0.3);
}

#sort1 {
background: #0f0;
li.--umb-sorter-placeholder span {
visibility: hidden;
}

#sort2 {
background: #c9da10;
li.--umb-sorter-placeholder::after {
content: '';
position: absolute;
inset: 0px;
border-radius: var(--uui-border-radius);
border: 1px dashed var(--uui-color-divider-emphasis);
}
`,
];
Expand Down