@operato/app 1.0.0 → 1.0.6

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "WebApplication production supporting components following open-wc recommendations",
4
4
  "license": "MIT",
5
5
  "author": "heartyoh",
6
- "version": "1.0.0",
6
+ "version": "1.0.6",
7
7
  "main": "dist/src/index.js",
8
8
  "module": "dist/src/index.js",
9
9
  "exports": {
@@ -24,9 +24,71 @@
24
24
  "./grist-editor/ox-grist-editor-json.js": "./dist/src/grist-editor/ox-grist-editor-json.js",
25
25
  "./grist-editor/ox-grist-editor-code.js": "./dist/src/grist-editor/ox-grist-editor-code.js",
26
26
  "./grist-editor/ox-grist-editor-resource-object.js": "./dist/src/grist-editor/ox-grist-editor-resource-object.js",
27
+ "./grist-editor/ox-grist-editor-resource-object-legacy.js": "./dist/src/grist-editor/ox-grist-editor-resource-object-legacy.js",
27
28
  "./grist-editor/ox-grist-editor-resource-code.js": "./dist/src/grist-editor/ox-grist-editor-resource-code.js",
28
29
  "./filter-renderer.js": "./dist/src/filter-renderer/index.js"
29
30
  },
31
+ "typesVersions": {
32
+ "*": {
33
+ "input.js": [
34
+ "dist/src/input/index.d.ts"
35
+ ],
36
+ "input/ox-input-graphql.js": [
37
+ "dist/src/input/ox-input-graphql.d.ts"
38
+ ],
39
+ "input/ox-input-background-pattern.js": [
40
+ "dist/src/input/ox-input-background-pattern.d.ts"
41
+ ],
42
+ "input/ox-input-fill-style.js": [
43
+ "dist/src/input/ox-input-fill-style.d.ts"
44
+ ],
45
+ "property-editor.js": [
46
+ "dist/src/property-editor/index.d.ts"
47
+ ],
48
+ "property-editor/ox-property-editor-graphql.js": [
49
+ "dist/src/property-editor/ox-property-editor-graphql.d.ts"
50
+ ],
51
+ "grist-editor.js": [
52
+ "dist/src/grist-editor/index.d.ts"
53
+ ],
54
+ "grist-editor/ox-grist-editor-resource-id.js": [
55
+ "dist/src/grist-editor/ox-grist-editor-resource-id.d.ts"
56
+ ],
57
+ "grist-editor/ox-selector-resource-id.js": [
58
+ "dist/src/grist-editor/ox-selector-resource-id.d.ts"
59
+ ],
60
+ "grist-editor/ox-grist-renderer-resource-id.js": [
61
+ "dist/src/grist-editor/ox-grist-renderer-resource-id.d.ts"
62
+ ],
63
+ "grist-editor/ox-selector-resource-object.js": [
64
+ "dist/src/grist-editor/ox-selector-resource-object.d.ts"
65
+ ],
66
+ "grist-editor/ox-grist-renderer-resource-object.js": [
67
+ "dist/src/grist-editor/ox-grist-renderer-resource-object.d.ts"
68
+ ],
69
+ "grist-editor/ox-grist-renderer-crontab.js": [
70
+ "dist/src/grist-editor/ox-grist-renderer-crontab.d.ts"
71
+ ],
72
+ "grist-editor/ox-grist-editor-json.js": [
73
+ "dist/src/grist-editor/ox-grist-editor-json.d.ts"
74
+ ],
75
+ "grist-editor/ox-grist-editor-code.js": [
76
+ "dist/src/grist-editor/ox-grist-editor-code.d.ts"
77
+ ],
78
+ "grist-editor/ox-grist-editor-resource-object.js": [
79
+ "dist/src/grist-editor/ox-grist-editor-resource-object.d.ts"
80
+ ],
81
+ "grist-editor/ox-grist-editor-resource-object-legacy.js": [
82
+ "dist/src/grist-editor/ox-grist-editor-resource-object-legacy.d.ts"
83
+ ],
84
+ "grist-editor/ox-grist-editor-resource-code.js": [
85
+ "dist/src/grist-editor/ox-grist-editor-resource-code.d.ts"
86
+ ],
87
+ "filter-renderer.js": [
88
+ "dist/src/filter-renderer/index.d.ts"
89
+ ]
90
+ }
91
+ },
30
92
  "publishConfig": {
31
93
  "access": "public",
32
94
  "@operato:registry": "https://registry.npmjs.org"
@@ -53,18 +115,18 @@
53
115
  "@material/mwc-button": "^0.26.1",
54
116
  "@material/mwc-icon": "^0.26.1",
55
117
  "@material/mwc-icon-button": "^0.26.1",
56
- "@operato/attachment": "^1.0.0",
57
- "@operato/data-grist": "^1.0.0",
58
- "@operato/font": "^1.0.0",
59
- "@operato/form": "^1.0.0",
60
- "@operato/graphql": "^1.0.0",
61
- "@operato/i18n": "^1.0.0",
62
- "@operato/input": "^1.0.0",
63
- "@operato/layout": "^1.0.0",
64
- "@operato/property-editor": "^1.0.0",
65
- "@operato/shell": "^1.0.0",
66
- "@operato/styles": "^1.0.0",
67
- "@operato/utils": "^1.0.0",
118
+ "@operato/attachment": "^1.0.6",
119
+ "@operato/data-grist": "^1.0.6",
120
+ "@operato/font": "^1.0.6",
121
+ "@operato/form": "^1.0.6",
122
+ "@operato/graphql": "^1.0.6",
123
+ "@operato/i18n": "^1.0.6",
124
+ "@operato/input": "^1.0.6",
125
+ "@operato/layout": "^1.0.6",
126
+ "@operato/property-editor": "^1.0.6",
127
+ "@operato/shell": "^1.0.6",
128
+ "@operato/styles": "^1.0.6",
129
+ "@operato/utils": "^1.0.6",
68
130
  "codemirror": "^5.64.0",
69
131
  "codemirror-graphql": "^1.3.2",
70
132
  "cronstrue": "^2.2.0",
@@ -101,9 +163,9 @@
101
163
  },
102
164
  "lint-staged": {
103
165
  "*.ts": [
104
- "eslint --fix",
166
+ "eslint --fid.t",
105
167
  "prettier --write"
106
168
  ]
107
169
  },
108
- "gitHead": "c383e4b11cb4195f32be9b0daa4342dda0bbb2ab"
170
+ "gitHead": "e0c4443fb011f84350b93f1c0efa6a96f76cb40b"
109
171
  }
@@ -0,0 +1,123 @@
1
+ import './ox-selector-resource-object-legacy.js'
2
+
3
+ import { html, TemplateResult } from 'lit'
4
+ import { customElement } from 'lit/decorators.js'
5
+
6
+ import { OxGristEditor } from '@operato/data-grist'
7
+ import { i18next } from '@operato/i18n'
8
+ import { openPopup, PopupHandle } from '@operato/layout'
9
+
10
+ @customElement('ox-grist-editor-resource-object-legacy')
11
+ export class OxGristEditorResourceObjectLegacy extends OxGristEditor {
12
+ private popup?: PopupHandle
13
+ private template?: TemplateResult
14
+
15
+ get editorTemplate() {
16
+ var value = this.value || {}
17
+
18
+ var { nameField = 'name', descriptionField = 'description' } = this.column.record.options || {}
19
+ var name, description
20
+ if (typeof nameField === 'function') {
21
+ name = nameField(value)
22
+ } else {
23
+ name = value[nameField]
24
+ }
25
+
26
+ if (typeof descriptionField === 'function') {
27
+ description = descriptionField(value)
28
+ } else {
29
+ description = value[descriptionField] && `(${value[descriptionField]})`
30
+ }
31
+
32
+ return html` ${!value ? html`` : html` <span style="flex:1">${name || ''}${description || ''}</span> `} `
33
+ }
34
+
35
+ async firstUpdated() {
36
+ this.template = ((this.column.record || {}).options || {}).template
37
+
38
+ await this.updateComplete
39
+
40
+ this.renderRoot.addEventListener('click', e => {
41
+ e.stopPropagation()
42
+
43
+ this.openSelector()
44
+ })
45
+
46
+ this.openSelector()
47
+ }
48
+
49
+ openSelector() {
50
+ if (this.popup) {
51
+ delete this.popup
52
+ }
53
+
54
+ var {
55
+ idField = 'id',
56
+ nameField = 'name',
57
+ descriptionField = 'description',
58
+ valueField = 'id',
59
+ queryName,
60
+ basicArgs,
61
+ select,
62
+ list
63
+ } = this.column.record.options || {}
64
+
65
+ const confirmCallback = (selected?: { [field: string]: any }) => {
66
+ this.dispatchEvent(
67
+ new CustomEvent('field-change', {
68
+ bubbles: true,
69
+ composed: true,
70
+ detail: {
71
+ before: this.value,
72
+ after: selected
73
+ ? {
74
+ ...(this.column.record.options.select || [])
75
+ .map((field: any) => field.name)
76
+ .reduce((obj: { [field: string]: any }, fieldName: string) => {
77
+ return (obj = {
78
+ ...obj,
79
+ [fieldName]: selected[fieldName]
80
+ })
81
+ }, {} as { [field: string]: any }),
82
+ [idField]: selected[idField],
83
+ [nameField]: selected[nameField],
84
+ [descriptionField]: selected[descriptionField]
85
+ }
86
+ : null,
87
+ record: this.record,
88
+ column: this.column,
89
+ row: this.row
90
+ }
91
+ })
92
+ )
93
+ }
94
+
95
+ var value = this.value || {}
96
+ var actualValue
97
+ if (typeof valueField === 'function') {
98
+ actualValue = valueField(value)
99
+ } else {
100
+ actualValue = value[valueField]
101
+ }
102
+
103
+ var template =
104
+ this.template ||
105
+ html`
106
+ <ox-selector-resource-object-legacy
107
+ .value=${actualValue}
108
+ .confirmCallback=${confirmCallback.bind(this)}
109
+ .queryName=${queryName}
110
+ .select=${select}
111
+ .list=${list}
112
+ .basicArgs=${basicArgs}
113
+ .valueField=${valueField}
114
+ ></ox-selector-resource-object-legacy>
115
+ `
116
+
117
+ this.popup = openPopup(template, {
118
+ backdrop: true,
119
+ size: 'large',
120
+ title: i18next.t('title.select_item')
121
+ })
122
+ }
123
+ }
@@ -59,9 +59,13 @@ export class OxGristEditorResourceObject extends OxGristEditor {
59
59
  queryName,
60
60
  basicArgs,
61
61
  select,
62
+ columns,
62
63
  list
63
64
  } = this.column.record.options || {}
64
65
 
66
+ /* select options is only for compatability, it might be able to be deprecated. */
67
+ columns = columns || select
68
+
65
69
  const confirmCallback = (selected?: { [field: string]: any }) => {
66
70
  this.dispatchEvent(
67
71
  new CustomEvent('field-change', {
@@ -71,7 +75,7 @@ export class OxGristEditorResourceObject extends OxGristEditor {
71
75
  before: this.value,
72
76
  after: selected
73
77
  ? {
74
- ...(this.column.record.options.select || [])
78
+ ...(columns || [])
75
79
  .map((field: any) => field.name)
76
80
  .reduce((obj: { [field: string]: any }, fieldName: string) => {
77
81
  return (obj = {
@@ -107,7 +111,7 @@ export class OxGristEditorResourceObject extends OxGristEditor {
107
111
  .value=${actualValue}
108
112
  .confirmCallback=${confirmCallback.bind(this)}
109
113
  .queryName=${queryName}
110
- .select=${select}
114
+ .columns=${columns}
111
115
  .list=${list}
112
116
  .basicArgs=${basicArgs}
113
117
  .valueField=${valueField}
@@ -62,7 +62,7 @@ export class OxSelectorResourceId extends LitElement {
62
62
  .selectedRecords=${this.selectedRecords}
63
63
  >
64
64
  <div id="filters" slot="headroom">
65
- <ox-filters-form></ox-filters-form>
65
+ <ox-filters-form autofocus></ox-filters-form>
66
66
  </div>
67
67
  </ox-grist>
68
68
 
@@ -0,0 +1,360 @@
1
+ import '@operato/data-grist'
2
+ import '@operato/form/ox-search-form.js'
3
+
4
+ import gql from 'graphql-tag'
5
+ import { css, html, LitElement } from 'lit'
6
+ import { customElement, property, query } from 'lit/decorators.js'
7
+
8
+ import {
9
+ ColumnConfig,
10
+ DataGrist,
11
+ FetchHandler,
12
+ FetchOption,
13
+ GristData,
14
+ GristEventHandler,
15
+ GristRecord,
16
+ ZERO_DATA
17
+ } from '@operato/data-grist'
18
+ import { MultiColumnFormStyles, SearchForm } from '@operato/form'
19
+ import { buildArgs, client, gqlContext } from '@operato/graphql'
20
+ import { i18next } from '@operato/i18n'
21
+ import { closePopup } from '@operato/popup'
22
+ import { isMobileDevice } from '@operato/utils'
23
+
24
+ @customElement('ox-selector-resource-object-legacy')
25
+ export class OxSelectorResourceObjectLegacy extends LitElement {
26
+ static styles = [
27
+ MultiColumnFormStyles,
28
+ css`
29
+ :host {
30
+ display: flex;
31
+ flex-direction: column;
32
+
33
+ background-color: #fff;
34
+ }
35
+
36
+ ox-grist {
37
+ flex: 1;
38
+ }
39
+
40
+ .button-container {
41
+ display: flex;
42
+ margin-left: auto;
43
+ }
44
+
45
+ form {
46
+ position: relative;
47
+ }
48
+
49
+ [search] {
50
+ position: absolute;
51
+ right: 0;
52
+ }
53
+ `
54
+ ]
55
+
56
+ @property({ type: String }) value?: string
57
+ @property({ type: Object }) config: any
58
+ @property({ type: Object }) data: GristData = ZERO_DATA
59
+ @property({ type: String }) queryName!: string
60
+ @property({ type: Object }) basicArgs: any
61
+ @property({ type: Object }) confirmCallback?: (record?: GristRecord | null) => void
62
+ @property({ type: Array }) selectedRecords: GristRecord[] = []
63
+
64
+ @property({ type: Array }) searchFields: any
65
+ @property({ type: Array }) select: {
66
+ type: string
67
+ name: string
68
+ header: string
69
+ subFields?: string[]
70
+ hidden: boolean
71
+ ignoreCondition: boolean
72
+ queryName: string
73
+ width: number
74
+ }[] = []
75
+ @property({ type: Object }) list: any
76
+ @property({ type: String }) valueField: string | ((item: any) => any) = 'id'
77
+
78
+ @query('ox-grist') grist!: DataGrist
79
+ @query('ox-search-form') searchForm!: SearchForm
80
+
81
+ render() {
82
+ return html`
83
+ <ox-search-form
84
+ id="search-form"
85
+ @keypress=${(e: KeyboardEvent) => {
86
+ if (e.key === 'Enter') {
87
+ this.grist.fetch()
88
+ }
89
+ }}
90
+ @submit=${(e: SubmitEvent) => this.grist.fetch()}
91
+ .fields=${this.searchFields}
92
+ autofocus
93
+ ></ox-search-form>
94
+
95
+ <ox-grist
96
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
97
+ .config=${this.config}
98
+ .data=${this.data}
99
+ .fetchHandler=${this.fetchHandler.bind(this)}
100
+ .selectedRecords=${this.selectedRecords}
101
+ >
102
+ <div slot="headroom" id="headroom">
103
+ <div id="filters">
104
+ <ox-filters-form></ox-filters-form>
105
+ </div>
106
+ </div>
107
+ </ox-grist>
108
+
109
+ <div class="button-container">
110
+ <mwc-button @click=${this.onempty.bind(this)}>${i18next.t('button.empty')}</mwc-button>
111
+ <mwc-button @click=${this.oncancel.bind(this)}>${i18next.t('button.cancel')}</mwc-button>
112
+ <mwc-button @click=${this.onconfirm.bind(this)}>${i18next.t('button.confirm')}</mwc-button>
113
+ </div>
114
+ `
115
+ }
116
+
117
+ onempty() {
118
+ this.confirmCallback && this.confirmCallback()
119
+ closePopup(this)
120
+ }
121
+
122
+ oncancel() {
123
+ closePopup(this)
124
+ }
125
+
126
+ onconfirm() {
127
+ this.confirmCallback && this.confirmCallback(this.selected)
128
+ closePopup(this)
129
+ }
130
+
131
+ fetchHandler: FetchHandler = async ({ filters, page, limit, sorters = [] }) => {
132
+ const response = await client.query({
133
+ query: gql`
134
+ query {
135
+ fetch: ${this.queryName} (${buildArgs(await this._buildConditions({ filters, page, limit, sorters }))}) {
136
+ ${this.getSelectFields()}
137
+ }
138
+ }
139
+ `,
140
+ context: gqlContext()
141
+ })
142
+
143
+ if (!response.errors) {
144
+ const records = response.data.fetch.items.map((item: any) => {
145
+ let rowValue
146
+
147
+ if (this.valueField && typeof this.valueField === 'function') {
148
+ rowValue = this.valueField(item)
149
+ } else if (this.valueField) {
150
+ rowValue = item[this.valueField]
151
+ } else {
152
+ rowValue = item.id
153
+ }
154
+
155
+ if (this.value && this.value === rowValue) {
156
+ this.selectedRecords = [item]
157
+ item['__selected__'] = true
158
+ }
159
+
160
+ return item
161
+ })
162
+ const total = response.data.fetch.total
163
+
164
+ return {
165
+ records,
166
+ total,
167
+ limit,
168
+ page
169
+ }
170
+ }
171
+ }
172
+
173
+ async firstUpdated() {
174
+ this.config = {
175
+ columns: [
176
+ {
177
+ type: 'gutter',
178
+ gutterName: 'sequence'
179
+ },
180
+ {
181
+ type: 'gutter',
182
+ gutterName: 'row-selector',
183
+ multiple: false
184
+ }
185
+ ],
186
+ rows: {
187
+ selectable: {
188
+ multiple: false
189
+ },
190
+ handlers: {
191
+ click: 'select-row',
192
+ dblclick: ((columns, data, column, record, rowIndex, field) => {
193
+ this.onconfirm()
194
+ }) as GristEventHandler
195
+ },
196
+ appendable: false
197
+ }
198
+ }
199
+
200
+ if (this.select && this.select.length > 0) {
201
+ let _searchFields = this.select.filter(selectField => !selectField.hidden && !selectField.ignoreCondition)
202
+ if (this.list && this.list.fields && this.list.fields.length > 0) {
203
+ _searchFields = _searchFields.filter(searchField => this.list.fields.indexOf(searchField.name) >= 0)
204
+ } else {
205
+ _searchFields = _searchFields.slice(0, 4)
206
+ }
207
+
208
+ this.searchFields = _searchFields.map(selectField => {
209
+ const fieldType = (selectField.type && selectField.type.toLowerCase()) || 'string'
210
+ const numberTypes = ['integer', 'float']
211
+ return {
212
+ label: selectField.header || i18next.t(`field.${selectField.name}`),
213
+ name: selectField.name,
214
+ type:
215
+ fieldType === 'string'
216
+ ? 'text'
217
+ : numberTypes.indexOf(fieldType) >= 0
218
+ ? 'number'
219
+ : fieldType === 'boolean'
220
+ ? 'checkbox'
221
+ : fieldType,
222
+ queryName: selectField.queryName,
223
+ props:
224
+ fieldType === 'string'
225
+ ? { searchOper: 'i_like' }
226
+ : fieldType === 'object'
227
+ ? { searchOper: 'in' }
228
+ : { searchOper: 'eq' },
229
+ attrs: fieldType === 'boolean' ? ['indeterminated'] : []
230
+ }
231
+ })
232
+ this.config = {
233
+ ...this.config,
234
+ columns: [
235
+ ...this.config.columns,
236
+ ...this.select.map(selectField => {
237
+ return {
238
+ ...selectField,
239
+ type: selectField.type || 'string',
240
+ width: selectField.width || 160,
241
+ header: selectField.header || i18next.t(`field.${selectField.name}`)
242
+ }
243
+ })
244
+ ]
245
+ }
246
+ } else {
247
+ this.searchFields = [
248
+ {
249
+ label: i18next.t('field.name'),
250
+ name: 'name',
251
+ type: 'text',
252
+ props: { searchOper: 'i_like' }
253
+ },
254
+ {
255
+ label: i18next.t('field.description'),
256
+ name: 'description',
257
+ type: 'text',
258
+ props: { searchOper: 'i_like' }
259
+ }
260
+ ]
261
+
262
+ this.config = {
263
+ ...this.config,
264
+ columns: [
265
+ ...this.config.columns,
266
+ {
267
+ type: 'string',
268
+ name: 'id',
269
+ header: i18next.t('field.id'),
270
+ hidden: true
271
+ },
272
+ {
273
+ type: 'string',
274
+ name: 'name',
275
+ header: i18next.t('field.name'),
276
+ record: {
277
+ align: 'left'
278
+ },
279
+ sortable: true,
280
+ width: 160
281
+ },
282
+ {
283
+ type: 'string',
284
+ name: 'description',
285
+ header: i18next.t('field.description'),
286
+ record: {
287
+ align: 'left'
288
+ },
289
+ sortable: true,
290
+ width: 300
291
+ }
292
+ ]
293
+ }
294
+ }
295
+
296
+ this.config = {
297
+ ...this.config,
298
+ list: {
299
+ ...this.list,
300
+ fields:
301
+ this.list && this.list.fields && this.list.fields.length > 0
302
+ ? this.list.fields
303
+ : (this.config.columns as ColumnConfig[])
304
+ .filter(column => column.type !== 'gutter')
305
+ .slice(0, 3)
306
+ .map(column => column.name)
307
+ }
308
+ }
309
+
310
+ await this.updateComplete
311
+ this.grist && this.grist.focus()
312
+ }
313
+
314
+ getSelectFields() {
315
+ if (this.select && this.select.length > 0) {
316
+ return `items {
317
+ ${this.select.map(selectField => {
318
+ return selectField.type === 'object'
319
+ ? `${selectField.name} { ${
320
+ selectField.subFields && selectField.subFields.length > 0
321
+ ? selectField.subFields.join(' ')
322
+ : `id name description`
323
+ } }`
324
+ : `${selectField.name}`
325
+ })}
326
+ }
327
+ total`
328
+ } else {
329
+ return `
330
+ items {
331
+ id
332
+ name
333
+ description
334
+ }
335
+ total
336
+ `
337
+ }
338
+ }
339
+
340
+ async _buildConditions({ filters, page, limit, sorters }: FetchOption) {
341
+ const queryConditions = {
342
+ filters: [],
343
+ ...this.basicArgs
344
+ }
345
+
346
+ queryConditions.filters = [...queryConditions.filters, ...(await this.searchForm.getQueryFilters())]
347
+ queryConditions.pagination = { page, limit }
348
+ queryConditions.sortings = sorters
349
+
350
+ return queryConditions
351
+ }
352
+
353
+ get selected() {
354
+ var grist = this.renderRoot.querySelector('ox-grist') as DataGrist
355
+
356
+ var selected = grist.selected
357
+
358
+ return selected && selected.length > 0 ? selected[0] : undefined
359
+ }
360
+ }