@mixd-id/web-scaffold 0.1.230406281 → 0.1.230406283

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mixd-id/web-scaffold",
3
3
  "private": false,
4
- "version": "0.1.230406281",
4
+ "version": "0.1.230406283",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -208,6 +208,7 @@ export default{
208
208
 
209
209
  .button-secondary{
210
210
  @apply bg-primary-100 border-primary-100 text-primary-700 rounded-lg text-text;
211
+ border: solid 1px rgb(var(--primary-100));
211
212
  }
212
213
  .button-secondary:hover{
213
214
  @apply bg-primary-200 border-primary-200;
@@ -4,48 +4,82 @@
4
4
  <div v-if="readyState === 1"
5
5
  class="flex-1 flex flex-row">
6
6
 
7
- <div v-if="computedPresetMode === 'sidebar' && sidebarOpen"
7
+ <div v-if="computedPresetMode === 'sidebar' && sidebar.open"
8
8
  class="relative flex flex-col border-r-[1px] border-text-50 panel-400"
9
- :style="sidebarWidth">
9
+ :style="sidebarStyle">
10
10
 
11
11
  <PresetBar :config="config"
12
12
  class="flex-1"
13
13
  @apply="load">
14
+
14
15
  <template #toolbar>
15
- <button type="button" class="px-1" @click="closeSidebar">
16
- <svg width="19" height="19" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M312.1 375c9.369 9.369 9.369 24.57 0 33.94s-24.57 9.369-33.94 0L160 289.9l-119 119c-9.369 9.369-24.57 9.369-33.94 0s-9.369-24.57 0-33.94L126.1 256L7.027 136.1c-9.369-9.369-9.369-24.57 0-33.94s24.57-9.369 33.94 0L160 222.1l119-119c9.369-9.369 24.57-9.369 33.94 0s9.369 24.57 0 33.94L193.9 256L312.1 375z"/></svg>
16
+ <button type="button" class="p-1" @click="togglePreset">
17
+ <svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"/></svg>
17
18
  </button>
18
19
  </template>
20
+
19
21
  </PresetBar>
20
22
 
21
- <div v-if="sidebarOpen"
23
+ <div v-if="sidebar.open"
22
24
  :class="$style.resize1"
23
25
  @mousedown="(e) => $util.dragResize(e, resize1)"></div>
24
26
  </div>
25
27
 
26
- <div class="flex-1 flex flex-col gap-3" :class="containerClass">
28
+ <div class="flex-1 flex flex-col" :class="containerClass">
27
29
 
28
30
  <slot name="head"
29
31
  :preset="preset"
30
32
  :presetSelector="$refs.presetSelector"
31
33
  :compPrefix="compPrefix">
32
- <div class="flex flex-col md:flex-row gap-3 md:items-start overflow-hidden leading-tight p-3 md:p-0 md:pl-3"
34
+ <div class="flex flex-col md:flex-row gap-3 md:items-start overflow-hidden leading-tight p-3 md:p-0 md:pl-3 panel-400 md:panel-none border-b-[1px] border-text-50 md:border-none"
33
35
  :class="headerClass">
34
36
 
35
- <div class="flex md:flex-1 flex-row gap-3">
36
- <div class="flex flex-col whitespace-nowrap text-ellipsis overflow-hidden">
37
- <small class="text-text-400 text-xs">{{ title ?? 'Untitled' }}</small>
38
- <h4 class="cursor-pointer whitespace-nowrap text-ellipsis overflow-hidden group hover:text-primary"
39
- @click="openPreset">
40
- {{ preset.name ?? 'Preset Name' }}
41
- <svg v-if="computedPresetMode === 'sidebar'" width="14" height="14" class="ml-1 inline fill-text pointer-events-none group-hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M280.3 134.4c-9.719-9-24.91-8.438-33.94 1.25c-9 9.719-8.469 24.88 1.25 33.94L314.9 232H144C130.8 232 120 242.8 120 256S130.8 280 144 280h170.9l-67.21 62.41c-9.719 9.062-10.25 24.22-1.25 33.94c9.031 9.688 24.22 10.25 33.94 1.25l112-104C397.2 269 400 262.7 400 256s-2.781-13.03-7.656-17.59L280.3 134.4zM256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 464c-114.7 0-208-93.31-208-208S141.3 48 256 48s208 93.31 208 208S370.7 464 256 464z"/></svg>
42
- <svg v-else-if="![ 'external' ].includes(computedPresetMode)" width="14" height="14" class="inline fill-text pointer-events-none group-hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"/></svg>
43
- </h4>
37
+ <div class="flex md:flex-1 flex-row items-start gap-3">
38
+
39
+ <div class="flex flex-row gap-2 md:gap-3 items-center" ref="title">
40
+ <div class="flex flex-col whitespace-nowrap text-ellipsis overflow-hidden">
41
+ <div class="cursor-pointer group" @click="$refs.contextMenu.open($refs.title)">
42
+ <small class="text-text-400 text-xs">{{ title ?? 'Untitled' }}</small>
43
+ <div class="flex flex-row items-baseline gap-1">
44
+ <h5 class="whitespace-nowrap relative top-[-2px] text-ellipsis overflow-hidden group-hover:text-primary">
45
+ {{ preset.name ?? 'Preset Name' }}
46
+ </h5>
47
+ <svg width="13" height="13" class="fill-text group-hover:fill-primary relative top-[-1px]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M310.6 246.6l-127.1 128C176.4 380.9 168.2 384 160 384s-16.38-3.125-22.63-9.375l-127.1-128C.2244 237.5-2.516 223.7 2.438 211.8S19.07 192 32 192h255.1c12.94 0 24.62 7.781 29.58 19.75S319.8 237.5 310.6 246.6z"/></svg>
48
+ </div>
49
+ </div>
50
+ <ContextMenu ref="contextMenu" class="panel-400">
51
+ <div class="flex flex-col min-w-[300px] divide-y divide-text-50">
52
+
53
+ <div class="p-3 flex flex-col gap-1 bg-base-300">
54
+ <button v-for="(preset, idx) in config.presets"
55
+ class="p-2 px-5 text-left flex flex-row items-center rounded-md"
56
+ :class="config.presetIdx === idx ? 'bg-primary-200 text-white' : 'hover:bg-primary-100'"
57
+ type="button"
58
+ @click="selectPreset(idx)">
59
+ <label class="flex-1 pr-12">
60
+ {{ preset.name }}
61
+ </label>
62
+ <div class="p-1">
63
+ <div v-if="idx < 10"
64
+ class="border-[1px] border-text-300 px-2 text-mono text-xs text-text-400 rounded-lg">
65
+ Alt {{ idx + 1 }}
66
+ </div>
67
+ </div>
68
+ </button>
69
+ </div>
70
+
71
+ <div class="px-3">
72
+ <button type="button" class="text-primary p-5 text-left w-full" @click="openPreset">Edit</button>
73
+ </div>
74
+
75
+ </div>
76
+ </ContextMenu>
77
+ </div>
44
78
  </div>
45
79
 
46
80
  <div class="flex-1"></div>
47
81
 
48
- <slot v-if="Boolean(toolbar)" name="toolbar"></slot>
82
+ <slot name="toolbar"></slot>
49
83
  </div>
50
84
 
51
85
  <div class="flex flex-row gap-2 gap-1" v-if="Boolean(toolbar)">
@@ -107,13 +141,13 @@
107
141
  ref="grid"
108
142
  :items="dataItems"
109
143
  :column="computedGridColumn"
110
- class="flex-1"
144
+ :class="gridClass"
111
145
  @scroll-end="loadNext"
112
146
  :container-class="`${gridContainerClass}`"
113
147
  :config="config">
114
148
  <template #item="{ item }">
115
149
  <slot name="gridItem" :item="item">
116
- <div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden">
150
+ <div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
117
151
  <div>
118
152
  <Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
119
153
  </div>
@@ -130,6 +164,7 @@
130
164
  </div>
131
165
 
132
166
  </div>
167
+
133
168
  </div>
134
169
  </template>
135
170
 
@@ -161,17 +196,26 @@ export default{
161
196
  }
162
197
  },
163
198
  gridColumn: [ Number, String ],
164
- gridContainerClass: String,
199
+ gridClass: {
200
+ type: String,
201
+ default: "flex-1 md:p-3"
202
+ },
203
+ gridContainerClass: {
204
+ type: String,
205
+ default: "md:gap-5"
206
+ },
165
207
  headerClass: String,
166
- presetMode: String,
167
- containerClass: String,
208
+ presetMode: {
209
+ type: String,
210
+ default: "sidebar"
211
+ },
212
+ containerClass: {
213
+ type: String,
214
+ default: "md:p-5 md:gap-3"
215
+ },
168
216
  toolbar: {
169
217
  type: [ String, Boolean ],
170
218
  default: true
171
- },
172
- editHash: {
173
- type: String,
174
- default: '#719ed'
175
219
  }
176
220
  },
177
221
 
@@ -211,7 +255,8 @@ export default{
211
255
  const columns = []
212
256
  for(let key in data.items[0]){
213
257
  if(key.startsWith('_')){
214
- const [ k1, k2 ] = key.substring(1).split('-')
258
+ let [ k1, ...k2 ] = key.substring(1).split('-')
259
+ k2 = k2.join('-')
215
260
 
216
261
  let label = k2.replace(/_/gi, ' ')
217
262
  const column = tableColumns[k1]
@@ -240,6 +285,8 @@ export default{
240
285
  this.preset.columns.push(...columns)
241
286
  }
242
287
 
288
+ this.loadEnums()
289
+
243
290
  Object.assign(this.data, data ?? {})
244
291
 
245
292
  this.$emit('after-load')
@@ -300,12 +347,40 @@ export default{
300
347
  }
301
348
  },
302
349
 
303
- resize1(w){
304
- if(!this.config.sidebarWidth)
305
- this.config.sidebarWidth = 360
350
+ loadEnums(){
351
+
352
+ for(let i in this.preset.columns){
353
+ const column = this.preset.columns[i]
306
354
 
307
- if(this.config.sidebarWidth + w >= 270 && this.config.sidebarWidth + w <= 600){
308
- this.config.sidebarWidth += w
355
+ if(column.enumSrc) {
356
+ const [ src, key, attr ] = column.enumSrc.split(':')
357
+ this.socket.send(src, {
358
+ columns: [
359
+ { key:attr, visible:true }
360
+ ],
361
+ filters: [
362
+ {
363
+ key:key, value: [ { operator:'in', value:[ 1 ] } ]
364
+ }
365
+ ]
366
+ })
367
+ .then(res => {
368
+ this.preset.columns[i].typeParams = (res.items ?? []).map(_ => {
369
+ return {
370
+ text: _[attr] ?? _[key],
371
+ value: _[key]
372
+ }
373
+ })
374
+ })
375
+ .catch(err => this.log('Load enums error', err))
376
+ }
377
+ }
378
+
379
+ },
380
+
381
+ resize1(w){
382
+ if(this.config.sidebar.width + w >= 270 && this.config.sidebar.width + w <= 600){
383
+ this.config.sidebar.width += w
309
384
  }
310
385
  },
311
386
 
@@ -314,15 +389,7 @@ export default{
314
389
  this.config.presetIdx = idx
315
390
  this.load()
316
391
  this.$refs.presetSelector.close()
317
- }
318
- },
319
-
320
- setDefaultPresetView(){
321
- if(window.innerWidth < 768){
322
- this.preset.view = 'grid'
323
- }
324
- else if(!this.preset.view){
325
- this.preset.view = window.innerWidth < 768 ? 'grid' : 'table'
392
+ this.$refs.contextMenu.close()
326
393
  }
327
394
  },
328
395
 
@@ -342,10 +409,7 @@ export default{
342
409
  break
343
410
 
344
411
  case 'sidebar':
345
- this.$router.push({
346
- ...this.$route,
347
- hash: this.$route.hash.replace(this.editHash, '') + this.editHash
348
- })
412
+ this.sidebar.open = true
349
413
  break
350
414
 
351
415
  default:
@@ -355,11 +419,17 @@ export default{
355
419
  }
356
420
  },
357
421
 
358
- closeSidebar(){
359
- this.$router.replace({
360
- ...this.$route,
361
- hash: this.$route.hash.replace(this.editHash, '')
362
- })
422
+ togglePreset(){
423
+ switch(this.computedPresetMode){
424
+
425
+ case 'sidebar':
426
+ this.sidebar.open = !this.sidebar.open
427
+ break
428
+
429
+ case 'popup':
430
+ this.$refs.presetSelector.open()
431
+ break
432
+ }
363
433
  },
364
434
 
365
435
  onSignal(event, items){
@@ -391,6 +461,29 @@ export default{
391
461
  resize(){
392
462
  this.$refs.table ? this.$refs.table.resize() : null
393
463
  this.$refs.grid ? this.$refs.grid.resize() : null
464
+ },
465
+
466
+ onKeyDown(e){
467
+
468
+ if(e.altKey){
469
+
470
+ switch(e.code){
471
+
472
+ case 'Backquote':
473
+ this.$refs.contextMenu.open(this.$refs.title)
474
+ break
475
+
476
+ default:
477
+ if(e.code.startsWith('Digit')){
478
+ const idx = parseInt(e.code.substring(5)) - 1
479
+ if(this.config.presets[idx]) {
480
+ this.selectPreset(idx)
481
+ }
482
+ }
483
+ break
484
+ }
485
+
486
+ }
394
487
  }
395
488
 
396
489
  },
@@ -406,7 +499,6 @@ export default{
406
499
 
407
500
  this.loadPreset()
408
501
  .then(() => {
409
- this.setDefaultPresetView()
410
502
  this.readyState = 1
411
503
  this.load()
412
504
  })
@@ -415,13 +507,13 @@ export default{
415
507
  this.socket.send('user.subscribe', { name:this.subscribeKey })
416
508
  this.socket.on(this.subscribeKey, this.onSignal)
417
509
  }
418
- else if(import.meta.DEV){
419
- console.warn('subscribeKey is not set')
420
- }
510
+
511
+ window.addEventListener('keydown', this.onKeyDown)
421
512
  },
422
513
 
423
514
  unmounted() {
424
515
  this.$removeResizeListener(this.$el)
516
+ window.removeEventListener('keydown', this.onKeyDown)
425
517
 
426
518
  if(this.subscribeKey) {
427
519
  this.socket.send('user.unsubscribe', {name: this.subscribeKey})
@@ -439,6 +531,23 @@ export default{
439
531
  const columns = []
440
532
 
441
533
  for(let row of this.preset.pivot.rows){
534
+ if(!tableColumns[row.key][0].defaultFormat)
535
+ tableColumns[row.key][0].defaultFormat = tableColumns[row.key][0].format
536
+
537
+ switch(row.aggregrate){
538
+ case 'year':
539
+ tableColumns[row.key][0].format = 'YYYY'
540
+ break
541
+
542
+ case 'month':
543
+ tableColumns[row.key][0].format = 'MMM YYYY'
544
+ break
545
+
546
+ case 'date':
547
+ tableColumns[row.key][0].format = 'D MMM YYYY'
548
+ break
549
+ }
550
+
442
551
  columns.push(tableColumns[row.key][0])
443
552
  }
444
553
 
@@ -453,7 +562,15 @@ export default{
453
562
 
454
563
  return columns
455
564
  }
456
- return this.preset.columns ?? this.config.columns
565
+
566
+ for(let idx in this.preset.columns){
567
+ if(this.preset.columns[idx].defaultFormat){
568
+ this.preset.columns[idx].format = this.preset.columns[idx].defaultFormat
569
+ delete this.preset.columns[idx].defaultFormat
570
+ }
571
+ }
572
+
573
+ return this.preset.columns
457
574
  },
458
575
 
459
576
  contentSlots(){
@@ -503,13 +620,19 @@ export default{
503
620
  (this.view ?? this.preset.view)
504
621
  },
505
622
 
506
- sidebarOpen(){
507
- return this.$route.hash.indexOf(this.editHash) >= 0
623
+ sidebar(){
624
+ if(!this.config.sidebar || !('open' in this.config.sidebar))
625
+ this.config.sidebar = {
626
+ open: false,
627
+ width: 270
628
+ }
629
+
630
+ return this.config.sidebar
508
631
  },
509
632
 
510
- sidebarWidth(){
633
+ sidebarStyle(){
511
634
  return {
512
- width: (!this.sidebarOpen ? 0 : (this.config.sidebarWidth ?? '360')) + 'px'
635
+ width: !this.sidebar.open ? 0 : this.sidebar.width + 'px'
513
636
  }
514
637
  },
515
638
 
@@ -535,12 +658,6 @@ export default{
535
658
  }
536
659
  },
537
660
 
538
- preset(to){
539
- if(!to.view){
540
- this.setDefaultPresetView()
541
- }
542
- },
543
-
544
661
  },
545
662
 
546
663
  }
@@ -553,8 +670,4 @@ export default{
553
670
  @apply w-[3px] cursor-ew-resize absolute top-0 right-0 bottom-0;
554
671
  }
555
672
 
556
- .headerSelected{
557
- @apply border-primary;
558
- }
559
-
560
673
  </style>
@@ -95,7 +95,7 @@ export default{
95
95
  width: [ String, Number ],
96
96
 
97
97
  useForm: {
98
- type: Boolean,
98
+ type: [ Boolean, String ],
99
99
  default: false
100
100
  },
101
101
 
@@ -57,6 +57,19 @@
57
57
  <Radio :name="value.key" :value="false" v-model="value.value" @change="apply">False</Radio>
58
58
  </div>
59
59
 
60
+ <div v-else-if="type === 'enum'" class="flex flex-row gap-2">
61
+
62
+ <div class="flex flex-col gap-2">
63
+ <Checkbox v-for="param in typeParams"
64
+ v-model="cValue.value"
65
+ :value="param.value"
66
+ @change="apply">
67
+ {{ param.text }}
68
+ </Checkbox>
69
+ </div>
70
+
71
+ </div>
72
+
60
73
  <div v-else-if="type === 'component'">
61
74
  <component :is="column.component"
62
75
  :value="value"
@@ -99,7 +112,11 @@ export default{
99
112
  type: Object,
100
113
  default: {}
101
114
  },
115
+
102
116
  type: String,
117
+
118
+ typeParams: Array,
119
+
103
120
  value: Object,
104
121
 
105
122
  },
@@ -109,6 +126,17 @@ export default{
109
126
  valueRequired(){
110
127
  return (!(['yesterday', 'today', 'thisWeek', 'thisMonth', 'thisYear'].includes(this.value.operator) ||
111
128
  this.type === 'boolean'));
129
+ },
130
+
131
+ cValue(){
132
+ if(this.type === 'enum' && (!this.value.operator || !Array.isArray(this.value.value))){
133
+ Object.assign(this.value, {
134
+ operator: 'in',
135
+ value: []
136
+ })
137
+ }
138
+
139
+ return this.value
112
140
  }
113
141
 
114
142
  },
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div :class="computedClass">
3
3
  <button v-for="(item, index) in filteredItems" :class="tabClass(item)"
4
- class=" whitespace-nowrap text-ellipsis overflow-hidden"
4
+ class="whitespace-nowrap"
5
5
  @click="onClick(item)" type="button" >
6
6
  <slot name="default" :="{ item, index }">
7
7
  <label class="px-1">
@@ -37,7 +37,8 @@
37
37
  <td v-for="column in visibleColumns"
38
38
  :class="tdClass(item, column)">
39
39
  <slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
40
- <div v-else :class="columnClass(column)" v-html="formatColumn(item, column)"></div>
40
+ <slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
41
+ <div v-else :class="columnClass(column)" v-html="formatColumn(item, column)"></div>
41
42
  </td>
42
43
  <td :class="$style.spacer"></td>
43
44
  </tr>
@@ -63,6 +64,7 @@
63
64
  <tr>
64
65
  <td v-for="column in columns" :style="thStyle(column)">
65
66
  <slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="items[0]"></slot>
67
+ <slot v-else-if="$slots.default" name="default" :column="column" :item="items[0]"></slot>
66
68
  <div v-else :class="columnClass(column)" v-html="formatColumn(items[0] ?? {}, column)"></div>
67
69
  </td>
68
70
  <td :class="$style.spacer"></td>
@@ -89,6 +91,8 @@ export default{
89
91
  props:{
90
92
  columns: Array,
91
93
 
94
+ itemClass: String,
95
+
92
96
  items: Array,
93
97
 
94
98
  pinned: Function,
@@ -218,9 +222,9 @@ export default{
218
222
  },
219
223
 
220
224
  tdClass(item, column){
221
- if(Array.isArray(item._highlight) && item._highlight.includes(column.key)){
222
- return this.$style.highlight
223
- }
225
+ if(Array.isArray(item._highlight) && item._highlight.includes(column.key))
226
+ return this.$style.highlight + (this.itemClass ? ' ' + this.itemClass : '')
227
+ return this.itemClass ?? ''
224
228
  },
225
229
 
226
230
  select(item, index){
@@ -393,7 +397,7 @@ export default{
393
397
  break
394
398
 
395
399
  case 'enum':
396
- const enumObj = column.typeParams.filter((_) => _.value === value).pop()
400
+ const enumObj = (column.typeParams ?? []).filter((_) => _.value === value).pop()
397
401
  text = enumObj ? enumObj.text : value
398
402
  break
399
403
  }
@@ -60,6 +60,7 @@ const plugin = Plugin(function({ addBase, addUtilities, config, theme }) {
60
60
  '--facebook-100': '205, 215, 231',
61
61
  '--facebook-500': '8, 102, 255',
62
62
 
63
+ '--panel-none': 'transparent',
63
64
  '--panel-300': 'rgb(235, 237, 240)',
64
65
  '--panel-400': 'rgb(245, 247, 250)',
65
66
  '--panel-500': 'rgb(255, 255, 255)',
@@ -234,6 +235,27 @@ const plugin = Plugin(function({ addBase, addUtilities, config, theme }) {
234
235
  transform: 'scale(.9)'
235
236
  },
236
237
 
238
+ '.openltr-move, .openltr-enter-active, .openltr-leave-active': {
239
+ transition: 'all 200ms cubic-bezier(0.25, 1, 0.5, 1)'
240
+ },
241
+
242
+ '.openltr-enter-from, .openltr-leave-to': {
243
+ opacity: 0,
244
+ transform: 'translateX(30px)'
245
+ },
246
+
247
+ '.openltr-leave-active': {
248
+ position: 'absolute',
249
+ right: 0,
250
+ width: '100%'
251
+ },
252
+
253
+ '.panel-none': {
254
+ '--webkit-backdrop-filter': 'var(--panel-none-backdrop-filter)',
255
+ 'backdrop-filter': 'var(--panel-none-backdrop-filter)',
256
+ 'background-color': 'var(--panel-none)',
257
+ },
258
+
237
259
  '.panel-300': {
238
260
  '--webkit-backdrop-filter': 'var(--panel-300-backdrop-filter)',
239
261
  'backdrop-filter': 'var(--panel-300-backdrop-filter)',
@@ -452,7 +452,7 @@ const afterItemToSequelizeWhere = async (order, afterItem) => {
452
452
  }
453
453
 
454
454
  const searchToSequelizeWhere = async (search, params) => {
455
- if(!search){
455
+ if(!search && (!Array.isArray(params) || params.length < 1)){
456
456
  return {
457
457
  where: {},
458
458
  replacements: []
@@ -657,7 +657,6 @@ const chartToSequelizeWhere = async (chart, opt) => {
657
657
  const pivotToSequelizeWhere = async (pivot, opt) => {
658
658
 
659
659
  const { model, sorts = [] } = opt
660
- //console.log(sorts)
661
660
 
662
661
  const attributes = []
663
662
  const group = []
@@ -665,7 +664,6 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
665
664
  const sortExists = sorts.length > 0
666
665
 
667
666
  const groupedSorts = groupBy(sorts, 'key')
668
- //console.log(util.inspect(pivot, false, null, true /* enable colors */))
669
667
 
670
668
  for(let row of pivot.rows){
671
669
 
@@ -674,15 +672,15 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
674
672
  switch(row.aggregrate){
675
673
 
676
674
  case 'date':
677
- attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%M-%d'), row.key ])
678
- group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%M-%d'), row.key ])
679
- if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%M-%d'), 'asc' ])
675
+ attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), row.key ])
676
+ group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), row.key ])
677
+ if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), 'asc' ])
680
678
  break
681
679
 
682
680
  case 'month':
683
- attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%M'), row.key ])
684
- group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%M'), row.key ])
685
- if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%M'), 'asc' ])
681
+ attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), row.key ])
682
+ group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), row.key ])
683
+ if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), 'asc' ])
686
684
  break
687
685
 
688
686
  case 'year':
@@ -701,6 +699,7 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
701
699
 
702
700
  const sortKeyToOrders = {} // Helper to determine group ordering
703
701
  for(let value of pivot.values){
702
+
704
703
  switch(value.aggregrate){
705
704
 
706
705
  case 'sum':
@@ -742,21 +741,19 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
742
741
  }
743
742
 
744
743
  const field = model.rawAttributes[value.key].field
745
- //console.log(field)
746
744
 
747
745
  groups.forEach(group => {
748
746
  if(group[value.key] === null){
749
- attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} is null THEN 1 ELSE 0 END)`), `_${value.key}-null` ])
747
+ attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} is null THEN 1 ELSE 0 END)`), `_${value.key}-(null)` ])
750
748
  }
751
749
  else if(group[value.key] === ''){
752
- attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = '' THEN 1 ELSE 0 END)`), `_${value.key}-empty` ])
750
+ attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = '' THEN 1 ELSE 0 END)`), `_${value.key}-(empty)` ])
753
751
  }
754
752
  else{
755
753
  const groupValue = (group[value.key] ?? '').toString().replace(/\s/g, '_')
756
754
  attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = '${group[value.key]}' THEN 1 ELSE 0 END)`), `_${value.key}-${groupValue}` ])
757
755
  }
758
756
  })
759
- //console.log(attributes)
760
757
 
761
758
  break
762
759
  }
@@ -773,9 +770,6 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
773
770
  }
774
771
  }
775
772
 
776
- //console.log(util.inspect(attributes, false, null, true /* enable colors */))
777
- //console.log(util.inspect(group, false, null, true /* enable colors */))
778
-
779
773
  return {
780
774
  attributes,
781
775
  group,
@@ -887,6 +881,121 @@ const filtersToSequelizeInclude = async(filters, opt, includes) => {
887
881
  return includes
888
882
  }
889
883
 
884
+ const syntaxSearchToSequelizeOpt = async(search, opt) => {
885
+
886
+ let where = {}
887
+ let replacements = []
888
+
889
+ const { config } = opt
890
+
891
+ let syntaxKey = search[1]
892
+ let syntaxVal = search[2].trim()
893
+
894
+ return {
895
+ where,
896
+ replacements
897
+ }
898
+ }
899
+
900
+ const presetToSequelizeList = async(preset, {
901
+ conn, model, order:initialOrder, config, where:initialWhere, replacements: initialReplacements
902
+ }) => {
903
+
904
+ if(!initialOrder){
905
+ initialOrder = [
906
+ [ 'updatedAt', 'desc' ],
907
+ [ 'id', 'desc' ],
908
+ ]
909
+ }
910
+
911
+ const { itemsPerPage = 20, filters = [], sorts, pivot, afterItem, search, id } = preset;
912
+
913
+ let where = {}
914
+ const replacements = []
915
+ const { order } = await sortsToSequelizeWhere(sorts, {
916
+ order: initialOrder
917
+ })
918
+
919
+ if(Array.isArray(id)){
920
+ where = {
921
+ ...where,
922
+ id: {
923
+ [Op.in]: id
924
+ }
925
+ }
926
+ }
927
+
928
+ const { where:filterWhere, replacements:filterReplacements } = await filtersToSequelizeWhere(filters, config)
929
+
930
+ let searchWhere = {}, searchReplacements = []
931
+ if(search){
932
+ const syntaxSearch = (search ?? '').match(/^(\w+)\:(.*)/)
933
+ if(syntaxSearch !== null){
934
+ const syntaxSearchOpt = syntaxSearchToSequelizeOpt(syntaxSearch, { config })
935
+ searchWhere = syntaxSearchOpt.where
936
+ searchReplacements = syntaxSearchOpt.replacements
937
+ }
938
+ else{
939
+ const match = (config.columns ?? []).filter(_ => (_.search && _.key)).map(_ => _.key)
940
+ if(match !== null){
941
+ const searchOpt = await searchToSequelizeWhere(search, {
942
+ match
943
+ })
944
+ searchWhere = searchOpt.where
945
+ searchReplacements = searchOpt.replacements
946
+ }
947
+ }
948
+ }
949
+
950
+ if(pivot && pivot.enabled){
951
+ const { attributes, group, order } = await pivotToSequelizeWhere(pivot, {
952
+ model,
953
+ conn
954
+ })
955
+
956
+ return {
957
+ attributes,
958
+ where: {
959
+ [Op.and]: [
960
+ initialWhere ?? {},
961
+ where,
962
+ filterWhere,
963
+ searchWhere
964
+ ]
965
+ },
966
+ replacements: [
967
+ ...filterReplacements,
968
+ ...searchReplacements
969
+ ],
970
+ group,
971
+ order
972
+ }
973
+ }
974
+ else{
975
+ const { where:afterItemWhere } = await afterItemToSequelizeWhere(order, afterItem)
976
+
977
+ return {
978
+ where: {
979
+ [Op.and]: [
980
+ where,
981
+ afterItemWhere,
982
+ filterWhere,
983
+ searchWhere,
984
+ ]
985
+ },
986
+ limit: itemsPerPage,
987
+ order,
988
+ replacements: [
989
+ ...(initialReplacements ?? []),
990
+ ...replacements,
991
+ ...filterReplacements,
992
+ ...searchReplacements
993
+ ]
994
+ }
995
+ }
996
+ }
997
+
998
+
890
999
  module.exports = {
891
1000
  filtersToSequelizeWhere,
892
1001
  afterItemToSequelizeWhere,
@@ -895,4 +1004,6 @@ module.exports = {
895
1004
  pivotToSequelizeWhere,
896
1005
  sortsToSequelizeWhere,
897
1006
  filtersToSequelizeInclude,
1007
+
1008
+ presetToSequelizeList
898
1009
  }
@@ -42,6 +42,13 @@ const setupConfig = (config) => {
42
42
  if(config.presets[idx].columns[0] && typeof config.presets[idx].columns[0] === 'string'){
43
43
  config.presets[idx].columns = pickColumns(config.columns ?? [], config.presets[idx].columns)
44
44
  }
45
+
46
+ if(!config.presets[idx].view)
47
+ config.presets[idx].view = 'table'
48
+ }
49
+
50
+ if(!config.presetIdx && config.presets.length > 0){
51
+ config.presetIdx = 0
45
52
  }
46
53
  }
47
54
 
package/src/utils/wss.mjs CHANGED
@@ -58,7 +58,10 @@ class WSS extends EventEmitter2{
58
58
  url: '',
59
59
  }, opt ?? {})
60
60
 
61
- this.connect().then()
61
+ this.connect()
62
+ .catch(e => {
63
+ this.emit('error', e, [])
64
+ })
62
65
 
63
66
  if(typeof window !== 'undefined'){
64
67
  window.addEventListener('visibilitychange', () => {
@@ -119,7 +122,7 @@ class WSS extends EventEmitter2{
119
122
  this._pendingSend = []
120
123
  }
121
124
  else if(_requestId){
122
- if(this._callbacks[_requestId]){
125
+ if(this._callbacks[_requestId] && this._instance.readyState === 1){
123
126
  const { cb, err, path, params, t1 } = this._callbacks[_requestId]
124
127
  status === 200 ? cb(data) : err(data)
125
128
  delete this._callbacks[_requestId]
@@ -167,6 +170,10 @@ class WSS extends EventEmitter2{
167
170
  };
168
171
  }
169
172
 
173
+ async close(){
174
+ this._instance.close()
175
+ }
176
+
170
177
  async reconnect(opt){
171
178
  if(this._instance){
172
179
  this._instance.close()
@@ -178,7 +185,7 @@ class WSS extends EventEmitter2{
178
185
  }
179
186
 
180
187
  sendSync(path, params, cb, err, override){
181
- if(this._instance.readyState > 1){
188
+ if(!this._instance || this._instance.readyState > 1){
182
189
  return
183
190
  }
184
191
  else if(!this._instance.isAuth){
@@ -212,7 +219,7 @@ class WSS extends EventEmitter2{
212
219
  }
213
220
 
214
221
  setTimeout(() => {
215
- if(this._callbacks[_requestId]){
222
+ if(this._callbacks[_requestId] && this._instance.readyState === 1){
216
223
  err({ message: 'Timeout' })
217
224
  delete this._callbacks[_requestId]
218
225
  }
@@ -3,9 +3,9 @@
3
3
 
4
4
  <TransitionGroup name="openltr" tag="div" class="flex-1 flex">
5
5
 
6
- <div v-if="$route.hash.indexOf(this.editHash) < 0" key="panel1" class="flex-1 flex flex-col">
6
+ <div v-if="presetBar.view === 1" key="panel1" class="flex-1 flex flex-col">
7
7
 
8
- <div class="flex flex-row items-center gap-2 p-5">
8
+ <div class="flex flex-row items-center gap-2 p-3">
9
9
  <slot name="start"></slot>
10
10
  <div class="flex-1">
11
11
  <h5 class="px-3">{{ config.name ?? 'Config' }}</h5>
@@ -22,7 +22,7 @@
22
22
  class="px-3 p-2 text-left hover:bg-primary-100 flex flex-row gap-2 items-center">
23
23
  <Radio v-model="config.presetIdx" :value="idx" @change="apply()" />
24
24
  <button type="button" class="flex-1 py-1 text-left overflow-hidden text-ellipsis whitespace-nowrap"
25
- @click="config.presetIdx = idx; apply()">
25
+ @click="select(idx)">
26
26
  {{ _preset.name }}
27
27
  </button>
28
28
  <button v-if="config.presetIdx === idx"
@@ -73,14 +73,18 @@
73
73
  <div v-else key="panel2" class="flex-1 flex flex-col">
74
74
 
75
75
  <div class="flex flex-row gap-2 p-3">
76
- <button type="button" @click="deselect">
77
- <svg width="14" height="14" class="fill-text hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M447.1 256c0 13.25-10.76 24.01-24.01 24.01H83.9l132.7 126.6c9.625 9.156 9.969 24.41 .8125 33.94c-9.156 9.594-24.34 9.938-33.94 .8125l-176-168C2.695 268.9 .0078 262.6 .0078 256S2.695 243.2 7.445 238.6l176-168C193 61.51 208.2 61.85 217.4 71.45c9.156 9.5 8.812 24.75-.8125 33.94l-132.7 126.6h340.1C437.2 232 447.1 242.8 447.1 256z"/></svg>
76
+ <button type="button" @click="deselect" class="p-1">
77
+ <svg width="16" height="16" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M80 208h-64C7.125 208 0 215.1 0 224v64c0 8.875 7.125 16 16 16h64C88.88 304 96 296.9 96 288V224C96 215.1 88.88 208 80 208zM80 368h-64C7.125 368 0 375.1 0 384v64c0 8.875 7.125 16 16 16h64C88.88 464 96 456.9 96 448v-64C96 375.1 88.88 368 80 368zM80 48h-64C7.125 48 0 55.13 0 64v64c0 8.875 7.125 16 16 16h64C88.88 144 96 136.9 96 128V64C96 55.13 88.88 48 80 48zM488 232H183.1C170.7 232 160 242.7 160 256s10.75 24 23.1 24H488C501.3 280 512 269.3 512 256S501.3 232 488 232zM488 72H183.1C170.7 72 160 82.75 160 95.1S170.7 120 183.1 120H488c13.25 0 24-10.75 24-23.1S501.3 72 488 72zM488 392H183.1C170.7 392 160 402.7 160 416s10.75 24 23.1 24H488c13.25 0 24-10.75 24-24S501.3 392 488 392z"/></svg>
78
78
  </button>
79
- <Textbox v-model="preset.name" class="flex-1 border-none bg-transparent font-bold" variant="minimal" />
79
+ <Textbox v-model="preset.name"
80
+ class="flex-1 rounded-none border-none bg-transparent font-bold"
81
+ item-class="p-1"
82
+ variant="minimal"
83
+ maxlength="100" />
80
84
  <slot name="toolbar" :edit="true"></slot>
81
85
  </div>
82
86
 
83
- <div class="flex items-center justify-center border-b-[1px] border-text-100">
87
+ <div class="flex items-center justify-center border-b-[1px] border-text-100 px-3">
84
88
  <Tabs :items="tabItems[config.type ?? 'list']" v-model="config.presetBarTabIndex" />
85
89
  </div>
86
90
 
@@ -88,7 +92,7 @@
88
92
 
89
93
  <div v-if="config.presetBarTabIndex === 1" class="flex-1 flex flex-col p-5">
90
94
  <ListItem :items="presetColumns"
91
- body-class="divide-y divide-text-50"
95
+ body-class="divide-y divide-text-50 rounded-lg border-[1px] border-text-50 bg-base-300"
92
96
  @reorder="(from, to) => { presetColumns.splice(to, 0, presetColumns.splice(from, 1)[0]); }">
93
97
  <template v-slot="{ item }">
94
98
  <div v-if="!item.key.startsWith('_') || (item.key.startsWith('_') && (preset.pivot && preset.pivot.enabled))" class="flex flex-row items-center gap-3 px-3 p-2 hover:bg-primary-100">
@@ -136,6 +140,7 @@
136
140
  <PresetSelectorFilterItem
137
141
  class="flex-1"
138
142
  :type="typeOf(column(filter.key).type)"
143
+ :typeParams="typeParamsOf(column(filter.key))"
139
144
  :value="filterVal"
140
145
  @change="apply()" />
141
146
 
@@ -169,11 +174,12 @@
169
174
 
170
175
  <div v-else-if="config.presetBarTabIndex === 3" class="flex-1 flex flex-col p-5">
171
176
 
172
- <ListItem :items="preset.sorts"
177
+ <ListItem v-if="Array.isArray(preset.sorts) && preset.sorts.length > 0"
178
+ :items="preset.sorts"
173
179
  @reorder="(from, to) => { preset.sorts.splice(to, 0, preset.sorts.splice(from, 1)[0]); apply() }"
174
- container-class="flex flex-col gap-2">
180
+ container-class="flex flex-col rounded-lg border-[1px] border-text-50 bg-base-300 divide-y divide-text-50">
175
181
  <template v-slot="{ item, index }">
176
- <div class="flex flex-row items-center gap-3">
182
+ <div class="flex flex-row items-center gap-3 p-1">
177
183
  <div data-reorder>
178
184
  <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
179
185
  </div>
@@ -208,13 +214,12 @@
208
214
  </template>
209
215
  </ListItem>
210
216
 
211
- <div class="p-5 text-center">
212
- <button type="button"
213
- class="text-primary"
214
- @click="addSort({ type:'asc' })">
215
- Add Sort
216
- </button>
217
- </div>
217
+ <button type="button"
218
+ class="text-primary flex flex-row items-center justify-center p-3"
219
+ @click="addSort({ type:'asc' })">
220
+ <svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
221
+ Add Sort
222
+ </button>
218
223
 
219
224
  </div>
220
225
 
@@ -455,19 +460,20 @@
455
460
  </div>
456
461
  <ListItem :items="presetPivot.rows"
457
462
  @reorder="(from, to) => { presetPivot.rows.splice(to, 0, presetPivot.rows.splice(from, 1)[0]); apply() }"
458
- class="mt-2 h-[90px] border-[1px] border-text-200 bg-base-400 rounded-lg p-0.5">
463
+ class="mt-2 h-[25vh] overflow-y-auto border-[1px] border-text-200 bg-base-400 rounded-lg p-0.5"
464
+ container-class="divide-y divide-text-50">
459
465
  <template v-slot="{ item, index }">
460
466
  <div class="flex flex-row items-center gap-3 p-2 bg-base-500 hover:bg-primary-100 first:rounded-t-md last:rounded-b-md">
461
467
  <div data-reorder v-if="(presetPivot.rows ?? []).length > 1">
462
468
  <svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
463
469
  </div>
464
470
  <div class="flex-1 flex flex-row gap-3">
465
- <strong class="flex-1">
471
+ <strong class="flex-1 cursor-pointer" @click="$refs.columnSelector.open(obj => Object.assign(item, obj))">
466
472
  {{ item.label ? item.label : item.key }}
467
473
  </strong>
468
474
  <select v-if="item.type === 'date'"
469
475
  v-model="item.aggregrate"
470
- class="appearance-none bg-transparent outline-none"
476
+ class="px-1 appearance-none bg-base-300 rounded-md border-[1px] border-text-50 outline-none"
471
477
  @change="apply()">
472
478
  <option value="date">Date</option>
473
479
  <option value="month">Month</option>
@@ -491,7 +497,7 @@
491
497
  </div>
492
498
  <ListItem :items="presetPivot.values"
493
499
  @reorder="(from, to) => { presetPivot.values.splice(to, 0, presetPivot.values.splice(from, 1)[0]); }"
494
- class="mt-2 h-[90px] border-[1px] border-text-200 bg-base-400 rounded-lg p-0.5">
500
+ class="mt-2 h-[25vh] overflow-y-auto border-[1px] border-text-200 bg-base-400 rounded-lg p-0.5">
495
501
  <template v-slot="{ item, index }">
496
502
  <div class="flex flex-row items-center gap-3 p-2 bg-base-500 hover:bg-primary-100 first:rounded-t-md last:rounded-b-md">
497
503
  <div data-reorder v-if="(presetPivot.values ?? []).length > 1">
@@ -502,7 +508,9 @@
502
508
  {{ item.label ? item.label : item.key }}
503
509
  </strong>
504
510
  <select v-model="item.aggregrate"
505
- class="appearance-none bg-transparent outline-none" @change="apply()">
511
+ class="px-1 appearance-none bg-base-300 rounded-md border-[1px] border-text-50 outline-none"
512
+ @change="apply()">
513
+ <option value="">Default</option>
506
514
  <option value="count">Count</option>
507
515
  <option v-if="[ 'number' ].includes(item.type)" value="sum">Sum</option>
508
516
  <option v-if="[ 'number' ].includes(item.type)" value="avg">Average</option>
@@ -525,12 +533,18 @@
525
533
  <div v-else-if="config.presetBarTabIndex === 9" class="flex-1 p-6 flex flex-col gap-6">
526
534
 
527
535
  <div>
528
- <strong>Preset</strong>
536
+ <div class="flex flex-row">
537
+ <strong class="flex-1">Preset</strong>
538
+ <button type="button" class="text-primary" @click="log(preset)">console.log</button>
539
+ </div>
529
540
  <ObjectTree :value="preset" />
530
541
  </div>
531
542
 
532
543
  <div>
533
- <strong>Config</strong>
544
+ <div class="flex flex-row">
545
+ <strong class="flex-1">Config</strong>
546
+ <button type="button" class="text-primary" @click="log(config)">console.log</button>
547
+ </div>
534
548
  <ObjectTree :value="config" />
535
549
  </div>
536
550
 
@@ -542,6 +556,7 @@
542
556
  :columns="config.columns" />
543
557
 
544
558
  </div>
559
+
545
560
  </TransitionGroup>
546
561
 
547
562
  </div>
@@ -561,12 +576,7 @@ export default{
561
576
 
562
577
  props: {
563
578
 
564
- config: Object,
565
-
566
- editHash: {
567
- type: String,
568
- default: '#e1a5f'
569
- }
579
+ config: Object
570
580
 
571
581
  },
572
582
 
@@ -652,18 +662,13 @@ export default{
652
662
  },
653
663
 
654
664
  select(idx){
655
- this.config.presetBarIndex = idx
656
- this.$router.push({
657
- ...this.$route,
658
- hash: (this.$route.hash.replace(this.editHash, '')) + this.editHash
659
- })
665
+ this.config.presetIdx = idx
666
+ this.presetBar.view = 2
667
+ this.apply()
660
668
  },
661
669
 
662
670
  deselect(){
663
- this.$router.replace({
664
- ...this.$route,
665
- hash: this.$route.hash.replace(this.editHash, '')
666
- })
671
+ this.config.presetBar.view = 1
667
672
  },
668
673
 
669
674
  filterColumnAdded(column){
@@ -677,10 +682,18 @@ export default{
677
682
  return 'date'
678
683
  else if([ 'number' ].includes(type))
679
684
  return 'number'
685
+ else if([ 'enum' ].includes(type))
686
+ return 'enum'
680
687
  else
681
688
  return 'text'
682
689
  },
683
690
 
691
+ typeParamsOf(column){
692
+ if([ 'enum' ].includes(column.type))
693
+ return column.typeParams
694
+ return []
695
+ },
696
+
684
697
  },
685
698
 
686
699
  computed: {
@@ -698,7 +711,7 @@ export default{
698
711
  },
699
712
 
700
713
  preset(){
701
- return this.config.presets[this.config.presetBarIndex]
714
+ return this.config.presets[this.config.presetIdx]
702
715
  },
703
716
 
704
717
  presetChart(){
@@ -742,6 +755,15 @@ export default{
742
755
  return this.chartOpt.yAxis
743
756
  },
744
757
 
758
+ presetBar(){
759
+ if(!this.config.presetBar || !this.config.presetBar.view)
760
+ this.config.presetBar = {
761
+ view: 2
762
+ }
763
+
764
+ return this.config.presetBar
765
+ }
766
+
745
767
  },
746
768
 
747
769
  data(){