@mixd-id/web-scaffold 0.1.230406284 → 0.1.230406286

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.230406284",
4
+ "version": "0.1.230406286",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div :class="$style.comp">
3
3
  <input :id="id" type="checkbox" :checked="checked" @change="onChange" :disabled="isDisabled"/>
4
- <label :for="id" :class="labelClass">
4
+ <label :for="id" :class="labelClass" class="whitespace-nowrap text-ellipsis overflow-hidden">
5
5
  <div :class="$style.indicator" v-if="Boolean(showIcon)">
6
6
  <Transition name="checkbox">
7
7
  <div v-if="checked">
@@ -127,7 +127,7 @@ export default {
127
127
  <style module>
128
128
 
129
129
  .comp{
130
- @apply flex items-center;
130
+ @apply flex flex-row;
131
131
  }
132
132
 
133
133
  .comp label{
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="flex flex-col">
3
3
 
4
- <div v-if="readyState === 1"
4
+ <div v-if="readyState >= 1"
5
5
  class="flex-1 flex flex-row">
6
6
 
7
7
  <div v-if="computedPresetMode === 'sidebar' && sidebar.open"
@@ -10,6 +10,7 @@
10
10
 
11
11
  <PresetBar :config="config"
12
12
  class="flex-1"
13
+ :enumCache="enumCache"
13
14
  @apply="load">
14
15
 
15
16
  <template #toolbar>
@@ -25,142 +26,210 @@
25
26
  @mousedown="(e) => $util.dragResize(e, resize1)"></div>
26
27
  </div>
27
28
 
28
- <div class="flex-1 flex flex-col" :class="containerClass">
29
-
30
- <slot name="head"
31
- :preset="preset"
32
- :presetSelector="$refs.presetSelector"
33
- :compPrefix="compPrefix">
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"
35
- :class="headerClass">
36
-
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>
29
+ <div class="flex-1 flex flex-col">
30
+
31
+ <div class="flex-1 flex flex-col" :class="containerClass">
32
+ <slot name="head"
33
+ :preset="preset"
34
+ :presetSelector="$refs.presetSelector"
35
+ :compPrefix="compPrefix">
36
+ <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"
37
+ :class="headerClass">
38
+
39
+ <div class="flex md:flex-1 flex-row items-start gap-3">
40
+
41
+ <div class="flex flex-row gap-2 md:gap-3 items-center" ref="title">
42
+ <div class="flex flex-col whitespace-nowrap text-ellipsis overflow-hidden">
43
+ <div class="cursor-pointer group" @click="$refs.contextMenu.open($refs.title)">
44
+ <small class="text-text-400 text-xs">{{ title ?? 'Untitled' }}</small>
45
+ <div class="flex flex-row items-baseline gap-1">
46
+ <h5 class="whitespace-nowrap relative top-[-2px] text-ellipsis overflow-hidden group-hover:text-primary">
47
+ {{ preset.name ?? 'Preset Name' }}
48
+ </h5>
49
+ <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>
50
+ </div>
48
51
  </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 }}
52
+ <ContextMenu ref="contextMenu" class="panel-400">
53
+ <div class="flex flex-col min-w-[300px] divide-y divide-text-50">
54
+
55
+ <div class="p-3 flex flex-col gap-1 bg-base-300">
56
+ <button v-for="(preset, idx) in config.presets"
57
+ class="p-2 px-5 text-left flex flex-row items-center rounded-md"
58
+ :class="config.presetIdx === idx ? 'bg-primary-200 text-white' : 'hover:bg-primary-100'"
59
+ type="button"
60
+ @click="selectPreset(idx)">
61
+ <label class="flex-1 pr-12">
62
+ {{ preset.name }}
63
+ </label>
64
+ <div class="p-1">
65
+ <div v-if="idx < 10"
66
+ class="border-[1px] border-text-300 px-2 text-mono text-xs text-text-400 rounded-lg">
67
+ Alt {{ idx + 1 }}
68
+ </div>
66
69
  </div>
67
- </div>
68
- </button>
69
- </div>
70
+ </button>
71
+ </div>
72
+
73
+ <div class="px-3">
74
+ <button type="button" class="text-primary p-5 text-left w-full" @click="openPreset">Edit</button>
75
+ </div>
70
76
 
71
- <div class="px-3">
72
- <button type="button" class="text-primary p-5 text-left w-full" @click="openPreset">Edit</button>
73
77
  </div>
78
+ </ContextMenu>
79
+ </div>
80
+ </div>
81
+
82
+ <div class="flex-1"></div>
83
+
84
+ <slot name="toolbar"></slot>
85
+ </div>
74
86
 
87
+ <div class="flex flex-row gap-2 gap-1" v-if="Boolean(toolbar)">
88
+ <Textbox class="flex-1 md:w-[240px]" placeholder="Search..." clearable="1"
89
+ v-model="preset.search"
90
+ @clear="delete preset.search; load()"
91
+ @keyup.enter="load">
92
+ <template #start>
93
+ <div class="p-2 pr-0">
94
+ <svg width="16" height="16" class="fill-text-300" 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="M504.1 471l-134-134C399.1 301.5 415.1 256.8 415.1 208c0-114.9-93.13-208-208-208S-.0002 93.13-.0002 208S93.12 416 207.1 416c48.79 0 93.55-16.91 129-45.04l134 134C475.7 509.7 481.9 512 488 512s12.28-2.344 16.97-7.031C514.3 495.6 514.3 480.4 504.1 471zM48 208c0-88.22 71.78-160 160-160s160 71.78 160 160s-71.78 160-160 160S48 296.2 48 208z"/></svg>
75
95
  </div>
76
- </ContextMenu>
96
+ </template>
97
+ </Textbox>
98
+
99
+ <div v-if="$slots.gridItem"
100
+ class="hidden md:grid grid-cols-2 gap-[1px] border-[1px] border-text-50 rounded-lg">
101
+ <Radio v-model="preset.view"
102
+ name="view"
103
+ value="table"
104
+ :custom="true"
105
+ class="p-2 px-3 rounded-lg rounded-r-none"
106
+ :class="preset.view === 'table' ? 'bg-primary-500' : 'bg-base-500'">
107
+ <svg width="14" height="14" :class="preset.view === 'table' ? 'fill-white' : 'fill-text'" 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="M448 32H64.05C28.7 32 .0492 60.65 .0492 96v320c0 35.35 28.65 64 64 64h383.1c35.35 0 64-28.65 64-64V96C512 60.65 483.4 32 448 32zM224 416H64v-96h160V416zM224 256H64V160h160V256zM448 416h-160v-96h160V416zM448 256h-160V160h160V256z"/></svg>
108
+ </Radio>
109
+ <Radio v-model="preset.view"
110
+ name="view"
111
+ value="grid"
112
+ :custom="true"
113
+ class="p-2 px-3 bg-base-500 rounded-lg rounded-l-none"
114
+ :class="preset.view === 'grid' ? 'bg-primary-500' : 'bg-base-500'">
115
+ <svg width="14" height="14" :class="preset.view === 'grid' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M144 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C176 302.3 161.7 288 144 288zM368 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C400 302.3 385.7 288 368 288zM592 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C624 302.3 609.7 288 592 288zM144 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C176 78.33 161.7 64 144 64zM368 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C400 78.33 385.7 64 368 64zM592 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C624 78.33 609.7 64 592 64z"/></svg>
116
+ </Radio>
77
117
  </div>
78
118
  </div>
79
119
 
80
- <div class="flex-1"></div>
81
-
82
- <slot name="toolbar"></slot>
83
120
  </div>
121
+ </slot>
122
+
123
+ <VirtualTable v-if="presetView === 'table'"
124
+ ref="table"
125
+ :columns="columns"
126
+ class="flex-1 rounded-lg panel-400"
127
+ :items="data.items"
128
+ :enumCache="enumCache"
129
+ @scroll-end="loadNext"
130
+ @item-click="onTableItemClick">
131
+
132
+ <template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
133
+ <div :class="getHeader(slot.replace('col-', ''))">
134
+ <slot :name="slot" :item="item" :index="index"></slot>
135
+ </div>
136
+ </template>
84
137
 
85
- <div class="flex flex-row gap-2 gap-1" v-if="Boolean(toolbar)">
86
- <Textbox class="flex-1 md:w-[240px]" placeholder="Search..." clearable="1"
87
- v-model="preset.search"
88
- @clear="delete preset.search; load()"
89
- @keyup.enter="load">
90
- <template #start>
91
- <div class="p-2 pr-0">
92
- <svg width="16" height="16" class="fill-text-300" 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="M504.1 471l-134-134C399.1 301.5 415.1 256.8 415.1 208c0-114.9-93.13-208-208-208S-.0002 93.13-.0002 208S93.12 416 207.1 416c48.79 0 93.55-16.91 129-45.04l134 134C475.7 509.7 481.9 512 488 512s12.28-2.344 16.97-7.031C514.3 495.6 514.3 480.4 504.1 471zM48 208c0-88.22 71.78-160 160-160s160 71.78 160 160s-71.78 160-160 160S48 296.2 48 208z"/></svg>
138
+ <template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
139
+ <slot :name="slot" :item="item" :index="index"></slot>
140
+ </template>
141
+
142
+ </VirtualTable>
143
+
144
+ <VirtualGrid v-else-if="presetView === 'grid'"
145
+ ref="grid"
146
+ :items="dataItems"
147
+ :column="computedGridColumn"
148
+ :class="gridClass"
149
+ @scroll-end="loadNext"
150
+ :container-class="`${gridContainerClass}`"
151
+ :config="config">
152
+ <template #item="{ item }">
153
+ <slot name="gridItem" :item="item" :enumCache="enumCache">
154
+ <div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
155
+ <div>
156
+ <Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
93
157
  </div>
94
- </template>
95
- </Textbox>
96
-
97
- <div v-if="$slots.gridItem"
98
- class="hidden md:grid grid-cols-2 gap-[1px] border-[1px] border-text-50 rounded-lg">
99
- <Radio v-model="preset.view"
100
- name="view"
101
- value="table"
102
- :custom="true"
103
- class="p-2 px-3 rounded-lg rounded-r-none"
104
- :class="preset.view === 'table' ? 'bg-primary-500' : 'bg-base-500'">
105
- <svg width="14" height="14" :class="preset.view === 'table' ? 'fill-white' : 'fill-text'" 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="M448 32H64.05C28.7 32 .0492 60.65 .0492 96v320c0 35.35 28.65 64 64 64h383.1c35.35 0 64-28.65 64-64V96C512 60.65 483.4 32 448 32zM224 416H64v-96h160V416zM224 256H64V160h160V256zM448 416h-160v-96h160V416zM448 256h-160V160h160V256z"/></svg>
106
- </Radio>
107
- <Radio v-model="preset.view"
108
- name="view"
109
- value="grid"
110
- :custom="true"
111
- class="p-2 px-3 bg-base-500 rounded-lg rounded-l-none"
112
- :class="preset.view === 'grid' ? 'bg-primary-500' : 'bg-base-500'">
113
- <svg width="14" height="14" :class="preset.view === 'grid' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M144 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C176 302.3 161.7 288 144 288zM368 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C400 302.3 385.7 288 368 288zM592 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C624 302.3 609.7 288 592 288zM144 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C176 78.33 161.7 64 144 64zM368 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C400 78.33 385.7 64 368 64zM592 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C624 78.33 609.7 64 592 64z"/></svg>
114
- </Radio>
115
- </div>
116
- </div>
158
+ <div class="flex-1 flex flex-col leading-tight">
159
+ <small>{{ item.code }}</small>
160
+ <strong>{{ item.name }}</strong>
161
+ </div>
162
+ </div>
163
+ </slot>
164
+ </template>
165
+ </VirtualGrid>
166
+
167
+ <PresetSelector ref="presetSelector" :config="config" @select="load" />
168
+ </div>
169
+
170
+ <div v-if="extBar.open && pivotEnabled"
171
+ :style="extStyle" class="border-t-[1px] border-text-50 flex flex-col relative md:p-5">
172
+
173
+ <div :class="$style.resize2"
174
+ @mousedown="(e) => $util.dragResize(e, resize2)">
175
+ <button type="button" @click.prevent="extBar.open = false"
176
+ :class="$style.extClose">
177
+ <svg width="14"
178
+ height="14"
179
+ class="fill-text-300 cursor-pointer"
180
+ 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.6 209.3l-191.1 183.1C235.1 397.8 229.1 400 224 400s-11.97-2.219-16.59-6.688L15.41 209.3C5.814 200.2 5.502 184.1 14.69 175.4c9.125-9.625 24.38-9.938 33.91-.7187L224 342.8l175.4-168c9.5-9.219 24.78-8.906 33.91 .7187C442.5 184.1 442.2 200.2 432.6 209.3z"/></svg>
181
+ </button>
182
+ </div>
117
183
 
184
+ <div v-if="readyState === 3" class="flex-1 flex items-center justify-center">
185
+ <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>
118
186
  </div>
119
- </slot>
120
187
 
121
- <VirtualTable v-if="presetView === 'table'"
122
- ref="table"
123
- :columns="columns"
124
- class="flex-1 rounded-lg panel-400"
125
- :items="data.items"
126
- @scroll-end="loadNext">
188
+ <VirtualTable v-else-if="presetView === 'table'"
189
+ ref="table"
190
+ :columns="preset.columns"
191
+ class="flex-1 rounded-lg panel-400"
192
+ :items="data.extItems"
193
+ :enumCache="enumCache"
194
+ @scroll-end="loadExtNext">
195
+
196
+ <template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
197
+ <div :class="getHeader(slot.replace('col-', ''))">
198
+ <slot :name="slot" :item="item" :index="index"></slot>
199
+ </div>
200
+ </template>
127
201
 
128
- <template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
129
- <div :class="getHeader(slot.replace('col-', ''))">
202
+ <template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
130
203
  <slot :name="slot" :item="item" :index="index"></slot>
131
- </div>
132
- </template>
133
-
134
- <template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
135
- <slot :name="slot" :item="item" :index="index"></slot>
136
- </template>
137
-
138
- </VirtualTable>
139
-
140
- <VirtualGrid v-else-if="presetView === 'grid'"
141
- ref="grid"
142
- :items="dataItems"
143
- :column="computedGridColumn"
144
- :class="gridClass"
145
- @scroll-end="loadNext"
146
- :container-class="`${gridContainerClass}`"
147
- :config="config">
148
- <template #item="{ item }">
149
- <slot name="gridItem" :item="item">
150
- <div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
151
- <div>
152
- <Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
153
- </div>
154
- <div class="flex-1 flex flex-col leading-tight">
155
- <small>{{ item.code }}</small>
156
- <strong>{{ item.name }}</strong>
204
+ </template>
205
+
206
+ </VirtualTable>
207
+
208
+ <VirtualGrid v-else-if="presetView === 'grid'"
209
+ ref="grid"
210
+ :items="data.extItems"
211
+ :column="computedGridColumn"
212
+ :class="gridClass"
213
+ @scroll-end="loadExtNext"
214
+ :container-class="`${gridContainerClass}`"
215
+ :config="config">
216
+ <template #item="{ item }">
217
+ <slot name="gridItem" :item="item">
218
+ <div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
219
+ <div>
220
+ <Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
221
+ </div>
222
+ <div class="flex-1 flex flex-col leading-tight">
223
+ <small>{{ item.code }}</small>
224
+ <strong>{{ item.name }}</strong>
225
+ </div>
157
226
  </div>
158
- </div>
159
- </slot>
160
- </template>
161
- </VirtualGrid>
227
+ </slot>
228
+ </template>
229
+ </VirtualGrid>
230
+
231
+ </div>
162
232
 
163
- <PresetSelector ref="presetSelector" :config="config" @select="load" />
164
233
  </div>
165
234
 
166
235
  </div>
@@ -180,6 +249,10 @@ import PresetBar from "../widgets/PresetBar.vue";
180
249
 
181
250
  export default{
182
251
 
252
+ emits: [ 'after-load', 'open-preset', 'signal', 'pivot-item-click' ],
253
+
254
+ inject: [ 'socket', 'toast' ],
255
+
183
256
  props: {
184
257
  config: {
185
258
  type: Object,
@@ -231,9 +304,103 @@ export default{
231
304
  .join(' ')
232
305
  },
233
306
 
307
+ generatePivotColumns(items){
308
+ if(!this.preset.pivot || !this.preset.pivot.enabled) return
309
+ if(!Array.isArray(items) && items.length < 1) return
310
+
311
+ const tableColumns = (this.preset.columns ?? this.config.columns)
312
+ .reduce((cur, obj) => {
313
+ cur[obj.key] = obj
314
+ return cur
315
+ }, {})
316
+
317
+ const currentPivotColumns = [ ...(this.preset.pivot.columns ?? []) ]
318
+ .reduce((cur, obj) => {
319
+ cur[obj.key] = obj
320
+ return cur
321
+ }, {})
322
+
323
+ const pivotColumns = []
324
+
325
+ for(let row of this.preset.pivot.rows){
326
+
327
+ let format
328
+ switch(row.aggregrate){
329
+ case 'year':
330
+ format = 'YYYY'
331
+ break
332
+
333
+ case 'month':
334
+ format = 'MMM YYYY'
335
+ break
336
+
337
+ case 'date':
338
+ format = 'D MMM YYYY'
339
+ break
340
+ }
341
+
342
+ if(currentPivotColumns[row.key]){
343
+ Object.assign(currentPivotColumns[row.key], {
344
+ format,
345
+ visible: true
346
+ })
347
+
348
+ pivotColumns.push(currentPivotColumns[row.key])
349
+ }
350
+ else{
351
+ pivotColumns.push({
352
+ ...tableColumns[row.key],
353
+ format,
354
+ visible: true
355
+ })
356
+ }
357
+ }
358
+
359
+ for(let key in items[0]){
360
+ if(key.startsWith('_')){
361
+ let [ k1, ...k2 ] = key.substring(1).split('-')
362
+ k2 = k2.join('-')
363
+
364
+ let label = k2.replace(/_/gi, ' ')
365
+ const column = tableColumns[k1]
366
+ if(column){
367
+ switch(column.type){
368
+ case 'enum':
369
+ label = (column.typeParams ?? []).find(_ => `${_.value}` === `${k2}`)?.text ?? label
370
+ break
371
+ }
372
+ }
373
+
374
+ if(currentPivotColumns[key]){
375
+ Object.assign(currentPivotColumns[key], {
376
+ visible: true
377
+ })
378
+
379
+ pivotColumns.push(currentPivotColumns[key])
380
+ }
381
+ else{
382
+ pivotColumns.push({
383
+ key,
384
+ label,
385
+ label2: (((tableColumns[k1] ?? {}).label ?? '') + ': ' + label).trim(),
386
+ type: 'number',
387
+ })
388
+ }
389
+ }
390
+ }
391
+
392
+ (this.preset.pivot.columns ?? []).filter(_ => !pivotColumns.find(i => i.key === _.key))
393
+ .forEach(_ => {
394
+ _.visible = false
395
+ pivotColumns.push(_)
396
+ })
397
+
398
+ this.preset.pivot.columns = pivotColumns
399
+
400
+ },
401
+
234
402
  load(){
235
403
  if(this.src){
236
-
237
404
  if(!this.preset.columns){
238
405
  this.preset.columns = JSON.parse(JSON.stringify(this.config.columns))
239
406
  }
@@ -244,54 +411,15 @@ export default{
244
411
  })
245
412
  .then(data => {
246
413
 
247
- // Handle non-aggregrated pivot values
248
- if((this.preset.pivot ?? {}).enabled && data.items.length > 0){
249
-
250
- const tableColumns = (this.preset.columns ?? this.config.columns).reduce((cur, obj) => {
251
- cur[obj.key] = obj
252
- return cur
253
- }, {})
254
-
255
- const columns = []
256
- for(let key in data.items[0]){
257
- if(key.startsWith('_')){
258
- let [ k1, ...k2 ] = key.substring(1).split('-')
259
- k2 = k2.join('-')
260
-
261
- let label = k2.replace(/_/gi, ' ')
262
- const column = tableColumns[k1]
263
- if(column){
264
- switch(column.type){
265
- case 'enum':
266
- label = (column.typeParams ?? []).find(_ => `${_.value}` === `${k2}`)?.text ?? label
267
- break
268
- }
269
- }
270
-
271
- if(!tableColumns[key]){
272
- columns.push({
273
- key,
274
- label,
275
- label2: (((tableColumns[k1] ?? {}).label ?? '') + ': ' + label).trim(),
276
- type: 'number',
277
- })
278
- }
279
- }
280
- }
281
-
282
- if(!Array.isArray(this.preset.columns)){
283
- this.preset.columns = JSON.parse(JSON.stringify(this.config.columns))
284
- }
285
- this.preset.columns.push(...columns)
286
- }
287
-
288
- this.loadEnums()
289
-
414
+ this.generatePivotColumns(data.items)
415
+ this.loadEnums(data.items)
290
416
  Object.assign(this.data, data ?? {})
291
-
292
417
  this.$emit('after-load')
418
+
419
+ })
420
+ .catch(err => {
421
+ this.toast(err)
293
422
  })
294
- .catch(err => this.toast(err))
295
423
  }
296
424
  return new Promise(resolve => resolve())
297
425
  },
@@ -312,6 +440,7 @@ export default{
312
440
  //console.log('next id', data.items[0].id)
313
441
 
314
442
  this.data.items.push(...data.items)
443
+ this.loadEnums(data.items)
315
444
  })
316
445
  .catch(err => this.toast(err))
317
446
  }
@@ -347,37 +476,94 @@ export default{
347
476
  }
348
477
  },
349
478
 
350
- loadEnums(){
479
+ loadEnums(items){
351
480
 
352
481
  for(let i in this.preset.columns){
353
482
  const column = this.preset.columns[i]
354
483
 
355
484
  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]
485
+
486
+ if(!this.enumCache[column.key])
487
+ this.enumCache[column.key] = {}
488
+
489
+ for(let item of items){
490
+ if(item[column.key] && !this.enumCache[column.key][item[column.key]]){
491
+ this.enumCache[column.key][item[column.key]] = { value:item[column.key] }
492
+ }
493
+ }
494
+
495
+ const reqItems = Object.values(this.enumCache[column.key]).filter(_ => !_.text).map(_ => _.value)
496
+
497
+ if(reqItems.length > 0){
498
+
499
+ const [ src, key, attr ] = column.enumSrc.split(':')
500
+ this.socket.send(src, {
501
+ columns: [
502
+ { key:attr, visible:true }
503
+ ],
504
+ itemsPerPage: 1000,
505
+ filters: [
506
+ {
507
+ key:key, value: [ { operator:'in', value:reqItems } ]
372
508
  }
373
- })
509
+ ]
374
510
  })
375
- .catch(err => this.log('Load enums error', err))
511
+ .then(res => {
512
+ (res.items ?? []).forEach(_ => {
513
+ this.enumCache[column.key][_[key]] = {
514
+ text: _[attr] ?? _[key],
515
+ value: _[key],
516
+ }
517
+ })
518
+
519
+ for(let key in this.enumCache[column.key]){
520
+ if(!this.enumCache[column.key][key].text)
521
+ this.enumCache[column.key][key].text = key
522
+ }
523
+ })
524
+ .catch(err => this.log('Load enums error', err))
525
+ }
376
526
  }
377
527
  }
378
528
 
379
529
  },
380
530
 
531
+ loadExt(){
532
+ if(!this.preset.filters || !this.extBar.open) return
533
+
534
+ this.readyState = 3
535
+ return this.socket.send(this.src, {
536
+ ...this.preset,
537
+ filters: [
538
+ ...(this.preset.filters ?? []),
539
+ ...(this.extBar.filters ?? [])
540
+ ],
541
+ pivot: null
542
+ })
543
+ .then((res) => {
544
+ this.data.extItems = res.items
545
+ })
546
+ .finally(_ => this.readyState = 1)
547
+ },
548
+
549
+ loadExtNext(){
550
+ if(!this.preset.filters) return
551
+
552
+ return this.socket.send(this.src, {
553
+ ...this.preset,
554
+ filters: [
555
+ ...(this.preset.filters ?? []),
556
+ ...(this.extBar.filters ?? [])
557
+ ],
558
+ pivot: null,
559
+ afterItem: this.data.extItems[this.data.extItems.length - 1],
560
+ })
561
+ .then(data => {
562
+ this.data.extItems.push(...data.items)
563
+ })
564
+ .catch(err => this.toast(err))
565
+ },
566
+
381
567
  resize1(w){
382
568
  if(this.config.sidebar.width + w >= 270 && this.config.sidebar.width + w <= 600){
383
569
  this.config.sidebar.width += w
@@ -433,6 +619,10 @@ export default{
433
619
  },
434
620
 
435
621
  onSignal(event, items){
622
+ if(this.pivotEnabled){
623
+ console.log('no background update due ti pivot')
624
+ return
625
+ }
436
626
 
437
627
  switch(event){
438
628
 
@@ -458,11 +648,49 @@ export default{
458
648
  this.$emit('signal', event, items)
459
649
  },
460
650
 
651
+ onTableItemClick(item, column){
652
+ if((this.preset.pivot ?? {}).enabled && (column.key ?? '').startsWith('_')){
653
+
654
+ const filters = []
655
+
656
+ for(let idx in this.preset.pivot.rows){
657
+ filters.push({
658
+ key: this.preset.pivot.rows[idx].key,
659
+ value: [{
660
+ operator: '=',
661
+ value: item[this.preset.pivot.rows[idx].key]
662
+ }]
663
+ })
664
+ }
665
+
666
+ const [ kk, vv ] = column.key.split('-')
667
+ if(![ 'count' ].includes(vv)){
668
+ filters.push({
669
+ key: kk.substring(1),
670
+ value: [{
671
+ operator: '=',
672
+ value: vv
673
+ }]
674
+ })
675
+ }
676
+
677
+ this.extBar.open = true
678
+ this.extBar.filters = filters
679
+ this.loadExt()
680
+ }
681
+ },
682
+
461
683
  resize(){
462
684
  this.$refs.table ? this.$refs.table.resize() : null
463
685
  this.$refs.grid ? this.$refs.grid.resize() : null
464
686
  },
465
687
 
688
+ resize2(_, h){
689
+ if(this.extBar.height - h >= 100 && this.extBar.height - h <= 600){
690
+ this.extBar.height -= h
691
+ }
692
+ },
693
+
466
694
  onKeyDown(e){
467
695
 
468
696
  if(e.altKey){
@@ -490,10 +718,6 @@ export default{
490
718
 
491
719
  components: {PresetBar, PresetSelector, VirtualGrid, VirtualTable},
492
720
 
493
- emits: [ 'after-load', 'open-preset', 'signal' ],
494
-
495
- inject: [ 'socket', 'toast' ],
496
-
497
721
  mounted(){
498
722
  this.$addResizeListener(this.$el, () => this.compPrefix = this.$util.calculateMediaPrefix(this.$el.clientWidth))
499
723
 
@@ -501,6 +725,7 @@ export default{
501
725
  .then(() => {
502
726
  this.readyState = 1
503
727
  this.load()
728
+ this.loadExt()
504
729
  })
505
730
 
506
731
  if(this.subscribeKey){
@@ -525,6 +750,8 @@ export default{
525
750
 
526
751
  columns(){
527
752
  if((this.preset.pivot ?? {}).enabled){
753
+ return this.preset.pivot.columns ?? []
754
+
528
755
 
529
756
  const tableColumns = groupBy(this.preset.columns ?? this.config.columns, 'key')
530
757
 
@@ -636,6 +863,26 @@ export default{
636
863
  }
637
864
  },
638
865
 
866
+ extBar(){
867
+ if(!this.config.extbar)
868
+ this.config.extbar = {
869
+ open: false,
870
+ height: Math.round(window.innerHeight / 2.2)
871
+ }
872
+
873
+ return this.config.extbar
874
+ },
875
+
876
+ extStyle(){
877
+ return {
878
+ height: this.extBar.height + "px"
879
+ }
880
+ },
881
+
882
+ pivotEnabled(){
883
+ return ((this.preset ?? {}).pivot ?? {}).enabled
884
+ }
885
+
639
886
  },
640
887
 
641
888
  data(){
@@ -646,6 +893,8 @@ export default{
646
893
  },
647
894
  observer: null,
648
895
  compPrefix: '',
896
+ enumCache: {},
897
+ extItems: null,
649
898
  }
650
899
  },
651
900
 
@@ -670,4 +919,12 @@ export default{
670
919
  @apply w-[3px] cursor-ew-resize absolute top-0 right-0 bottom-0;
671
920
  }
672
921
 
922
+ .resize2{
923
+ @apply h-[3px] cursor-n-resize absolute top-0 left-0 right-0 flex items-center justify-center;
924
+ }
925
+
926
+ .extClose{
927
+ @apply w-[21px] h-[21px] rounded-full bg-base-300 flex items-center justify-center hover:bg-red-600;
928
+ }
929
+
673
930
  </style>
@@ -5,7 +5,7 @@
5
5
  <div v-if="type === 'date'" class="flex-1 flex flex-row gap-2">
6
6
  <div class="flex-1 flex flex-row gap-2">
7
7
  <Dropdown v-model="value.operator"
8
- class="w-[100px]"
8
+ :class="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear' ].includes(value.operator) ? 'w-[100px]' : 'w-full'"
9
9
  @change="apply">
10
10
  <option value="=">=</option>
11
11
  <option value=">">&gt;</option>
@@ -17,9 +17,10 @@
17
17
  <option value="today">Today</option>
18
18
  <option value="thisWeek">This Week</option>
19
19
  <option value="thisMonth">This Month</option>
20
+ <option value="lastMonth">Last Month</option>
20
21
  <option value="thisYear">This Year</option>
21
22
  </Dropdown>
22
- <div v-if="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'thisYear' ].includes(value.operator)"
23
+ <div v-if="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear' ].includes(value.operator)"
23
24
  class="flex-1 flex flex-row gap-2">
24
25
  <Datepicker class="flex-1"
25
26
  mode="popup"
@@ -57,16 +58,16 @@
57
58
  <Radio :name="value.key" :value="false" v-model="value.value" @change="apply">False</Radio>
58
59
  </div>
59
60
 
60
- <div v-else-if="type === 'enum'" class="flex flex-row gap-2">
61
+ <div v-else-if="type === 'enum' && Array.isArray(cTypeParams) && cTypeParams.length > 0"
62
+ class="flex flex-col gap-1">
61
63
 
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>
64
+ <Checkbox v-for="param in cTypeParams"
65
+ v-model="cValue.value"
66
+ class="flex-1"
67
+ :value="param.value"
68
+ @change="apply">
69
+ {{ param.text }}
70
+ </Checkbox>
70
71
 
71
72
  </div>
72
73
 
@@ -119,12 +120,21 @@ export default{
119
120
 
120
121
  value: Object,
121
122
 
123
+ enumCache: Object,
124
+
122
125
  },
123
126
 
124
127
  computed:{
125
128
 
129
+ cTypeParams(){
130
+ if(this.enumCache && this.column && this.enumCache[this.column.key]){
131
+ return Object.values(this.enumCache[this.column.key])
132
+ }
133
+ return this.typeParams
134
+ },
135
+
126
136
  valueRequired(){
127
- return (!(['yesterday', 'today', 'thisWeek', 'thisMonth', 'thisYear'].includes(this.value.operator) ||
137
+ return (!(['yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear'].includes(this.value.operator) ||
128
138
  this.type === 'boolean'));
129
139
  },
130
140
 
@@ -35,7 +35,8 @@
35
35
  @click="select(item, index)"
36
36
  :class="trClass(item, index)">
37
37
  <td v-for="column in visibleColumns"
38
- :class="tdClass(item, column)">
38
+ :class="tdClass(item, column)"
39
+ @click="$emit('item-click', item, column)">
39
40
  <slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
40
41
  <slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
41
42
  <div v-else :class="columnClass(column)" v-html="formatColumn(item, column)"></div>
@@ -86,11 +87,13 @@ const _DEFAULT_COLUMN_WIDTH = '100px'
86
87
 
87
88
  export default{
88
89
 
89
- emits: [ 'scroll-end' ],
90
+ emits: [ 'scroll-end', 'item-click' ],
90
91
 
91
92
  props:{
92
93
  columns: Array,
93
94
 
95
+ enumCache: Object,
96
+
94
97
  itemClass: String,
95
98
 
96
99
  items: Array,
@@ -105,7 +108,7 @@ export default{
105
108
  appearances: {
106
109
  type: Object,
107
110
  default: {}
108
- }
111
+ },
109
112
  },
110
113
 
111
114
  data(){
@@ -397,8 +400,18 @@ export default{
397
400
  break
398
401
 
399
402
  case 'enum':
400
- const enumObj = (column.typeParams ?? []).filter((_) => _.value === value).pop()
401
- text = enumObj ? enumObj.text : value
403
+ let enumText
404
+
405
+ if(Array.isArray(column.typeParams)){
406
+ const typeParam = (column.typeParams ?? []).filter((_) => _.value === value).pop()
407
+ enumText = typeParam && typeParam.text ? typeParam.text : enumText
408
+ }
409
+
410
+ if(this.enumCache && this.enumCache[column.key] && this.enumCache[column.key][value]){
411
+ enumText = this.enumCache[column.key][value].text ?? enumText
412
+ }
413
+
414
+ text = enumText ? enumText : text
402
415
  break
403
416
  }
404
417
 
@@ -99,6 +99,22 @@ const getValue = (filter, opt) => {
99
99
  }
100
100
  break
101
101
 
102
+ case 'lastMonth':
103
+ withoutKey ?
104
+ whereObj = {
105
+ [Op.between]: [
106
+ dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD 00:00:00'),
107
+ dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD 23:59:59'),
108
+ ]
109
+ } :
110
+ whereObj[key] = {
111
+ [Op.between]: [
112
+ dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD 00:00:00'),
113
+ dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD 23:59:59'),
114
+ ]
115
+ }
116
+ break
117
+
102
118
  case 'thisYear':
103
119
  withoutKey ?
104
120
  whereObj = {
@@ -770,6 +786,8 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
770
786
  }
771
787
  }
772
788
 
789
+ attributes.push([ literal(`1`), 'isPivot' ])
790
+
773
791
  return {
774
792
  attributes,
775
793
  group,
@@ -116,11 +116,16 @@
116
116
 
117
117
  <div v-for="(filter, idx) in preset.filters">
118
118
 
119
- <div class="flex flex-row items-start gap-2">
120
- <Checkbox v-model="filter.enabled" :default="true" />
119
+ <div class="flex flex-row items-start">
120
+ <Checkbox v-model="filter.enabled" :default="true" @change="apply" />
121
121
  <div class="flex-1 flex flex-col gap-3">
122
- <div class="flex-1 flex flex-row gap-3">
123
- <strong class="flex-1">{{ column(filter.key).label ?? filter.key }}</strong>
122
+ <div class="flex-1 flex flex-row gap-2">
123
+ <strong class="flex-1 cursor-pointer" @click="filter._collapsed = !filter._collapsed">
124
+ <svg class="inline fill-text transition-transform relative top-[-1px]"
125
+ :class="filter._collapsed === true ? `` : `rotate-90`"
126
+ width="11" height="11" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M118.6 105.4l128 127.1C252.9 239.6 256 247.8 256 255.1s-3.125 16.38-9.375 22.63l-128 127.1c-9.156 9.156-22.91 11.9-34.88 6.943S64 396.9 64 383.1V128c0-12.94 7.781-24.62 19.75-29.58S109.5 96.23 118.6 105.4z"/></svg>
127
+ {{ column(filter.key).label ?? filter.key }}
128
+ </strong>
124
129
  <select v-if="filter.value.length > 1"
125
130
  v-model="filter.modifier"
126
131
  class="appearance-none bg-text-50 min-w-[60px] text-text-400 px-2 outline-none"
@@ -128,34 +133,40 @@
128
133
  <option value="and">And</option>
129
134
  <option value="or">Or</option>
130
135
  </select>
131
- <button type="button" @click="filter.value.push({})">
136
+ <button v-if="typeOf(column(filter.key).type) !== 'enum' && filter._collapsed !== true" type="button" @click="filter.value.push({})">
132
137
  <svg width="14" height="14" class="fill-text-300" 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>
133
138
  </button>
134
139
  <button type="button" class="p-1" @click="preset.filters.splice(idx, 1); apply()">
135
140
  <svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
136
141
  </button>
137
142
  </div>
138
- <div v-if="Array.isArray(filter.value)" class="flex flex-col gap-2">
139
- <div class="flex flex-row" v-for="(filterVal, filterIdx) in filter.value">
140
- <PresetSelectorFilterItem
141
- class="flex-1"
142
- :type="typeOf(column(filter.key).type)"
143
- :typeParams="typeParamsOf(column(filter.key))"
144
- :value="filterVal"
145
- @change="apply()" />
146
-
147
- <button v-if="filter.value.length > 1"
148
- type="button"
149
- class="p-1"
150
- @click="filter.value.splice(filterIdx, 1); apply()">
151
- <svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
152
- </button>
143
+ <div v-if="filter._collapsed !== true">
144
+ <div v-if="Array.isArray(filter.value)" class="flex flex-col gap-2">
145
+ <div class="flex flex-row" v-for="(filterVal, filterIdx) in filter.value">
146
+ <PresetSelectorFilterItem
147
+ class="flex-1"
148
+ :column="column(filter.key)"
149
+ :type="typeOf(column(filter.key).type)"
150
+ :typeParams="typeParamsOf(column(filter.key))"
151
+ :value="filterVal"
152
+ :enumCache="enumCache"
153
+ @change="apply()" />
154
+
155
+ <button v-if="filter.value.length > 1"
156
+ type="button"
157
+ class="p-1"
158
+ @click="filter.value.splice(filterIdx, 1); apply()">
159
+ <svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
160
+ </button>
161
+ </div>
153
162
  </div>
163
+ <PresetSelectorFilterItem v-else
164
+ :column="column(filter.key)"
165
+ :type="typeOf(column(filter.key).type)"
166
+ :value="filter"
167
+ :enumCache="enumCache"
168
+ @change="apply()" />
154
169
  </div>
155
- <PresetSelectorFilterItem v-else
156
- :type="typeOf(column(filter.key).type)"
157
- :value="filter"
158
- @change="apply()" />
159
170
  </div>
160
171
  </div>
161
172
 
@@ -453,7 +464,7 @@
453
464
 
454
465
  <div>
455
466
  <div class="flex flex-row items-center gap-2">
456
- <label>Rows</label>
467
+ <label>Groups</label>
457
468
  <button type="button" @click="$refs.columnSelector.open(obj => addPivot('rows', obj))">
458
469
  <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>
459
470
  </button>
@@ -468,7 +479,8 @@
468
479
  <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>
469
480
  </div>
470
481
  <div class="flex-1 flex flex-row gap-3">
471
- <strong class="flex-1 cursor-pointer" @click="$refs.columnSelector.open(obj => Object.assign(item, obj))">
482
+ <strong class="flex-1 cursor-pointer text-ellipsis overflow-hidden whitespace-nowrap"
483
+ @click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() });">
472
484
  {{ item.label ? item.label : item.key }}
473
485
  </strong>
474
486
  <select v-if="item.type === 'date'"
@@ -490,7 +502,7 @@
490
502
 
491
503
  <div>
492
504
  <div class="flex flex-row items-center gap-2">
493
- <label>Values</label>
505
+ <label>Aggregrates</label>
494
506
  <button type="button" @click="$refs.columnSelector.open(obj => addPivot('values', obj))">
495
507
  <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>
496
508
  </button>
@@ -504,7 +516,8 @@
504
516
  <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>
505
517
  </div>
506
518
  <div class="flex-1 flex flex-row gap-3">
507
- <strong class="flex-1">
519
+ <strong class="flex-1 cursor-pointer text-ellipsis overflow-hidden whitespace-nowrap"
520
+ @click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() });">
508
521
  {{ item.label ? item.label : item.key }}
509
522
  </strong>
510
523
  <select v-model="item.aggregrate"
@@ -518,7 +531,7 @@
518
531
  <option v-if="[ 'number' ].includes(item.type)" value="max">Max</option>
519
532
  </select>
520
533
  </div>
521
- <button type="button" @click="presetPivot.values.splice(index, 1)">
534
+ <button type="button" @click="presetPivot.values.splice(index, 1); apply()">
522
535
  <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>
523
536
  </button>
524
537
  </div>
@@ -576,7 +589,9 @@ export default{
576
589
 
577
590
  props: {
578
591
 
579
- config: Object
592
+ config: Object,
593
+
594
+ enumCache: Object
580
595
 
581
596
  },
582
597
 
@@ -611,11 +626,12 @@ export default{
611
626
 
612
627
  if(this.preset){
613
628
  if(this.config.presetBarTabIndex === 2){
614
- if(Array.isArray(this.preset.filters)){
629
+ return true
630
+ /*if(Array.isArray(this.preset.filters)){
615
631
  if(this.preset.filters.filter(_ => !_.key).length > 0){
616
632
  return false
617
633
  }
618
- }
634
+ }*/
619
635
  }
620
636
  else if(this.config.presetBarTabIndex === 3){
621
637
  if(Array.isArray(this.preset.sorts)){
@@ -643,7 +659,7 @@ export default{
643
659
  },
644
660
 
645
661
  column(key){
646
- return this.config.columns.filter(_ => _.key === key).pop() ?? {}
662
+ return this.preset.columns.filter(_ => _.key === key).pop() ?? {}
647
663
  },
648
664
 
649
665
  duplicate(idx){
@@ -689,8 +705,9 @@ export default{
689
705
  },
690
706
 
691
707
  typeParamsOf(column){
692
- if([ 'enum' ].includes(column.type))
708
+ if([ 'enum' ].includes(column.type)){
693
709
  return column.typeParams
710
+ }
694
711
  return []
695
712
  },
696
713