@operato/attachment 7.1.30 → 7.1.32

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
@@ -2,7 +2,7 @@
2
2
  "name": "@operato/attachment",
3
3
  "description": "Webcomponent attachment following open-wc recommendations",
4
4
  "author": "heartyoh",
5
- "version": "7.1.30",
5
+ "version": "7.1.32",
6
6
  "main": "dist/src/index.js",
7
7
  "module": "dist/src/index.js",
8
8
  "exports": {
@@ -54,13 +54,13 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@material/web": "^2.0.0",
57
- "@operato/data-grist": "^7.1.30",
58
- "@operato/graphql": "^7.1.1",
59
- "@operato/i18n": "^7.1.1",
60
- "@operato/input": "^7.1.30",
61
- "@operato/property-editor": "^7.1.30",
62
- "@operato/styles": "^7.1.27",
63
- "@operato/utils": "^7.1.1",
57
+ "@operato/data-grist": "^7.1.32",
58
+ "@operato/graphql": "^7.1.32",
59
+ "@operato/i18n": "^7.1.32",
60
+ "@operato/input": "^7.1.32",
61
+ "@operato/property-editor": "^7.1.32",
62
+ "@operato/styles": "^7.1.32",
63
+ "@operato/utils": "^7.1.32",
64
64
  "i18next": "^23.11.5",
65
65
  "lit": "^3.1.2"
66
66
  },
@@ -96,5 +96,5 @@
96
96
  "prettier --write"
97
97
  ]
98
98
  },
99
- "gitHead": "863eb4937334eb66a3cd0e5dbea5c77bc1afc472"
99
+ "gitHead": "c4f5ed0b63d49d02c5a155b592c0ecf681c92cf7"
100
100
  }
package/.editorconfig DELETED
@@ -1,29 +0,0 @@
1
- # EditorConfig helps developers define and maintain consistent
2
- # coding styles between different editors and IDEs
3
- # editorconfig.org
4
-
5
- root = true
6
-
7
-
8
- [*]
9
-
10
- # Change these settings to your own preference
11
- indent_style = space
12
- indent_size = 2
13
-
14
- # We recommend you to keep these unchanged
15
- end_of_line = lf
16
- charset = utf-8
17
- trim_trailing_whitespace = true
18
- insert_final_newline = true
19
-
20
- [*.md]
21
- trim_trailing_whitespace = false
22
-
23
- [*.json]
24
- indent_size = 2
25
-
26
- [*.{html,js,md}]
27
- block_comment_start = /**
28
- block_comment = *
29
- block_comment_end = */
@@ -1,3 +0,0 @@
1
- module.exports = {
2
- stories: ['../dist/stories/**/*.stories.{js,md,mdx}'],
3
- };
@@ -1,8 +0,0 @@
1
- import { storybookPlugin } from '@web/dev-server-storybook';
2
- import baseConfig from '../web-dev-server.config.mjs';
3
-
4
- export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
5
- ...baseConfig,
6
- open: '/',
7
- plugins: [storybookPlugin({ type: 'web-components' }), ...baseConfig.plugins],
8
- });
package/demo/index.html DELETED
@@ -1,43 +0,0 @@
1
- <!doctype html>
2
- <html lang="en-GB">
3
- <head>
4
- <meta charset="utf-8" />
5
- <style>
6
- body {
7
- background: #fafafa;
8
- --ox-checkbox-size: 12px;
9
- }
10
-
11
- #demo {
12
- position: relative;
13
- height: 300px;
14
- background-color: lightgray;
15
- vertical-align: middle;
16
- }
17
- </style>
18
- <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet" />
19
- <link
20
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL@20..48,100..700,0..1"
21
- rel="stylesheet"
22
- />
23
- <link
24
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL@20..48,100..700,0..1"
25
- rel="stylesheet"
26
- />
27
- <link
28
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL@20..48,100..700,0..1"
29
- rel="stylesheet"
30
- />
31
- </head>
32
- <body>
33
- <div id="demo"></div>
34
-
35
- <script type="module">
36
- import { html, render } from 'lit'
37
-
38
- const parent = document.querySelector('#demo')
39
-
40
- render(html`clike anywhere in this box to utils mini.`, parent)
41
- </script>
42
- </body>
43
- </html>
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './ox-attachment-list'
@@ -1,507 +0,0 @@
1
- import '@operato/data-grist'
2
- import '@operato/input/ox-input-file.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
- import { openPopup, PopupHandle } from '@operato/popup'
8
-
9
- import {
10
- ColumnConfig,
11
- DataGrist,
12
- FetchOption,
13
- FieldRenderer,
14
- FilterValue,
15
- GristData,
16
- GristRecord,
17
- PaginationConfig,
18
- SortersConfig
19
- } from '@operato/data-grist'
20
- import { buildArgs, client } from '@operato/graphql'
21
- import { ScrollbarStyles, CommonGristStyles, CommonHeaderStyles } from '@operato/styles'
22
- import { i18next } from '@operato/i18n'
23
- import { copyToClipboard, sleep } from '@operato/utils'
24
-
25
- const FETCH_ATTACHMENT_LIST_GQL = (listParam: any) => {
26
- return gql`
27
- {
28
- attachments(${buildArgs(listParam)}) {
29
- items {
30
- id
31
- name
32
- description
33
- mimetype
34
- encoding
35
- category
36
- fullpath
37
- path
38
- tags
39
- createdAt
40
- updatedAt
41
- }
42
- total
43
- }
44
- }
45
- `
46
- }
47
-
48
- const DELETE_ATTACHMENT_GQL = gql`
49
- mutation DeleteAttachment($id: String!) {
50
- deleteAttachment(id: $id)
51
- }
52
- `
53
-
54
- const CREATE_ATTACHMENTS_GQL = gql`
55
- mutation ($attachments: [NewAttachment!]!) {
56
- createAttachments(attachments: $attachments) {
57
- id
58
- name
59
- description
60
- mimetype
61
- encoding
62
- category
63
- path
64
- tags
65
- createdAt
66
- updatedAt
67
- }
68
- }
69
- `
70
-
71
- const UPDATE_ATTACHMENT_GQL = gql`
72
- mutation UpdateAttachment($patch: AttachmentPatch!, $id: String!) {
73
- updateAttachment(patch: $patch, id: $id) {
74
- id
75
- name
76
- description
77
- mimetype
78
- encoding
79
- category
80
- path
81
- tags
82
- createdAt
83
- updatedAt
84
- }
85
- }
86
- `
87
-
88
- @customElement('ox-attachment-list')
89
- export class OxAttachmentList extends LitElement {
90
- static styles = [
91
- ScrollbarStyles,
92
- CommonGristStyles,
93
- CommonHeaderStyles,
94
- css`
95
- :host {
96
- display: flex;
97
-
98
- width: 100%;
99
-
100
- --grid-record-emphasized-background-color: red;
101
- --grid-record-emphasized-color: yellow;
102
- }
103
-
104
- ox-grist {
105
- flex: 1;
106
- overflow-y: auto;
107
-
108
- --grid-record-emphasized-background-color: red;
109
- --grid-record-emphasized-color: yellow;
110
- }
111
-
112
- .header .filters {
113
- flex-direction: column;
114
- align-items: stretch;
115
- }
116
- `
117
- ]
118
-
119
- @property({ type: String }) category: string = ''
120
- @property({ type: Boolean }) creatable: boolean = false
121
- @property({ type: Boolean, attribute: 'without-search' }) withoutSearch: boolean = false
122
-
123
- @query('ox-grist') grist!: DataGrist
124
- @query('ox-input-file') fileUploader!: any
125
-
126
- render() {
127
- return html`
128
- <ox-grist .config=${this.gristConfig} .mode=${'CARD'} auto-fetch .fetchHandler=${this.fetchHandler.bind(this)}>
129
- <div slot="headroom" class="header">
130
- <div class="filters">
131
- <ox-input-file
132
- accept="*/*"
133
- multiple="true"
134
- hide-filelist
135
- @change=${this.onCreateAttachment.bind(this)}
136
- ></ox-input-file>
137
-
138
- <ox-filters-form autofocus .withoutSearch=${this.withoutSearch}></ox-filters-form>
139
- </div>
140
- </div>
141
- </ox-grist>
142
- `
143
- }
144
-
145
- async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
146
- const { items: records, total } = await this.getAttachments({ page, limit, filters, sortings })
147
-
148
- return {
149
- total,
150
- records
151
- }
152
- }
153
-
154
- get gristConfig() {
155
- return {
156
- list: {
157
- thumbnail: 'thumbnail',
158
- fields: ['name'],
159
- details: ['tags', 'updatedAt']
160
- },
161
- columns: [
162
- {
163
- type: 'gutter',
164
- gutterName: 'dirty'
165
- },
166
- {
167
- type: 'gutter',
168
- gutterName: 'sequence'
169
- },
170
- {
171
- type: 'gutter',
172
- gutterName: 'button',
173
- icon: 'tag',
174
- title: i18next.t('field.hashtags'),
175
- handlers: {
176
- click: (
177
- columns: ColumnConfig[],
178
- data: GristData,
179
- column: ColumnConfig,
180
- record: GristRecord,
181
- rowIndex: number,
182
- target: HTMLElement
183
- ): void => {
184
- this.popupHashtagEditor(record)
185
- }
186
- }
187
- },
188
- {
189
- type: 'gutter',
190
- gutterName: 'button',
191
- icon: 'open_in_new',
192
- title: i18next.t('button.open-in-new'),
193
- handlers: {
194
- click: (
195
- columns: ColumnConfig[],
196
- data: GristData,
197
- column: ColumnConfig,
198
- record: GristRecord,
199
- rowIndex: number,
200
- target: HTMLElement
201
- ): void => {
202
- window.open(record.fullpath, '_blank')
203
- }
204
- }
205
- },
206
- {
207
- type: 'gutter',
208
- gutterName: 'button',
209
- icon: 'link',
210
- title: i18next.t('button.copy-url'),
211
- handlers: {
212
- click: async (
213
- columns: ColumnConfig[],
214
- data: GristData,
215
- column: ColumnConfig,
216
- record: GristRecord,
217
- rowIndex: number,
218
- target: HTMLElement
219
- ): Promise<void> => {
220
- var { protocol, hostname, port } = location
221
- await copyToClipboard(`${protocol}//${hostname}:${port}${record.fullpath}`)
222
-
223
- target.setAttribute('data-tooltip', 'url copied!')
224
-
225
- const rect = target.getBoundingClientRect()
226
- target.style.setProperty('--tooltip-top', `${rect.top}px`)
227
- target.style.setProperty('--tooltip-left', `${rect.left}px`)
228
-
229
- await sleep(2000)
230
- target.removeAttribute('data-tooltip')
231
- }
232
- }
233
- },
234
- {
235
- type: 'gutter',
236
- gutterName: 'button',
237
- icon: 'save_alt',
238
- iconOnly: false,
239
- title: i18next.t('button.download'),
240
- handlers: {
241
- click: (
242
- columns: ColumnConfig[],
243
- data: GristData,
244
- column: ColumnConfig,
245
- record: GristRecord,
246
- rowIndex: number,
247
- target: HTMLElement
248
- ): void => {
249
- const element = document.createElement('a')
250
- element.setAttribute('href', record.fullpath)
251
- element.setAttribute('download', record.name!)
252
- document.body.appendChild(element)
253
- element.click()
254
- element.remove()
255
- }
256
- }
257
- },
258
- {
259
- type: 'gutter',
260
- gutterName: 'button',
261
- icon: 'delete',
262
- iconOnly: false,
263
- danger: true,
264
- title: i18next.t('button.delete'),
265
- handlers: {
266
- click: async (
267
- columns: ColumnConfig[],
268
- data: GristData,
269
- column: ColumnConfig,
270
- record: GristRecord,
271
- rowIndex: number,
272
- target: HTMLElement
273
- ): Promise<void> => {
274
- await this.deleteAttachment(record.id!)
275
- this.refreshAttachments()
276
- }
277
- }
278
- },
279
- {
280
- type: 'string',
281
- name: 'id',
282
- hidden: true
283
- },
284
- {
285
- type: 'string',
286
- name: 'name',
287
- header: i18next.t('field.name'),
288
- width: 200,
289
- filter: 'search',
290
- sortable: true
291
- },
292
- {
293
- type: 'string',
294
- name: 'description',
295
- header: i18next.t('field.description'),
296
- width: 200,
297
- filter: 'search'
298
- },
299
- {
300
- type: 'select-buttons',
301
- name: 'category',
302
- header: i18next.t('field.category'),
303
- hidden: true,
304
- filter: {
305
- operator: 'in',
306
- options: ['audio', 'video', 'image', 'text', 'application'],
307
- value: this.category,
308
- label: ''
309
- }
310
- },
311
- {
312
- type: 'hashtags',
313
- name: 'tags',
314
- header: i18next.t('field.hashtags'),
315
- label: true,
316
- record: {
317
- editable: false
318
- },
319
- filter: 'search',
320
- width: 200
321
- },
322
- {
323
- type: 'image',
324
- name: 'thumbnail',
325
- hidden: true,
326
- record: {
327
- editable: false,
328
- renderer: function (value, column, record, rowIndex, owner) {
329
- return record.category == 'image'
330
- ? html` <img src=${record.fullpath} style="max-width: 100%; max-height: 100%;" /> `
331
- : record.category == 'video'
332
- ? html` <video src=${record.fullpath} style="width: 100%; height: 100%;" controls></video> `
333
- : html`
334
- <div style="width: 100%; height: 100%;" etc>
335
- <md-icon style="--md-icon-size: 24px;">insert_drive_file</md-icon>
336
- <span>${record.path.substr(record.path.lastIndexOf('.'))}</span>
337
- </div>
338
- `
339
- } as FieldRenderer
340
- },
341
- handlers: {
342
- click: (
343
- columns: ColumnConfig[],
344
- data: GristData,
345
- column: ColumnConfig,
346
- record: GristRecord,
347
- rowIndex: number,
348
- target: HTMLElement
349
- ): void => {
350
- this.onClickSelect(record)
351
- }
352
- },
353
- width: 120
354
- },
355
- {
356
- type: 'datetime',
357
- name: 'updatedAt',
358
- header: i18next.t('field.updated-at'),
359
- sortable: true,
360
- width: 180
361
- },
362
- {
363
- type: 'datetime',
364
- name: 'createdAt',
365
- header: i18next.t('field.created-at'),
366
- sortable: true,
367
- width: 180
368
- }
369
- ],
370
- rows: {
371
- appendable: false,
372
- selectable: {
373
- multiple: true
374
- },
375
- classifier: function (
376
- record: GristRecord,
377
- rowIndex: number
378
- ): { emphasized?: boolean | string | string[]; [key: string]: any } | void {}
379
- },
380
- sorters: [
381
- {
382
- name: 'name',
383
- desc: false
384
- }
385
- ],
386
- pagination: {
387
- pages: [20, 30, 50, 100, 200]
388
- }
389
- }
390
- }
391
-
392
- async firstUpdated() {
393
- this.refreshAttachments()
394
- }
395
-
396
- onClickSelect(attachment: any) {
397
- this.dispatchEvent(
398
- new CustomEvent('attachment-selected', {
399
- composed: true,
400
- bubbles: true,
401
- detail: {
402
- attachment
403
- }
404
- })
405
- )
406
- }
407
-
408
- async onCreateAttachment(e: CustomEvent) {
409
- const files = e.detail
410
-
411
- await this.createAttachments(files)
412
- this.refreshAttachments()
413
- }
414
-
415
- async onDeleteAttachment(id: string) {
416
- await this.deleteAttachment(id)
417
-
418
- this.refreshAttachments()
419
- }
420
-
421
- async refreshAttachments() {
422
- this.grist.fetch()
423
- }
424
-
425
- async getAttachments({
426
- page = 1,
427
- limit = 30,
428
- filters = [],
429
- sortings = []
430
- }: { page?: number; limit?: number; filters?: FilterValue[]; sortings?: SortersConfig } = {}) {
431
- var pagination: PaginationConfig = {
432
- limit,
433
- page
434
- }
435
-
436
- var params = {
437
- filters,
438
- sortings,
439
- pagination
440
- }
441
-
442
- var attachmentListResponse = await client.query({
443
- query: FETCH_ATTACHMENT_LIST_GQL(params)
444
- })
445
-
446
- return attachmentListResponse?.data?.attachments || {}
447
- }
448
-
449
- async createAttachments(files: File[]) {
450
- /*
451
- ref. https://github.com/jaydenseric/graphql-multipart-request-spec#client
452
- */
453
-
454
- const response = await client.mutate({
455
- mutation: CREATE_ATTACHMENTS_GQL,
456
- variables: {
457
- attachments: files.map(file => {
458
- return { file }
459
- })
460
- },
461
- context: {
462
- hasUpload: true
463
- }
464
- })
465
- }
466
-
467
- async updateAttachment(patch: any) {
468
- const response = await client.mutate({
469
- mutation: UPDATE_ATTACHMENT_GQL,
470
- variables: patch
471
- })
472
- }
473
-
474
- async deleteAttachment(id: string) {
475
- const response = await client.mutate({
476
- mutation: DELETE_ATTACHMENT_GQL,
477
- variables: {
478
- id
479
- }
480
- })
481
-
482
- return response.data
483
- }
484
-
485
- popupHashtagEditor(record: GristRecord) {
486
- const hashtags = Array.isArray(record.tags) ? [...record.tags] : []
487
-
488
- const confirmCallback = async (newval: any) => {
489
- await this.updateAttachment({
490
- id: record.id,
491
- patch: { tags: newval }
492
- })
493
-
494
- this.refreshAttachments()
495
- }
496
-
497
- openPopup(
498
- html`
499
- <ox-popup-hashtags-input .value=${hashtags} .confirmCallback=${confirmCallback}></ox-popup-hashtags-input>
500
- `,
501
- {
502
- backdrop: true,
503
- title: i18next.t('title.edit hashtags')
504
- }
505
- )
506
- }
507
- }