@mixd-id/web-scaffold 0.1.230406290 → 0.1.230406292

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.230406290",
4
+ "version": "0.1.230406292",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -146,7 +146,7 @@ export default{
146
146
  @apply flex items-center justify-center
147
147
  }
148
148
  .button:disabled{
149
- @apply opacity-60;
149
+ @apply text-opacity-50;
150
150
  }
151
151
  .button:active{
152
152
  @apply top-[1px] left-[1px] relative;
@@ -164,7 +164,7 @@ export default{
164
164
  @apply bg-primary-600;
165
165
  }
166
166
  .button-primary:disabled{
167
- @apply bg-primary-500 opacity-50 top-0 left-0 cursor-not-allowed;
167
+ @apply bg-primary-500 top-0 left-0 cursor-not-allowed;
168
168
  @apply top-0 left-0;
169
169
  }
170
170
  .button-primary *{
@@ -188,7 +188,7 @@ export default{
188
188
  .button-outline:hover{
189
189
  }
190
190
  .button-outline:disabled{
191
- @apply opacity-50 top-0 left-0 cursor-not-allowed;
191
+ @apply top-0 left-0 cursor-not-allowed;
192
192
  @apply text-text border-primary-500;
193
193
  }
194
194
  .button-outline *{
@@ -214,7 +214,7 @@ export default{
214
214
  @apply bg-primary-200 border-primary-200;
215
215
  }
216
216
  .button-secondary:disabled{
217
- @apply bg-primary-100 opacity-50 top-0 left-0 cursor-not-allowed;
217
+ @apply bg-primary-100 top-0 left-0 cursor-not-allowed;
218
218
  }
219
219
  .button-secondary *{
220
220
  @apply text-text-500 fill-white;
@@ -238,7 +238,7 @@ export default{
238
238
  @apply bg-red-600 border-red-600;
239
239
  }
240
240
  .button-red:disabled{
241
- @apply bg-red-500 border-red-500 opacity-50 top-0 left-0 cursor-not-allowed;
241
+ @apply bg-red-500 border-red-500 top-0 left-0 cursor-not-allowed;
242
242
  }
243
243
  .button-red *{
244
244
  @apply text-white fill-white;
@@ -288,7 +288,7 @@ export default{
288
288
  }
289
289
 
290
290
  .img{
291
- @apply object-contain w-full h-full;
291
+ @apply w-full h-full;
292
292
  }
293
293
 
294
294
  .editArea{
@@ -120,7 +120,11 @@
120
120
  </div>
121
121
  </slot>
122
122
 
123
- <VirtualTable v-if="presetView === 'table' || pivotEnabled"
123
+ <div v-if="readyState === 3" class="flex-1 flex items-center justify-center">
124
+ <svg class="animate-spin" width="36" height="36" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
125
+ </div>
126
+
127
+ <VirtualTable v-else-if="presetView === 'table' || pivotEnabled"
124
128
  ref="table"
125
129
  :columns="columns"
126
130
  class="flex-1 rounded-lg panel-400"
@@ -310,6 +314,7 @@ export default{
310
314
  this.preset.columns = JSON.parse(JSON.stringify(this.config.columns))
311
315
  }
312
316
 
317
+ this.readyState = 3
313
318
  return this.socket.send(this.src, {
314
319
  ...this.preset,
315
320
  itemsPerPage: this.data.itemsPerPage,
@@ -325,6 +330,7 @@ export default{
325
330
  .catch(err => {
326
331
  this.toast(err)
327
332
  })
333
+ .finally(_ => this.readyState = 1)
328
334
  }
329
335
  return new Promise(resolve => resolve())
330
336
  },
@@ -399,7 +405,7 @@ export default{
399
405
 
400
406
  const reqItems = Object.values(this.enumCache[column.key]).filter(_ => !_.text).map(_ => _.value)
401
407
 
402
- if(reqItems.length > 0){
408
+ if(reqItems.length > 0 && reqItems !== this.lastEnumItems){
403
409
 
404
410
  const [ src, key, attr ] = column.enumSrc.split(':')
405
411
  this.socket.send(src, {
@@ -425,6 +431,8 @@ export default{
425
431
  if(!this.enumCache[column.key][key].text)
426
432
  this.enumCache[column.key][key].text = key
427
433
  }
434
+
435
+ this.lastEnumItems = reqItems
428
436
  })
429
437
  .catch(err => this.log('Load enums error', err))
430
438
  }
@@ -581,7 +589,7 @@ export default{
581
589
  let [ kk, ...vv ] = column.key.split('-')
582
590
  vv = vv.join('-')
583
591
 
584
- if(![ 'count' ].includes(vv)){
592
+ if(![ 'count', 'countDistinct' ].includes(vv)){
585
593
 
586
594
  const key = kk.substring(1)
587
595
  const presetColumn = this.preset.columns.find(_ => _.key === key)
@@ -627,7 +635,7 @@ export default{
627
635
  switch(e.code){
628
636
 
629
637
  case 'Backquote':
630
- this.$refs.contextMenu.open(this.$refs.title)
638
+ this.togglePreset()
631
639
  break
632
640
 
633
641
  default:
@@ -824,6 +832,7 @@ export default{
824
832
  compPrefix: '',
825
833
  enumCache: {},
826
834
  extItems: null,
835
+ lastEnumItems: null,
827
836
  }
828
837
  },
829
838
 
@@ -850,7 +859,6 @@ export default{
850
859
 
851
860
  .resize2{
852
861
  @apply h-[5px] hover:bg-primary bg-text-50 cursor-n-resize absolute top-0 left-0 right-0 flex items-center justify-center;
853
- @apply z-20;
854
862
  }
855
863
 
856
864
  .extClose{
@@ -4,10 +4,11 @@
4
4
  <table :class="$style.table" ref="tableHead" :style="tableHeadStyle">
5
5
  <thead>
6
6
  <tr>
7
- <th v-for="column in visibleColumns" :style="thStyle(column)">
7
+ <th v-for="column in visibleColumns" :style="thStyle(column)"
8
+ v-tooltip="`${column.label ? column.label : column.key}`">
8
9
  <slot v-if="$slots['col-' + column.key]" :name="'col-' + column.key" :column="column"></slot>
9
10
  <div v-else :class="headerColumnClass(column)">
10
- <span>{{ column.label2 ? column.label2 : (column.label ? column.label : column.key) }}</span>
11
+ <span>{{ column.label2 ? column.label2 : (column.label ?? column.key) }}</span>
11
12
  </div>
12
13
  <div :class="$style.separator" @mousedown="startResize($event, column)"></div>
13
14
  </th>
@@ -39,7 +40,7 @@
39
40
  @click="$emit('item-click', item, column)">
40
41
  <slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
41
42
  <slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
42
- <div v-else :class="columnClass(column)" v-html="formatColumn(item, column)"></div>
43
+ <div v-else :class="columnClass(column, item)" v-html="formatColumn(item, column)"></div>
43
44
  </td>
44
45
  <td :class="$style.spacer"></td>
45
46
  </tr>
@@ -322,7 +323,7 @@ export default{
322
323
  window.addEventListener('mouseup', onMouseUp)
323
324
  },
324
325
 
325
- columnClass(column){
326
+ columnClass(column, item){
326
327
 
327
328
  let align
328
329
  if(!column.align){
@@ -333,9 +334,118 @@ export default{
333
334
  }
334
335
  }
335
336
 
337
+ let appearanceClass = ''
338
+ if(item && Array.isArray(column.appearances)){
339
+
340
+ let value
341
+ switch(column.type){
342
+
343
+ case 'number':
344
+ value = parseInt(item[column.key])
345
+ break
346
+
347
+ default:
348
+ value = (item[column.key] ?? '').toString()
349
+ break
350
+ }
351
+
352
+ for(let appearance of column.appearances){
353
+
354
+ let conditionMatched = false
355
+ for(let condition of appearance.conditions ?? []){
356
+ switch(condition.operator){
357
+
358
+ case '>':
359
+ if(value > condition.value){
360
+ conditionMatched = true
361
+ }
362
+ break
363
+
364
+ case '>=':
365
+ if(value >= condition.value){
366
+ conditionMatched = true
367
+ }
368
+ break
369
+
370
+ case '=':
371
+ if(value === condition.value){
372
+ conditionMatched = true
373
+ }
374
+ break
375
+
376
+ case '<':
377
+ if(value < condition.value){
378
+ conditionMatched = true
379
+ }
380
+ break
381
+
382
+ case '<=':
383
+ if(value <= condition.value){
384
+ conditionMatched = true
385
+ }
386
+ break
387
+
388
+ case 'startsWith':
389
+ if(value.startsWith(condition.value)){
390
+ conditionMatched = true
391
+ }
392
+ break
393
+
394
+ case 'notStartsWith':
395
+ if(!value.startsWith(condition.value)){
396
+ conditionMatched = true
397
+ }
398
+ break
399
+
400
+ case 'contains':
401
+ if(value.indexOf(condition.value) >= 0){
402
+ conditionMatched = true
403
+ }
404
+ break
405
+
406
+ case 'notContains':
407
+ if(value.indexOf(condition.value) < 0){
408
+ conditionMatched = true
409
+ }
410
+ break
411
+
412
+ case 'endsWith':
413
+ if(value.endsWith(condition.value)){
414
+ conditionMatched = true
415
+ }
416
+ break
417
+
418
+ case 'notEndsWith':
419
+ if(!value.endsWith(condition.value)){
420
+ conditionMatched = true
421
+ }
422
+ break
423
+
424
+ case 'none':
425
+ conditionMatched = true
426
+ break
427
+
428
+ case 'regex':
429
+ break
430
+
431
+ }
432
+
433
+ if(conditionMatched) break
434
+ }
435
+
436
+ console.log('value', value, conditionMatched)
437
+
438
+ if(conditionMatched){
439
+ appearanceClass = appearance.class
440
+ break
441
+ }
442
+ }
443
+ }
444
+
336
445
  return [
337
446
  this.$style.tdDiv,
338
- this.$style['align-' + align]
447
+ this.$style['align-' + align],
448
+ appearanceClass
339
449
  ]
340
450
  .join(' ')
341
451
  },
package/src/index.js CHANGED
@@ -332,7 +332,7 @@ export default{
332
332
  tooltip.classList.add('active')
333
333
  }
334
334
  }
335
- }, 1300)
335
+ }, 1000)
336
336
  el.setAttribute('data-tooltip-open', true)
337
337
  })
338
338
 
@@ -565,6 +565,7 @@ export default{
565
565
  app.component('SvgSetting', defineAsyncComponent(() => import("./widgets/SvgSetting.vue")))
566
566
  app.component('PresetBar', defineAsyncComponent(() => import("./widgets/PresetBar.vue")))
567
567
  app.component('PresetSelector', defineAsyncComponent(() => import("./widgets/PresetSelector.vue")))
568
+ app.component('PresetBarPivotColumnEdit', defineAsyncComponent(() => import("./widgets/PresetBarPivotColumnEdit.vue")))
568
569
  app.component('LogViewer', defineAsyncComponent(() => import("./widgets/LogViewer.vue")))
569
570
  app.component('ColumnSelector', defineAsyncComponent(() => import("./widgets/ColumnSelector.vue")))
570
571
  },
@@ -61,7 +61,10 @@ export const componentMixin = {
61
61
  itemRatio: Array,
62
62
 
63
63
  containerClass: String,
64
- itemClass: String,
64
+ itemClass: {
65
+ type: String,
66
+ default: 'bg-contain'
67
+ },
65
68
 
66
69
  htmlText: String,
67
70
 
@@ -737,33 +737,34 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
737
737
 
738
738
  const sortKeyToOrders = {} // Helper to determine group ordering
739
739
  for(let value of pivot.values){
740
+ const valueKey = model.rawAttributes[value.key].field
740
741
 
741
742
  switch(value.aggregrate){
742
743
 
743
744
  case 'sum':
744
- attributes.push([ fn('SUM', literal(`${model.name}.${value.key}`)), `_${value.key}-${value.aggregrate}` ])
745
+ attributes.push([ fn('SUM', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
745
746
  break
746
747
 
747
748
  case 'avg':
748
- attributes.push([ fn('AVG', literal(`${model.name}.${value.key}`)), `_${value.key}-${value.aggregrate}` ])
749
+ attributes.push([ fn('AVG', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
749
750
  break
750
751
 
751
752
  case 'min':
752
- attributes.push([ fn('MIN', literal(`${model.name}.${value.key}`)), `_${value.key}-${value.aggregrate}` ])
753
+ attributes.push([ fn('MIN', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
753
754
  break
754
755
 
755
756
  case 'max':
756
- attributes.push([ fn('MAX', literal(`${model.name}.${value.key}`)), `_${value.key}-${value.aggregrate}` ])
757
+ attributes.push([ fn('MAX', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
757
758
  break
758
759
 
759
760
  case 'countDistinct':
760
- attributes.push([ literal(`COUNT(DISTINCT ${model.name}.${value.key})`), `_${value.key}-${value.aggregrate}` ])
761
- sortKeyToOrders[`_${value.key}-${value.aggregrate}`] = literal(`COUNT(DISTINCT ${model.name}.${value.key})`)
761
+ attributes.push([ literal(`COUNT(DISTINCT ${model.name}.${valueKey})`), `_${value.key}-${value.aggregrate}` ])
762
+ sortKeyToOrders[`_${value.key}-${value.aggregrate}`] = literal(`COUNT(DISTINCT ${model.name}.${valueKey})`)
762
763
  break
763
764
 
764
765
  case 'count':
765
- attributes.push([ fn('COUNT', literal(`${model.name}.${value.key}`)), `_${value.key}-${value.aggregrate}` ])
766
- sortKeyToOrders[`_${value.key}-${value.aggregrate}`] = fn('COUNT', literal(`${model.name}.${value.key}`))
766
+ attributes.push([ fn('COUNT', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
767
+ sortKeyToOrders[`_${value.key}-${value.aggregrate}`] = fn('COUNT', literal(`${model.name}.${valueKey}`))
767
768
  break
768
769
 
769
770
  default:
@@ -139,6 +139,9 @@ const generatePivotColumns = (preset, items) => {
139
139
  label,
140
140
  label2: (((tableColumns[k1] ?? {}).label ?? '') + ': ' + label).trim(),
141
141
  type: 'number',
142
+ appearances: [
143
+ { class:"text-primary cursor-pointer", conditions:[ { operator:'none' } ] }
144
+ ]
142
145
  })
143
146
  }
144
147
  }
@@ -35,23 +35,19 @@ export default{
35
35
 
36
36
  emits: [ 'select' ],
37
37
 
38
- props: {
39
-
40
- columns: Array,
41
-
42
- },
43
-
44
38
  data(){
45
39
  return {
46
40
  isOpen: false,
47
41
  context: null,
42
+ columns: []
48
43
  }
49
44
  },
50
45
 
51
46
  methods: {
52
47
 
53
- open(context){
48
+ open(context, columns){
54
49
  this.context = context
50
+ this.columns = columns
55
51
  this.isOpen = true
56
52
  },
57
53
 
@@ -14,7 +14,7 @@
14
14
  </div>
15
15
 
16
16
  <div class="p-3">
17
- <div class="p-2">
17
+ <div class="px-2">
18
18
  <label class="text-text-300">Presets</label>
19
19
  </div>
20
20
  <div class="flex flex-col divide-y divide-text-50 bg-base-500 border-[1px] border-text-50 overflow-hidden rounded-lg">
@@ -95,7 +95,8 @@
95
95
  body-class="divide-y divide-text-50 rounded-lg border-[1px] border-text-50 bg-base-300"
96
96
  @reorder="(from, to) => { presetColumns.splice(to, 0, presetColumns.splice(from, 1)[0]); }">
97
97
  <template v-slot="{ item }">
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">
98
+ <div v-if="!item.key.startsWith('_') || (item.key.startsWith('_') && (preset.pivot && preset.pivot.enabled))"
99
+ class="flex flex-row items-center gap-3 px-3 p-1 hover:bg-primary-100">
99
100
  <div data-reorder>
100
101
  <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>
101
102
  </div>
@@ -103,7 +104,9 @@
103
104
  <div>
104
105
  <Checkbox v-model="item.visible" default="true" />
105
106
  </div>
106
- {{ item.label2 ? item.label2 : (item.label ? item.label : item.key) }}
107
+ <Textbox v-model="item.label2" :placeholder="item.label"
108
+ class="border-none bg-transparent" :class="$style.columnTextbox"
109
+ item-class="p-1 px-0" />
107
110
  </div>
108
111
  </div>
109
112
  </template>
@@ -174,7 +177,7 @@
174
177
 
175
178
  <button type="button"
176
179
  class="text-primary flex flex-row items-center justify-center p-3"
177
- @click="$refs.columnSelector.open(addFilter)">
180
+ @click="$refs.columnSelector.open(addFilter, filterColumns)">
178
181
  <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>
179
182
  Add Filter
180
183
  </button>
@@ -465,7 +468,7 @@
465
468
  <div>
466
469
  <div class="flex flex-row items-center gap-2">
467
470
  <label>Groups</label>
468
- <button type="button" @click="$refs.columnSelector.open(obj => addPivot('rows', obj))">
471
+ <button type="button" @click="$refs.columnSelector.open(obj => addPivot('rows', obj), pivotColumns)">
469
472
  <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>
470
473
  </button>
471
474
  </div>
@@ -480,7 +483,7 @@
480
483
  </div>
481
484
  <div class="flex-1 flex flex-row gap-3">
482
485
  <strong class="flex-1 cursor-pointer text-ellipsis overflow-hidden whitespace-nowrap"
483
- @click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() });">
486
+ @click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() }, pivotColumns);">
484
487
  {{ item.label ? item.label : item.key }}
485
488
  </strong>
486
489
  <select v-if="item.type === 'date'"
@@ -503,7 +506,7 @@
503
506
  <div>
504
507
  <div class="flex flex-row items-center gap-2">
505
508
  <label>Aggregrates</label>
506
- <button type="button" @click="$refs.columnSelector.open(obj => addPivot('values', obj))">
509
+ <button type="button" @click="$refs.columnSelector.open(obj => addPivot('values', obj), pivotColumns)">
507
510
  <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>
508
511
  </button>
509
512
  </div>
@@ -518,7 +521,7 @@
518
521
  </div>
519
522
  <div class="flex-1 flex flex-row gap-3">
520
523
  <strong class="flex-1 cursor-pointer text-ellipsis overflow-hidden whitespace-nowrap"
521
- @click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() });">
524
+ @click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() }, pivotColumns);">
522
525
  {{ item.label ? item.label : item.key }}
523
526
  </strong>
524
527
  <select v-model="item.aggregrate"
@@ -558,14 +561,21 @@
558
561
  <div data-reorder>
559
562
  <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>
560
563
  </div>
561
- <Textbox class="flex-1 border-none" v-model="item.label2" :placeholder="item.label"
564
+ <Textbox class="flex-1 border-none" :class="$style.columnTextbox" v-model="item.label2" :placeholder="item.label"
562
565
  item-class="p-1"/>
566
+ <button type="button" v-if="[ 'number' ].includes(item.type)" @click="">
567
+ <svg width="13" height="13" :class="(item.appearances ?? []).length > 0 ? 'fill-primary' : 'fill-text-300 hover:fill-primary'"
568
+ @click="$refs.presetBarPivotColumnEdit.open(item, next => { Object.assign(item, next); apply() })"
569
+ 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="M176 0h-128C21.49 0 0 21.49 0 48v128C0 202.5 21.49 224 48 224h128C202.5 224 224 202.5 224 176v-128C224 21.49 202.5 0 176 0zM176 288h-128C21.49 288 0 309.5 0 336v128C0 490.5 21.49 512 48 512h128C202.5 512 224 490.5 224 464v-128C224 309.5 202.5 288 176 288zM464 0h-128C309.5 0 288 21.49 288 48v128C288 202.5 309.5 224 336 224h128C490.5 224 512 202.5 512 176v-128C512 21.49 490.5 0 464 0zM488 376h-64v-64C424 298.8 413.3 288 400 288s-24 10.75-24 24v64h-64c-13.25 0-24 10.75-24 24s10.75 24 24 24h64v64c0 13.25 10.75 24 24 24s24-10.75 24-24v-64h64c13.25 0 24-10.75 24-24S501.3 376 488 376z"/></svg>
570
+ </button>
563
571
  <button type="button" @click="presetPivot.columns.splice(index, 1); presetPivot.manualColumns = true; apply()">
564
572
  <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"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
565
573
  </button>
566
574
  </div>
567
575
  </template>
568
576
  </ListItem>
577
+
578
+ <PresetBarPivotColumnEdit ref="presetBarPivotColumnEdit" />
569
579
  </div>
570
580
 
571
581
  </div>
@@ -594,8 +604,7 @@
594
604
 
595
605
  </div>
596
606
 
597
- <ColumnSelector ref="columnSelector"
598
- :columns="config.columns" />
607
+ <ColumnSelector ref="columnSelector" />
599
608
 
600
609
  </div>
601
610
 
@@ -756,6 +765,18 @@ export default{
756
765
  return this.presetChart[this.chartType]
757
766
  },
758
767
 
768
+ filterColumns(){
769
+ return this.config.columns.filter(_ => {
770
+ return _.filterable !== false
771
+ })
772
+ },
773
+
774
+ pivotColumns(){
775
+ return this.config.columns.filter(_ => {
776
+ return _.pivot !== false
777
+ })
778
+ },
779
+
759
780
  preset(){
760
781
  return this.config.presets[this.config.presetIdx]
761
782
  },
@@ -846,4 +867,8 @@ export default{
846
867
  @apply flex flex-col relative;
847
868
  }
848
869
 
870
+ .columnTextbox input::placeholder{
871
+ @apply text-text;
872
+ }
873
+
849
874
  </style>
@@ -0,0 +1,176 @@
1
+ <template>
2
+ <Modal ref="modal" :hash="hash" :state="state" width="480" height="480">
3
+ <template v-slot:head>
4
+ <div class="relative p-6">
5
+ <h3>{{ obj.label }}</h3>
6
+ <div class="absolute top-0 right-0 p-2">
7
+ <button type="button" class="p-2" @click="close">
8
+ <svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
9
+ <path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
10
+ </svg>
11
+ </button>
12
+ </div>
13
+ </div>
14
+ </template>
15
+ <template v-slot:foot>
16
+ <div class="p-6">
17
+ <Button class="w-[100px]" @click="apply">Apply</Button>
18
+ </div>
19
+ </template>
20
+ <div class="flex-1 p-6 flex flex-col gap-4">
21
+
22
+ <div class="flex flex-col gap-1">
23
+ <div class="flex flex-row gap-2">
24
+ <label class="text-text-400">Appearances</label>
25
+ <button type="button" class="text-primary" @click="appearances.push({ conditions:[{}], class:'' })">
26
+ <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 256c0 17.69-14.33 32.01-32 32.01H256v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144H48c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99H192v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144C417.7 224 432 238.3 432 256z"/></svg>
27
+ </button>
28
+ </div>
29
+
30
+ <ListItem :items="appearances"
31
+ class="bg-transparent"
32
+ @reorder="(from, to) => { appearances.splice(to, 0, appearances.splice(from, 1)[0]); }"
33
+ container-class="flex flex-col gap-1">
34
+ <template v-slot="{ item, index }">
35
+ <div class="flex flex-row items-center gap-3">
36
+ <div data-reorder>
37
+ <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>
38
+ </div>
39
+ <div class="flex-1 flex flex-col gap-1">
40
+ <div v-for="condition in item.conditions" class="flex flex-row gap-1">
41
+ <Dropdown v-model="condition.operator" :class="condition.operator === 'none' ? 'w-[200px]' : 'w-[100px]'">
42
+ <option v-for="op in operators" :value="op.value">{{ op.text }}</option>
43
+ <option value="none">No Condition</option>
44
+ </Dropdown>
45
+ <Textbox v-if="condition.operator !== 'none'" v-model.numberconditions="condition.value" class="w-[100px]"/>
46
+ </div>
47
+ </div>
48
+ <div :class="item.class" class="px-2">
49
+ Sample
50
+ </div>
51
+ <div>
52
+ <Dropdown class="w-[120px]" v-model="item.class">
53
+ <option value="text-primary cursor-pointer">Default</option>
54
+ <option value="bg-red-100 text-red-600">Red 1</option>
55
+ <option value="bg-red-200 text-red-600">Red 2</option>
56
+ <option value="bg-red-300 text-red-600">Red 3</option>
57
+ <option value="bg-red-400 text-white">Red 4</option>
58
+ <option value="bg-red-500 text-white">Red 5</option>
59
+ <option value="bg-green-100 text-green-700">Green 1</option>
60
+ <option value="bg-green-200 text-green-700">Green 2</option>
61
+ <option value="bg-green-300 text-green-700">Green 3</option>
62
+ <option value="bg-green-400 text-white">Green 4</option>
63
+ <option value="bg-green-500 border-[1px] border-green-600 text-white">Green 5</option>
64
+ <option value="text-primary underline cursor-pointer">Underline</option>
65
+ </Dropdown>
66
+ </div>
67
+ <div>
68
+ <button type="button" @click="appearances.splice(index, 1)">
69
+ <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"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
70
+ </button>
71
+ </div>
72
+ </div>
73
+ </template>
74
+ </ListItem>
75
+ </div>
76
+ </div>
77
+ </Modal>
78
+ </template>
79
+
80
+ <script>
81
+
82
+ export default{
83
+
84
+ emits: [ 'apply' ],
85
+
86
+ inject: [ 'socketEmit2' ],
87
+
88
+ props: {
89
+
90
+ hash: String,
91
+ state: Boolean
92
+
93
+ },
94
+
95
+ computed: {
96
+
97
+ appearances(){
98
+ if(!Array.isArray(this.obj.appearances)){
99
+ this.obj.appearances = []
100
+ }
101
+ return this.obj.appearances
102
+ },
103
+
104
+ operators(){
105
+
106
+ let operators = []
107
+
108
+ switch(this.obj?.type){
109
+
110
+ case 'number':
111
+ operators = [
112
+ { value:">", text:">" },
113
+ { value:">=", text:">=" },
114
+ { value:"=", text:"=" },
115
+ { value:"<=", text:"<=" },
116
+ { value:"<", text:"<" }
117
+ ]
118
+ break
119
+
120
+ default:
121
+ operators = [
122
+ { value:"contains", text:"Contains" },
123
+ { value:"startsWith", text:"Starts With" },
124
+ { value:"endsWith", text:"Ends With" },
125
+ { value:"notContains", text:"Not Contains" },
126
+ { value:"notStartsWith", text:"Not Starts With" },
127
+ { value:"notEndsWith", text:"Not StartEnds With" },
128
+ { value:"regex", text:"Match Pattern" }
129
+ ]
130
+ break
131
+ }
132
+
133
+ return operators
134
+ }
135
+
136
+ },
137
+
138
+ data(){
139
+ return {
140
+ obj: null,
141
+ callback: null
142
+ }
143
+ },
144
+
145
+ methods: {
146
+
147
+ apply(){
148
+ if(typeof this.callback === 'function')
149
+ this.callback(this.obj)
150
+ this.$emit('apply', this.obj)
151
+ this.close()
152
+ },
153
+
154
+ open(obj, callback){
155
+ this.obj = JSON.parse(JSON.stringify(obj))
156
+ this.callback = callback
157
+ this.$refs.modal.open()
158
+ },
159
+
160
+ close(){
161
+ this.$refs.modal.close()
162
+ }
163
+
164
+ }
165
+
166
+ }
167
+
168
+ </script>
169
+
170
+ <style module>
171
+
172
+ .comp{
173
+
174
+ }
175
+
176
+ </style>