import '@operato/data-grist'
import gql from 'graphql-tag'
import { css, html, LitElement } from 'lit'
import { customElement, property, query } from 'lit/decorators.js'
import { DataGrist } from '@operato/data-grist/ox-grist.js'
import { buildArgs, client } from '@operato/graphql'
import { i18next } from '@operato/i18n'
import { isMobileDevice } from '@operato/utils'
import { MultiColumnFormStyles } from '@operato/form'
import { CommonHeaderStyles } from '@operato/styles'
import { FetchOption } from '@operato/data-grist'
@customElement('entity-selector')
export class EntitySelector extends LitElement {
static styles = [
CommonHeaderStyles,
MultiColumnFormStyles,
css`
:host {
display: flex;
flex-direction: column;
background-color: var(--md-sys-color-surface);
}
ox-grist {
flex: 1;
}
form {
position: relative;
}
[search] {
position: absolute;
right: 0;
}
`
]
@property({ type: String }) value?: string
@property({ type: Array }) searchFields: any
@property({ type: Object }) config: any
@property({ type: Object }) data: any
@property({ type: String }) queryName!: string
@property({ type: Array }) select: any
@property({ type: Object }) list: any
@property({ type: Object }) basicArgs: any
@property({ type: Array }) selectedRecords: any
@query('ox-grist') grist!: DataGrist
@query('search-form') searchForm!: HTMLFormElement
render() {
return html`
{
if (e.keyCode === 13) {
this.data = await this.grist.fetch()
}
}}"
@submit="${async e => (this.data = await this.grist.fetch())}"
.fields="${this.searchFields}"
>
`
}
onCancel() {
history.back()
}
onConfirm() {
this.dispatchEvent(
new CustomEvent('entity-selected', {
detail: {
entity: this.selected
}
})
)
}
async fetchHandler({ page, limit, sorters = [] }: FetchOption) {
const response = await client.query({
query: gql`
query {
${this.queryName} (${buildArgs(await this._buildConditions(page, limit, sorters))}) {
${this.getSelectFields()}
}
}
`
})
if (!response.errors) {
const records = response.data[this.queryName].items.map(item => {
if (this.value === item.id) {
this.selectedRecords = [item]
item['__selected__'] = true
}
return item
})
const total = response.data[this.queryName].total
return {
records,
total,
limit,
page
}
}
}
async firstUpdated() {
this.config = {
columns: [
{
type: 'gutter',
gutterName: 'sequence'
},
{
type: 'gutter',
gutterName: 'row-selector',
multiple: false
}
],
rows: {
selectable: {
multiple: false
},
handlers: {
click: 'select-row',
dblclick: (columns, data, column, record, rowIndex, field) => {
this.onConfirm()
}
},
appendable: false
}
}
if (this.select && this.select.length > 0) {
let _searchFields = this.select.filter(selectField => !selectField.hidden)
if (this.list && this.list.fields && this.list.fields.length > 0) {
_searchFields = _searchFields.filter(searchField => this.list.fields.indexOf(searchField.name) >= 0)
} else {
_searchFields = _searchFields.slice(0, 4)
}
this.searchFields = _searchFields.map(selectField => {
const fieldType = (selectField.type && selectField.type.toLowerCase()) || 'string'
const numberTypes = ['integer', 'float']
return {
label: selectField.header || i18next.t(`field.${selectField.name}`),
name: selectField.name,
type:
fieldType === 'string'
? 'text'
: numberTypes.indexOf(fieldType) >= 0
? 'number'
: fieldType === 'boolean'
? 'checkbox'
: fieldType,
queryName: selectField.queryName,
props:
fieldType === 'string'
? { searchOper: 'i_like' }
: fieldType === 'object'
? { searchOper: 'in' }
: { searchOper: 'eq' },
attrs: fieldType === 'boolean' ? ['indeterminated'] : []
}
})
this.config = {
...this.config,
columns: [
...this.config.columns,
...this.select.map(selectField => {
return {
...selectField,
type: selectField.type || 'string',
width: selectField.width || 160,
header: selectField.header || i18next.t(`field.${selectField.name}`)
}
})
]
}
} else {
this.searchFields = [
{
label: i18next.t('field.name'),
name: 'name',
type: 'text',
props: { searchOper: 'i_like' }
},
{
label: i18next.t('field.description'),
name: 'description',
type: 'text',
props: { searchOper: 'i_like' }
}
]
this.config = {
...this.config,
columns: [
...this.config.columns,
{
type: 'string',
name: 'id',
header: i18next.t('field.id'),
hidden: true
},
{
type: 'string',
name: 'name',
header: i18next.t('field.name'),
record: {
align: 'left'
},
sortable: true,
width: 160
},
{
type: 'string',
name: 'description',
header: i18next.t('field.description'),
record: {
align: 'left'
},
sortable: true,
width: 300
}
]
}
}
this.config = {
...this.config,
list: {
...this.list,
fields:
this.list && this.list.fields && this.list.fields.length > 0
? this.list.fields
: this.config.columns
.filter(column => column.type !== 'gutter')
.slice(0, 3)
.map(column => column.name)
}
}
await this.updateComplete
this.grist && this.grist.focus()
}
getSelectFields() {
if (this.select && this.select.length > 0) {
return `items {
${this.select.map(selectField => {
return selectField.type === 'object'
? `${selectField.name} { ${selectField.subFields && selectField.subFields.length > 0 ? selectField.subFields.join(' ') : `id name description`} }`
: `${selectField.name}`
})}
}
total`
} else {
return `
items {
id
name
description
}
total
`
}
}
async _buildConditions(page, limit, sorters) {
const queryConditions = {
filters: [],
...this.basicArgs
}
queryConditions.filters = [...queryConditions.filters, ...(await this.searchForm.getQueryFilters())]
queryConditions.pagination = { page, limit }
queryConditions.sortings = sorters
return queryConditions
}
get selected() {
var selected = this.grist.selected
return selected && selected.length > 0 ? selected[0] : undefined
}
}