@fluid-topics/ft-reorderable-list 1.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,30 @@
1
+ import { ElementDefinitionsMap, FtLitElement } from "@fluid-topics/ft-wc-utils";
2
+ import { FtdsReorderableListItem, FtdsReorderableListProperties } from "./ftds-reorderable-list.properties";
3
+ export declare class FtdsReorderableList extends FtLitElement implements FtdsReorderableListProperties {
4
+ static elementDefinitions: ElementDefinitionsMap;
5
+ static styles: import("lit").CSSResult;
6
+ editable: boolean;
7
+ removable: boolean;
8
+ items: FtdsReorderableListItem[];
9
+ list?: HTMLElement;
10
+ draggableItems?: HTMLElement[];
11
+ notDraggedItems?: HTMLElement[];
12
+ dragGrips?: HTMLElement[];
13
+ private draggingItem?;
14
+ protected render(): import("lit").TemplateResult<1>;
15
+ private renderItemLeftItems;
16
+ private renderItemLeftActions;
17
+ private onDragButtonKeyDown;
18
+ private keepFocusOnItem;
19
+ private moveItemUp;
20
+ private moveItemDown;
21
+ private swapItems;
22
+ private onDragStart;
23
+ private onDragOver;
24
+ private onDragEnd;
25
+ private syncReferenceList;
26
+ private getDragAfterElement;
27
+ private moveFocusToPreviousItem;
28
+ private moveFocusToNextItem;
29
+ private getSelectedOption;
30
+ }
@@ -0,0 +1,257 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html, nothing } from "lit";
8
+ import { ifDefined } from "lit/directives/if-defined.js";
9
+ import { property, query, queryAll } from "lit/decorators.js";
10
+ import { repeat } from "lit/directives/repeat.js";
11
+ import { live } from "lit/directives/live.js";
12
+ import { DesignSystemFamily, DesignSystemSize, FtLitElement } from "@fluid-topics/ft-wc-utils";
13
+ import { styles } from "./ftds-reorderable-list.styles";
14
+ import { FtTypography, FtTypographyVariants } from "@fluid-topics/ft-typography";
15
+ import { FtdsButton } from "@fluid-topics/ft-button";
16
+ import { FtIcon, FtIcons } from "@fluid-topics/ft-icon";
17
+ import { FtdsNotice } from "@fluid-topics/ft-notice";
18
+ class FtdsReorderableList extends FtLitElement {
19
+ constructor() {
20
+ super(...arguments);
21
+ this.editable = false;
22
+ this.removable = false;
23
+ this.items = [];
24
+ this.draggingItem = undefined;
25
+ }
26
+ render() {
27
+ return html `
28
+ <div class="ftds-reorderable-list--container">
29
+ <ol role="listbox"
30
+ aria-activedescendant=""
31
+ @dragstart="${this.onDragStart}"
32
+ @dragover="${this.onDragOver}"
33
+ @dragend="${this.onDragEnd}">
34
+ ${repeat(this.items, (item) => live(item.id), (item) => {
35
+ return html `
36
+ <li role="option"
37
+ draggable="true"
38
+ id="${item.id}">
39
+ <div class="ftds-reorderable-list-item--left">
40
+ ${this.renderItemLeftItems(item)}
41
+ ${this.renderItemLeftActions(item)}
42
+ ${item.warningMessage ? html `
43
+ <ftds-notice dense variant="warning"
44
+ size="${DesignSystemSize.large}">
45
+ ${item.warningMessage}
46
+ </ftds-notice>` : nothing}
47
+ </div>
48
+ <div class="ftds-reorderable-list-item--right">
49
+ ${this.editable ? html `
50
+ <ftds-button icon=${FtIcons.ADMIN}
51
+ family="${DesignSystemFamily.brand}"
52
+ size="${DesignSystemSize.large}"></ftds-button>
53
+ ` : nothing}
54
+ ${this.removable ? html `
55
+ <ftds-button icon=${FtIcons.TRASH}
56
+ family="${DesignSystemFamily.brand}"
57
+ size="${DesignSystemSize.large}"></ftds-button>
58
+ ` : nothing}
59
+ </div>
60
+ </li>`;
61
+ })}
62
+ </ol>
63
+ <slot part="actions"></slot>
64
+ </div>
65
+ `;
66
+ }
67
+ renderItemLeftItems(item) {
68
+ return html `
69
+ <ft-icon class="ftds-reorderable-list-item--drag-grip"
70
+ value="${FtIcons.GRIP_LINES_SOLID}"
71
+ family="${DesignSystemFamily.neutral}"
72
+ size="${DesignSystemSize.medium}"
73
+ data-item-id="${item.id}"
74
+ tabindex="1"
75
+ @keydown="${this.onDragButtonKeyDown}"
76
+ @focus="${(event) => {
77
+ var _a;
78
+ let selected = this.getSelectedOption(event);
79
+ selected.setAttribute("aria-selected", "true");
80
+ (_a = this.list) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-activedescendant", `option-${item.id}`);
81
+ }}"
82
+ @focusout="${(event) => {
83
+ var _a;
84
+ let selected = this.getSelectedOption(event);
85
+ selected.setAttribute("aria-selected", "false");
86
+ (_a = this.list) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-activedescendant", "");
87
+ }}"
88
+ @click="${(e) => {
89
+ e.target.focus();
90
+ }}"
91
+
92
+ ></ft-icon>
93
+ <ft-typography variant="${FtTypographyVariants.body2semibold}">
94
+ ${item.label}
95
+ </ft-typography>
96
+ `;
97
+ }
98
+ renderItemLeftActions(item) {
99
+ if (item.actions) {
100
+ return html `
101
+ <div class="ftds-reorderable-list-item--left-actions">
102
+ ${repeat(item.actions, (action) => html `
103
+ <ftds-button icon="${action.icon}"
104
+ ?disabled=${action.disabled}
105
+ family="${DesignSystemFamily.neutral}"
106
+ size="${DesignSystemSize.large}"
107
+ label="${ifDefined(action.label)}"
108
+ @click="${action.run}">
109
+ ${ifDefined(action.text)}
110
+ </ftds-button>
111
+ `)}
112
+ </div>`;
113
+ }
114
+ else {
115
+ return nothing;
116
+ }
117
+ }
118
+ async onDragButtonKeyDown(event) {
119
+ const itemId = event.target.dataset.itemId;
120
+ const itemIndex = this.items.findIndex((e) => e.id === itemId);
121
+ this.dragGrips[itemIndex].classList.remove("dragging");
122
+ if (itemIndex > -1 && ["PageUp", "PageDown", "ArrowUp", "ArrowDown"].includes(event.key)) {
123
+ event.preventDefault();
124
+ const move = event.altKey || event.key.startsWith("Page");
125
+ const up = event.key.includes("Up");
126
+ if (move) {
127
+ if (up) {
128
+ this.moveItemUp(itemIndex);
129
+ }
130
+ else {
131
+ this.moveItemDown(itemIndex);
132
+ }
133
+ await this.keepFocusOnItem(itemId);
134
+ }
135
+ else {
136
+ if (up) {
137
+ this.moveFocusToPreviousItem(itemIndex);
138
+ }
139
+ else {
140
+ this.moveFocusToNextItem(itemIndex);
141
+ }
142
+ }
143
+ }
144
+ }
145
+ async keepFocusOnItem(itemId) {
146
+ await this.updateComplete;
147
+ let gripButton = this.shadowRoot.querySelector(`ft-icon[data-item-id="${itemId}"]`);
148
+ gripButton.classList.add("dragging");
149
+ gripButton.focus();
150
+ }
151
+ moveItemUp(itemIndex) {
152
+ if (itemIndex > 0) {
153
+ this.items = this.swapItems(itemIndex, itemIndex - 1);
154
+ }
155
+ }
156
+ moveItemDown(itemIndex) {
157
+ if (itemIndex < this.items.length - 1) {
158
+ this.items = this.swapItems(itemIndex, itemIndex + 1);
159
+ }
160
+ }
161
+ swapItems(index1, index2) {
162
+ const reorderedItems = [...this.items];
163
+ const temp = reorderedItems[index1];
164
+ reorderedItems[index1] = reorderedItems[index2];
165
+ reorderedItems[index2] = temp;
166
+ return reorderedItems;
167
+ }
168
+ onDragStart(event) {
169
+ event.dataTransfer.effectAllowed = 'move';
170
+ this.draggingItem = event.target;
171
+ this.draggingItem.classList.add("dragging");
172
+ }
173
+ onDragOver(event) {
174
+ var _a;
175
+ event.preventDefault();
176
+ const draggingOverItem = this.getDragAfterElement(this.list, event.clientY);
177
+ this.draggingItem.style.cursor = "grabbing";
178
+ (_a = this.draggableItems) === null || _a === void 0 ? void 0 : _a.forEach(item => item.classList.remove("over"));
179
+ if (draggingOverItem) {
180
+ this.list.insertBefore(this.draggingItem, draggingOverItem);
181
+ }
182
+ else {
183
+ this.list.appendChild(this.draggingItem);
184
+ }
185
+ }
186
+ onDragEnd(event) {
187
+ var _a;
188
+ this.syncReferenceList();
189
+ event.target.classList.remove("dragging");
190
+ (_a = this.draggableItems) === null || _a === void 0 ? void 0 : _a.forEach(item => item.classList.remove("over"));
191
+ this.draggingItem.remove();
192
+ this.draggingItem = undefined;
193
+ }
194
+ syncReferenceList() {
195
+ let newOrder = [...this.dragGrips].map((e) => e.dataset.itemId);
196
+ this.items = newOrder.map(id => this.items.find(item => item.id === id));
197
+ }
198
+ getDragAfterElement(container, y) {
199
+ const draggableElements = [...this.notDraggedItems];
200
+ let closest = { offset: Number.NEGATIVE_INFINITY, element: null };
201
+ for (const child of draggableElements) {
202
+ const box = child.getBoundingClientRect();
203
+ const offset = y - box.top - box.height / 2;
204
+ if (offset < 0 && offset > closest.offset) {
205
+ closest = { offset: offset, element: child };
206
+ return closest.element;
207
+ }
208
+ }
209
+ return null;
210
+ }
211
+ moveFocusToPreviousItem(itemIndex) {
212
+ if (itemIndex > 0) {
213
+ let buttonToFocus = this.dragGrips[itemIndex - 1];
214
+ buttonToFocus === null || buttonToFocus === void 0 ? void 0 : buttonToFocus.focus();
215
+ }
216
+ }
217
+ moveFocusToNextItem(itemIndex) {
218
+ if (itemIndex < this.items.length - 1) {
219
+ let buttonToFocus = this.dragGrips[itemIndex + 1];
220
+ buttonToFocus === null || buttonToFocus === void 0 ? void 0 : buttonToFocus.focus();
221
+ }
222
+ }
223
+ getSelectedOption(event) {
224
+ let target = event.target;
225
+ const itemId = target.dataset.itemId;
226
+ return [...this.draggableItems].find((e) => e.id === itemId);
227
+ }
228
+ }
229
+ FtdsReorderableList.elementDefinitions = {
230
+ "ft-typography": FtTypography,
231
+ "ftds-button": FtdsButton,
232
+ "ft-icon": FtIcon,
233
+ "ftds-notice": FtdsNotice,
234
+ };
235
+ FtdsReorderableList.styles = styles;
236
+ __decorate([
237
+ property({ type: Boolean })
238
+ ], FtdsReorderableList.prototype, "editable", void 0);
239
+ __decorate([
240
+ property({ type: Boolean })
241
+ ], FtdsReorderableList.prototype, "removable", void 0);
242
+ __decorate([
243
+ property()
244
+ ], FtdsReorderableList.prototype, "items", void 0);
245
+ __decorate([
246
+ query("ol")
247
+ ], FtdsReorderableList.prototype, "list", void 0);
248
+ __decorate([
249
+ queryAll("li")
250
+ ], FtdsReorderableList.prototype, "draggableItems", void 0);
251
+ __decorate([
252
+ queryAll("li:not(.dragging)")
253
+ ], FtdsReorderableList.prototype, "notDraggedItems", void 0);
254
+ __decorate([
255
+ queryAll("ft-icon[data-item-id]")
256
+ ], FtdsReorderableList.prototype, "dragGrips", void 0);
257
+ export { FtdsReorderableList };
@@ -0,0 +1,19 @@
1
+ import { FtIcons } from "@fluid-topics/ft-icon";
2
+ export interface FtdsReorderableListProperties {
3
+ items: FtdsReorderableListItem[];
4
+ editable: boolean;
5
+ removable: boolean;
6
+ }
7
+ export interface FtdsReorderableListItem {
8
+ id: string;
9
+ label: string;
10
+ warningMessage: string;
11
+ actions: FtdsReorderableListItemAction[];
12
+ }
13
+ export interface FtdsReorderableListItemAction {
14
+ label: string;
15
+ text?: string;
16
+ icon: FtIcons;
17
+ disabled?: boolean;
18
+ run?: (event: MouseEvent | KeyboardEvent) => void;
19
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export declare const FtdsReorderableListItemCssVariables: {
2
+ padding: import("@fluid-topics/ft-wc-utils").FtCssVariable;
3
+ };
4
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,70 @@
1
+ import { css } from "lit";
2
+ import { FtCssVariableFactory } from "@fluid-topics/ft-wc-utils";
3
+ import { reorderableList } from "@fluid-topics/design-system-variables";
4
+ export const FtdsReorderableListItemCssVariables = {
5
+ padding: FtCssVariableFactory.create("--ftds-reorderable-list-item--padding", "", "SIZE", "8px"),
6
+ };
7
+ // language=CSS
8
+ export const styles = css `
9
+ .ftds-reorderable-list--container {
10
+ display: flex;
11
+ flex-direction: column;
12
+ background-color: ${reorderableList.backgroundColor};
13
+ border: ${reorderableList.borderWidth} solid ${reorderableList.borderColor};
14
+ border-radius: ${reorderableList.borderRadius};
15
+ padding: ${reorderableList.verticalPadding};
16
+ list-style-type: none;
17
+ gap: ${reorderableList.reorderableItemsGap};
18
+ }
19
+
20
+ ol[role=listbox] {
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: ${reorderableList.reorderableItemsGap};
24
+ list-style-type: none;
25
+ margin: 0;
26
+ padding: 0;
27
+ }
28
+
29
+ ol li {
30
+ display: flex;
31
+ background-color: ${reorderableList.reorderableItemBackgroundColor};
32
+ border-radius: ${reorderableList.borderRadius};
33
+ box-shadow: ${reorderableList.boxShadow};
34
+ padding: ${FtdsReorderableListItemCssVariables.padding};
35
+ gap: ${reorderableList.reorderableItemsGap};
36
+ min-height: 40px;
37
+ }
38
+
39
+ ol li:hover {
40
+ cursor: grab
41
+ }
42
+
43
+ .ftds-reorderable-list-item--left {
44
+ display: flex;
45
+ flex-grow: 1;
46
+ align-items: center;
47
+ gap: ${reorderableList.gap};
48
+ }
49
+
50
+ .ftds-reorderable-list-item--left-actions {
51
+ display: flex;
52
+ gap: ${reorderableList.gap};
53
+ }
54
+
55
+ .ftds-reorderable-list-item--right {
56
+ display: flex;
57
+ justify-content: end;
58
+ }
59
+
60
+ .ftds-reorderable-list-item--drag-grip {
61
+ opacity: ${reorderableList.reorderableItemGripOpacity};
62
+ }
63
+
64
+ .ftds-reorderable-list-item--drag-grip:focus {
65
+ outline: 2px solid #0e98b4;
66
+ outline-offset: 2px;
67
+ border-radius: 999px;
68
+ }
69
+
70
+ `;
@@ -0,0 +1,3 @@
1
+ export * from "./ftds-reorderable-list.styles";
2
+ export * from "./ftds-reorderable-list.properties";
3
+ export * from "./ftds-reorderable-list";
package/build/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { customElement } from "@fluid-topics/ft-wc-utils";
2
+ import { FtdsReorderableList } from "./ftds-reorderable-list";
3
+ export * from "./ftds-reorderable-list.styles";
4
+ export * from "./ftds-reorderable-list.properties";
5
+ export * from "./ftds-reorderable-list";
6
+ customElement("ftds-reorderable-list")(FtdsReorderableList);
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@fluid-topics/ft-reorderable-list",
3
+ "version": "1.2.12",
4
+ "description": "An reorderable list component",
5
+ "keywords": [
6
+ "Lit"
7
+ ],
8
+ "author": "Fluid Topics <devtopics@antidot.net>",
9
+ "license": "ISC",
10
+ "main": "build/index.js",
11
+ "web": "build/ft-orderable-list.min.js",
12
+ "typings": "build/index",
13
+ "files": [
14
+ "build/**/*.js",
15
+ "build/**/*.ts"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "ssh://git@scm.mrs.antidot.net:2222/fluidtopics/ft-web-components.git"
20
+ },
21
+ "dependencies": {
22
+ "@fluid-topics/design-system-variables": "0.1.8",
23
+ "@fluid-topics/ft-wc-utils": "1.2.12",
24
+ "lit": "3.1.0"
25
+ },
26
+ "devDependencies": {
27
+ "@fluid-topics/ft-wc-test-utils": "1.2.12"
28
+ },
29
+ "gitHead": "a32b515ab03f4e6ffd796a7149856513d5b71400"
30
+ }