@nyaruka/temba-components 0.123.0 → 0.124.0
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.
- package/.github/copilot-instructions.md +22 -4
- package/CHANGELOG.md +11 -0
- package/demo/drag-drop-demo.html +141 -0
- package/demo/index.html +15 -0
- package/demo/test-drag-drop.html +94 -0
- package/dist/temba-components.js +323 -191
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/fields/FieldManager.js +27 -34
- package/out-tsc/src/fields/FieldManager.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +257 -60
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/omnibox/Omnibox.js +1 -1
- package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
- package/out-tsc/src/select/Select.js +198 -38
- package/out-tsc/src/select/Select.js.map +1 -1
- package/out-tsc/src/webchat/WebChat.js +5 -2
- package/out-tsc/src/webchat/WebChat.js.map +1 -1
- package/out-tsc/test/temba-flow-editor-node.test.js +273 -0
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor.test.js +244 -0
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -0
- package/out-tsc/test/temba-flow-plumber.test.js +145 -0
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -0
- package/out-tsc/test/temba-flow-render.test.js +171 -0
- package/out-tsc/test/temba-flow-render.test.js.map +1 -0
- package/out-tsc/test/temba-omnibox.test.js +2 -3
- package/out-tsc/test/temba-omnibox.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +134 -53
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-sortable-list.test.js +91 -15
- package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
- package/out-tsc/test/temba-webchat-lightbox-fix.test.js +42 -0
- package/out-tsc/test/temba-webchat-lightbox-fix.test.js.map +1 -0
- package/out-tsc/test/utils.test.js +30 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/flow/editor-basic.png +0 -0
- package/screenshots/truth/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dropped.png +0 -0
- package/screenshots/truth/list/sortable.png +0 -0
- package/screenshots/truth/omnibox/selected.png +0 -0
- package/screenshots/truth/select/disabled-multi-selection.png +0 -0
- package/screenshots/truth/select/disabled-selection.png +0 -0
- package/screenshots/truth/select/disabled.png +0 -0
- package/screenshots/truth/select/embedded.png +0 -0
- package/screenshots/truth/select/empty-options.png +0 -0
- package/screenshots/truth/select/expression-selected.png +0 -0
- package/screenshots/truth/select/expressions.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/local-options.png +0 -0
- package/screenshots/truth/select/multi-reorder-final.png +0 -0
- package/screenshots/truth/select/multi-reorder-initial.png +0 -0
- package/screenshots/truth/select/multi-with-endpoint.png +0 -0
- package/screenshots/truth/select/multiple-initial-values.png +0 -0
- package/screenshots/truth/select/remote-options.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/screenshots/truth/select/search-multi-no-matches.png +0 -0
- package/screenshots/truth/select/search-selected-focus.png +0 -0
- package/screenshots/truth/select/search-selected.png +0 -0
- package/screenshots/truth/select/search-with-selected.png +0 -0
- package/screenshots/truth/select/searching.png +0 -0
- package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
- package/screenshots/truth/select/selected-multi.png +0 -0
- package/screenshots/truth/select/selected-single.png +0 -0
- package/screenshots/truth/select/selection-clearable.png +0 -0
- package/screenshots/truth/select/static-initial-value.png +0 -0
- package/screenshots/truth/select/static-initial-via-selected.png +0 -0
- package/screenshots/truth/select/truncated-selection.png +0 -0
- package/screenshots/truth/select/with-placeholder.png +0 -0
- package/screenshots/truth/select/without-placeholder.png +0 -0
- package/screenshots/truth/templates/default.png +0 -0
- package/screenshots/truth/templates/unapproved.png +0 -0
- package/src/fields/FieldManager.ts +30 -38
- package/src/list/SortableList.ts +291 -67
- package/src/omnibox/Omnibox.ts +1 -1
- package/src/select/Select.ts +213 -42
- package/src/webchat/WebChat.ts +5 -2
- package/test/temba-flow-editor-node.test.ts +344 -0
- package/test/temba-flow-editor.test.ts +301 -0
- package/test/temba-flow-plumber.test.ts +189 -0
- package/test/temba-flow-render.test.ts +220 -0
- package/test/temba-omnibox.test.ts +2 -3
- package/test/temba-select.test.ts +180 -79
- package/test/temba-sortable-list.test.ts +108 -15
- package/test/temba-webchat-lightbox-fix.test.ts +57 -0
- package/test/utils.test.ts +52 -0
|
@@ -120,7 +120,7 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
120
120
|
border-radius: var(--curvature);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
.featured temba-sortable-list .field:hover {
|
|
123
|
+
.featured:not(.dragging) temba-sortable-list .field:hover {
|
|
124
124
|
cursor: move;
|
|
125
125
|
border-color: #e6e6e6;
|
|
126
126
|
background: #fcfcfc;
|
|
@@ -163,30 +163,26 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
163
163
|
this.filterFields();
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
-
handleSaveOrder(event) {
|
|
167
|
-
const list = event.currentTarget;
|
|
168
|
-
postJSON(this.priorityEndpoint, list
|
|
169
|
-
.getIds()
|
|
170
|
-
.reverse()
|
|
171
|
-
.reduce((map, key, idx) => {
|
|
172
|
-
map[key] = idx;
|
|
173
|
-
return map;
|
|
174
|
-
}, {})).then(() => {
|
|
175
|
-
this.store.refreshFields();
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
166
|
handleOrderChanged(event) {
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
this.featuredFields
|
|
183
|
-
this.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
167
|
+
// Apply the reordering immediately - the SortableList now provides accurate indexes
|
|
168
|
+
const [fromIdx, toIdx] = event.detail.swap;
|
|
169
|
+
const temp = this.featuredFields[fromIdx];
|
|
170
|
+
this.featuredFields.splice(fromIdx, 1);
|
|
171
|
+
this.featuredFields.splice(toIdx, 0, temp);
|
|
172
|
+
this.requestUpdate();
|
|
173
|
+
// Save the new order to the server
|
|
174
|
+
const list = event.currentTarget;
|
|
175
|
+
setTimeout(() => {
|
|
176
|
+
postJSON(this.priorityEndpoint, list
|
|
177
|
+
.getIds()
|
|
178
|
+
.reverse()
|
|
179
|
+
.reduce((map, key, idx) => {
|
|
180
|
+
map[key] = idx;
|
|
181
|
+
return map;
|
|
182
|
+
}, {})).then(() => {
|
|
183
|
+
this.store.refreshFields();
|
|
184
|
+
});
|
|
185
|
+
}, 0);
|
|
190
186
|
}
|
|
191
187
|
handleFieldAction(event) {
|
|
192
188
|
const ele = event.target;
|
|
@@ -210,10 +206,13 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
210
206
|
display: flex;
|
|
211
207
|
flex-direction: row;
|
|
212
208
|
align-items: center;
|
|
213
|
-
padding: 0.25em 1em;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
209
|
+
padding: 0.25em 1em;"
|
|
210
|
+
@click=${(e) => {
|
|
211
|
+
const ele = e.currentTarget;
|
|
212
|
+
const key = ele.dataset.key;
|
|
213
|
+
const action = ele.dataset.action;
|
|
214
|
+
this.fireCustomEvent(CustomEventType.Selection, { key, action });
|
|
215
|
+
}}
|
|
217
216
|
>
|
|
218
217
|
<div
|
|
219
218
|
style="display: flex; min-width: 200px; width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 2em"
|
|
@@ -285,10 +284,7 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
285
284
|
`
|
|
286
285
|
: html `
|
|
287
286
|
<temba-sortable-list
|
|
288
|
-
@change=${this.handleSaveOrder}
|
|
289
287
|
@temba-order-changed=${this.handleOrderChanged}
|
|
290
|
-
@temba-drag-start=${this.handleDragStart}
|
|
291
|
-
@temba-drag-stop=${this.handleDragStop}
|
|
292
288
|
>
|
|
293
289
|
${this.featuredFields.map((field) => this.renderField(field))}
|
|
294
290
|
</temba-sortable-list>
|
|
@@ -318,9 +314,6 @@ __decorate([
|
|
|
318
314
|
__decorate([
|
|
319
315
|
property({ type: Object, attribute: false })
|
|
320
316
|
], FieldManager.prototype, "otherFieldKeys", void 0);
|
|
321
|
-
__decorate([
|
|
322
|
-
property({ type: String })
|
|
323
|
-
], FieldManager.prototype, "draggingId", void 0);
|
|
324
317
|
__decorate([
|
|
325
318
|
property({ type: String })
|
|
326
319
|
], FieldManager.prototype, "query", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldManager.js","sourceRoot":"","sources":["../../../src/fields/FieldManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { ContactField, CustomEventType } from '../interfaces';\n\nimport { SortableList } from '../list/SortableList';\nimport { EndpointMonitorElement } from '../store/EndpointMonitorElement';\nimport { postJSON } from '../utils';\n\nconst TYPE_NAMES = {\n text: 'Text',\n numeric: 'Number',\n number: 'Number',\n datetime: 'Date & Time',\n state: 'State',\n ward: 'Ward',\n district: 'District'\n};\n\nconst matches = (field: ContactField, query: string): boolean => {\n if (!query) {\n return true;\n }\n const search = (\n field.label +\n field.key +\n TYPE_NAMES[field.value_type]\n ).toLowerCase();\n if (search.toLowerCase().indexOf(query) > -1) {\n return true;\n }\n return false;\n};\n\nexport class FieldManager extends EndpointMonitorElement {\n static get styles() {\n return css`\n :host {\n display: flex;\n flex-grow: 1;\n flex-direction: column;\n min-height: 0px;\n }\n\n .featured,\n .other-fields {\n background: #fff;\n border-radius: var(--curvature);\n box-shadow: var(--shadow);\n margin-bottom: 1em;\n display: flex;\n flex-direction: column;\n }\n\n .featured {\n max-height: 40%;\n }\n\n .other-fields {\n flex-grow: 2;\n min-height: 0px;\n margin-bottom: 0px;\n }\n\n temba-textinput {\n margin-bottom: 1em;\n }\n\n .scroll-box {\n overflow-y: auto;\n flex-grow: 1;\n flex-direction: column;\n display: flex;\n }\n\n .header temba-icon {\n margin-right: 0.5em;\n }\n\n .label {\n flex-grow: 1;\n }\n\n .header {\n padding: 0.5em 1em;\n display: flex;\n align-items: flex-start;\n border-bottom: 1px solid var(--color-widget-border);\n }\n\n .featured-field {\n user-select: none;\n }\n\n temba-sortable-list {\n padding: 0.5em 0em;\n width: 100%;\n overflow-y: auto;\n }\n\n .scroll-box {\n padding: 0.5em 0em;\n }\n\n temba-icon[name='usages']:hover {\n --icon-color: var(--color-link-primary);\n }\n\n .field:hover temba-icon[name='delete_small'] {\n opacity: 1 !important;\n cursor: pointer !important;\n pointer-events: all !important;\n }\n\n temba-icon[name='delete_small']:hover {\n --icon-color: var(--color-link-primary);\n }\n\n .field {\n border: 1px solid transparent;\n margin: 0 0.5em;\n border-radius: var(--curvature);\n }\n\n .featured temba-sortable-list .field:hover {\n cursor: move;\n border-color: #e6e6e6;\n background: #fcfcfc;\n }\n `;\n }\n\n @property({ type: String, attribute: 'priority-endpoint' })\n priorityEndpoint: string;\n\n @property({ type: Object, attribute: false })\n featuredFields: ContactField[];\n\n @property({ type: Object, attribute: false })\n otherFieldKeys: string[] = [];\n\n @property({ type: String })\n draggingId: string;\n\n @property({ type: String })\n query = '';\n\n protected firstUpdated(\n _changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(_changedProperties);\n this.url = this.store.fieldsEndpoint;\n }\n\n private filterFields() {\n const filteredKeys = this.store.getFieldKeys().filter((key) => {\n const field = this.store.getContactField(key);\n if (field.featured) {\n return false;\n }\n return matches(field, this.query);\n });\n\n // sort by the label instead of the key\n filteredKeys.sort((a, b) => {\n return this.store\n .getContactField(a)\n .label.localeCompare(this.store.getContactField(b).label);\n });\n\n const featured: ContactField[] = [];\n this.store.getFeaturedFields().forEach((field) => {\n if (matches(field, this.query)) {\n featured.push(field);\n }\n });\n\n this.otherFieldKeys = filteredKeys;\n this.featuredFields = featured;\n }\n\n protected updated(\n properties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.update(properties);\n if (properties.has('data')) {\n this.filterFields();\n } else if (properties.has('query')) {\n this.filterFields();\n }\n }\n\n private handleSaveOrder(event) {\n const list = event.currentTarget as SortableList;\n postJSON(\n this.priorityEndpoint,\n list\n .getIds()\n .reverse()\n .reduce((map, key, idx) => {\n map[key] = idx;\n return map;\n }, {})\n ).then(() => {\n this.store.refreshFields();\n });\n }\n\n private handleOrderChanged(event) {\n const swapsies = event.detail;\n const temp = this.featuredFields[swapsies.fromIdx];\n this.featuredFields[swapsies.fromIdx] = this.featuredFields[swapsies.toIdx];\n this.featuredFields[swapsies.toIdx] = temp;\n this.requestUpdate('featuredFields');\n }\n\n private handleDragStart(event) {\n this.draggingId = event.detail.id;\n }\n\n private handleDragStop() {\n this.draggingId = null;\n }\n\n private handleFieldAction(event: MouseEvent) {\n const ele = event.target as HTMLDivElement;\n const key = ele.dataset.key;\n const action = ele.dataset.action;\n this.fireCustomEvent(CustomEventType.Selection, { key, action });\n }\n\n private handleSearch(event) {\n this.query = (event.target.value || '').trim();\n }\n\n private hasUsages(field: ContactField): boolean {\n return (\n field.usages.campaign_events + field.usages.flows + field.usages.groups >\n 0\n );\n }\n\n private renderField(field: ContactField) {\n return html`\n <div\n class=\"field sortable\"\n id=\"${field.key}\"\n style=\"\n display: flex; \n flex-direction: row; \n align-items: center;\n padding: 0.25em 1em; \n ${field.key === this.draggingId\n ? 'background: var(--color-selection)'\n : ''}\"\n >\n <div\n style=\"display: flex; min-width: 200px; width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 2em\"\n >\n <span\n @click=${this.handleFieldAction}\n data-key=${field.key}\n data-action=\"update\"\n style=\"color: var(--color-link-primary); cursor:pointer;\"\n >\n ${field.label}\n </span>\n ${this.hasUsages(field)\n ? html`\n <temba-icon\n size=\"0.8\"\n style=\"color: #ccc; margin-left: 0.7em;\"\n name=\"usages\"\n data-key=${field.key}\n data-action=\"usages\"\n @click=${this.handleFieldAction}\n clickable\n ></temba-icon>\n `\n : null}\n <div class=\"flex-grow:1\"></div>\n </div>\n <div\n style=\"flex-grow:1; font-family: Roboto Mono, monospace; font-size:0.8em;\"\n >\n @fields.${field.key}\n </div>\n <div>${TYPE_NAMES[field.value_type]}</div>\n <temba-icon\n style=\"pointer-events:none;color:#ccc;margin-left:0.3em;margin-right:-0.5em;opacity:0\"\n name=\"delete_small\"\n data-key=${field.key}\n data-action=\"delete\"\n @click=${this.handleFieldAction}\n ></temba-icon>\n </div>\n `;\n }\n\n public render(): TemplateResult {\n if (!this.featuredFields) {\n return null;\n }\n\n return html`\n <temba-textinput\n id=\"search\"\n placeholder=\"Search\"\n @change=${this.handleSearch}\n clearable\n value=${this.query}\n ></temba-textinput>\n\n ${this.featuredFields.length > 0\n ? html`\n <div class=\"featured\">\n <div class=\"header\">\n <temba-icon name=\"featured\"></temba-icon>\n <div class=\"label\">Featured</div>\n </div>\n ${this.query\n ? html`\n <div class=\"scroll-box\">\n ${this.featuredFields.map((field) =>\n this.renderField(field)\n )}\n </div>\n `\n : html`\n <temba-sortable-list\n @change=${this.handleSaveOrder}\n @temba-order-changed=${this.handleOrderChanged}\n @temba-drag-start=${this.handleDragStart}\n @temba-drag-stop=${this.handleDragStop}\n >\n ${this.featuredFields.map((field) =>\n this.renderField(field)\n )}\n </temba-sortable-list>\n `}\n </div>\n `\n : null}\n\n <div class=\"other-fields\">\n <div class=\"header\">\n <temba-icon name=\"fields\"></temba-icon>\n <div class=\"label\">Everything Else</div>\n </div>\n <div class=\"scroll-box\">\n ${this.otherFieldKeys.map((field) =>\n this.renderField(this.store.getContactField(field))\n )}\n </div>\n </div>\n `;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"FieldManager.js","sourceRoot":"","sources":["../../../src/fields/FieldManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { ContactField, CustomEventType } from '../interfaces';\n\nimport { SortableList } from '../list/SortableList';\nimport { EndpointMonitorElement } from '../store/EndpointMonitorElement';\nimport { postJSON } from '../utils';\n\nconst TYPE_NAMES = {\n text: 'Text',\n numeric: 'Number',\n number: 'Number',\n datetime: 'Date & Time',\n state: 'State',\n ward: 'Ward',\n district: 'District'\n};\n\nconst matches = (field: ContactField, query: string): boolean => {\n if (!query) {\n return true;\n }\n const search = (\n field.label +\n field.key +\n TYPE_NAMES[field.value_type]\n ).toLowerCase();\n if (search.toLowerCase().indexOf(query) > -1) {\n return true;\n }\n return false;\n};\n\nexport class FieldManager extends EndpointMonitorElement {\n static get styles() {\n return css`\n :host {\n display: flex;\n flex-grow: 1;\n flex-direction: column;\n min-height: 0px;\n }\n\n .featured,\n .other-fields {\n background: #fff;\n border-radius: var(--curvature);\n box-shadow: var(--shadow);\n margin-bottom: 1em;\n display: flex;\n flex-direction: column;\n }\n\n .featured {\n max-height: 40%;\n }\n\n .other-fields {\n flex-grow: 2;\n min-height: 0px;\n margin-bottom: 0px;\n }\n\n temba-textinput {\n margin-bottom: 1em;\n }\n\n .scroll-box {\n overflow-y: auto;\n flex-grow: 1;\n flex-direction: column;\n display: flex;\n }\n\n .header temba-icon {\n margin-right: 0.5em;\n }\n\n .label {\n flex-grow: 1;\n }\n\n .header {\n padding: 0.5em 1em;\n display: flex;\n align-items: flex-start;\n border-bottom: 1px solid var(--color-widget-border);\n }\n\n .featured-field {\n user-select: none;\n }\n\n temba-sortable-list {\n padding: 0.5em 0em;\n width: 100%;\n overflow-y: auto;\n }\n\n .scroll-box {\n padding: 0.5em 0em;\n }\n\n temba-icon[name='usages']:hover {\n --icon-color: var(--color-link-primary);\n }\n\n .field:hover temba-icon[name='delete_small'] {\n opacity: 1 !important;\n cursor: pointer !important;\n pointer-events: all !important;\n }\n\n temba-icon[name='delete_small']:hover {\n --icon-color: var(--color-link-primary);\n }\n\n .field {\n border: 1px solid transparent;\n margin: 0 0.5em;\n border-radius: var(--curvature);\n }\n\n .featured:not(.dragging) temba-sortable-list .field:hover {\n cursor: move;\n border-color: #e6e6e6;\n background: #fcfcfc;\n }\n `;\n }\n\n @property({ type: String, attribute: 'priority-endpoint' })\n priorityEndpoint: string;\n\n @property({ type: Object, attribute: false })\n featuredFields: ContactField[];\n\n @property({ type: Object, attribute: false })\n otherFieldKeys: string[] = [];\n\n @property({ type: String })\n query = '';\n\n protected firstUpdated(\n _changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(_changedProperties);\n this.url = this.store.fieldsEndpoint;\n }\n\n private filterFields() {\n const filteredKeys = this.store.getFieldKeys().filter((key) => {\n const field = this.store.getContactField(key);\n if (field.featured) {\n return false;\n }\n return matches(field, this.query);\n });\n\n // sort by the label instead of the key\n filteredKeys.sort((a, b) => {\n return this.store\n .getContactField(a)\n .label.localeCompare(this.store.getContactField(b).label);\n });\n\n const featured: ContactField[] = [];\n this.store.getFeaturedFields().forEach((field) => {\n if (matches(field, this.query)) {\n featured.push(field);\n }\n });\n\n this.otherFieldKeys = filteredKeys;\n this.featuredFields = featured;\n }\n\n protected updated(\n properties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.update(properties);\n if (properties.has('data')) {\n this.filterFields();\n } else if (properties.has('query')) {\n this.filterFields();\n }\n }\n\n private handleOrderChanged(event) {\n // Apply the reordering immediately - the SortableList now provides accurate indexes\n const [fromIdx, toIdx] = event.detail.swap;\n\n const temp = this.featuredFields[fromIdx];\n this.featuredFields.splice(fromIdx, 1);\n this.featuredFields.splice(toIdx, 0, temp);\n this.requestUpdate();\n\n // Save the new order to the server\n const list = event.currentTarget as SortableList;\n setTimeout(() => {\n postJSON(\n this.priorityEndpoint,\n list\n .getIds()\n .reverse()\n .reduce((map, key, idx) => {\n map[key] = idx;\n return map;\n }, {})\n ).then(() => {\n this.store.refreshFields();\n });\n }, 0);\n }\n\n private handleFieldAction(event: MouseEvent) {\n const ele = event.target as HTMLDivElement;\n const key = ele.dataset.key;\n const action = ele.dataset.action;\n this.fireCustomEvent(CustomEventType.Selection, { key, action });\n }\n\n private handleSearch(event) {\n this.query = (event.target.value || '').trim();\n }\n\n private hasUsages(field: ContactField): boolean {\n return (\n field.usages.campaign_events + field.usages.flows + field.usages.groups >\n 0\n );\n }\n\n private renderField(field: ContactField) {\n return html`\n <div\n class=\"field sortable\"\n id=\"${field.key}\"\n style=\"\n display: flex; \n flex-direction: row; \n align-items: center;\n padding: 0.25em 1em;\"\n @click=${(e: MouseEvent) => {\n const ele = e.currentTarget as HTMLDivElement;\n const key = ele.dataset.key;\n const action = ele.dataset.action;\n this.fireCustomEvent(CustomEventType.Selection, { key, action });\n }}\n >\n <div\n style=\"display: flex; min-width: 200px; width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 2em\"\n >\n <span\n @click=${this.handleFieldAction}\n data-key=${field.key}\n data-action=\"update\"\n style=\"color: var(--color-link-primary); cursor:pointer;\"\n >\n ${field.label}\n </span>\n ${this.hasUsages(field)\n ? html`\n <temba-icon\n size=\"0.8\"\n style=\"color: #ccc; margin-left: 0.7em;\"\n name=\"usages\"\n data-key=${field.key}\n data-action=\"usages\"\n @click=${this.handleFieldAction}\n clickable\n ></temba-icon>\n `\n : null}\n <div class=\"flex-grow:1\"></div>\n </div>\n <div\n style=\"flex-grow:1; font-family: Roboto Mono, monospace; font-size:0.8em;\"\n >\n @fields.${field.key}\n </div>\n <div>${TYPE_NAMES[field.value_type]}</div>\n <temba-icon\n style=\"pointer-events:none;color:#ccc;margin-left:0.3em;margin-right:-0.5em;opacity:0\"\n name=\"delete_small\"\n data-key=${field.key}\n data-action=\"delete\"\n @click=${this.handleFieldAction}\n ></temba-icon>\n </div>\n `;\n }\n\n public render(): TemplateResult {\n if (!this.featuredFields) {\n return null;\n }\n\n return html`\n <temba-textinput\n id=\"search\"\n placeholder=\"Search\"\n @change=${this.handleSearch}\n clearable\n value=${this.query}\n ></temba-textinput>\n\n ${this.featuredFields.length > 0\n ? html`\n <div class=\"featured\">\n <div class=\"header\">\n <temba-icon name=\"featured\"></temba-icon>\n <div class=\"label\">Featured</div>\n </div>\n ${this.query\n ? html`\n <div class=\"scroll-box\">\n ${this.featuredFields.map((field) =>\n this.renderField(field)\n )}\n </div>\n `\n : html`\n <temba-sortable-list\n @temba-order-changed=${this.handleOrderChanged}\n >\n ${this.featuredFields.map((field) =>\n this.renderField(field)\n )}\n </temba-sortable-list>\n `}\n </div>\n `\n : null}\n\n <div class=\"other-fields\">\n <div class=\"header\">\n <temba-icon name=\"fields\"></temba-icon>\n <div class=\"label\">Everything Else</div>\n </div>\n <div class=\"scroll-box\">\n ${this.otherFieldKeys.map((field) =>\n this.renderField(this.store.getContactField(field))\n )}\n </div>\n </div>\n `;\n }\n}\n"]}
|
|
@@ -7,7 +7,7 @@ import { RapidElement } from '../RapidElement';
|
|
|
7
7
|
* A simple list that can be sorted by dragging
|
|
8
8
|
*/
|
|
9
9
|
// how far we have to drag before it starts
|
|
10
|
-
const DRAG_THRESHOLD =
|
|
10
|
+
const DRAG_THRESHOLD = 2;
|
|
11
11
|
export class SortableList extends RapidElement {
|
|
12
12
|
static get styles() {
|
|
13
13
|
return css `
|
|
@@ -17,18 +17,54 @@ export class SortableList extends RapidElement {
|
|
|
17
17
|
|
|
18
18
|
.container {
|
|
19
19
|
user-select: none;
|
|
20
|
+
position: relative;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.container.horizontal {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-wrap: wrap;
|
|
26
|
+
align-items: center;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
.dragging {
|
|
23
30
|
background: var(--color-selection);
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
.dragged-item {
|
|
34
|
+
opacity: 0;
|
|
35
|
+
pointer-events: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
.sortable {
|
|
27
39
|
transition: all 300ms ease-in-out;
|
|
28
40
|
display: flex;
|
|
29
41
|
padding: 0.4em 0;
|
|
30
42
|
}
|
|
31
43
|
|
|
44
|
+
.container.horizontal .sortable {
|
|
45
|
+
padding: 0;
|
|
46
|
+
margin-right: 0.25em;
|
|
47
|
+
margin-bottom: 0.25em;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.drop-indicator {
|
|
51
|
+
position: absolute;
|
|
52
|
+
background: var(--color-primary-dark, #1c7cd6);
|
|
53
|
+
z-index: 1000;
|
|
54
|
+
pointer-events: none;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.container.horizontal .drop-indicator {
|
|
58
|
+
width: 2px;
|
|
59
|
+
margin-top: -5px;
|
|
60
|
+
padding-bottom: 10px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.container:not(.horizontal) .drop-indicator {
|
|
64
|
+
height: 2px;
|
|
65
|
+
left: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
32
68
|
.sortable:hover temba-icon {
|
|
33
69
|
opacity: 1;
|
|
34
70
|
cursor: move;
|
|
@@ -36,8 +72,14 @@ export class SortableList extends RapidElement {
|
|
|
36
72
|
|
|
37
73
|
.ghost {
|
|
38
74
|
position: absolute;
|
|
39
|
-
opacity: 0.
|
|
75
|
+
opacity: 0.7;
|
|
40
76
|
transition: none;
|
|
77
|
+
background: var(--color-background, white);
|
|
78
|
+
border: 1px solid var(--color-primary, #1c7cd6);
|
|
79
|
+
border-radius: 4px;
|
|
80
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
81
|
+
pointer-events: none;
|
|
82
|
+
z-index: 999;
|
|
41
83
|
}
|
|
42
84
|
|
|
43
85
|
.slot {
|
|
@@ -57,13 +99,19 @@ export class SortableList extends RapidElement {
|
|
|
57
99
|
}
|
|
58
100
|
constructor() {
|
|
59
101
|
super();
|
|
102
|
+
this.horizontal = false;
|
|
60
103
|
this.ghostElement = null;
|
|
61
104
|
this.downEle = null;
|
|
62
105
|
this.xOffset = 0;
|
|
63
106
|
this.yOffset = 0;
|
|
64
107
|
this.yDown = 0;
|
|
108
|
+
this.xDown = 0;
|
|
65
109
|
this.draggingIdx = -1;
|
|
66
110
|
this.draggingEle = null;
|
|
111
|
+
this.dropIndicator = null;
|
|
112
|
+
this.pendingDropIndex = -1;
|
|
113
|
+
this.pendingTargetElement = null;
|
|
114
|
+
this.clickBlocker = null;
|
|
67
115
|
this.handleMouseMove = this.handleMouseMove.bind(this);
|
|
68
116
|
this.handleMouseUp = this.handleMouseUp.bind(this);
|
|
69
117
|
this.handleMouseDown = this.handleMouseDown.bind(this);
|
|
@@ -71,109 +119,249 @@ export class SortableList extends RapidElement {
|
|
|
71
119
|
firstUpdated(_changedProperties) {
|
|
72
120
|
super.firstUpdated(_changedProperties);
|
|
73
121
|
}
|
|
74
|
-
|
|
122
|
+
getSortableElements() {
|
|
75
123
|
return this.shadowRoot
|
|
76
124
|
.querySelector('slot')
|
|
77
125
|
.assignedElements()
|
|
78
|
-
.
|
|
126
|
+
.filter((ele) => ele.classList.contains('sortable'));
|
|
127
|
+
}
|
|
128
|
+
getIds() {
|
|
129
|
+
return this.getSortableElements().map((ele) => ele.id);
|
|
79
130
|
}
|
|
80
131
|
getRowIndex(id) {
|
|
81
|
-
return this.
|
|
82
|
-
.querySelector('slot')
|
|
83
|
-
.assignedElements()
|
|
84
|
-
.findIndex((ele) => ele.id === id);
|
|
132
|
+
return this.getSortableElements().findIndex((ele) => ele.id === id);
|
|
85
133
|
}
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
134
|
+
getDropTargetInfo(mouseX, mouseY) {
|
|
135
|
+
const elements = this.getSortableElements().filter((ele) => { var _a; return ele.id !== ((_a = this.draggingEle) === null || _a === void 0 ? void 0 : _a.id); });
|
|
136
|
+
if (elements.length === 0)
|
|
137
|
+
return null;
|
|
138
|
+
if (this.horizontal) {
|
|
139
|
+
// For horizontal layout, find the insertion point based on mouse X position
|
|
140
|
+
for (let i = 0; i < elements.length; i++) {
|
|
141
|
+
const ele = elements[i];
|
|
142
|
+
const rect = ele.getBoundingClientRect();
|
|
143
|
+
const centerX = rect.left + rect.width / 2;
|
|
144
|
+
if (mouseX < centerX) {
|
|
145
|
+
// Insert before this element
|
|
146
|
+
return { element: ele, insertAfter: false };
|
|
147
|
+
}
|
|
96
148
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
149
|
+
// If we're past all elements, insert after the last one
|
|
150
|
+
return {
|
|
151
|
+
element: elements[elements.length - 1],
|
|
152
|
+
insertAfter: true
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// For vertical layout, find the insertion point based on mouse Y position
|
|
157
|
+
for (let i = 0; i < elements.length; i++) {
|
|
158
|
+
const ele = elements[i];
|
|
159
|
+
const rect = ele.getBoundingClientRect();
|
|
160
|
+
const centerY = rect.top + rect.height / 2;
|
|
161
|
+
if (mouseY < centerY) {
|
|
162
|
+
// Insert before this element
|
|
163
|
+
return { element: ele, insertAfter: false };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// If we're past all elements, insert after the last one
|
|
167
|
+
return {
|
|
168
|
+
element: elements[elements.length - 1],
|
|
169
|
+
insertAfter: true
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
showDropIndicator(targetElement, insertAfter) {
|
|
174
|
+
this.hideDropIndicator();
|
|
175
|
+
if (!targetElement)
|
|
176
|
+
return;
|
|
177
|
+
const container = this.shadowRoot.querySelector('.container');
|
|
178
|
+
this.dropIndicator = document.createElement('div');
|
|
179
|
+
this.dropIndicator.className = 'drop-indicator';
|
|
180
|
+
const targetRect = targetElement.getBoundingClientRect();
|
|
181
|
+
const containerRect = container.getBoundingClientRect();
|
|
182
|
+
if (this.horizontal) {
|
|
183
|
+
// For horizontal layout, show vertical line
|
|
184
|
+
this.dropIndicator.style.height = targetRect.height + 'px';
|
|
185
|
+
this.dropIndicator.style.top = targetRect.top - containerRect.top + 'px';
|
|
186
|
+
if (insertAfter) {
|
|
187
|
+
// Show line after target
|
|
188
|
+
this.dropIndicator.style.left =
|
|
189
|
+
targetRect.right - containerRect.left + 'px';
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// Show line before target
|
|
193
|
+
this.dropIndicator.style.left =
|
|
194
|
+
targetRect.left - containerRect.left + 'px';
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// For vertical layout, show horizontal line
|
|
199
|
+
this.dropIndicator.style.width = targetRect.width + 'px';
|
|
200
|
+
this.dropIndicator.style.left =
|
|
201
|
+
targetRect.left - containerRect.left + 'px';
|
|
202
|
+
if (insertAfter) {
|
|
203
|
+
// Show line after target
|
|
204
|
+
this.dropIndicator.style.top =
|
|
205
|
+
targetRect.bottom - containerRect.top + 'px';
|
|
100
206
|
}
|
|
101
207
|
else {
|
|
102
|
-
//
|
|
103
|
-
|
|
208
|
+
// Show line before target
|
|
209
|
+
this.dropIndicator.style.top =
|
|
210
|
+
targetRect.top - containerRect.top + 'px';
|
|
104
211
|
}
|
|
105
|
-
}
|
|
106
|
-
|
|
212
|
+
}
|
|
213
|
+
container.appendChild(this.dropIndicator);
|
|
214
|
+
}
|
|
215
|
+
hideDropIndicator() {
|
|
216
|
+
if (this.dropIndicator) {
|
|
217
|
+
this.dropIndicator.remove();
|
|
218
|
+
this.dropIndicator = null;
|
|
219
|
+
}
|
|
107
220
|
}
|
|
108
221
|
handleMouseDown(event) {
|
|
109
222
|
let ele = event.target;
|
|
110
223
|
ele = ele.closest('.sortable');
|
|
111
224
|
if (ele) {
|
|
225
|
+
event.preventDefault();
|
|
226
|
+
event.stopPropagation();
|
|
112
227
|
this.downEle = ele;
|
|
113
228
|
this.draggingId = ele.id;
|
|
114
229
|
this.draggingIdx = this.getRowIndex(ele.id);
|
|
115
230
|
this.draggingEle = ele;
|
|
116
|
-
|
|
117
|
-
|
|
231
|
+
// Use getBoundingClientRect for accurate offsets
|
|
232
|
+
const rect = ele.getBoundingClientRect();
|
|
233
|
+
this.xOffset = event.clientX - rect.left;
|
|
234
|
+
this.yOffset = event.clientY - rect.top;
|
|
118
235
|
this.yDown = event.clientY;
|
|
236
|
+
this.xDown = event.clientX;
|
|
119
237
|
document.addEventListener('mousemove', this.handleMouseMove);
|
|
120
238
|
document.addEventListener('mouseup', this.handleMouseUp);
|
|
121
239
|
}
|
|
122
240
|
}
|
|
123
241
|
handleMouseMove(event) {
|
|
124
|
-
const scrollTop = this.shadowRoot
|
|
125
|
-
.querySelector('slot')
|
|
126
|
-
.assignedElements()[0].parentElement.scrollTop;
|
|
127
242
|
if (!this.ghostElement &&
|
|
128
243
|
this.downEle &&
|
|
129
|
-
Math.abs(event.clientY - this.yDown) > DRAG_THRESHOLD
|
|
244
|
+
(Math.abs(event.clientY - this.yDown) > DRAG_THRESHOLD ||
|
|
245
|
+
Math.abs(event.clientX - this.xDown) > DRAG_THRESHOLD)) {
|
|
130
246
|
this.fireCustomEvent(CustomEventType.DragStart, {
|
|
131
247
|
id: this.downEle.id
|
|
132
248
|
});
|
|
133
249
|
this.ghostElement = this.downEle.cloneNode(true);
|
|
134
250
|
this.ghostElement.classList.add('ghost');
|
|
135
|
-
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
this.
|
|
251
|
+
// dim the original element while dragging
|
|
252
|
+
this.downEle.style.pointerEvents = 'none';
|
|
253
|
+
this.downEle.style.opacity = '0.5';
|
|
254
|
+
const rect = this.downEle.getBoundingClientRect();
|
|
255
|
+
this.ghostElement.style.transition = 'transform 300ms linear';
|
|
256
|
+
this.ghostElement.style.width = rect.width + 'px';
|
|
257
|
+
this.ghostElement.style.height = rect.height + 'px';
|
|
258
|
+
this.ghostElement.style.position = 'fixed';
|
|
259
|
+
this.ghostElement.style.left = event.clientX - this.xOffset + 'px';
|
|
260
|
+
this.ghostElement.style.top = event.clientY - this.yOffset + 'px';
|
|
261
|
+
this.ghostElement.style.pointerEvents = 'none';
|
|
262
|
+
this.ghostElement.style.border =
|
|
263
|
+
'1px solid var(--color-primary, #1c7cd6)';
|
|
264
|
+
this.ghostElement.style.zIndex = '99999';
|
|
265
|
+
this.ghostElement.style.background = '#fff';
|
|
266
|
+
this.ghostElement.style.opacity = '0.7';
|
|
267
|
+
this.ghostElement.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.2)';
|
|
268
|
+
this.ghostElement.style.borderRadius = 'var(--curvature)';
|
|
269
|
+
// allow component to customize the ghost node
|
|
270
|
+
if (this.prepareGhost) {
|
|
271
|
+
this.prepareGhost(this.ghostElement);
|
|
272
|
+
}
|
|
273
|
+
document.body.appendChild(this.ghostElement);
|
|
274
|
+
// this.downEle = null;
|
|
275
|
+
// Add global click blocker when drag starts
|
|
276
|
+
if (!this.clickBlocker) {
|
|
277
|
+
this.clickBlocker = (e) => {
|
|
278
|
+
e.stopPropagation();
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
};
|
|
281
|
+
// Use capture phase to intercept clicks before they reach any elements
|
|
282
|
+
document.addEventListener('click', this.clickBlocker, true);
|
|
283
|
+
}
|
|
144
284
|
}
|
|
145
285
|
if (this.ghostElement) {
|
|
146
286
|
this.ghostElement.style.left = event.clientX - this.xOffset + 'px';
|
|
147
|
-
this.ghostElement.style.top =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
287
|
+
this.ghostElement.style.top = event.clientY - this.yOffset + 'px';
|
|
288
|
+
const targetInfo = this.getDropTargetInfo(event.clientX, event.clientY);
|
|
289
|
+
if (targetInfo) {
|
|
290
|
+
const { element: targetElement, insertAfter } = targetInfo;
|
|
291
|
+
const targetIdx = this.getRowIndex(targetElement.id);
|
|
292
|
+
const originalDragIdx = this.getRowIndex(this.draggingEle.id);
|
|
293
|
+
// Calculate the intended drop index
|
|
294
|
+
let dropIdx = targetIdx;
|
|
295
|
+
if (insertAfter) {
|
|
296
|
+
dropIdx += 1;
|
|
297
|
+
}
|
|
298
|
+
// Adjust dropIdx if dragging forward in the list
|
|
299
|
+
if (originalDragIdx < dropIdx) {
|
|
300
|
+
dropIdx -= 1;
|
|
301
|
+
}
|
|
302
|
+
// Store pending drop info but don't fire event yet
|
|
303
|
+
this.dropTargetId = targetElement.id;
|
|
304
|
+
this.pendingDropIndex = dropIdx;
|
|
305
|
+
this.pendingTargetElement = targetElement;
|
|
306
|
+
// Show drop indicator
|
|
307
|
+
this.showDropIndicator(targetElement, insertAfter);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
this.hideDropIndicator();
|
|
311
|
+
this.dropTargetId = null;
|
|
312
|
+
this.pendingDropIndex = -1;
|
|
313
|
+
this.pendingTargetElement = null;
|
|
163
314
|
}
|
|
164
315
|
}
|
|
165
316
|
}
|
|
166
|
-
handleMouseUp() {
|
|
167
|
-
if (this.draggingId) {
|
|
317
|
+
handleMouseUp(evt) {
|
|
318
|
+
if (this.draggingId && this.ghostElement) {
|
|
319
|
+
evt.preventDefault();
|
|
320
|
+
evt.stopPropagation();
|
|
321
|
+
// restore visibility of the dragged item
|
|
322
|
+
if (this.downEle) {
|
|
323
|
+
this.downEle.style.pointerEvents = '';
|
|
324
|
+
this.downEle.style.opacity = '1';
|
|
325
|
+
}
|
|
326
|
+
// fire the order changed event only when dropped if we have a valid drop position
|
|
327
|
+
if (this.pendingDropIndex >= 0 && this.pendingTargetElement) {
|
|
328
|
+
const originalDragIdx = this.getRowIndex(this.draggingEle.id);
|
|
329
|
+
// use swap-based logic - report which indexes need to be swapped
|
|
330
|
+
const fromIdx = originalDragIdx;
|
|
331
|
+
const toIdx = this.pendingDropIndex;
|
|
332
|
+
// only fire if the position actually changed
|
|
333
|
+
if (fromIdx !== toIdx) {
|
|
334
|
+
this.fireCustomEvent(CustomEventType.OrderChanged, {
|
|
335
|
+
swap: [fromIdx, toIdx]
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
168
339
|
this.fireCustomEvent(CustomEventType.DragStop, {
|
|
169
340
|
id: this.draggingId
|
|
170
341
|
});
|
|
171
342
|
this.draggingId = null;
|
|
343
|
+
this.dropTargetId = null;
|
|
172
344
|
this.downEle = null;
|
|
345
|
+
this.pendingDropIndex = -1;
|
|
346
|
+
this.pendingTargetElement = null;
|
|
173
347
|
if (this.ghostElement) {
|
|
174
|
-
|
|
348
|
+
// Remove from body if present
|
|
349
|
+
if (this.ghostElement.parentNode) {
|
|
350
|
+
this.ghostElement.parentNode.removeChild(this.ghostElement);
|
|
351
|
+
}
|
|
175
352
|
this.ghostElement = null;
|
|
176
353
|
}
|
|
354
|
+
this.hideDropIndicator();
|
|
355
|
+
// Keep the click blocker active for a short time after drop
|
|
356
|
+
if (this.clickBlocker) {
|
|
357
|
+
// We'll clean it up after a timeout
|
|
358
|
+
setTimeout(() => {
|
|
359
|
+
if (this.clickBlocker) {
|
|
360
|
+
document.removeEventListener('click', this.clickBlocker, true);
|
|
361
|
+
this.clickBlocker = null;
|
|
362
|
+
}
|
|
363
|
+
}, 100);
|
|
364
|
+
}
|
|
177
365
|
}
|
|
178
366
|
document.removeEventListener('mousemove', this.handleMouseMove);
|
|
179
367
|
document.removeEventListener('mouseup', this.handleMouseUp);
|
|
@@ -181,7 +369,7 @@ export class SortableList extends RapidElement {
|
|
|
181
369
|
}
|
|
182
370
|
render() {
|
|
183
371
|
return html `
|
|
184
|
-
<div class="container">
|
|
372
|
+
<div class="container ${this.horizontal ? 'horizontal' : ''}">
|
|
185
373
|
<slot @mousedown=${this.handleMouseDown}></slot>
|
|
186
374
|
</div>
|
|
187
375
|
`;
|
|
@@ -190,4 +378,13 @@ export class SortableList extends RapidElement {
|
|
|
190
378
|
__decorate([
|
|
191
379
|
property({ type: String })
|
|
192
380
|
], SortableList.prototype, "draggingId", void 0);
|
|
381
|
+
__decorate([
|
|
382
|
+
property({ type: Boolean })
|
|
383
|
+
], SortableList.prototype, "horizontal", void 0);
|
|
384
|
+
__decorate([
|
|
385
|
+
property({ type: String })
|
|
386
|
+
], SortableList.prototype, "dropTargetId", void 0);
|
|
387
|
+
__decorate([
|
|
388
|
+
property({ attribute: false })
|
|
389
|
+
], SortableList.prototype, "prepareGhost", void 0);
|
|
193
390
|
//# sourceMappingURL=SortableList.js.map
|